Skip to content

Conversation

@ProtossManse
Copy link
Collaborator

@ProtossManse ProtossManse commented Jan 29, 2026

🔗 관련 이슈

📙 작업 설명

  • 약 두 달 전 작업했던 내용의 PR이 누락되어 지금 올립니다.

💬 추가 설명 or 리뷰 포인트 (선택)

  • elderId를 내부 스토리지에 저장하도록 변경했습니다.
  • CallTimeScreen에서 eldersInfoViewModel 의존을 제거하고 ViewModel에서 eldersId Repository를 참조하도록 변경했습니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 결제 금액 자동 계산 기능 추가 (보호자당 월 29,000원 기준)
    • 노인 ID 및 정보 관리 개선
    • 오프라인 환경에서의 노인 데이터 로컬 저장 지원
  • 개선사항

    • 돌봄 시간 설정 UI/UX 개선 및 안정성 향상
    • 로그인 후 데이터 동기화 성능 개선
    • 사용자 정보 관리 프로세스 최적화

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

elderIds를 메모리 기반 저장소에서 DataStore를 통한 로컬 스토리지로 이전하고, Flow 기반 API로 전환합니다. 관련 Repository, ViewModel, UI 레이어를 업데이트하여 새로운 비동기 지속성 패턴을 반영합니다.

Changes

Cohort / File(s) Summary
Data Model & Serialization
app/src/main/java/com/konkuk/medicarecall/data/model/ElderIds.kt, app/src/main/java/com/konkuk/medicarecall/data/util/ElderIdsSerializer.kt
ElderIds 데이터 클래스 추가 및 Base64/JSON 기반 Serializer 구현으로 DataStore 직렬화 지원.
Repository Layer
app/src/main/java/com/konkuk/medicarecall/data/repository/ElderIdRepository.kt, app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt
메모리 기반 저장에서 DataStore 기반 지속성으로 마이그레이션; getElderIds()를 Flow 반환으로 변경; suspend 함수 추가 (updateElderIds, updateElderId, clearElderIds).
Use Case & Domain
app/src/main/java/com/konkuk/medicarecall/domain/usecase/CheckLoginStatusUseCase.kt
개별 addElderId 호출을 updateElderIds의 배치 작업으로 대체; HTTP 404 처리 로직 확장.
ViewModels
app/src/main/java/com/konkuk/medicarecall/ui/feature/home/viewmodel/HomeViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/viewmodel/CallTimeViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/viewmodel/PaymentViewModel.kt, app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/EldersInfoViewModel.kt
elderIdRepository 의존성 추가 및 Flow 기반 elder ID 데이터 통합; PaymentViewModel 신규 추가; State 관리 방식 정규화.
Senior/Login ViewModel
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/viewmodel/LoginElderViewModel.kt
불필요한 ElderIdRepository 및 EldersInfoRepository 의존성 제거; updateAllElders(), updateAllEldersHealthInfo() 메서드 삭제.
UI Screens
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt
인덱스 기반 선택에서 ID 기반 선택으로 전환; EldersInfoViewModel 제거; elderIdMap 직접 사용으로 단순화; 로딩 상태 처리 최적화.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

refactor

Suggested reviewers

  • ikseong00
  • librawish808
  • alswlekk

Poem

메모리는 휘발하고, 데이터는 영원하고 ✨
DataStore에 담아 차곡차곡 💾
Flow 따라 흐르는 elderIds의 여정
리팩토링의 완성, 앱은 더욱 견고해지리라 🏗️

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 변경사항인 elderIds를 내부 스토리지(DataStore)로 저장하도록 수정한 것을 명확하게 설명하고 있습니다.
Linked Issues check ✅ Passed PR은 #167(DataStore 저장), #177(elderId 리팩터링), #206(시간 설정 부분)의 요구사항을 충족합니다. ElderIds 데이터 모델 추가, DataStore 기반 저장소 구현, Flow 기반 데이터 접근, 관련 코드 업데이트가 모두 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 elderIds 저장 방식 변경과 관련된 범위 내 리팩터링입니다. ElderIds 모델, 저장소 구현, 직렬화, 관련 ViewModels 및 화면 업데이트가 모두 연관되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ProtossManse ProtossManse changed the title Refactor/elderid #177 [Refactor] elderId 내부 스토리지 저장 방식으로 수정 #177 Jan 29, 2026
Copy link

@coderabbitai coderabbitai bot left a 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

EldersInfoRepository import가 더 이상 사용되지 않는 것 같습니다.

Comment on lines 69 to 98
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)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "CallTimeScreen.kt" -type f

Repository: 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.kt

Repository: 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.kt

Repository: 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).

Comment on lines 40 to 55
// 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")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "CallTimeViewModel.kt" -type f

Repository: 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.kt

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 4328


두 상태가 따로 관리되고 있어서 동기화되지 않네요.

현재 코드를 보니 _elderIdMap은 초기화 때 한 번만 업데이트되고 (first()로), _elderIdscollect에서 계속 갱신되고 있어요. 그러다 보니 새로운 데이터가 들어오면 _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().

Comment on lines 49 to 55
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd -t f PaymentScreen.kt

Repository: 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 -200

Repository: 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 3

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 975


선택 상태가 UI에 반영 안 됨

로컬 isClickedfalse로 고정되어 있고, 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.

Copy link

@coderabbitai coderabbitai bot left a 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도 참조 중입니다.

선언된 필드명과 사용하는 필드명을 통일해야 합니다:

  • _timeMaptimeMap
  • _isLoadingisLoading
  • _lastError / lastErrorerror
수정 예시
     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, koinViewModel import를 제거해 주세요.

🤖 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.

Comment on lines +3 to 20
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) }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "ElderIdRepositoryImpl.kt" | head -5

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 167


🏁 Script executed:

fd "ElderIdRepositoryImpl" --type f

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 165


🏁 Script executed:

cat -n app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/ElderIdRepositoryImpl.kt

Repository: 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.

Suggested change
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.

Comment on lines +134 to 141
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
}

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

중괄호 직전 공백 라인 제거 필요합니다.
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.

Suggested change
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.

Comment on lines +91 to +98
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) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "CallTimeScreen.kt" -type f

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 173


🏁 Script executed:

fd "CallTimeScreen.kt" --type f

Repository: 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.kt

Repository: 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.kt

Repository: 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.kt

Repository: 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.kt

Repository: 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.kt

Repository: 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 f

Repository: 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.kt

Repository: 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 -100

Repository: 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 -50

Repository: 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.kt

Repository: 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.kt

Repository: 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.kt

Repository: 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().

Comment on lines +29 to 31
val elderIdMap = _elderIdMap.asStateFlow();

// Time data
private val _timeMap = MutableStateFlow<Map<Int, CallTimes>>(emptyMap())
val timeMap: StateFlow<Map<Int, CallTimes>> = _timeMap.asStateFlow()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

불필요한 세미콜론/연속 공백 줄 제거가 필요합니다.
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.

Suggested change
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.

Comment on lines +20 to +26
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 사용이 여러 문제를 만들고 있습니다.

  1. 누락된 import로 컴파일 실패: androidx.compose.runtime에서 mutableStateOf, getValue, setValue를 import하지 않아 컴파일되지 않습니다.

  2. 존재하지 않는 필드 참조: 49-50줄에서 _error, _errorMessage를 참조하는데, 실제 필드는 error, errorMessage입니다 (언더스코어 없음).

  3. 아키텍처 문제: 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.

Comment on lines 41 to 58
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
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.kt

Repository: 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 -20

Repository: 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.kt

Repository: 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.kt

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 1768


🏁 Script executed:

# Search for updateElderIds function signature
rg 'fun updateElderIds' --type kotlin

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 377


🏁 Script executed:

# Search for getElders function signature
rg 'fun getElders' --type kotlin

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 900


🏁 Script executed:

# Check ElderIdRepository class
fd 'ElderIdRepository.kt' --type f

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 157


🏁 Script executed:

# Check EldersInfoRepository class  
fd 'EldersInfoRepository.kt' --type f

Repository: Medicare-Call/Medicare-Call-Android

Length of output: 160


onSuccess 람다에서 suspend 함수 호출 + 존재하지 않는 필드 참조로 컴파일 실패합니다.

getElders()는 suspend 함수지만, Result.onSuccess의 람다는 suspend 람다가 아니라서 updateElderIds() 호출이 불가합니다. 추가로 _error_errorMessage 필드는 존재하지 않습니다(실제로는 errorerrorMessage). 분기를 명시적으로 나눠 처리하면 두 문제가 모두 해결됩니다.

🔧 수정 방향
         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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants