-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] elderId 내부 스토리지 저장 방식으로 수정 #177 #243
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
…factor/elderinfo-#206
[Refactor] ElderInfo 관련 시간설정, 결제 화면, 뷰모델 등 수정 #206
[Fix] elderId 관련 온보딩 플로우 수정
WalkthroughelderIds를 메모리 기반 저장소에서 DataStore를 통한 로컬 스토리지로 이전하고, Flow 기반 API로 전환합니다. 관련 Repository, ViewModel, UI 레이어를 업데이트하여 새로운 비동기 지속성 패턴을 반영합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Compose UI<br/>(CallTimeScreen)
participant VM as CallTimeViewModel
participant Repo as ElderIdRepository
participant DS as DataStore<br/>(Local Storage)
rect rgba(100, 150, 255, 0.5)
Note over UI,DS: Elder IDs 초기 로드
VM->>Repo: getElderIds()
Repo->>DS: 읽기 (elderIds)
DS-->>Repo: Map<Int, String>
Repo-->>VM: Flow<Map<Int, String>>
VM->>VM: _elderIdMap 업데이트
VM-->>UI: elderIdMap 상태 수집
UI->>UI: ID 기반 선택 UI 렌더링
end
rect rgba(100, 255, 150, 0.5)
Note over UI,DS: Elder ID 업데이트
UI->>VM: submitAllByIds(timeMap)
VM->>Repo: updateElderIds(elderIdMap)
Repo->>DS: 쓰기 (serialized elderIds)
DS-->>Repo: 완료
Repo-->>VM: 성공
VM->>VM: isLoading 해제
VM-->>UI: 상태 반영
UI->>UI: 네비게이션 진행
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
…all-Android into refactor/elderid-#177
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: 7
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/domain/usecase/CheckLoginStatusUseCase.kt (1)
42-63: 예외 처리 누락 가능성
when (exception)블록이HttpException만 처리하고 있어요. 다른 타입의 예외(예:IOException,SocketTimeoutException등)가 발생하면 조용히 무시되고 다음 반복으로 넘어가서, 실제로 에러가 있어도 홈 화면으로 이동할 수 있습니다.네트워크 관련 다른 예외들도 적절히 처리하는 것이 좋을 것 같습니다.
🛡️ 제안하는 수정
.onFailure { exception -> when (exception) { is HttpException -> { val code = exception.code() val errorBody = exception.response()?.errorBody()?.string() Log.e("httplog", "HTTP 에러 발생 - 코드: $code, 메시지: $errorBody") when (code) { 404 -> { // 404 Not Found 에러 처리 Log.d("httplog", "시간 설정 정보 없음, 시간 등록 화면으로") return@runCatching NavigationDestination.GoToTimeSetting } else -> { return@runCatching NavigationDestination.GoToLogin } } } + else -> { + Log.e("httplog", "시간 설정 확인 중 예외 발생", exception) + return@runCatching NavigationDestination.GoToLogin + } } }
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt`:
- Around line 3-31: The updateElderId method currently reads with
getElderIds().first() then writes, which can lose concurrent updates; change it
to perform the modification inside the DataStore's atomic updateData block by
calling context.elderIdsDataStore.updateData { current -> val m =
current.elderIds.toMutableMap(); m[elderId] = name; current.copy(elderIds = m) }
so the read-modify-write occurs atomically within updateData (refer to function
updateElderId and the elderIdsDataStore and updateData symbols).
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt`:
- Around line 69-98: Reference to a non-existent elderIdMap causes the compile
error and unsafe uses of .first() and animateScrollToItem(id) can crash; replace
all elderIdMap usages with the actual elderMap (e.g., where selectedId is
initialized and where isAllComplete is called), guard against an empty elderMap
when computing selectedId by using a safe fallback (use firstOrNull() or
conditional logic to show an empty state if elderMap is empty), and when
scrolling convert from an elder id to its index (use elderMap.keys.toList() or
elderIds.indexOf(selectedId)) and only call animateScrollToItem with a valid
index (check >=0 and within itemCount) to avoid out-of-range crashes; update any
call to callTimeViewModel.isAllComplete(...) to pass elderMap.keys (not
elderIdMap.keys).
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`:
- Around line 57-60: In CallTimeViewModel update the erroneous reference to
lastError by assigning the caught exception to the renamed state variable
(error) instead; replace the line using lastError.value = e with the correct
state variable access (e.g., error.value = e) inside the catch block so the
ViewModel compiles and uses the new `error` property.
- Around line 40-55: You’re initializing _elderIdMap once (using first()) while
_elderIds is updated continuously in observeElderIds(), causing them to drift
out of sync; change observeElderIds() so the collect { result -> ... } block
updates both _elderIds and _elderIdMap together (derive the map form from the
collected result) instead of doing a one-time _elderIdMap initialization,
ensuring both mutable states (_elderIds and _elderIdMap) are updated atomically
inside the collect branch that consumes elderIdRepository.getElderIds().
- Around line 34-38: The update lambda passed to MutableStateFlow._elderIdMap
cannot call suspend functions like elderIdRepository.getElderIds().first(); move
the suspend call out of the update block inside the init's viewModelScope.launch
(e.g., val ids = elderIdRepository.getElderIds().first()), then call
_elderIdMap.update { /* produce new state from ids */ } or directly set
_elderIdMap.value = /* new state based on ids */; keep references to
_elderIdMap, elderIdRepository.getElderIds(), viewModelScope.launch and init
when applying the change.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt`:
- Around line 49-55: The local mutable state isClicked in PaymentScreen is never
updated by the UI and causes the selection UI to stay inactive; remove the local
isClicked and instead subscribe to the view model's selection state
(paymentViewModel.isPaymentSelected) inside PaymentScreen (e.g.,
collectAsState/observeAsState) and use that value for the button border and CTA
enabled logic; update the payment option button click handler to call the
existing viewModel method that toggles/sets isPaymentSelected (refer to
paymentViewModel.isPaymentSelected and the toggle/set method you already call at
line ~143) and keep totalAmount from paymentViewModel.getTotalPrice() unchanged.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt`:
- Line 22: 생성자에서 주입된 사용되지 않는 의존성 elderInfoRepo: EldersInfoRepository를 제거하세요:
NaverPayViewModel의 생성자 시그니처에서 elderInfoRepo 파라미터를 삭제하고 관련 코드에서 해당 변수를 참조하는 부분이
전혀 없는지 확인한 뒤 불필요한 import 문(예: EldersInfoRepository)도 제거하세요; 기존에 대체된
elderIdRepository를 그대로 유지하고, 컴파일 에러가 생기면 생성자 호출부(뷰모델 바인딩/DI 모듈)도 함께 수정하여 파라미터
목록을 일치시켜 주세요.
🧹 Nitpick comments (4)
app/src/main/java/com/konkuk/medicarecall/domain/usecase/CheckLoginStatusUseCase.kt (2)
30-34: Kotlin 관용적 표현 사용 권장
put()메서드 대신 Kotlin의 인덱스 연산자[]를 사용하면 더 간결하고 관용적입니다.♻️ 제안하는 수정
val elderIdMap: MutableMap<Int, String> = mutableMapOf() elders.forEach { elderInfo -> - elderIdMap.put(elderInfo.elderId, elderInfo.name) + elderIdMap[elderInfo.elderId] = elderInfo.name }또는 더 간결하게:
val elderIdMap = elders.associate { it.elderId to it.name }.toMutableMap()
38-40: 구조 분해 선언으로 가독성 개선
it.key를 사용하는 대신 구조 분해를 활용하면 의도가 더 명확해집니다.♻️ 제안하는 수정
// 2. 시간 설정 확인 -elderIdMap.forEach { - eldersInfoRepository.getCareCallTimes(it.key) +elderIdMap.forEach { (elderId, _) -> + eldersInfoRepository.getCareCallTimes(elderId)app/src/main/java/com/konkuk/medicarecall/data/model/ElderIds.kt (1)
3-5: LGTM - DataStore 직렬화를 위한 심플한 데이터 클래스구조가 깔끔합니다. DataStore Serializer에서
defaultValue를 제공할 때ElderIds(emptyMap())을 사용할 것으로 보이는데, 여기서 기본값을 지정해두면 사용처에서 더 편할 수 있습니다:data class ElderIds( val elderIds: Map<Int, String> = emptyMap() )필수는 아니고 선택 사항입니다.
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt (1)
12-12: 사용되지 않는 import
EldersInfoRepositoryimport가 더 이상 사용되지 않는 것 같습니다.
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt
Show resolved
Hide resolved
| val elderMap = callTimeViewModel.elderIds | ||
| // Map<Int, String> -> viewmodel에서 직접 가져옴 | ||
| val isLoading = callTimeViewModel.isLoading.value | ||
|
|
||
| val isLoading = eldersInfoViewModel.isLoading.value | ||
| val error = eldersInfoViewModel.error.value | ||
| val nameIdList = eldersInfoViewModel.elderNameIdMapList | ||
|
|
||
| when { | ||
| isLoading -> { | ||
| Box( | ||
| Modifier | ||
| .fillMaxSize() | ||
| .background(MediCareCallTheme.colors.bg) | ||
| .systemBarsPadding(), | ||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| CircularProgressIndicator( | ||
| color = MediCareCallTheme.colors.main, | ||
| modifier = Modifier.align(Alignment.Center), | ||
| ) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| error != null -> { | ||
| Column( | ||
| Modifier.fillMaxSize(), | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| verticalArrangement = Arrangement.Center, | ||
| ) { | ||
| Text("어르신 정보를 불러오지 못했어요.\n잠시 후 다시 시도해 주세요.") | ||
| Spacer(Modifier.height(12.dp)) | ||
| CTAButton( | ||
| type = CTAButtonType.GREEN, | ||
| text = "다시 시도", | ||
| onClick = { eldersInfoViewModel.refresh() }, | ||
| ) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| nameIdList.isEmpty() -> { | ||
| Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { | ||
| Text("등록된 어르신이 없습니다.") | ||
| } | ||
| return | ||
| if (isLoading) { | ||
| Box( | ||
| Modifier | ||
| .fillMaxSize() | ||
| .background(MediCareCallTheme.colors.bg), | ||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| CircularProgressIndicator( | ||
| color = MediCareCallTheme.colors.main, | ||
| ) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| val scrollState = rememberScrollState() // 스크롤 상태 | ||
| var showBottomSheet by remember { mutableStateOf(false) } // 하단 시트 제어 | ||
| val elderNames = nameIdList.map { it.keys.first() } // 어르신 이름 리스트 | ||
| val elderIds = nameIdList.map { it.values.first() } // 어르신 아이디 리스트 | ||
| val elderNames = elderMap.values.toList() | ||
| val elderIds = elderMap.keys.toList() | ||
|
|
||
| var selectedIndex by remember { mutableIntStateOf(0) } // 선택된 어르신 인덱스 | ||
| val selectedId = elderIds.getOrNull(selectedIndex) ?: 0 // 선택된 어르신 아이디 | ||
| // var selectedIndex by remember { mutableIntStateOf(0) } // 선택된 어르신 인덱스 | ||
| var selectedId by remember { mutableIntStateOf(elderIdMap.keys.first()) } // 선택된 어르신 아이디 | ||
| val saved = callTimeViewModel.timeMap[selectedId] ?: CallTimes() | ||
| var selectedTabIndex by remember { mutableIntStateOf(0) } | ||
|
|
||
| val allComplete = callTimeViewModel.isAllComplete(elderIds) | ||
| val allComplete = callTimeViewModel.isAllComplete(elderIdMap.keys) | ||
|
|
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.
🧩 Analysis chain
🏁 Script executed:
find . -name "CallTimeScreen.kt" -type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 173
🏁 Script executed:
wc -l ./app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 177
🏁 Script executed:
cat -n ./app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 17913
elderIdMap 오타로 컴파일 실패 + 빈 목록/스크롤 인덱스 처리 필수
변수 선언은 elderMap인데 93, 97, 204, 206, 318번 줄에서 elderIdMap을 참조해서 컴파일 에러가 발생합니다. 추가로 93번 줄의 .first()는 빈 목록일 때 크래시나고, 221번 줄의 animateScrollToItem(id)는 id가 인덱스가 아니라 범위를 벗어날 수 있어요.
🛠️ 수정 안내
- var selectedId by remember { mutableIntStateOf(elderIdMap.keys.first()) }
+ if (elderIds.isEmpty()) {
+ // TODO: 빈 상태 UI
+ return
+ }
+ var selectedId by remember(elderIds) { mutableIntStateOf(elderIds.first()) }
@@
- val allComplete = callTimeViewModel.isAllComplete(elderIdMap.keys)
+ val allComplete = callTimeViewModel.isAllComplete(elderIds)
@@
- items(elderIdMap.keys.toList()) { id ->
+ items(elderIds) { id ->
Text(
- text = elderIdMap[id] ?: "",
+ text = elderMap[id] ?: "",
@@
.clickable {
selectedId = id
scope.launch {
- listState.animateScrollToItem(id)
+ val index = elderIds.indexOf(id)
+ if (index >= 0) listState.animateScrollToItem(index)
}
}
@@
- elderIds = elderIdMap.keys.toList(),
+ elderIds = elderIds,🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt`
around lines 69 - 98, Reference to a non-existent elderIdMap causes the compile
error and unsafe uses of .first() and animateScrollToItem(id) can crash; replace
all elderIdMap usages with the actual elderMap (e.g., where selectedId is
initialized and where isAllComplete is called), guard against an empty elderMap
when computing selectedId by using a safe fallback (use firstOrNull() or
conditional logic to show an empty state if elderMap is empty), and when
scrolling convert from an elder id to its index (use elderMap.keys.toList() or
elderIds.indexOf(selectedId)) and only call animateScrollToItem with a valid
index (check >=0 and within itemCount) to avoid out-of-range crashes; update any
call to callTimeViewModel.isAllComplete(...) to pass elderMap.keys (not
elderIdMap.keys).
...c/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt
Show resolved
Hide resolved
| // Flow -> State 로 뱐환해서 보관 | ||
| private val _elderIds = mutableStateOf<Map<Int, String>>(emptyMap()) | ||
| val elderIds get() = _elderIds.value // UI에서 접근할 값 | ||
|
|
||
| init { | ||
| observeElderIds() | ||
| } | ||
|
|
||
| // suspend + Flow 안전하게 처리하는 함수 | ||
| private fun observeElderIds() { | ||
| viewModelScope.launch { | ||
| try { | ||
| elderIdRepository.getElderIds() | ||
| .collect { result -> | ||
| _elderIds.value = result | ||
| Log.d("CallTimeViewModel", "elderIds 업데이트: $result") |
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.
🧩 Analysis chain
🏁 Script executed:
find . -name "CallTimeViewModel.kt" -type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 179
🏁 Script executed:
cat -n ./app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 4328
두 상태가 따로 관리되고 있어서 동기화되지 않네요.
현재 코드를 보니 _elderIdMap은 초기화 때 한 번만 업데이트되고 (first()로), _elderIds는 collect에서 계속 갱신되고 있어요. 그러다 보니 새로운 데이터가 들어오면 _elderIds만 업데이트되고 _elderIdMap은 그대로 머물러서 둘이 쉽게 불일치할 수 있습니다.
collect 블록에서 두 상태를 함께 갱신하는 게 안전합니다:
수정안
elderIdRepository.getElderIds()
.collect { result ->
_elderIds.value = result
+ _elderIdMap.update { result }
Log.d("CallTimeViewModel", "elderIds 업데이트: $result")
}🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`
around lines 40 - 55, You’re initializing _elderIdMap once (using first()) while
_elderIds is updated continuously in observeElderIds(), causing them to drift
out of sync; change observeElderIds() so the collect { result -> ... } block
updates both _elderIds and _elderIdMap together (derive the map form from the
collected result) instead of doing a one-time _elderIdMap initialization,
ensuring both mutable states (_elderIds and _elderIdMap) are updated atomically
inside the collect branch that consumes elderIdRepository.getElderIds().
...c/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt
Show resolved
Hide resolved
| paymentViewModel: PaymentViewModel = hiltViewModel(), | ||
| ) { | ||
| val scrollState = rememberScrollState() | ||
| val elderMap = paymentViewModel.elderMap | ||
| val elders = elderMap.values.toList() | ||
| var isClicked by remember { mutableStateOf(false) } | ||
| val elders = elderInfoViewModel.eldersInfoList.map { it.name } | ||
| val totalAmount = paymentViewModel.getTotalPrice() // Assuming 29,000 is the monthly fee per 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.
🧩 Analysis chain
🏁 Script executed:
fd -t f PaymentScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 169
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt | head -200Repository: Medicare-Call/Medicare-Call-Android
Length of output: 8003
🏁 Script executed:
rg "isPaymentSelected" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/Repository: Medicare-Call/Medicare-Call-Android
Length of output: 381
🏁 Script executed:
rg "isClicked" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt -A 3 -B 3Repository: Medicare-Call/Medicare-Call-Android
Length of output: 975
선택 상태가 UI에 반영 안 됨
로컬 isClicked가 false로 고정되어 있고, 143번 줄의 버튼 클릭은 뷰모델 상태만 토글합니다. 하지만 UI(150번, 163번, 165번 줄)는 여전히 로컬 isClicked를 바라보고 있어서 버튼 테두리와 CTA 버튼이 영구적으로 비활성 상태로 유지됩니다. 뷰모델의 isPaymentSelected를 직접 구독하도록 변경해 주세요.
🛠️ 제안 수정
- var isClicked by remember { mutableStateOf(false) }
+ val isClicked by paymentViewModel.isPaymentSelected🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt`
around lines 49 - 55, The local mutable state isClicked in PaymentScreen is
never updated by the UI and causes the selection UI to stay inactive; remove the
local isClicked and instead subscribe to the view model's selection state
(paymentViewModel.isPaymentSelected) inside PaymentScreen (e.g.,
collectAsState/observeAsState) and use that value for the button border and CTA
enabled logic; update the payment option button click handler to call the
existing viewModel method that toggles/sets isPaymentSelected (refer to
paymentViewModel.isPaymentSelected and the toggle/set method you already call at
line ~143) and keep totalAmount from paymentViewModel.getTotalPrice() unchanged.
...rc/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/NaverPayViewModel.kt
Outdated
Show resolved
Hide resolved
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
app/src/main/java/com/konkuk/medicarecall/domain/usecase/CheckLoginStatusUseCase.kt (1)
39-64: HttpException 외 오류가 조용히 무시됩니다.
주석에 “어떤 에러든 로그인 화면”이라고 되어 있는데, 현재는 HttpException만 처리하고 나머지 예외는 계속 진행됩니다. 비‑HttpException도 로그인 이동(또는 throw)으로 처리해 주세요.🔧 수정 방향
.onFailure { exception -> when (exception) { is HttpException -> { val code = exception.code() val errorBody = exception.response()?.errorBody()?.toString() @@ else -> { return@runCatching NavigationDestination.GoToLogin } } } + else -> { + return@runCatching NavigationDestination.GoToLogin + } } }app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt (2)
6-14: Unused import 때문에 CI가 깨집니다.
EldersInfoRepository,CancellationException,StateFlow가 사용되지 않아 빌드가 실패합니다. 제거해 주세요.
62-113:_timeMap,_isLoading,_lastError필드가 존재하지 않아 컴파일 실패합니다.코드는
_timeMap(라인 63, 69, 89),_isLoading(라인 105),_lastError(라인 112)를 참조하지만, 실제 선언된 필드는timeMap,isLoading,error입니다 (라인 25-27). 또한 라인 57에서 선언되지 않은lastError도 참조 중입니다.선언된 필드명과 사용하는 필드명을 통일해야 합니다:
_timeMap→timeMap_isLoading→isLoading_lastError/lastError→error수정 예시
fun setTimes(id: Int, times: CallTimes) { - _timeMap.update { currentMap -> - currentMap.toMutableMap().apply { put(id, times) } - } + timeMap[id] = times } fun isCompleteFor(id: Int): Boolean { - val t = _timeMap.value[id] ?: return false + val t = timeMap[id] ?: return false return t.first != null && t.second != null && t.third != null } @@ @@ val times = _timeMap.value[id] ?: error("'$id'의 시간이 비어있습니다.") + val times = timeMap[id] ?: error("'$id'의 시간이 비어있습니다.") @@ } catch (t: Throwable) { Log.e("CallTimeViewModel", "submitAllByName failed", t) error.value = t onError(t) } finally { - _isLoading.value = false + isLoading.value = false } } } @@ fun clearError() { - _lastError.value = null + error.value = null }app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt (1)
35-46: Unused import 때문에 CI가 실패합니다.
중복/미사용collectAsStateWithLifecycle,koinViewModelimport를 제거해 주세요.
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt`:
- Around line 3-20: The class is using a non-injected context and missing the
ElderIdsSerializer import; inject a Context into ElderIdRepositoryImpl (e.g.,
add a constructor parameter or use Koin/DI annotation) so the class can
reference context for the extension property Context.elderIdsDataStore and
updateElderIds, and add the missing import for ElderIdsSerializer so the
dataStore delegate compiles; ensure the constructor injection is
annotated/visible to your DI framework and update usages of
context.elderIdsDataStore in updateElderIds to use the injected context
instance.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt`:
- Around line 134-141: Remove the blank line immediately before the closing
brace in the block that restores selection (the block using
KEY_SELECTED_ELDER_ID and variable restoredId) so there is no empty line before
the '}' that ends the if/else block; update the block around restoredId,
_elderInfoList, and _selectedElderId to have the closing brace directly after
the last statement.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt`:
- Around line 91-98: Local UI checks use local mutable state (showBottomSheet,
selectedTabIndex) instead of collecting the ViewModel state, so UI doesn't react
to callTimeViewModel changes; replace the local remember { mutableStateOf(...) }
/ mutableIntStateOf(...) usages with collected State from callTimeViewModel (use
collectAsStateWithLifecycle on the ViewModel's StateFlow properties that back
showBottomSheet and selectedTabIndex) and use those collected values in the UI;
keep calling setShowBottomSheet() and setSelectedTabIndex() on callTimeViewModel
but remove or sync the local vars (showBottomSheet, selectedTabIndex) so the
composable reads state from callTimeViewModel via collectAsStateWithLifecycle().
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`:
- Around line 29-31: Remove the unnecessary semicolon and any consecutive blank
lines in the declaration of elderIdMap; replace "val elderIdMap =
_elderIdMap.asStateFlow();" with a semicolon-free single-line declaration (use
val elderIdMap = _elderIdMap.asStateFlow()) and ensure no extra blank lines
around it to satisfy NoSemicolons and NoConsecutiveBlankLines rules, locating
the change in CallTimeViewModel where elderIdMap is defined.
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt`:
- Around line 20-26: Replace the Compose mutableStateOf-based properties with
Flow-backed state and fix the incorrect underscore references: remove
eldersInfoList, isLoading, error, errorMessage defined via mutableStateOf and
instead define a private MutableStateFlow<List<EldersInfoResponseDto>>
_eldersInfoList and public StateFlow eldersInfoList, plus private
MutableStateFlow<Boolean> _isLoading / public StateFlow isLoading and private
MutableStateFlow<Throwable?> _error / public StateFlow error and private
MutableStateFlow<String?> _errorMessage / public StateFlow errorMessage; update
all assignments to set .value on the private MutableStateFlow instances (e.g.,
_eldersInfoList.value = list, _error.value = null, _errorMessage.value = null)
and remove or fix any references to non-existent names (change references of
error and errorMessage to _error/.value when mutating), and remove unnecessary
mutableStateOf/getValue/setValue imports or replace with kotlinx.coroutines.flow
imports as appropriate.
- Around line 41-58: getElders() is a suspend function so using Result.onSuccess
lambda prevents calling suspend updateElderIds() and the code incorrectly
references non-existent _error/_errorMessage fields; replace the
onSuccess/onFailure chain inside EldersInfoViewModel with an explicit suspend
try/catch: call val list = eldersInfoRepository.getElders() inside try, set
eldersInfoList, build mapped and call elderIdRepository.updateElderIds(mapped)
(suspend), clear error and errorMessage via the existing error and errorMessage
properties, and in catch set error.value, errorMessage and Log.e; ensure
isLoading.value = false runs after the try/catch.
| import android.content.Context | ||
| import androidx.datastore.dataStore | ||
| import com.konkuk.medicarecall.data.model.ElderIds | ||
| import com.konkuk.medicarecall.data.repository.ElderIdRepository | ||
| import org.koin.core.annotation.Single | ||
|
|
||
| val Context.elderIdsDataStore by dataStore( | ||
| fileName = "elderIds", | ||
| serializer = ElderIdsSerializer, | ||
| ) | ||
|
|
||
|
|
||
| @Single | ||
| class ElderIdRepositoryImpl : ElderIdRepository { | ||
|
|
||
| private val elderIds: MutableList<Map<String, Int>> = mutableListOf() | ||
|
|
||
| override fun addElderId(name: String, id: Int) { | ||
| this.elderIds.add(mapOf(name to id)) | ||
| override suspend fun updateElderIds(elderIdMap: Map<Int, String>) { | ||
| context.elderIdsDataStore.updateData { it.copy(elderIds = elderIdMap) } | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "ElderIdRepositoryImpl.kt" | head -5Repository: Medicare-Call/Medicare-Call-Android
Length of output: 167
🏁 Script executed:
fd "ElderIdRepositoryImpl" --type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 165
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 1593
context 미주입 + ElderIdsSerializer import 누락으로 컴파일 실패합니다.
context가 클래스에 주입되지 않았는데 메서드 내에서 사용되고 있고(19, 25, 29, 34줄), ElderIdsSerializer도 import가 없습니다(11줄 참조). 두 가지 모두 즉시 컴파일 에러가 발생합니다.
🛠️ 수정 예시
import android.content.Context
import androidx.datastore.dataStore
import com.konkuk.medicarecall.data.model.ElderIds
+import com.konkuk.medicarecall.data.util.ElderIdsSerializer
import com.konkuk.medicarecall.data.repository.ElderIdRepository
import org.koin.core.annotation.Single
@@
`@Single`
-class ElderIdRepositoryImpl : ElderIdRepository {
+class ElderIdRepositoryImpl(
+ private val context: Context,
+) : ElderIdRepository {📝 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.
| import android.content.Context | |
| import androidx.datastore.dataStore | |
| import com.konkuk.medicarecall.data.model.ElderIds | |
| import com.konkuk.medicarecall.data.repository.ElderIdRepository | |
| import org.koin.core.annotation.Single | |
| val Context.elderIdsDataStore by dataStore( | |
| fileName = "elderIds", | |
| serializer = ElderIdsSerializer, | |
| ) | |
| @Single | |
| class ElderIdRepositoryImpl : ElderIdRepository { | |
| private val elderIds: MutableList<Map<String, Int>> = mutableListOf() | |
| override fun addElderId(name: String, id: Int) { | |
| this.elderIds.add(mapOf(name to id)) | |
| override suspend fun updateElderIds(elderIdMap: Map<Int, String>) { | |
| context.elderIdsDataStore.updateData { it.copy(elderIds = elderIdMap) } | |
| } | |
| import android.content.Context | |
| import androidx.datastore.dataStore | |
| import com.konkuk.medicarecall.data.model.ElderIds | |
| import com.konkuk.medicarecall.data.util.ElderIdsSerializer | |
| import com.konkuk.medicarecall.data.repository.ElderIdRepository | |
| import org.koin.core.annotation.Single | |
| val Context.elderIdsDataStore by dataStore( | |
| fileName = "elderIds", | |
| serializer = ElderIdsSerializer, | |
| ) | |
| `@Single` | |
| class ElderIdRepositoryImpl( | |
| private val context: Context, | |
| ) : ElderIdRepository { | |
| override suspend fun updateElderIds(elderIdMap: Map<Int, String>) { | |
| context.elderIdsDataStore.updateData { it.copy(elderIds = elderIdMap) } | |
| } |
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt`
around lines 3 - 20, The class is using a non-injected context and missing the
ElderIdsSerializer import; inject a Context into ElderIdRepositoryImpl (e.g.,
add a constructor parameter or use Koin/DI annotation) so the class can
reference context for the extension property Context.elderIdsDataStore and
updateElderIds, and add the missing import for ElderIdsSerializer so the
dataStore delegate compiles; ensure the constructor injection is
annotated/visible to your DI framework and update usages of
context.elderIdsDataStore in updateElderIds to use the injected context
instance.
| val restoredId = savedStateHandle.get<Int?>(KEY_SELECTED_ELDER_ID) | ||
| if (restoredId != null && _elderInfoList.value.any { it.id == restoredId }) { | ||
| _selectedElderId.value = restoredId | ||
| } else if (_selectedElderId.value == null && _elderInfoList.value.isNotEmpty()) { | ||
| _selectedElderId.value = _elderInfoList.value.first().id | ||
| } | ||
|
|
||
| } |
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.
중괄호 직전 공백 라인 제거 필요합니다.
CI에서 NoBlankLineBeforeRbrace가 발생하고 있어요. 빈 줄을 삭제해 주세요.
🧹 예시 수정
} else if (_selectedElderId.value == null && _elderInfoList.value.isNotEmpty()) {
_selectedElderId.value = _elderInfoList.value.first().id
}
-
}📝 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.
| val restoredId = savedStateHandle.get<Int?>(KEY_SELECTED_ELDER_ID) | |
| if (restoredId != null && _elderInfoList.value.any { it.id == restoredId }) { | |
| _selectedElderId.value = restoredId | |
| } else if (_selectedElderId.value == null && _elderInfoList.value.isNotEmpty()) { | |
| _selectedElderId.value = _elderInfoList.value.first().id | |
| } | |
| } | |
| val restoredId = savedStateHandle.get<Int?>(KEY_SELECTED_ELDER_ID) | |
| if (restoredId != null && _elderInfoList.value.any { it.id == restoredId }) { | |
| _selectedElderId.value = restoredId | |
| } else if (_selectedElderId.value == null && _elderInfoList.value.isNotEmpty()) { | |
| _selectedElderId.value = _elderInfoList.value.first().id | |
| } | |
| } |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 140-140: NoBlankLineBeforeRbrace: Unexpected blank line(s) before ***
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt`
around lines 134 - 141, Remove the blank line immediately before the closing
brace in the block that restores selection (the block using
KEY_SELECTED_ELDER_ID and variable restoredId) so there is no empty line before
the '}' that ends the if/else block; update the block around restoredId,
_elderInfoList, and _selectedElderId to have the closing brace directly after
the last statement.
| var showBottomSheet by remember { mutableStateOf(false) } // 하단 시트 제어 | ||
| val elderNames = elderMap.values.toList() | ||
| val elderIds = elderMap.keys.toList() | ||
|
|
||
| val selectedId = elderIds.getOrNull(selectedIndex) ?: 0 // 선택된 어르신 아이디 | ||
| val saved = timeMap[selectedId] ?: CallTimes() | ||
| // var selectedIndex by remember { mutableIntStateOf(0) } // 선택된 어르신 인덱스 | ||
| var selectedId by remember { mutableIntStateOf(elderIdMap.keys.first()) } // 선택된 어르신 아이디 | ||
| val saved = callTimeViewModel.timeMap[selectedId] ?: CallTimes() | ||
| var selectedTabIndex by remember { mutableIntStateOf(0) } |
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.
🧩 Analysis chain
🏁 Script executed:
find . -name "CallTimeScreen.kt" -type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 173
🏁 Script executed:
fd "CallTimeScreen.kt" --type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 171
🏁 Script executed:
wc -l app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 175
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt | sed -n '80,110p'Repository: Medicare-Call/Medicare-Call-Android
Length of output: 1415
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt | sed -n '330,350p'Repository: Medicare-Call/Medicare-Call-Android
Length of output: 1326
🏁 Script executed:
grep -n "showBottomSheet" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 194
🏁 Script executed:
grep -n "selectedTabIndex" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 203
🏁 Script executed:
grep -n "showBottomSheet = true\|showBottomSheet\s*=" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 61
🏁 Script executed:
grep -n "setShowBottomSheet\|setSelectedTabIndex" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 795
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt | sed -n '200,250p'Repository: Medicare-Call/Medicare-Call-Android
Length of output: 2775
🏁 Script executed:
fd "CallTimeViewModel" --type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 177
🏁 Script executed:
grep -rn "class CallTimeViewModel" app/Repository: Medicare-Call/Medicare-Call-Android
Length of output: 225
🏁 Script executed:
wc -l app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 181
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt | head -100Repository: Medicare-Call/Medicare-Call-Android
Length of output: 4165
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt | tail -50Repository: Medicare-Call/Medicare-Call-Android
Length of output: 1813
🏁 Script executed:
grep -n "showBottomSheet\|selectedTabIndex" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 165
🏁 Script executed:
grep -n "_showBottomSheet\|_selectedTabIndex\|_timeMap\|_isLoading\|_lastError" app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 428
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 4917
로컬 상태와 ViewModel 상태가 동기화되지 않아 BottomSheet가 반응하지 않습니다.
버튼 클릭 시 ViewModel의 setShowBottomSheet()과 setSelectedTabIndex()를 호출하지만, UI는 여전히 로컬 상태(remember { mutableStateOf(false) })를 확인하고 있습니다. ViewModel 상태 변화가 UI에 반영되지 않습니다. ViewModel에서 StateFlow로 노출된 상태를 collectAsStateWithLifecycle()으로 수집하도록 변경해 주세요.
예시 방향
- var showBottomSheet by remember { mutableStateOf(false) }
- var selectedTabIndex by remember { mutableIntStateOf(0) }
+ val showBottomSheet by callTimeViewModel.showBottomSheet.collectAsStateWithLifecycle()
+ val selectedTabIndex by callTimeViewModel.selectedTabIndex.collectAsStateWithLifecycle()🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt`
around lines 91 - 98, Local UI checks use local mutable state (showBottomSheet,
selectedTabIndex) instead of collecting the ViewModel state, so UI doesn't react
to callTimeViewModel changes; replace the local remember { mutableStateOf(...) }
/ mutableIntStateOf(...) usages with collected State from callTimeViewModel (use
collectAsStateWithLifecycle on the ViewModel's StateFlow properties that back
showBottomSheet and selectedTabIndex) and use those collected values in the UI;
keep calling setShowBottomSheet() and setSelectedTabIndex() on callTimeViewModel
but remove or sync the local vars (showBottomSheet, selectedTabIndex) so the
composable reads state from callTimeViewModel via collectAsStateWithLifecycle().
| val elderIdMap = _elderIdMap.asStateFlow(); | ||
|
|
||
| // Time data | ||
| private val _timeMap = MutableStateFlow<Map<Int, CallTimes>>(emptyMap()) | ||
| val timeMap: StateFlow<Map<Int, CallTimes>> = _timeMap.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.
불필요한 세미콜론/연속 공백 줄 제거가 필요합니다.
CI에서 NoSemicolons, NoConsecutiveBlankLines가 발생합니다.
🧹 예시 수정
- val elderIdMap = _elderIdMap.asStateFlow();
-
+ val elderIdMap = _elderIdMap.asStateFlow()📝 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.
| val elderIdMap = _elderIdMap.asStateFlow(); | |
| // Time data | |
| private val _timeMap = MutableStateFlow<Map<Int, CallTimes>>(emptyMap()) | |
| val timeMap: StateFlow<Map<Int, CallTimes>> = _timeMap.asStateFlow() | |
| val elderIdMap = _elderIdMap.asStateFlow() |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 31-31: NoConsecutiveBlankLines: Needless blank line(s)
[error] 29-29: NoSemicolons: Unnecessary semicolon
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt`
around lines 29 - 31, Remove the unnecessary semicolon and any consecutive blank
lines in the declaration of elderIdMap; replace "val elderIdMap =
_elderIdMap.asStateFlow();" with a semicolon-free single-line declaration (use
val elderIdMap = _elderIdMap.asStateFlow()) and ensure no extra blank lines
around it to satisfy NoSemicolons and NoConsecutiveBlankLines rules, locating
the change in CallTimeViewModel where elderIdMap is defined.
| var eldersInfoList by mutableStateOf<List<EldersInfoResponseDto>>(emptyList()) | ||
| private set | ||
|
|
||
| private val _eldersInfoList = MutableStateFlow<List<EldersInfoResponseDto>>(emptyList()) | ||
| val eldersInfoList: StateFlow<List<EldersInfoResponseDto>> = _eldersInfoList.asStateFlow() | ||
|
|
||
| private val _isLoading = MutableStateFlow(false) | ||
| val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow() | ||
|
|
||
| private val _error = MutableStateFlow<Throwable?>(null) | ||
| val error: StateFlow<Throwable?> = _error.asStateFlow() | ||
|
|
||
|
|
||
| private val _elderNameIdMapList = MutableStateFlow(elderIdRepository.getElderIds()) | ||
| val elderNameIdMapList: StateFlow<List<Map<String, Int>>> = _elderNameIdMapList.asStateFlow() | ||
|
|
||
| private val _errorMessage = MutableStateFlow<String?>(null) | ||
| val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow() | ||
| val isLoading = mutableStateOf(false) | ||
| val error = mutableStateOf<Throwable?>(null) | ||
| var errorMessage by mutableStateOf<String?>(null) | ||
| private set |
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.
🧩 Analysis chain
🏁 Script executed:
cat -n "app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt"Repository: Medicare-Call/Medicare-Call-Android
Length of output: 2467
mutableStateOf 사용이 여러 문제를 만들고 있습니다.
-
누락된 import로 컴파일 실패:
androidx.compose.runtime에서mutableStateOf,getValue,setValue를 import하지 않아 컴파일되지 않습니다. -
존재하지 않는 필드 참조: 49-50줄에서
_error,_errorMessage를 참조하는데, 실제 필드는error,errorMessage입니다 (언더스코어 없음). -
아키텍처 문제: ViewModel에서
mutableStateOf는 Compose UI 상태용이라 부적절합니다. 이미 import된MutableStateFlow/StateFlow를 사용해야 합니다.
-import android.util.Log
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto
-import com.konkuk.medicarecall.data.repository.ElderIdRepository
-import com.konkuk.medicarecall.data.repository.EldersInfoRepository
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
-import org.koin.android.annotation.KoinViewModel
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto
+import com.konkuk.medicarecall.data.repository.ElderIdRepository
+import com.konkuk.medicarecall.data.repository.EldersInfoRepository
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import org.koin.android.annotation.KoinViewModel
- var eldersInfoList by mutableStateOf<List<EldersInfoResponseDto>>(emptyList())
+ private val _eldersInfoList = MutableStateFlow<List<EldersInfoResponseDto>>(emptyList())
+ val eldersInfoList: StateFlow<List<EldersInfoResponseDto>> = _eldersInfoList.asStateFlow()
- val isLoading = mutableStateOf(false)
- val error = mutableStateOf<Throwable?>(null)
- var errorMessage by mutableStateOf<String?>(null)
+ val isLoading = MutableStateFlow(false)
+ val error = MutableStateFlow<Throwable?>(null)
+ val errorMessage = MutableStateFlow<String?>(null)그리고 43줄: eldersInfoList = list 대신 _eldersInfoList.value = list, 49-50줄: error.value = null, errorMessage.value = null으로 수정해야 합니다.
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt`
around lines 20 - 26, Replace the Compose mutableStateOf-based properties with
Flow-backed state and fix the incorrect underscore references: remove
eldersInfoList, isLoading, error, errorMessage defined via mutableStateOf and
instead define a private MutableStateFlow<List<EldersInfoResponseDto>>
_eldersInfoList and public StateFlow eldersInfoList, plus private
MutableStateFlow<Boolean> _isLoading / public StateFlow isLoading and private
MutableStateFlow<Throwable?> _error / public StateFlow error and private
MutableStateFlow<String?> _errorMessage / public StateFlow errorMessage; update
all assignments to set .value on the private MutableStateFlow instances (e.g.,
_eldersInfoList.value = list, _error.value = null, _errorMessage.value = null)
and remove or fix any references to non-existent names (change references of
error and errorMessage to _error/.value when mutating), and remove unnecessary
mutableStateOf/getValue/setValue imports or replace with kotlinx.coroutines.flow
imports as appropriate.
| eldersInfoRepository.getElders() | ||
| .onSuccess { list -> | ||
| Log.d("EldersInfoViewModel", "노인 개인 정보 불러오기 성공: ${list.size}개") | ||
| _eldersInfoList.value = list | ||
| eldersInfoList = list | ||
|
|
||
| // 이름→ID 매핑(순서 유지) | ||
| val mapped = list.map { mapOf(it.name to it.elderId) } | ||
| _elderNameIdMapList.value = mapped | ||
|
|
||
| // ElderIdRepository 동기화 | ||
| // NOTE: 중복 적재를 피하려면 ElderIdRepository에 replaceAll(...)을 추가하는 걸 추천. | ||
| val repoCurrent = elderIdRepository.getElderIds() | ||
| if (force || repoCurrent.isEmpty()) { | ||
| // 간단 동기화(초기 1회 or refresh 시) | ||
| mapped.forEach { m -> | ||
| val e = m.entries.first() | ||
| elderIdRepository.addElderId(e.key, e.value) | ||
| } | ||
| } | ||
| // 서버 → DataStore 전체 동기화 | ||
| val mapped = list.associate { it.elderId to it.name } | ||
| elderIdRepository.updateElderIds(mapped) | ||
|
|
||
| _error.value = null | ||
| _errorMessage.value = null | ||
| } | ||
| .onFailure { | ||
| _error.value = it | ||
| _errorMessage.value = "노인 개인 정보를 불러오지 못했습니다." | ||
| Log.e("EldersInfoViewModel", "노인 개인 정보 로딩 실패: ${it.message}", it) | ||
| error.value = it | ||
| errorMessage = "노인 개인 정보를 불러오지 못했습니다." | ||
| Log.e("EldersInfoViewModel", "load 실패", it) | ||
| } | ||
|
|
||
| _isLoading.value = false | ||
| isLoading.value = false | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
cat -n app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt | sed -n '30,65p'Repository: Medicare-Call/Medicare-Call-Android
Length of output: 1186
🏁 Script executed:
# Search for _error and _errorMessage field definitions
rg '_error|_errorMessage' app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 165
🏁 Script executed:
# Search for error and errorMessage field definitions
rg 'error.*=' app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt | head -20Repository: Medicare-Call/Medicare-Call-Android
Length of output: 310
🏁 Script executed:
# Check the full ViewModel class definition to understand the field structure
wc -l app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 176
🏁 Script executed:
# Get the beginning of the file to see field declarations
head -50 app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.ktRepository: Medicare-Call/Medicare-Call-Android
Length of output: 1768
🏁 Script executed:
# Search for updateElderIds function signature
rg 'fun updateElderIds' --type kotlinRepository: Medicare-Call/Medicare-Call-Android
Length of output: 377
🏁 Script executed:
# Search for getElders function signature
rg 'fun getElders' --type kotlinRepository: Medicare-Call/Medicare-Call-Android
Length of output: 900
🏁 Script executed:
# Check ElderIdRepository class
fd 'ElderIdRepository.kt' --type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 157
🏁 Script executed:
# Check EldersInfoRepository class
fd 'EldersInfoRepository.kt' --type fRepository: Medicare-Call/Medicare-Call-Android
Length of output: 160
onSuccess 람다에서 suspend 함수 호출 + 존재하지 않는 필드 참조로 컴파일 실패합니다.
getElders()는 suspend 함수지만, Result.onSuccess의 람다는 suspend 람다가 아니라서 updateElderIds() 호출이 불가합니다. 추가로 _error와 _errorMessage 필드는 존재하지 않습니다(실제로는 error와 errorMessage). 분기를 명시적으로 나눠 처리하면 두 문제가 모두 해결됩니다.
🔧 수정 방향
viewModelScope.launch {
- eldersInfoRepository.getElders()
- .onSuccess { list ->
- eldersInfoList = list
-
- // 서버 → DataStore 전체 동기화
- val mapped = list.associate { it.elderId to it.name }
- elderIdRepository.updateElderIds(mapped)
-
- _error.value = null
- _errorMessage.value = null
- }
- .onFailure {
- error.value = it
- errorMessage = "노인 개인 정보를 불러오지 못했습니다."
- Log.e("EldersInfoViewModel", "load 실패", it)
- }
+ val result = eldersInfoRepository.getElders()
+ if (result.isSuccess) {
+ val list = result.getOrThrow()
+ eldersInfoList = list
+ val mapped = list.associate { it.elderId to it.name }
+ elderIdRepository.updateElderIds(mapped)
+ error.value = null
+ errorMessage = null
+ } else {
+ val t = result.exceptionOrNull()
+ error.value = t
+ errorMessage = "노인 개인 정보를 불러오지 못했습니다."
+ Log.e("EldersInfoViewModel", "load 실패", t)
+ }
isLoading.value = false
}🤖 Prompt for AI Agents
In
`@app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt`
around lines 41 - 58, getElders() is a suspend function so using
Result.onSuccess lambda prevents calling suspend updateElderIds() and the code
incorrectly references non-existent _error/_errorMessage fields; replace the
onSuccess/onFailure chain inside EldersInfoViewModel with an explicit suspend
try/catch: call val list = eldersInfoRepository.getElders() inside try, set
eldersInfoList, build mapped and call elderIdRepository.updateElderIds(mapped)
(suspend), clear error and errorMessage via the existing error and errorMessage
properties, and in catch set error.value, errorMessage and Log.e; ensure
isLoading.value = false runs after the try/catch.
🔗 관련 이슈
📙 작업 설명
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.