Skip to content

Commit da49a87

Browse files
committed
[Feat] 리뷰 번역 API 연동 및 노출 조건 수정 [#530]
1 parent af0a1ac commit da49a87

14 files changed

Lines changed: 201 additions & 37 deletions

File tree

app/src/main/java/com/eatssu/android/data/remote/dto/response/ReviewTranslationResponse.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,14 @@ import kotlinx.serialization.Serializable
77
@Serializable
88
data class ReviewTranslationResponse(
99
@SerialName("reviewId") val reviewId: Long? = null,
10-
@SerialName("sourceLanguage") val sourceLanguage: String? = null,
11-
@SerialName("targetLanguage") val targetLanguage: String? = null,
10+
@SerialName("language") val language: String? = null,
1211
@SerialName("translatedContent") val translatedContent: String? = null,
13-
@SerialName("provider") val provider: String? = null,
1412
@SerialName("cached") val cached: Boolean? = null,
1513
)
1614

1715
fun ReviewTranslationResponse.toDomain() = ReviewTranslation(
1816
reviewId = reviewId ?: -1L,
19-
sourceLanguage = sourceLanguage.orEmpty(),
20-
targetLanguage = targetLanguage.orEmpty(),
17+
language = language.orEmpty(),
2118
translatedContent = translatedContent.orEmpty(),
22-
provider = provider.orEmpty(),
2319
cached = cached ?: false,
2420
)

app/src/main/java/com/eatssu/android/data/remote/service/ReviewService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ interface ReviewService {
8888
@GET("users/v2/reviews") // 내가 쓴 리뷰
8989
suspend fun getMyReviews(): ApiResult<MyReviewListResponse>
9090

91-
@GET("/v2/reviews/{reviewId}/translation")
91+
@POST("/v2/reviews/{reviewId}/translate")
9292
suspend fun getReviewTranslation(
9393
@Path("reviewId") reviewId: Long,
94-
@Query("targetLanguage") targetLanguage: String,
94+
@Query("language") language: String,
9595
): ApiResult<ReviewTranslationResponse>
9696

9797
}

app/src/main/java/com/eatssu/android/domain/model/ReviewTranslation.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package com.eatssu.android.domain.model
22

33
data class ReviewTranslation(
44
val reviewId: Long,
5-
val sourceLanguage: String,
6-
val targetLanguage: String,
5+
val language: String,
76
val translatedContent: String,
8-
val provider: String,
97
val cached: Boolean,
108
)

app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,11 @@ fun ReviewListScreen(
138138
onReviewWriteButtonClick = onWriteButtonClick,
139139
onModifyClick = onModifyClick,
140140
onDeleteClick = { reviewId -> viewModel.deleteReview(reviewId) },
141-
onTranslationClick = { review -> viewModel.toggleReviewTranslation(review, targetLanguage) },
141+
onTranslationClick = { review ->
142+
targetLanguage?.let { viewModel.toggleReviewTranslation(review, it) }
143+
},
142144
targetLanguage = targetLanguage,
145+
isLoggedIn = viewModel.isLoggedIn,
143146
)
144147
}
145148

@@ -156,7 +159,8 @@ internal fun ReviewListScreen(
156159
onModifyClick: (Review) -> Unit,
157160
onDeleteClick: (reviewId: Long) -> Unit,
158161
onTranslationClick: (Review) -> Unit,
159-
targetLanguage: String,
162+
targetLanguage: String?,
163+
isLoggedIn: Boolean,
160164
) {
161165
val context = LocalContext.current
162166
val analyticsTracker = LocalAnalyticsTracker.current
@@ -379,7 +383,11 @@ internal fun ReviewListScreen(
379383
translatedContent = translationState?.translatedContent,
380384
isTranslationVisible = translationState?.isTranslated == true,
381385
isTranslationLoading = translationState?.isLoading == true,
382-
showTranslationAction = shouldShowReviewTranslationAction(targetLanguage),
386+
showTranslationAction = shouldShowReviewTranslationAction(
387+
targetLanguage = targetLanguage,
388+
isLoggedIn = isLoggedIn,
389+
content = it.content,
390+
) && translationState?.isUnavailable != true,
383391
onTranslationClick = { onTranslationClick(it) },
384392
onMoreClick = {
385393
if (it.isWriter) {
@@ -655,6 +663,7 @@ fun ReviewListPreview() {
655663
onDeleteClick = {},
656664
onTranslationClick = {},
657665
targetLanguage = "EN",
666+
isLoggedIn = true,
658667
translationStates = emptyMap(),
659668
uiState = UiState.Success(
660669
ReviewListState(
@@ -694,6 +703,7 @@ fun ReviewListLoadingPreview() {
694703
onDeleteClick = {},
695704
onTranslationClick = {},
696705
targetLanguage = "EN",
706+
isLoggedIn = true,
697707
translationStates = emptyMap(),
698708
uiState = UiState.Success(
699709
ReviewListState(
@@ -733,6 +743,7 @@ fun ReviewListEmptyPreview() {
733743
onDeleteClick = {},
734744
onTranslationClick = {},
735745
targetLanguage = "EN",
746+
isLoggedIn = true,
736747
translationStates = emptyMap(),
737748
uiState = UiState.Success(
738749
ReviewListState(
@@ -772,6 +783,7 @@ fun ReviewListErrorPreview() {
772783
onDeleteClick = {},
773784
onTranslationClick = {},
774785
targetLanguage = "EN",
786+
isLoggedIn = true,
775787
translationStates = emptyMap(),
776788
uiState = UiState.Error,
777789
reviewPagingItems = rememberPreviewPagingItems(pagingData),

app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.paging.cachedIn
77
import com.eatssu.android.R
88
import com.eatssu.android.domain.model.Review
99
import com.eatssu.android.domain.model.ReviewInfo
10+
import com.eatssu.android.domain.usecase.auth.GetAccessTokenUseCase
1011
import com.eatssu.android.domain.usecase.review.DeleteReviewUseCase
1112
import com.eatssu.android.domain.usecase.review.GetReviewInfoUseCase
1213
import com.eatssu.android.domain.usecase.review.GetReviewListPagedUseCase
@@ -36,8 +37,12 @@ class ReviewListViewModel @Inject constructor(
3637
private val getReviewListPagedUseCase: GetReviewListPagedUseCase,
3738
private val deleteReviewUseCase: DeleteReviewUseCase,
3839
private val getReviewTranslationUseCase: GetReviewTranslationUseCase,
40+
private val getAccessTokenUseCase: GetAccessTokenUseCase,
3941
) : ViewModel() {
4042

43+
val isLoggedIn: Boolean
44+
get() = getAccessTokenUseCase().isNotBlank()
45+
4146
private val _uiState = MutableStateFlow<UiState<ReviewListState>>(UiState.Init)
4247
val uiState: StateFlow<UiState<ReviewListState>> = _uiState.asStateFlow()
4348

@@ -131,16 +136,19 @@ class ReviewListViewModel @Inject constructor(
131136

132137
val translation = getReviewTranslationUseCase(review.reviewId, targetLanguage)
133138
if (translation == null || translation.translatedContent.isBlank()) {
134-
_translationStates.value = _translationStates.value - review.reviewId
135-
_uiEvent.emit(
136-
UiEvent.ShowToast(
137-
UiText.StringResource(R.string.toast_review_translation_failed),
138-
ToastType.ERROR
139-
)
139+
_translationStates.value = _translationStates.value + (
140+
review.reviewId to ReviewTranslationUiState(isUnavailable = true)
140141
)
141142
return@launch
142143
}
143144

145+
if (translation.translatedContent.trim().equals(review.content.trim(), ignoreCase = true)) {
146+
_translationStates.value = _translationStates.value + (
147+
review.reviewId to ReviewTranslationUiState(isUnavailable = true)
148+
)
149+
return@launch
150+
}
151+
144152
_translationStates.value = _translationStates.value + (
145153
review.reviewId to ReviewTranslationUiState(
146154
translatedContent = translation.translatedContent,
Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
package com.eatssu.android.presentation.cafeteria.review.translation
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.ui.platform.LocalConfiguration
45

56
@Composable
6-
fun currentReviewTranslationTargetLanguage(): String = "EN"
7+
fun currentReviewTranslationTargetLanguage(): String? =
8+
reviewTranslationTargetLanguage(LocalConfiguration.current.locales[0].language)
79

8-
fun shouldShowReviewTranslationAction(targetLanguage: String): Boolean = targetLanguage == "EN"
10+
fun reviewTranslationTargetLanguage(appLanguageCode: String): String? =
11+
ENGLISH_TARGET_LANGUAGE.takeIf { appLanguageCode == ENGLISH_APP_LANGUAGE_CODE }
12+
13+
fun shouldShowReviewTranslationAction(
14+
targetLanguage: String?,
15+
isLoggedIn: Boolean,
16+
content: String,
17+
): Boolean = isLoggedIn &&
18+
targetLanguage == ENGLISH_TARGET_LANGUAGE &&
19+
content.any { it.isLetter() && it.code > ASCII_MAX_CODE }
20+
21+
private const val ASCII_MAX_CODE = 0x7F
22+
private const val ENGLISH_APP_LANGUAGE_CODE = "en"
23+
private const val ENGLISH_TARGET_LANGUAGE = "EN"

app/src/main/java/com/eatssu/android/presentation/cafeteria/review/translation/ReviewTranslationUiState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ data class ReviewTranslationUiState(
44
val translatedContent: String? = null,
55
val isLoading: Boolean = false,
66
val isTranslated: Boolean = false,
7+
val isUnavailable: Boolean = false,
78
)

app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListScreen.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ fun MyReviewListScreen(
8181
onBack = onBack,
8282
onDeleteClick = { reviewId -> viewModel.deleteReview(reviewId) },
8383
onModifyClick = onModifyClick,
84-
onTranslationClick = { review -> viewModel.toggleReviewTranslation(review, targetLanguage) },
84+
onTranslationClick = { review ->
85+
targetLanguage?.let { viewModel.toggleReviewTranslation(review, it) }
86+
},
8587
targetLanguage = targetLanguage,
88+
isLoggedIn = viewModel.isLoggedIn,
8689
)
8790
}
8891

@@ -96,7 +99,8 @@ internal fun MyReviewListScreen(
9699
onModifyClick: (Review) -> Unit,
97100
onDeleteClick: (reviewId: Long) -> Unit,
98101
onTranslationClick: (Review) -> Unit,
99-
targetLanguage: String,
102+
targetLanguage: String?,
103+
isLoggedIn: Boolean,
100104
) {
101105
var showBottomSheet by remember { mutableStateOf(false) }
102106
var selectedReview by remember { mutableStateOf<Review?>(null) }
@@ -157,7 +161,11 @@ internal fun MyReviewListScreen(
157161
translatedContent = translationState?.translatedContent,
158162
isTranslationVisible = translationState?.isTranslated == true,
159163
isTranslationLoading = translationState?.isLoading == true,
160-
showTranslationAction = shouldShowReviewTranslationAction(targetLanguage),
164+
showTranslationAction = shouldShowReviewTranslationAction(
165+
targetLanguage = targetLanguage,
166+
isLoggedIn = isLoggedIn,
167+
content = item.content,
168+
) && translationState?.isUnavailable != true,
161169
onTranslationClick = { onTranslationClick(item) },
162170
onMoreClick = {
163171
selectedReview = item
@@ -239,6 +247,7 @@ fun ReviewListPreview() {
239247
onModifyClick = {},
240248
onTranslationClick = {},
241249
targetLanguage = "EN",
250+
isLoggedIn = true,
242251
translationStates = emptyMap(),
243252
uiState = UiState.Success(
244253
MyReviewState.ReviewExists(
@@ -340,6 +349,7 @@ fun ReviewListEmptyPreview() {
340349
onModifyClick = {},
341350
onTranslationClick = {},
342351
targetLanguage = "EN",
352+
isLoggedIn = true,
343353
translationStates = emptyMap(),
344354
uiState = UiState.Success(
345355
MyReviewState.NoReview

app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewViewModel.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.eatssu.android.R
66
import com.eatssu.android.domain.model.Review
7+
import com.eatssu.android.domain.usecase.auth.GetAccessTokenUseCase
78
import com.eatssu.android.domain.usecase.review.DeleteReviewUseCase
89
import com.eatssu.android.domain.usecase.review.GetMyReviewsUseCase
910
import com.eatssu.android.domain.usecase.review.GetReviewTranslationUseCase
@@ -28,8 +29,12 @@ class MyReviewViewModel @Inject constructor(
2829
private val getUserNickNameUseCase: GetUserNickNameUseCase,
2930
private val deleteReviewUseCase: DeleteReviewUseCase,
3031
private val getReviewTranslationUseCase: GetReviewTranslationUseCase,
32+
private val getAccessTokenUseCase: GetAccessTokenUseCase,
3133
) : ViewModel() {
3234

35+
val isLoggedIn: Boolean
36+
get() = getAccessTokenUseCase().isNotBlank()
37+
3338
private val _uiState: MutableStateFlow<UiState<MyReviewState>> = MutableStateFlow(UiState.Init)
3439
val uiState: StateFlow<UiState<MyReviewState>> = _uiState.asStateFlow()
3540

@@ -114,16 +119,19 @@ class MyReviewViewModel @Inject constructor(
114119

115120
val translation = getReviewTranslationUseCase(review.reviewId, targetLanguage)
116121
if (translation == null || translation.translatedContent.isBlank()) {
117-
_translationStates.value = _translationStates.value - review.reviewId
118-
_uiEvent.emit(
119-
UiEvent.ShowToast(
120-
UiText.StringResource(R.string.toast_review_translation_failed),
121-
ToastType.ERROR
122-
)
122+
_translationStates.value = _translationStates.value + (
123+
review.reviewId to ReviewTranslationUiState(isUnavailable = true)
123124
)
124125
return@launch
125126
}
126127

128+
if (translation.translatedContent.trim().equals(review.content.trim(), ignoreCase = true)) {
129+
_translationStates.value = _translationStates.value + (
130+
review.reviewId to ReviewTranslationUiState(isUnavailable = true)
131+
)
132+
return@launch
133+
}
134+
127135
_translationStates.value = _translationStates.value + (
128136
review.reviewId to ReviewTranslationUiState(
129137
translatedContent = translation.translatedContent,

app/src/test/java/com/eatssu/android/data/remote/repository/ReviewRepositoryImplBehaviorSpec.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,8 @@ class ReviewRepositoryImplBehaviorSpec : AppBehaviorSpec({
392392
coEvery { service.getReviewTranslation(1L, "EN") } returns ApiResult.Success(
393393
ReviewTranslationResponse(
394394
reviewId = 1L,
395-
sourceLanguage = "KO",
396-
targetLanguage = "EN",
395+
language = "EN",
397396
translatedContent = "It was delicious.",
398-
provider = "DEEPL",
399397
cached = true,
400398
)
401399
)

0 commit comments

Comments
 (0)