-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] 온보딩 상태 관리 방식 변경 #115 #136
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
Conversation
- 오로지 이벤트만 전달하는 것으로 수정.
- viewmodel: 각 텍스트필드별 함수 추가 - screen: UiState의 값 참조하도록 변경, 어르신 목록 UI 구현 - UiState: 어르신 리스트에 기본 객체 추가, Index 정보 가지고 있게 구현
- ElderMedInfoScreen으로 이동하기 전 어르신 수 만큼 어르신 건강정보 리스트 초기화
- `uiState.value =` 으로 대입. -> `uiState.update`로 변경
Walkthrough로그인(시니어) 플로우의 상태 관리를 전면 개편. 뷰모델을 패키지 이동·재구성하고, UiState(개인/건강) 도입. 여러 컴포넌트와 스크린을 콜백 기반·불변 데이터로 리팩터링. 성별(Boolean)과 모델 기본값 정리. 일부 파일은 import 경로만 수정. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant Info as LoginElderInfoScreen
participant Med as LoginElderMedInfoScreen
participant VM as LoginElderViewModel
participant EldersRepo as EldersInfoRepository
participant IdRepo as ElderIdRepository
participant RegRepo as ElderRegisterRepository
U->>Info: 입력/추가/선택/삭제
Info->>VM: updateElder*/addElder/removeElder/selectElder
VM-->>Info: elderUiState(StateFlow)
U->>Med: 질병/복약/노트 편집
Med->>VM: updateDiseasesText/addDisease/removeDisease
Med->>VM: selectMedicationTime/updateMedicationText/addMedication/removeMedication
Med->>VM: addHealthNote/removeHealthNote
VM-->>Med: elderHealthUiState(StateFlow)
U->>Med: 완료(제출)
Med->>VM: updateAllElders
VM->>IdRepo: getElderIds()
VM->>EldersRepo: updateElders(data with ids)
VM-->>Med: 업데이트 결과 로그
Med->>VM: updateAllEldersHealthInfo
VM->>IdRepo: getElderIds()
VM->>RegRepo: updateHealthInfo(per elder)
VM-->>Med: 결과 로그
Med->>VM: postElderAndHealth
VM->>RegRepo: postElderAndHealth(data)
RegRepo-->>VM: success/failure
VM-->>Med: 결과 로그/콜백
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt (1)
190-206: 비동기 작업을 suspend 파이프라인으로 묶고 완료 후 네비게이션
- ViewModel에
submitAll()같은 suspend 함수로 3개 작업을 순차/병렬 제어하고 예외 처리- UI에서는
coroutineScope.launch { if (viewModel.submitAll().isSuccess) navigate() else showError() }형태로 호출
🧹 Nitpick comments (11)
app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt (2)
113-113: 불필요한 null 체크 제거 권장
isMale이 이제 non-nullableBoolean타입이므로isMale != null체크는 항상 true이고 의미가 없습니다. 이 조건을 제거하는 것이 좋겠습니다.type = if (name.matches(Regex("^[가-힣a-zA-Z]*$")) && birth.length == 8 && birth.isValidDate() - && isMale != null ) CTAButtonType.GREEN else CTAButtonType.DISABLED,
117-117: Boolean 체크 단순화 가능
isMale == true는 그냥isMale로 단순화할 수 있습니다. 가독성이 약간 개선됩니다.- val gender = if (isMale == true) GenderType.MALE else GenderType.FEMALE + val gender = if (isMale) GenderType.MALE else GenderType.FEMALEapp/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderChip.kt (1)
41-45: 선택 컴포넌트는 clickable 대신 selectable 사용 + InteractionSource null 지양
- 선택 상태를 보조공학에 노출하려면 Modifier.selectable을 쓰는 게 맞습니다. 현재 clickable은 선택 여부를 스크린리더가 알기 어렵습니다.
- interactionSource = null 대신 remember { MutableInteractionSource() } 사용 권장.
예시 diff:
- .clickable( - indication = null, - interactionSource = null, - onClick = onClick, - ), + .selectable( + selected = selected, + onClick = onClick, + role = androidx.compose.ui.semantics.Role.Tab, + interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() }, + indication = null, + ),참고: selectable 사용 시
import androidx.compose.foundation.selection.selectable필요.app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderHealthUiState.kt (1)
6-12: selectedIndex 범위 불변식 보장 필요elderHealthList 변경(추가/삭제) 시 selectedIndex가 항상 0..lastIndex를 유지하도록 ViewModel에서 강제해 주세요. 삭제 시 특히 index < selectedIndex인 경우 selectedIndex를 -1 보정해야 선택 항목이 바뀌지 않습니다.
app/src/main/java/com/konkuk/medicarecall/ui/model/ElderHealthData.kt (1)
6-10: 내부 가변 프로퍼티(var id) 제거 권장 (UiState 일관성/성능)UiState 계열 모델에서 가변 필드는 재구성/동등성 비교를 어렵게 합니다. 가능하면 불변으로 두고 copy로 갱신하세요. Compose 최적화를 위해 @immutable 또는 @stable도 고려해 주세요.
예시 diff:
- var id: Int? = null, + val id: Int? = null,(필요 시 copy(id = …)로 갱신)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt (2)
68-69: collectAsStateWithLifecycle 사용 권장수명 주기 인지 수집으로 누수/불필요 리컴포지션을 줄이세요.
예시:
val uiState by loginElderViewModel.elderUiState.collectAsStateWithLifecycle()(의존성: androidx.lifecycle:lifecycle-runtime-compose)
243-244: 내부 위젯에 외부 modifier 전달 지양CTAButton의 modifier에 화면의 modifier를 재사용하면 예기치 못한 결합이 생깁니다. 로컬 Modifier를 사용하세요.
예시 diff:
- modifier.padding(bottom = 20.dp), + Modifier.padding(bottom = 20.dp),app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt (2)
95-99: 선택 인덱스 범위 가드 추가 권장외부에서 잘못된 index가 들어오면 이후 접근에서 IOB 위험이 있습니다.
fun selectElder(index: Int) { - _elderUiState.update { state -> - state.copy(selectedIndex = index) - } + _elderUiState.update { state -> + val bounded = index.coerceIn(0, (state.eldersList.lastIndex).coerceAtLeast(0)) + state.copy(selectedIndex = bounded) + } }
118-126: 입력 검증 강화 제안(숫자 형식)길이만 체크하면 형식 오류가 섞여 들어올 수 있습니다. 숫자만 허용하도록 보강하세요.
return elderUiState.value.eldersList.all { - it.name.isNotBlank() && - it.birthDate.length == 8 && - it.phoneNumber.length == 11 && + it.name.isNotBlank() && + it.birthDate.length == 8 && it.birthDate.all(Char::isDigit) && + it.phoneNumber.length == 11 && it.phoneNumber.all(Char::isDigit) && it.relationship.isNotBlank() && it.livingType.isNotBlank() }app/src/main/java/com/konkuk/medicarecall/ui/common/component/MedicationItem.kt (1)
143-151: 약 추가 후 선택된 시간대도 해제하면 UX가 더 자연스럽습니다주석상 “입력 필드와 선택된 시간 초기화”지만 현재는 입력만 초기화됩니다. stateless 패턴을 유지하며 토글 콜백으로 해제할 수 있어요.
clickPlus = { if (inputText.isNotBlank() && selectedList.isNotEmpty()) { selectedList.forEach { time -> onAddMedication(time, inputText) } - // 사용성 개선: 약 추가 후 입력 필드와 선택된 시간 초기화 - onTextChange("") + // 사용성 개선: 약 추가 후 입력/선택 초기화 + onTextChange("") + selectedList.forEach { time -> onSelectTime(time) } // 선택 해제 } },app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt (1)
91-94: scrollState 중복 선언(섀도잉)바깥
scrollState와 동일 이름을 내부에서 다시 선언해 섀도잉되고 있습니다. 재사용 또는 변수명 정리로 혼동을 줄이는 편이 좋습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
app/src/main/java/com/konkuk/medicarecall/MainActivity.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/navigation/NavGraph.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/common/component/DiseaseNamesItem.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/common/component/GenderToggleButton.kt(2 hunks)app/src/main/java/com/konkuk/medicarecall/ui/common/component/MedicationItem.kt(5 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt(6 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/LoginElderViewModel.kt(0 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderChip.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderInputForm.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt(6 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt(4 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderHealthUiState.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderUiState.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDetailScreen.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/model/ElderData.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/model/ElderHealthData.kt(1 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/LoginElderViewModel.kt
🧰 Additional context used
🧬 Code graph analysis (6)
app/src/main/java/com/konkuk/medicarecall/ui/common/component/DiseaseNamesItem.kt (2)
app/src/main/java/com/konkuk/medicarecall/ui/common/component/ChipItem.kt (1)
ChipItem(22-52)app/src/main/java/com/konkuk/medicarecall/ui/common/component/AddTextField.kt (1)
AddTextField(20-66)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderInputForm.kt (3)
app/src/main/java/com/konkuk/medicarecall/ui/common/component/DefaultTextField.kt (1)
DefaultTextField(18-64)app/src/main/java/com/konkuk/medicarecall/ui/common/component/GenderToggleButton.kt (1)
GenderToggleButton(21-87)app/src/main/java/com/konkuk/medicarecall/ui/common/component/DefaultDropDown.kt (1)
DefaultDropdown(41-175)
app/src/main/java/com/konkuk/medicarecall/ui/common/component/MedicationItem.kt (1)
app/src/main/java/com/konkuk/medicarecall/ui/common/component/AddTextField.kt (1)
AddTextField(20-66)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt (5)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/component/LoginBackButton.kt (1)
LoginBackButton(17-37)app/src/main/java/com/konkuk/medicarecall/ui/common/component/DiseaseNamesItem.kt (1)
DiseaseNamesItem(17-69)app/src/main/java/com/konkuk/medicarecall/ui/common/component/MedicationItem.kt (1)
MedicationItem(60-155)app/src/main/java/com/konkuk/medicarecall/ui/common/component/ChipItem.kt (1)
ChipItem(22-52)app/src/main/java/com/konkuk/medicarecall/ui/common/component/DefaultSnackBar.kt (1)
DefaultSnackBar(17-37)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt (4)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/component/LoginBackButton.kt (1)
LoginBackButton(17-37)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderChip.kt (1)
ElderChip(24-77)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderInputForm.kt (1)
ElderInputForm(27-120)app/src/main/java/com/konkuk/medicarecall/ui/common/component/DefaultSnackBar.kt (1)
DefaultSnackBar(17-37)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderChip.kt (1)
app/src/main/java/com/konkuk/medicarecall/ui/theme/Theme.kt (1)
MediCareCallTheme(38-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (10)
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt (2)
50-50: 성별 기본값이 남성(true)으로 설정되었는지 확인해 주세요.이제
isMale이 non-nullable Boolean으로 변경되고 기본값이true로 설정되었습니다. 이것이 의도된 비즈니스 로직인지 확인이 필요합니다. 성별을 명시적으로 선택하도록 유도하는 것이 더 나을 수도 있습니다.
70-72: LGTM!
onGenderChanged가 non-nullableBoolean을 받도록 변경되어 null 처리가 필요 없어졌습니다.GenderToggleButton의 변경사항과 일관성 있게 정리되었네요.app/src/main/java/com/konkuk/medicarecall/ui/common/component/GenderToggleButton.kt (1)
22-87: LGTM!
GenderToggleButton이 nullable Boolean 대신 non-nullable Boolean을 사용하도록 깔끔하게 리팩터링되었습니다. 기본값true를 제공하여 사용처에서 초기화가 간편해졌고, 콜백도 non-nullable로 통일되어 일관성이 좋습니다.app/src/main/java/com/konkuk/medicarecall/ui/common/component/DiseaseNamesItem.kt (1)
17-69: LGTM!
DiseaseNamesItem이 stateless 컴포넌트로 훌륭하게 리팩터링되었습니다. 이제 내부에서 상태를 직접 변경하지 않고 콜백을 통해 상위로 이벤트를 전달하는 구조로, 단방향 데이터 플로우와 ViewModel 중심의 상태 관리 원칙에 부합합니다.app/src/main/java/com/konkuk/medicarecall/navigation/NavGraph.kt (1)
43-43: LGTM!
LoginElderViewModel의 import 경로가viewmodel패키지로 이동되었습니다. 패키지 구조 개선 작업의 일환으로 보이며, 로직 변경 없이 경로만 업데이트되었습니다.app/src/main/java/com/konkuk/medicarecall/MainActivity.kt (1)
43-43: LGTM!
MainActivity에서도LoginElderViewModelimport 경로가 업데이트되었습니다. 패키지 재구성 작업과 일관성 있게 적용되었네요.app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt (1)
65-65: LGTM!
isMale상태가 non-nullableBoolean으로 변경되었습니다.GenderToggleButton의 API 변경과 일관성 있게 적용되었고, 초기값도 enum에서 Boolean으로 매핑되어 안전합니다.app/src/main/java/com/konkuk/medicarecall/ui/model/ElderData.kt (1)
6-6: 성별 기본값 설정을 확인해 주세요.
gender필드에 기본값true(남성)가 추가되어ElderData()생성 시 성별을 명시하지 않아도 됩니다. 이것이 의도된 것인지, 아니면 성별 선택을 필수로 하는 것이 더 나은지 검토가 필요할 수 있습니다.app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderUiState.kt (1)
1-8: LGTM!새로운
LoginElderUiState클래스가 추가되어 어르신 로그인 플로우의 UI 상태를 중앙 집중식으로 관리할 수 있게 되었습니다.eldersList와selectedIndex로 상태를 깔끔하게 표현하고 있으며, 기본값 제공으로 초기화가 간편합니다. ViewModel 기반 상태 관리 리팩터링의 핵심 부분이네요.app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt (1)
94-116: 선택 인덱스 분리 사용(elders vs health)이 의도인지 확인 필요상단 칩 하이라이트는
uiState.selectedIndex(health)기준이며, elderUiState의 selectedIndex와는 별개로 움직입니다. 의도된 분리라면 OK, 아니라면 동기화/단일 소스화를 고려해 주세요.
| Icon( | ||
| painter = painterResource(id = R.drawable.ic_close), | ||
| contentDescription = "remove", | ||
| modifier = Modifier | ||
| .size(16.dp) | ||
| .clickable( | ||
| indication = null, | ||
| interactionSource = null, | ||
| onClick = { onRemove() }, | ||
| ), | ||
| tint = | ||
| if (selected) MediCareCallTheme.colors.main | ||
| else MediCareCallTheme.colors.gray3, | ||
| ) |
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.
삭제 아이콘 탭 영역 48dp 이상 확보 + IconButton 사용 권장
- 현재 size(16.dp) + clickable은 터치 타깃이 너무 작아 접근성/사용성 문제를 유발합니다.
- IconButton은 최소 터치 영역을 보장합니다. 또한 contentDescription는 stringResource로 i18n 권장.
예시 diff:
- Icon(
- painter = painterResource(id = R.drawable.ic_close),
- contentDescription = "remove",
- modifier = Modifier
- .size(16.dp)
- .clickable(
- indication = null,
- interactionSource = null,
- onClick = { onRemove() },
- ),
- tint =
- if (selected) MediCareCallTheme.colors.main
- else MediCareCallTheme.colors.gray3,
- )
+ androidx.compose.material3.IconButton(
+ onClick = onRemove,
+ modifier = Modifier.size(40.dp) // 최소 터치 타깃 확보
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_close),
+ contentDescription = stringResource(id = R.string.accessibility_remove_chip),
+ tint = if (selected) MediCareCallTheme.colors.main else MediCareCallTheme.colors.gray3,
+ modifier = Modifier.size(16.dp)
+ )
+ }주의: R.string.accessibility_remove_chip 추가 필요.
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderChip.kt
around lines 61 to 74, the delete icon uses Icon with size(16.dp) + clickable
which yields a touch target smaller than the recommended 48dp and a hardcoded
contentDescription; replace the Icon+clickable with an IconButton (or a Box with
minimum touch size) to guarantee at least a 48.dp touch target, preserve the
tint logic based on selected, call onRemove from the IconButton onClick, and
change contentDescription to stringResource(R.string.accessibility_remove_chip)
(add that string resource if missing) to support i18n/accessibility.
| Column( | ||
| verticalArrangement = Arrangement.Center, | ||
| ) { | ||
| Column( | ||
| verticalArrangement = Arrangement.Center | ||
| ) { | ||
| DefaultTextField( | ||
| value = loginElderViewModel.nameList[index], | ||
| onValueChange = { loginElderViewModel.onNameChanged(index, it) }, | ||
| category = "이름", | ||
| placeHolder = "이름" | ||
| ) | ||
| Spacer(Modifier.height(20.dp)) | ||
| DefaultTextField( | ||
| loginElderViewModel.dateOfBirthList[index], | ||
| { input -> | ||
| val filtered = input.filter { it.isDigit() }.take(8) | ||
| loginElderViewModel.onDOBChanged(index, filtered) | ||
| }, | ||
| category = "생년월일", | ||
| placeHolder = "YYYY / MM / DD", | ||
| keyboardType = KeyboardType.Number, | ||
| visualTransformation = DateOfBirthVisualTransformation(), | ||
| maxLength = 8 | ||
| DefaultTextField( |
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.
modifier 파라미터 미사용
호출처에서 전달한 modifier가 반영되지 않습니다. Column에 전달하세요.
예시 diff:
- Column(
- verticalArrangement = Arrangement.Center,
- ) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.Center,
+ ) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Column( | |
| verticalArrangement = Arrangement.Center, | |
| ) { | |
| Column( | |
| verticalArrangement = Arrangement.Center | |
| ) { | |
| DefaultTextField( | |
| value = loginElderViewModel.nameList[index], | |
| onValueChange = { loginElderViewModel.onNameChanged(index, it) }, | |
| category = "이름", | |
| placeHolder = "이름" | |
| ) | |
| Spacer(Modifier.height(20.dp)) | |
| DefaultTextField( | |
| loginElderViewModel.dateOfBirthList[index], | |
| { input -> | |
| val filtered = input.filter { it.isDigit() }.take(8) | |
| loginElderViewModel.onDOBChanged(index, filtered) | |
| }, | |
| category = "생년월일", | |
| placeHolder = "YYYY / MM / DD", | |
| keyboardType = KeyboardType.Number, | |
| visualTransformation = DateOfBirthVisualTransformation(), | |
| maxLength = 8 | |
| DefaultTextField( | |
| Column( | |
| modifier = modifier, | |
| verticalArrangement = Arrangement.Center, | |
| ) { | |
| DefaultTextField( |
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/component/ElderInputForm.kt
around lines 40 to 43, the passed modifier parameter is not applied because
Column is created without it; update the Column call to accept and forward the
modifier argument (e.g., Column(modifier = modifier, verticalArrangement =
Arrangement.Center) { ... }) so the caller's modifier is honored.
| itemsIndexed(uiState.eldersList) { index, it -> | ||
| ElderChip( | ||
| name = it.name, | ||
| selected = index == selectedIndex, | ||
| onRemove = { | ||
| if (selectedIndex == uiState.eldersList.size - 1) | ||
| loginElderViewModel.selectElder(selectedIndex - 1) | ||
| loginElderViewModel.removeElder(index) | ||
| }, | ||
| onClick = { loginElderViewModel.selectElder(index) }, | ||
| ) |
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.
어르신 삭제 시 selectedIndex 보정 로직 보강 필요
현재는 “선택된 항목이 마지막인 경우”만 보정합니다. 삭제 대상이 선택 인덱스보다 앞이면 선택 항목이 한 칸 밀려 의도와 달라집니다. 삭제 후 인덱스를 일관되게 보정하세요.
예시 diff:
- onRemove = {
- if (selectedIndex == uiState.eldersList.size - 1)
- loginElderViewModel.selectElder(selectedIndex - 1)
- loginElderViewModel.removeElder(index)
- },
+ onRemove = {
+ val lastIndex = uiState.eldersList.lastIndex
+ val newSelected = when {
+ index == selectedIndex && selectedIndex == lastIndex -> selectedIndex - 1
+ index < selectedIndex -> selectedIndex - 1
+ else -> selectedIndex
+ }.coerceAtLeast(0)
+ loginElderViewModel.removeElder(index)
+ loginElderViewModel.selectElder(newSelected)
+ },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| itemsIndexed(uiState.eldersList) { index, it -> | |
| ElderChip( | |
| name = it.name, | |
| selected = index == selectedIndex, | |
| onRemove = { | |
| if (selectedIndex == uiState.eldersList.size - 1) | |
| loginElderViewModel.selectElder(selectedIndex - 1) | |
| loginElderViewModel.removeElder(index) | |
| }, | |
| onClick = { loginElderViewModel.selectElder(index) }, | |
| ) | |
| itemsIndexed(uiState.eldersList) { index, it -> | |
| ElderChip( | |
| name = it.name, | |
| selected = index == selectedIndex, | |
| onRemove = { | |
| val lastIndex = uiState.eldersList.lastIndex | |
| val newSelected = when { | |
| // removed the selected item at the end → move selection back | |
| index == selectedIndex && selectedIndex == lastIndex -> | |
| selectedIndex - 1 | |
| // removed an item before the selected one → shift selection back | |
| index < selectedIndex -> | |
| selectedIndex - 1 | |
| // in all other cases, keep selection | |
| else -> | |
| selectedIndex | |
| }.coerceAtLeast(0) | |
| loginElderViewModel.removeElder(index) | |
| loginElderViewModel.selectElder(newSelected) | |
| }, | |
| onClick = { loginElderViewModel.selectElder(index) }, | |
| ) |
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt
around lines 110 to 120, the selectedIndex correction only handles the case when
the selected item is the last one; you must also adjust the selected index when
a removed item is before the current selection. Compute the newSelectedIndex
based on the removed index: if removedIndex < selectedIndex then decrement
selectedIndex by 1, else if selectedIndex == uiState.eldersList.size - 1 and
removing that last item then decrement, otherwise keep it; ensure
newSelectedIndex is bounded >= 0; call
loginElderViewModel.selectElder(newSelectedIndex) (or none if list becomes
empty) and then remove the elder (or compute then update viewModel in the
correct order) so selection stays consistent after removal.
| else if (!uiState.eldersList.filter { it.phoneNumber.isNotEmpty() } | ||
| .all { it.phoneNumber.startsWith("010") }) | ||
| coroutineScope.launch { | ||
| snackBarState.showSnackbar( | ||
| "휴대폰 번호를 다시 확인해주세요", | ||
| duration = SnackbarDuration.Short | ||
| duration = SnackbarDuration.Short, | ||
| ) |
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.
휴대폰 번호 검증 강화 필요
현재는 "010" 시작만 확인하여 길이 11 미만도 통과합니다. 정규식으로 11자리 보장하세요.
예시 diff:
- else if (!uiState.eldersList.filter { it.phoneNumber.isNotEmpty() }
- .all { it.phoneNumber.startsWith("010") })
+ else if (!uiState.eldersList.filter { it.phoneNumber.isNotEmpty() }
+ .all { it.phoneNumber.matches(Regex("^010\\d{8}$")) })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else if (!uiState.eldersList.filter { it.phoneNumber.isNotEmpty() } | |
| .all { it.phoneNumber.startsWith("010") }) | |
| coroutineScope.launch { | |
| snackBarState.showSnackbar( | |
| "휴대폰 번호를 다시 확인해주세요", | |
| duration = SnackbarDuration.Short | |
| duration = SnackbarDuration.Short, | |
| ) | |
| else if (!uiState.eldersList | |
| .filter { it.phoneNumber.isNotEmpty() } | |
| .all { it.phoneNumber.matches(Regex("^010\\d{8}$")) }) | |
| coroutineScope.launch { | |
| snackBarState.showSnackbar( | |
| "휴대폰 번호를 다시 확인해주세요", | |
| duration = SnackbarDuration.Short, | |
| ) |
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt
around lines 230 to 236, the current check only ensures phone numbers start with
"010" and allows shorter than 11 digits; replace that predicate with a regex
check that non-empty phone numbers must match exactly 11 digits starting with
010 (e.g. use pattern "^010\\d{8}$") so only valid 11-digit numbers pass, and
keep the snackbar behavior the same when the validation fails.
| val elderUiState by loginElderViewModel.elderUiState.collectAsState() | ||
|
|
||
| val uiState by loginElderViewModel.elderHealthUiState.collectAsState() | ||
| val selectedIndex = uiState.selectedIndex | ||
|
|
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.
elderHealthList 직접 인덱싱 범위 체크 없음 → IOB 위험
selectedIndex가 리스트 범위를 벗어나면 크래시 납니다. 안전 바인딩으로 치환을 권장합니다.
- val uiState by loginElderViewModel.elderHealthUiState.collectAsState()
- val selectedIndex = uiState.selectedIndex
+ val uiState by loginElderViewModel.elderHealthUiState.collectAsState()
+ val selectedIndex = uiState.selectedIndex
+ val currentHealth = uiState.elderHealthList.getOrNull(selectedIndex) ?: return
@@
- DiseaseNamesItem(
- inputText = uiState.diseaseInputText,
- diseaseList = uiState.elderHealthList[selectedIndex].diseaseNames,
+ DiseaseNamesItem(
+ inputText = uiState.diseaseInputText,
+ diseaseList = currentHealth.diseaseNames,
@@
- MedicationItem(
- medicationSchedule = uiState.elderHealthList[selectedIndex].medicationMap,
+ MedicationItem(
+ medicationSchedule = currentHealth.medicationMap,
@@
- if (uiState.elderHealthList[selectedIndex].notes.isNotEmpty()) {
+ if (currentHealth.notes.isNotEmpty()) {
@@
- uiState.elderHealthList[selectedIndex].notes.forEach { note ->
+ currentHealth.notes.forEach { note ->Also applies to: 136-144, 147-155, 165-173
🤖 Prompt for AI Agents
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt
lines 59-63 (also fix similar usages at 136-144, 147-155, 165-173): the code
reads uiState.selectedIndex and directly indexes elderHealthList which can crash
if selectedIndex is out of bounds; replace direct indexing with safe access
(check that selectedIndex is within 0 until elderHealthList.size or use
elderHealthList.getOrNull(selectedIndex) ?: /* fallback */) and provide a
sensible fallback UI/state (e.g., null-handling, default selectedIndex, or early
return) so out-of-range indices do not cause IOOB exceptions; apply the same
safe-access pattern to the other referenced line ranges.
| fun addElder() { | ||
| _elderUiState.update { state -> | ||
| state.copy( | ||
| eldersList = state.eldersList + ElderData(), | ||
| selectedIndex = state.selectedIndex + 1, | ||
| ) | ||
| } | ||
| } |
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.
어르신 추가/삭제 시 건강정보 상태와 동기화되지 않아 인덱스 불일치/IOB 위험
- addElder/removeElder가 elderHealthUiState를 함께 갱신하지 않습니다. 이후 화면에서
elderHealthList[selectedIndex]접근 시 크기 불일치로 IOB가 납니다. - 삭제 시 selectedIndex 보정도 필요합니다.
다음과 같이 두 UiState를 함께 업데이트해 주세요.
fun addElder() {
- _elderUiState.update { state ->
- state.copy(
- eldersList = state.eldersList + ElderData(),
- selectedIndex = state.selectedIndex + 1,
- )
- }
+ _elderUiState.update { state ->
+ val newSelected = state.eldersList.size // 추가 후 마지막 인덱스
+ state.copy(
+ eldersList = state.eldersList + ElderData(),
+ selectedIndex = newSelected,
+ )
+ }
+ _elderHealthUiState.update { state ->
+ val newList = state.elderHealthList + ElderHealthData()
+ state.copy(
+ elderHealthList = newList,
+ selectedIndex = newList.lastIndex,
+ )
+ }
}
fun removeElder(index: Int) {
- _elderUiState.update { state ->
- state.copy(
- eldersList = state.eldersList.filterIndexed { i, _ -> i != index },
- )
- }
+ _elderUiState.update { state ->
+ val newList = state.eldersList.filterIndexed { i, _ -> i != index }
+ val newSelected = when {
+ newList.isEmpty() -> 0
+ state.selectedIndex > newList.lastIndex -> newList.lastIndex
+ else -> state.selectedIndex
+ }
+ state.copy(eldersList = newList, selectedIndex = newSelected)
+ }
+ _elderHealthUiState.update { state ->
+ val newList = state.elderHealthList.filterIndexed { i, _ -> i != index }
+ val newSelected = when {
+ newList.isEmpty() -> 0
+ state.selectedIndex > newList.lastIndex -> newList.lastIndex
+ else -> state.selectedIndex
+ }
+ state.copy(elderHealthList = newList, selectedIndex = newSelected)
+ }
}Also applies to: 110-116
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt
around lines 101-108 (and similarly 110-116), addElder/removeElder currently
only updates elderUiState causing elderHealthUiState length to get out of sync
and leading to index-out-of-bounds when accessing
elderHealthList[selectedIndex]; update both UiStates together: when adding,
append a new default ElderHealthData to elderHealthUiState and increment
selectedIndex to point to the new item; when removing, remove the corresponding
element from elderHealthUiState and adjust selectedIndex to remain within
0..(newList.size-1) (and avoid negative values); perform the two updates in
sequence/atomically so both lists remain synchronized.
| fun initElderHealthData() { | ||
| _elderHealthUiState.update { state -> | ||
| state.copy(elderHealthList = List(elderUiState.value.eldersList.size) { ElderHealthData() }) | ||
| } | ||
| } |
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 | 🟠 Major
🧩 Analysis chain
initElderHealthData 호출 시점 보강 필요
지금은 수동 호출 전제입니다. 초기화/리스트 변경(add/remove) 시점에 자동 동기화가 안전합니다.
// flows 선언 아래에 추가
+init {
+ initElderHealthData()
+}또는 add/remove에서 직접 동기화(위 코멘트의 diff 참고).
#!/bin/bash
rg -n 'initElderHealthData' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true
rg -n 'init\s*{' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true
🏁 Script executed:
#!/bin/bash
rg -n 'initElderHealthData' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true
rg -n 'init\s*{' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || trueLength of output: 314
#!/bin/bash
rg -nP '^[[:space:]]*init\s*\{' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true
rg -n 'init{' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true🏁 Script executed:
#!/bin/bash
rg -nP '^[[:space:]]*init\s*\{' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || true
rg -nP '\binitElderHealthData\s*\(' -C2 app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt || trueLength of output: 234
initElderHealthData 자동 호출 추가
ViewModel 초기화(init 블록)에 initElderHealthData() 호출을 추가하고, eldersList 변경(add/remove) 시에도 동일 로직을 호출하세요.
| fun addMedication(time: MedicationTimeType?, medicine: String) { | ||
| if (time == null) return | ||
|
|
||
| _elderHealthUiState.update { state -> | ||
| state.copy( | ||
| elderHealthList = state.elderHealthList.mapIndexed { index, elder -> | ||
| if (index == state.selectedIndex) { | ||
| val currentList = elder.medicationMap[time] ?: emptyList() | ||
|
|
||
| val updatedMap = | ||
| elder.medicationMap + if (medicine !in (elder.medicationMap[time] | ||
| ?: emptyList()) | ||
| ) (time to (currentList + medicine)) else return | ||
| elder.copy(medicationMap = updatedMap) | ||
| } else { | ||
| elder | ||
| } | ||
| }, | ||
| ) | ||
| } | ||
| } |
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.
addMedication의 비지역 return으로 상태 업데이트가 중단될 수 있음
mapIndexed 내부 else return은 비지역 return으로 함수 전체를 빠져나가 update 자체가 실행되지 않을 수 있어 가독성과 안정성 모두 떨어집니다. 중복이면 현재 elder만 그대로 반환하도록 재구성하세요.
fun addMedication(time: MedicationTimeType?, medicine: String) {
if (time == null) return
-
- _elderHealthUiState.update { state ->
- state.copy(
- elderHealthList = state.elderHealthList.mapIndexed { index, elder ->
- if (index == state.selectedIndex) {
- val currentList = elder.medicationMap[time] ?: emptyList()
-
- val updatedMap =
- elder.medicationMap + if (medicine !in (elder.medicationMap[time]
- ?: emptyList())
- ) (time to (currentList + medicine)) else return
- elder.copy(medicationMap = updatedMap)
- } else {
- elder
- }
- },
- )
- }
+ val trimmed = medicine.trim()
+ if (trimmed.isEmpty()) return
+ _elderHealthUiState.update { state ->
+ state.copy(
+ elderHealthList = state.elderHealthList.mapIndexed { index, elder ->
+ if (index == state.selectedIndex) {
+ val currentList = elder.medicationMap[time].orEmpty()
+ if (trimmed in currentList) elder
+ else elder.copy(
+ medicationMap = elder.medicationMap + (time to (currentList + trimmed))
+ )
+ } else elder
+ }
+ )
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fun addMedication(time: MedicationTimeType?, medicine: String) { | |
| if (time == null) return | |
| _elderHealthUiState.update { state -> | |
| state.copy( | |
| elderHealthList = state.elderHealthList.mapIndexed { index, elder -> | |
| if (index == state.selectedIndex) { | |
| val currentList = elder.medicationMap[time] ?: emptyList() | |
| val updatedMap = | |
| elder.medicationMap + if (medicine !in (elder.medicationMap[time] | |
| ?: emptyList()) | |
| ) (time to (currentList + medicine)) else return | |
| elder.copy(medicationMap = updatedMap) | |
| } else { | |
| elder | |
| } | |
| }, | |
| ) | |
| } | |
| } | |
| fun addMedication(time: MedicationTimeType?, medicine: String) { | |
| if (time == null) return | |
| val trimmed = medicine.trim() | |
| if (trimmed.isEmpty()) return | |
| _elderHealthUiState.update { state -> | |
| state.copy( | |
| elderHealthList = state.elderHealthList.mapIndexed { index, elder -> | |
| if (index == state.selectedIndex) { | |
| val currentList = elder.medicationMap[time].orEmpty() | |
| if (trimmed in currentList) elder | |
| else elder.copy( | |
| medicationMap = elder.medicationMap + (time to (currentList + trimmed)) | |
| ) | |
| } else elder | |
| } | |
| ) | |
| } | |
| } |
| fun updateAllElders() { // getElderIds.isNotEmpty == true | ||
| viewModelScope.launch { | ||
| val elderIds = elderIdRepository.getElderIds() | ||
| elderIds.filterIndexed { index, it -> | ||
| it.values.first() == elderUiState.value.eldersList[index].id | ||
| }.forEachIndexed { index, it -> | ||
| eldersInfoRepository.updateElder( | ||
| it.values.first(), elderUiState.value.eldersList[index], | ||
| ).onSuccess { | ||
| Log.d("httplog", "어르신 재등록(수정) 성공") | ||
| }.onFailure { exception -> | ||
| Log.e("httplog", "어르신 정보 등록 실패: ${exception.message}") | ||
|
|
||
|
|
||
| } | ||
| } |
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.
updateAllElders: filterIndexed → forEachIndexed로 인덱스가 재시작하여 elder 매칭이 틀어집니다 (OOB 위험 포함)
filterIndexed { index, it -> ... }.forEachIndexed { index, it -> ... }에서 두 번째 index는 필터된 리스트 기준으로 0부터 다시 시작합니다. 결과적으로 잘못된 elder를 업데이트하거나 IOB가 날 수 있습니다.eldersList[index]접근도getOrNull로 안전하게.
fun updateAllElders() { // getElderIds.isNotEmpty == true
viewModelScope.launch {
val elderIds = elderIdRepository.getElderIds()
- elderIds.filterIndexed { index, it ->
- it.values.first() == elderUiState.value.eldersList[index].id
- }.forEachIndexed { index, it ->
- eldersInfoRepository.updateElder(
- it.values.first(), elderUiState.value.eldersList[index],
- ).onSuccess {
+ val elders = elderUiState.value.eldersList
+ elderIds.forEachIndexed { idx, entry ->
+ val id = entry.values.first()
+ val elder = elders.getOrNull(idx) ?: return@forEachIndexed
+ eldersInfoRepository.updateElder(id, elder).onSuccess {
Log.d("httplog", "어르신 재등록(수정) 성공")
}.onFailure { exception ->
Log.e("httplog", "어르신 정보 등록 실패: ${exception.message}")
}
- }
+ }
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fun updateAllElders() { // getElderIds.isNotEmpty == true | |
| viewModelScope.launch { | |
| val elderIds = elderIdRepository.getElderIds() | |
| elderIds.filterIndexed { index, it -> | |
| it.values.first() == elderUiState.value.eldersList[index].id | |
| }.forEachIndexed { index, it -> | |
| eldersInfoRepository.updateElder( | |
| it.values.first(), elderUiState.value.eldersList[index], | |
| ).onSuccess { | |
| Log.d("httplog", "어르신 재등록(수정) 성공") | |
| }.onFailure { exception -> | |
| Log.e("httplog", "어르신 정보 등록 실패: ${exception.message}") | |
| } | |
| } | |
| fun updateAllElders() { // getElderIds.isNotEmpty == true | |
| viewModelScope.launch { | |
| val elderIds = elderIdRepository.getElderIds() | |
| val elders = elderUiState.value.eldersList | |
| elderIds.forEachIndexed { idx, entry -> | |
| val id = entry.values.first() | |
| val elder = elders.getOrNull(idx) ?: return@forEachIndexed | |
| eldersInfoRepository.updateElder(id, elder).onSuccess { | |
| Log.d("httplog", "어르신 재등록(수정) 성공") | |
| }.onFailure { exception -> | |
| Log.e("httplog", "어르신 정보 등록 실패: ${exception.message}") | |
| } | |
| } | |
| } | |
| } |
| fun updateAllEldersHealthInfo() { | ||
| viewModelScope.launch { | ||
| val elderIds = elderIdRepository.getElderIds() | ||
| elderIds.filterIndexed { index, it -> | ||
| it.values.first() == elderHealthUiState.value.elderHealthList[index].id | ||
| }.forEachIndexed { index, it -> | ||
| runCatching { | ||
| elderRegisterRepository.postElderHealthInfo( | ||
| it.values.first(), | ||
| elderHealthUiState.value.elderHealthList[index], | ||
| ) | ||
| }.onSuccess { | ||
| Log.d("httplog", "어르신 건강정보 재등록(수정) 성공") | ||
| } | ||
| } | ||
| } | ||
| } |
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.
updateAllEldersHealthInfo도 동일한 인덱스 드리프트/에러 누락
- 같은 패턴의 인덱스 드리프트가 있어 잘못된 매칭 혹은 IOB가 발생할 수 있습니다.
- 실패 로그가 없습니다.
fun updateAllEldersHealthInfo() {
viewModelScope.launch {
val elderIds = elderIdRepository.getElderIds()
- elderIds.filterIndexed { index, it ->
- it.values.first() == elderHealthUiState.value.elderHealthList[index].id
- }.forEachIndexed { index, it ->
- runCatching {
- elderRegisterRepository.postElderHealthInfo(
- it.values.first(),
- elderHealthUiState.value.elderHealthList[index],
- )
- }.onSuccess {
- Log.d("httplog", "어르신 건강정보 재등록(수정) 성공")
- }
- }
+ val healthList = elderHealthUiState.value.elderHealthList
+ elderIds.forEachIndexed { idx, entry ->
+ val id = entry.values.first()
+ val health = healthList.getOrNull(idx) ?: return@forEachIndexed
+ runCatching {
+ elderRegisterRepository.postElderHealthInfo(id, health)
+ }.onSuccess {
+ Log.d("httplog", "어르신 건강정보 재등록(수정) 성공")
+ }.onFailure { e ->
+ Log.e("httplog", "어르신 건강정보 등록 실패: ${e.message}")
+ }
+ }
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fun updateAllEldersHealthInfo() { | |
| viewModelScope.launch { | |
| val elderIds = elderIdRepository.getElderIds() | |
| elderIds.filterIndexed { index, it -> | |
| it.values.first() == elderHealthUiState.value.elderHealthList[index].id | |
| }.forEachIndexed { index, it -> | |
| runCatching { | |
| elderRegisterRepository.postElderHealthInfo( | |
| it.values.first(), | |
| elderHealthUiState.value.elderHealthList[index], | |
| ) | |
| }.onSuccess { | |
| Log.d("httplog", "어르신 건강정보 재등록(수정) 성공") | |
| } | |
| } | |
| } | |
| } | |
| fun updateAllEldersHealthInfo() { | |
| viewModelScope.launch { | |
| val elderIds = elderIdRepository.getElderIds() | |
| val healthList = elderHealthUiState.value.elderHealthList | |
| elderIds.forEachIndexed { idx, entry -> | |
| val id = entry.values.first() | |
| val health = healthList.getOrNull(idx) ?: return@forEachIndexed | |
| runCatching { | |
| elderRegisterRepository.postElderHealthInfo(id, health) | |
| } | |
| .onSuccess { | |
| Log.d("httplog", "어르신 건강정보 재등록(수정) 성공") | |
| } | |
| .onFailure { e -> | |
| Log.e("httplog", "어르신 건강정보 등록 실패: ${e.message}") | |
| } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt
around lines 314 to 330, the code matches elderIds to elderHealthUiState by
relying on the same index (risking index drift/IOB) and does not log failures;
instead, build a lookup (map) from elderHealthUiState.value.elderHealthList by
id, then iterate elderIds and for each id find the corresponding UI state via
that map (skip or log if not found), call
elderRegisterRepository.postElderHealthInfo with the matched id and state, and
add onFailure/exception handling to log the error details so failures are
visible.
librawish808
left a comment
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.
LGTM 고생하셨습니다!
ikseong00
left a comment
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.
좋습니다!
| private val _elderUiState = MutableStateFlow(LoginElderUiState()) | ||
| val elderUiState: StateFlow<LoginElderUiState> = _elderUiState.asStateFlow() | ||
|
|
||
| private val _elderHealthUiState = MutableStateFlow(LoginElderHealthUiState()) | ||
| val elderHealthUiState: StateFlow<LoginElderHealthUiState> = _elderHealthUiState.asStateFlow() |
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.
이 두 개를 합쳐서 그냥 LoginElderUiState() 로 묶는 건 어떨까요??
각자 다른 뷰모델에서 사용된다면 분리하는 게 맞겠지만,
하나의 뷰모델에서 선언되고 뷰모델의 생명주기와 동일하게 간다면, 굳이 따로 가져가기보다 합치는 게 해당 UiState 를 수정하거나 사용할 때 편리할 것 같습니다!
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.
viewModel 함수 주석에 달아놨었는데, 이후 viewmodel을 분리하는 게 나을 것 같아서 별도의 UiState로 구현해두었습니다!
🔗 관련 이슈
📙 작업 설명
Summary by CodeRabbit