Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds AI summary functionality to display user profile summaries in Korean and Japanese, expands job options in the required info form, and implements gender-based control for matching selection. The changes include API integration, UI updates, and data persistence improvements.
Key changes:
- AI summary API integration with bilingual (Korean/Japanese) display in the waiting screen
- Expanded job options from 5 to 24 choices in the required info form
- Gender storage in DataStore and gender-based matching selection controls
- Removed toast notifications when matching queries fail
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| AiSummaryResponse.kt | New data model for AI summary API response with Korean and Japanese text fields |
| MatchingService.kt | Added AI summary endpoint and renamed matching selection method to gender-specific name |
| DataStoreManager.kt | Added gender storage and retrieval functionality |
| RequiredInfoMapper.kt | Expanded job mapping from 5 to 24 job types including healthcare, education, and public service roles |
| Step5Content.kt | Updated UI job options list to match expanded mapper entries |
| RequiredInfoViewModel.kt | Added gender persistence when submitting required info form |
| HomeViewModel.kt | Added AI summary fetching logic and refactored state updates to use update pattern |
| HomeScreen.kt | Added AI summary display in waiting screen with loading and error states |
| MainRouteScreen.kt | Added gender-based control flow for enabling/disabling matching selection |
| Localization.kt | Added Japanese translations for AI summary UI text |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true | ||
| ) | ||
| } | ||
| } else { | ||
| _uiState.value = HomeUiState(matchings = data) | ||
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = data, | ||
| selectedMatching = null, | ||
| isWaiting = false | ||
| ) | ||
| } |
There was a problem hiding this comment.
The state updates in fetchFemaleMatchings don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When matchings data is updated, these fields will be reset to their default null/false values because the copy operation only specifies isLoading, matchings, selectedMatching, and isWaiting. This will cause the AI summary to disappear if matchings are refreshed after the AI summary has loaded.
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true | ||
| ) | ||
| } |
There was a problem hiding this comment.
The state updates don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When matchings fail to load, these fields will be reset to their default values, causing the AI summary to disappear.
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true |
There was a problem hiding this comment.
The state updates don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When a matching is selected, these fields will be reset to their default values, causing the AI summary to disappear from the waiting screen if the user returns to it.
| isWaiting = true | |
| isWaiting = true, | |
| aiSummaryKo = it.aiSummaryKo, | |
| aiSummaryJa = it.aiSummaryJa, | |
| isAiSummaryLoading = it.isAiSummaryLoading, | |
| aiSummaryError = it.aiSummaryError |
|
|
||
| @GET("members/status") | ||
| suspend fun getMyStatus(): MyStatusResponse | ||
| fun femaleSelectMatching(@Path("matchingId") matchingId: Long): Call<Void> |
There was a problem hiding this comment.
Function name 'femaleSelectMatching' uses gender-specific naming which may not scale well if male users need similar functionality in the future. Consider a more generic name like 'selectMatching' or 'confirmMatching' that doesn't encode gender into the API method name.
| fun femaleSelectMatching(@Path("matchingId") matchingId: Long): Call<Void> | |
| fun selectMatching(@Path("matchingId") matchingId: Long): Call<Void> |
| fun job(koreanLabel: String): String? = when (koreanLabel) { | ||
| "의사" -> "DOCTOR" | ||
| "약사" -> "PHARMACIST" | ||
| "간호사" -> "NURSE" | ||
| "교사" -> "TEACHER" | ||
| "교수" -> "PROFESSOR" | ||
| "변호사" -> "LAWYER" | ||
| "회계사" -> "ACCOUNTANT" | ||
| "엔지니어" -> "ENGINEER" | ||
| "개발자" -> "DEVELOPER" | ||
| "디자이너" -> "DESIGNER" | ||
| "공무원" -> "CIVIL_SERVANT" | ||
| "경찰" -> "POLICE" | ||
| "소방관" -> "FIREFIGHTER" | ||
| "군인" -> "MILITARY" | ||
| "사업가" -> "ENTREPRENEUR" | ||
| "경영인" -> "CEO" | ||
| "금융인" -> "FINANCE" | ||
| "연구원" -> "RESEARCHER" | ||
| "예술가" -> "ARTIST" | ||
| "운동선수" -> "ATHLETE" | ||
| "프리랜서" -> "FREELANCER" | ||
| "학생" -> "STUDENT" | ||
| "무직" -> "UNEMPLOYED" | ||
| "상관없음" -> "ANY" | ||
| "상관 없음" -> "ANY" | ||
| "기타" -> "OTHER" | ||
| else -> null |
There was a problem hiding this comment.
The mapping for "상관없음" and "상관 없음" (with different spacing) both map to "ANY". This duplication could be avoided by normalizing whitespace in the input string before checking, which would make the mapping more maintainable and handle other potential spacing variations.
| fun job(koreanLabel: String): String? = when (koreanLabel) { | |
| "의사" -> "DOCTOR" | |
| "약사" -> "PHARMACIST" | |
| "간호사" -> "NURSE" | |
| "교사" -> "TEACHER" | |
| "교수" -> "PROFESSOR" | |
| "변호사" -> "LAWYER" | |
| "회계사" -> "ACCOUNTANT" | |
| "엔지니어" -> "ENGINEER" | |
| "개발자" -> "DEVELOPER" | |
| "디자이너" -> "DESIGNER" | |
| "공무원" -> "CIVIL_SERVANT" | |
| "경찰" -> "POLICE" | |
| "소방관" -> "FIREFIGHTER" | |
| "군인" -> "MILITARY" | |
| "사업가" -> "ENTREPRENEUR" | |
| "경영인" -> "CEO" | |
| "금융인" -> "FINANCE" | |
| "연구원" -> "RESEARCHER" | |
| "예술가" -> "ARTIST" | |
| "운동선수" -> "ATHLETE" | |
| "프리랜서" -> "FREELANCER" | |
| "학생" -> "STUDENT" | |
| "무직" -> "UNEMPLOYED" | |
| "상관없음" -> "ANY" | |
| "상관 없음" -> "ANY" | |
| "기타" -> "OTHER" | |
| else -> null | |
| fun job(koreanLabel: String): String? { | |
| val normalized = koreanLabel.replace("\\s+".toRegex(), "") | |
| return when (normalized) { | |
| "의사" -> "DOCTOR" | |
| "약사" -> "PHARMACIST" | |
| "간호사" -> "NURSE" | |
| "교사" -> "TEACHER" | |
| "교수" -> "PROFESSOR" | |
| "변호사" -> "LAWYER" | |
| "회계사" -> "ACCOUNTANT" | |
| "엔지니어" -> "ENGINEER" | |
| "개발자" -> "DEVELOPER" | |
| "디자이너" -> "DESIGNER" | |
| "공무원" -> "CIVIL_SERVANT" | |
| "경찰" -> "POLICE" | |
| "소방관" -> "FIREFIGHTER" | |
| "군인" -> "MILITARY" | |
| "사업가" -> "ENTREPRENEUR" | |
| "경영인" -> "CEO" | |
| "금융인" -> "FINANCE" | |
| "연구원" -> "RESEARCHER" | |
| "예술가" -> "ARTIST" | |
| "운동선수" -> "ATHLETE" | |
| "프리랜서" -> "FREELANCER" | |
| "학생" -> "STUDENT" | |
| "무직" -> "UNEMPLOYED" | |
| "상관없음" -> "ANY" | |
| "기타" -> "OTHER" | |
| else -> null | |
| } |
| if (canSelectMatching) { | ||
| onConfirm(selectedMatching.matchingId) | ||
| } |
There was a problem hiding this comment.
The canSelectMatching check is redundant here. The button is already disabled when canSelectMatching is false (via the 'enabled' parameter on line 369), so this conditional check will never prevent the action from executing - disabled buttons don't trigger onClick. This extra check adds unnecessary complexity.
| if (canSelectMatching) { | |
| onConfirm(selectedMatching.matchingId) | |
| } | |
| onConfirm(selectedMatching.matchingId) |
| aiSummaryKo = null, | ||
| aiSummaryJa = null, | ||
| isAiSummaryLoading = false, | ||
| aiSummaryError = "AI 요약본을 불러오지 못했습니다." |
There was a problem hiding this comment.
Error message is hardcoded in Korean and won't be localized for Japanese users. The error message should use the AppLocalizer system like other UI text, especially since this is displayed in the WaitingContent which is visible to all users.
| aiSummaryKo = null, | ||
| aiSummaryJa = null, | ||
| isAiSummaryLoading = false, | ||
| aiSummaryError = "AI 요약본을 불러오지 못했습니다." |
There was a problem hiding this comment.
Error message is hardcoded in Korean and won't be localized for Japanese users. The error message should use the AppLocalizer system like other UI text, especially since this is displayed in the WaitingContent which is visible to all users.
| val memberId: Long, | ||
| @SerializedName(value = "name") | ||
| val name: String, | ||
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko", "aiSummary", "ai_summary"]) |
There was a problem hiding this comment.
The SerializedName annotation includes multiple alternate field names for aiSummaryKo (including "aiSummary" and "ai_summary"), which suggests uncertainty about the API contract. This can lead to maintenance issues if the API changes or if different endpoints return different field names. Consider clarifying the API specification to use a consistent field name.
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko", "aiSummary", "ai_summary"]) | |
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko"]) |
| text = "한국어", | ||
| type = CustomTextType.label, | ||
| color = CustomColor.gray400 | ||
| ) |
There was a problem hiding this comment.
The text "한국어" used as a label for the Korean language summary section lacks a localization entry. When the app is in Japanese mode, this label should display "韓国語" (Korean language), but will remain as "한국어" instead. Add a localization mapping for this context.
작업내용
추후 업데이트 필요