Skip to content

Commit 708c53f

Browse files
authored
[FIX/#1557] 콕지르기 QA 사항 반영 (#1559)
* [FIX/#1557] OnboardingPokeUserViewModel 내 비어있는 유저 리스트 처리 로직 수정 - `getOnboardingPokeUserListUseCase` 호출 결과가 비어있을 경우를 대비하여 `firstOrNull` 및 기본값 처리 로직 추가 - 유저 리스트가 없을 때 발생할 수 있는 인덱스 참조 오류 방지 및 빈 상태의 `UiState.Success` 반환하도록 개선 * [FEAT/#1557] PokeNotificationScreen 내 뒤로가기 동작 추가 - `PokeNotificationScreen`에 `navigateUp` 콜백 파라미터 추가 및 네비게이션 그래프 연결 - 앱바 툴바에 `setOnSingleClickListener`를 통한 뒤로가기 로직 적용 - `PokeNotificationScreen` 내 불필요한 공백 및 코드 포맷팅 수정 * [REFACTOR/#1557] 익명 프로필 이미지 고정 리소스 적용 및 UI 로직 수정 - 익명 사용자의 프로필 이미지를 서버 전달 값 대신 로컬 리소스(`image_anonymous_profile`)를 사용하도록 변경 - `PokeUserViewHolder`, `PokeNotificationAdapter`, `PokeScreen` 등에서 익명 여부에 따른 이름 표시 및 정보 텍스트 가시성 로직 수정 - 익명 유저의 경우 유저 정보(기수, 파트)를 숨기도록 처리함에 따라 UI 일관성 확보 * [FIX] 콕 찌르기 후 UI 업데이트 로직 수정 및 바텀시트 처리 개선 - `PokeMainViewModel`: `pokeSimilarFriendUiState` 업데이트 시 기존 데이터를 직접 수정하던 방식의 문제점, ui 업데이트가 반영되지 않아 새로운 객체 생성 방식으로 수정 - `PokeScreen`: 콕 찌르기 API 응답 결과(성공, 에러, 실패)에 따라 메시지 바텀시트가 닫히도록 로직 추가 - `PokeScreen`: 메시지 바텀시트 호출 시 고정된 태그(`MessageListBottomSheet`)를 사용하도록 변경하여 관리 효율화 - `PokeScreen`: `UiState` 분기 처리 코드의 가독성 개선을 위한 줄바꿈 및 포맷팅 정리 * [FIX/#1557] Poke UI 상태 초기화 및 BottomSheet 처리 로직 최적화 - `PokeMainViewModel` 내 UI 상태 초기화를 위한 `resetPokeUserUiState` 추가 - `PokeScreen`에서 중복된 BottomSheet 해제 로직을 통합하고, 처리 완료 후 UI 상태를 초기화하도록 변경 * [REFACTOR/#1557] Poke 관련 ApiError 발생 시 노출되는 토스트 메시지 리소스 변경 - `UiState.ApiError` 발생 시 `poke_user_alert_exceeded` 대신 `toast_poke_error` 메시지를 사용하도록 수정 - `FriendListSummaryActivity`, `PokeNotificationScreen`, `PokeMainActivity` 등 Poke 관련 주요 화면에 적용 * [REFACTOR/#1557] PokeMainViewModel 내 콕 찌르기 상태 업데이트 로직 수정 - `userId`가 일치하는 사용자의 `isAlreadyPoke` 상태만 `true`로 변경하도록 조건부 로직 최적화 * [FIX/#1557] 콕 찌르기 메인 내 친구 목록 노출 및 예외 처리 로직 수정 - 친구 목록이 비어있는 경우(`EMPTY_POKE_FRIEND`)에 대한 대응 로직 추가 - `UiState` 결과에 따라 `layout_poke_my_friend` 영역의 가시성(visible/false) 제어 추가 - `PokeMainViewModel`에서 빈 리스트 수신 시 에러 상태(`ApiError`)로 방출하도록 변경 - 친구 목록 비어있음 상태일 때는 에러 토스트를 노출하지 않도록 방어 로직 적용
1 parent 911c620 commit 708c53f

14 files changed

Lines changed: 109 additions & 37 deletions

feature/poke/src/main/java/org/sopt/official/feature/poke/friend/detail/FriendListDetailBottomSheetFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class FriendListDetailBottomSheetFragment : BottomSheetDialogFragment() {
319319

320320
is UiState.ApiError -> {
321321
messageListBottomSheet?.dismiss()
322-
showPokeToast(getString(R.string.poke_user_alert_exceeded))
322+
showPokeToast(getString(R.string.toast_poke_error))
323323
}
324324

325325
is UiState.Failure -> {

feature/poke/src/main/java/org/sopt/official/feature/poke/friend/summary/FriendListSummaryActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ class FriendListSummaryActivity : AppCompatActivity() {
355355

356356
is UiState.ApiError -> {
357357
messageListBottomSheet?.dismiss()
358-
showPokeToast(getString(R.string.poke_user_alert_exceeded))
358+
showPokeToast(getString(R.string.toast_poke_error))
359359
}
360360

361361
is UiState.Failure -> {

feature/poke/src/main/java/org/sopt/official/feature/poke/friend/summary/FriendListSummaryScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ fun FriendListSummaryScreen(
224224

225225
is UiState.ApiError -> {
226226
dismissBottomSheet(fragmentManager, bottomSheetTag)
227-
fragmentActivity?.showPokeToast(context.getString(R.string.poke_user_alert_exceeded))
227+
fragmentActivity?.showPokeToast(context.getString(R.string.toast_poke_error))
228228
}
229229

230230
is UiState.Failure -> {

feature/poke/src/main/java/org/sopt/official/feature/poke/main/PokeMainActivity.kt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,22 @@ class PokeMainActivity : AppCompatActivity() {
193193
.onEach {
194194
when (it) {
195195
is UiState.Loading -> {}
196-
is UiState.Success<PokeUser> -> initPokeFriendView(it.data)
197-
is UiState.ApiError -> showPokeToast(getString(R.string.toast_poke_error))
198-
is UiState.Failure -> showPokeToast(it.throwable.message ?: getString(R.string.toast_poke_error))
196+
is UiState.Success<PokeUser> -> {
197+
binding.layoutPokeMyFriend.setVisible(true)
198+
initPokeFriendView(it.data)
199+
}
200+
201+
is UiState.ApiError -> {
202+
binding.layoutPokeMyFriend.setVisible(false)
203+
if (it.statusCode != POKE_FRIEND_EMPTY_STATUS_CODE) {
204+
showPokeToast(getString(R.string.toast_poke_error))
205+
}
206+
}
207+
208+
is UiState.Failure -> {
209+
binding.layoutPokeMyFriend.setVisible(false)
210+
showPokeToast(it.throwable.message ?: getString(R.string.toast_poke_error))
211+
}
199212
}
200213
}
201214
.launchIn(lifecycleScope)
@@ -261,7 +274,7 @@ class PokeMainActivity : AppCompatActivity() {
261274

262275
is UiState.ApiError -> {
263276
messageListBottomSheet?.dismiss()
264-
showPokeToast(getString(R.string.poke_user_alert_exceeded))
277+
showPokeToast(getString(R.string.toast_poke_error))
265278
}
266279

267280
is UiState.Failure -> {

feature/poke/src/main/java/org/sopt/official/feature/poke/main/PokeMainViewModel.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import org.sopt.official.feature.poke.UiState
4343
import timber.log.Timber
4444
import javax.inject.Inject
4545

46+
internal const val POKE_FRIEND_EMPTY_STATUS_CODE = "EMPTY_POKE_FRIEND"
47+
4648
@HiltViewModel
4749
class PokeMainViewModel @Inject constructor(
4850
private val getPokeMeUseCase: GetPokeMeUseCase,
@@ -87,8 +89,10 @@ class PokeMainViewModel @Inject constructor(
8789
viewModelScope.launch {
8890
_pokeFriendUiState.emit(UiState.Loading)
8991
getPokeFriendUseCase.invoke()
90-
.onSuccess {
91-
_pokeFriendUiState.emit(UiState.Success(it[0]))
92+
.onSuccess { friendList ->
93+
friendList.firstOrNull()?.let {
94+
_pokeFriendUiState.emit(UiState.Success(it))
95+
} ?: _pokeFriendUiState.emit(UiState.ApiError(POKE_FRIEND_EMPTY_STATUS_CODE, ""))
9296
}
9397
.onApiError { statusCode, responseMessage ->
9498
_pokeFriendUiState.emit(UiState.ApiError(statusCode, responseMessage))
@@ -163,15 +167,24 @@ class PokeMainViewModel @Inject constructor(
163167
}
164168
if (_pokeSimilarFriendUiState.value is UiState.Success<List<PokeRandomUserList.PokeRandomUsers>>) {
165169
val oldData = (_pokeSimilarFriendUiState.value as UiState.Success<List<PokeRandomUserList.PokeRandomUsers>>).data
166-
for (friend in oldData) {
167-
friend.userInfoList.find { it.userId == userId }?.isAlreadyPoke = true
170+
val updatedSimilarFriends = oldData.map { randomUsers ->
171+
randomUsers.copy(
172+
userInfoList = randomUsers.userInfoList.map { pokeUser ->
173+
if (pokeUser.userId == userId) pokeUser.copy(isAlreadyPoke = true)
174+
else pokeUser
175+
}
176+
)
168177
}
169178
_pokeSimilarFriendUiState.emit(UiState.Loading)
170-
_pokeSimilarFriendUiState.emit(UiState.Success(oldData))
179+
_pokeSimilarFriendUiState.emit(UiState.Success(updatedSimilarFriends))
171180
}
172181
}
173182
}
174183

184+
fun resetPokeUserUiState() {
185+
_pokeUserUiState.value = UiState.Loading
186+
}
187+
175188
fun setAnonymousFriend(pokeUser: PokeUser?) {
176189
_anonymousFriend.value = pokeUser
177190
}

feature/poke/src/main/java/org/sopt/official/feature/poke/main/PokeScreen.kt

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,15 @@ import org.sopt.official.feature.poke.friend.summary.FriendListSummaryActivity
6363
import org.sopt.official.feature.poke.message.MessageListBottomSheetFragment
6464
import org.sopt.official.feature.poke.user.PokeUserListClickListener
6565
import org.sopt.official.feature.poke.util.addOnAnimationEndListener
66+
import org.sopt.official.feature.poke.util.dismissBottomSheet
6667
import org.sopt.official.feature.poke.util.isBestFriend
6768
import org.sopt.official.feature.poke.util.isSoulMate
6869
import org.sopt.official.feature.poke.util.setRelationStrokeColor
6970
import org.sopt.official.feature.poke.util.showPokeToast
7071
import org.sopt.official.model.UserStatus
7172

73+
private const val MESSAGE_LIST_BOTTOM_SHEET_TAG = "MessageListBottomSheet"
74+
7275
@Composable
7376
fun PokeScreen(
7477
paddingValues: PaddingValues,
@@ -88,6 +91,8 @@ fun PokeScreen(
8891
context.findActivity<FragmentActivity>()
8992
}
9093

94+
val fragmentManager = fragmentActivity?.supportFragmentManager
95+
9196
val pokeMeUiState by viewModel.pokeMeUiState.collectAsStateWithLifecycle()
9297
val pokeFriendUiState by viewModel.pokeFriendUiState.collectAsStateWithLifecycle()
9398
val pokeSimilarFriendUiState by viewModel.pokeSimilarFriendUiState.collectAsStateWithLifecycle()
@@ -151,10 +156,12 @@ fun PokeScreen(
151156
pokeMainListAdapter.submitList(state.data)
152157
currentBinding.refreshLayoutPokeMain.isRefreshing = false
153158
}
159+
154160
is UiState.ApiError, is UiState.Failure -> {
155161
activity?.showPokeToast(context.getString(R.string.toast_poke_error))
156162
currentBinding.refreshLayoutPokeMain.isRefreshing = false
157163
}
164+
158165
is UiState.Loading -> {}
159166
}
160167
}
@@ -168,9 +175,11 @@ fun PokeScreen(
168175
initPokeMeView(currentBinding, state.data, tracker, userStatus, activity, viewModel)
169176
}
170177
}
178+
171179
is UiState.ApiError, is UiState.Failure -> {
172180
currentBinding.layoutSomeonePokeMe.setVisible(false)
173181
}
182+
174183
else -> {}
175184
}
176185
}
@@ -180,12 +189,23 @@ fun PokeScreen(
180189
binding?.let { currentBinding ->
181190
when (val state = pokeFriendUiState) {
182191
is UiState.Success -> {
192+
currentBinding.layoutPokeMyFriend.setVisible(true)
183193
if (activity is FragmentActivity) {
184194
initPokeFriendView(currentBinding, state.data, tracker, userStatus, activity, viewModel)
185195
}
186196
}
187-
is UiState.ApiError -> activity?.showPokeToast(context.getString(R.string.toast_poke_error))
188-
is UiState.Failure -> activity?.showPokeToast(state.throwable.message ?: context.getString(R.string.toast_poke_error))
197+
198+
is UiState.ApiError -> {
199+
currentBinding.layoutPokeMyFriend.setVisible(false)
200+
if (state.statusCode != POKE_FRIEND_EMPTY_STATUS_CODE) {
201+
activity?.showPokeToast(context.getString(R.string.toast_poke_error))
202+
}
203+
}
204+
205+
is UiState.Failure -> {
206+
currentBinding.layoutPokeMyFriend.setVisible(false)
207+
activity?.showPokeToast(state.throwable.message ?: context.getString(R.string.toast_poke_error))
208+
}
189209
else -> {}
190210
}
191211
}
@@ -231,10 +251,19 @@ fun PokeScreen(
231251
}
232252
}
233253
}
234-
is UiState.ApiError -> activity?.showPokeToast(context.getString(R.string.poke_user_alert_exceeded))
235-
is UiState.Failure -> activity?.showPokeToast(state.throwable.message ?: context.getString(R.string.toast_poke_error))
254+
255+
is UiState.ApiError -> {
256+
activity?.showPokeToast(context.getString(R.string.toast_poke_error))
257+
}
258+
259+
is UiState.Failure -> {
260+
activity?.showPokeToast(state.throwable.message ?: context.getString(R.string.toast_poke_error))
261+
}
262+
236263
else -> {}
237264
}
265+
dismissBottomSheet(fragmentManager, MESSAGE_LIST_BOTTOM_SHEET_TAG)
266+
viewModel.resetPokeUserUiState()
238267
}
239268
}
240269

@@ -335,9 +364,7 @@ private fun initPokeMeView(
335364
}
336365

337366
if (pokeMeItem.isAnonymous) {
338-
pokeMeItem.anonymousImage.takeIf { it.isNotEmpty() }?.let {
339-
imgUserProfileSomeonePokeMe.load(it) { transformations(CircleCropTransformation()) }
340-
} ?: imgUserProfileSomeonePokeMe.setImageResource(R.drawable.ic_empty_profile)
367+
imgUserProfileSomeonePokeMe.load(R.drawable.image_anonymous_profile) { transformations(CircleCropTransformation()) }
341368
tvUserNameSomeonePokeMe.text = pokeMeItem.anonymousName
342369
tvFriendsStatusSomeonePokeMe.visibility = View.GONE
343370
tvUserGenerationSomeonePokeMe.visibility = View.GONE
@@ -414,7 +441,7 @@ private fun initPokeFriendView(
414441
}
415442

416443
if (pokeFriendItem.isAnonymous) {
417-
imgUserProfilePokeMyFriend.load(pokeFriendItem.anonymousImage) { transformations(CircleCropTransformation()) }
444+
imgUserProfilePokeMyFriend.load(R.drawable.image_anonymous_profile) { transformations(CircleCropTransformation()) }
418445
tvUserNamePokeMyFriend.text = pokeFriendItem.anonymousName
419446
} else {
420447
pokeFriendItem.profileImage.takeIf { it.isNotEmpty() }?.let {
@@ -447,7 +474,7 @@ private fun showMessageListBottomSheet(
447474
viewModel: PokeMainViewModel,
448475
userId: Int,
449476
pokeMessageType: PokeMessageType,
450-
isFirstMeet: Boolean = false
477+
isFirstMeet: Boolean = false,
451478
) {
452479
val messageListBottomSheet =
453480
MessageListBottomSheetFragment.Builder()
@@ -462,5 +489,5 @@ private fun showMessageListBottomSheet(
462489
}
463490
.create()
464491

465-
messageListBottomSheet.show(activity.supportFragmentManager, messageListBottomSheet.tag)
492+
messageListBottomSheet.show(activity.supportFragmentManager, MESSAGE_LIST_BOTTOM_SHEET_TAG)
466493
}

feature/poke/src/main/java/org/sopt/official/feature/poke/navigation/PokeNavGraph.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ fun NavGraphBuilder.pokeNavGraph(
8787
PokeNotificationScreen(
8888
paddingValues = paddingValues,
8989
userStatus = userStatus,
90+
navigateUp = navController::navigateUp,
9091
)
9192
}
9293

feature/poke/src/main/java/org/sopt/official/feature/poke/notification/PokeNotificationActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ class PokeNotificationActivity : AppCompatActivity() {
261261

262262
is UiState.ApiError -> {
263263
messageListBottomSheet?.dismiss()
264-
showPokeToast(getString(R.string.poke_user_alert_exceeded))
264+
showPokeToast(getString(R.string.toast_poke_error))
265265
}
266266

267267
is UiState.Failure -> {

feature/poke/src/main/java/org/sopt/official/feature/poke/notification/PokeNotificationAdapter.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ class PokeNotificationAdapter(
8888
fun onBind(item: PokeUser) {
8989
with(viewBinding) {
9090
if (item.isAnonymous) {
91-
item.anonymousImage.takeIf { it.isNotEmpty() }?.let {
92-
imgUserProfile.load(it) { transformations(CircleCropTransformation()) }
93-
} ?: imgUserProfile.setImageResource(R.drawable.ic_empty_profile)
91+
imgUserProfile.load(R.drawable.image_anonymous_profile) { transformations(CircleCropTransformation()) }
9492
tvUserName.text = item.anonymousName
9593
tvUserGeneration.visibility = View.GONE
9694
tvUserFriendsStatus.visibility = View.GONE

feature/poke/src/main/java/org/sopt/official/feature/poke/notification/PokeNotificationScreen.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import kotlinx.coroutines.launch
5454
import org.sopt.official.analytics.EventType
5555
import org.sopt.official.analytics.compose.LocalTracker
5656
import org.sopt.official.common.context.findActivity
57+
import org.sopt.official.common.util.setOnSingleClickListener
5758
import org.sopt.official.domain.poke.entity.PokeUser
5859
import org.sopt.official.domain.poke.type.PokeMessageType
5960
import org.sopt.official.feature.poke.R
@@ -74,6 +75,7 @@ import org.sopt.official.model.UserStatus
7475
fun PokeNotificationScreen(
7576
paddingValues: PaddingValues,
7677
userStatus: UserStatus,
78+
navigateUp: () -> Unit,
7779
viewModel: PokeNotificationViewModel = hiltViewModel()
7880
) {
7981
val context = LocalContext.current
@@ -189,14 +191,17 @@ fun PokeNotificationScreen(
189191
if (!currentBinding.animationFriendViewLottie.isAnimating) {
190192
if (isBestFriend) {
191193
currentBinding.layoutAnonymousFriendLottie.visibility = View.VISIBLE
192-
currentBinding.tvFreindLottie.text = context.getString(R.string.anonymous_to_friend, user.anonymousName, "단짝친구가")
193-
currentBinding.tvFreindLottieHint.text = context.getString(R.string.anonymous_user_info_part, user.generation, user.part)
194+
currentBinding.tvFreindLottie.text =
195+
context.getString(R.string.anonymous_to_friend, user.anonymousName, "단짝친구가")
196+
currentBinding.tvFreindLottieHint.text =
197+
context.getString(R.string.anonymous_user_info_part, user.generation, user.part)
194198
currentBinding.animationFriendViewLottie.setAnimation(R.raw.friendtobestfriend)
195199
currentBinding.animationFriendViewLottie.playAnimation()
196200
} else if (isSoulMate) {
197201
viewModel.setAnonymousFriend(user)
198202
currentBinding.layoutAnonymousFriendLottie.visibility = View.VISIBLE
199-
currentBinding.tvFreindLottie.text = context.getString(R.string.anonymous_to_friend, user.anonymousName, "천생연분이")
203+
currentBinding.tvFreindLottie.text =
204+
context.getString(R.string.anonymous_to_friend, user.anonymousName, "천생연분이")
200205
currentBinding.animationFriendViewLottie.setAnimation(R.raw.bestfriendtosoulmate)
201206
currentBinding.animationFriendViewLottie.playAnimation()
202207
}
@@ -207,7 +212,7 @@ fun PokeNotificationScreen(
207212

208213
is UiState.ApiError -> {
209214
dismissBottomSheet(fragmentManager, bottomSheetTag)
210-
fragmentActivity?.showPokeToast(context.getString(R.string.poke_user_alert_exceeded))
215+
fragmentActivity?.showPokeToast(context.getString(R.string.toast_poke_error))
211216
}
212217

213218
is UiState.Failure -> {
@@ -225,7 +230,7 @@ fun PokeNotificationScreen(
225230
properties = mapOf("view_type" to userStatus.name),
226231
)
227232

228-
onPauseOrDispose { }
233+
onPauseOrDispose { }
229234
}
230235

231236
AndroidViewBinding(
@@ -237,6 +242,7 @@ fun PokeNotificationScreen(
237242
binding = this
238243
if (recyclerviewPokeNotification.adapter == null) {
239244
includeAppBar.textViewTitle.text = root.context.getString(R.string.poke_title)
245+
includeAppBar.toolbar.setOnSingleClickListener { navigateUp() }
240246

241247
recyclerviewPokeNotification.adapter = adapter
242248

0 commit comments

Comments
 (0)