Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.konkuk.medicarecall.data.api.fcm

import com.konkuk.medicarecall.data.dto.request.NotificationStatusRequestDto
import com.konkuk.medicarecall.data.dto.response.NotificationPageResponseDto
import de.jensklingenberg.ktorfit.Response
import de.jensklingenberg.ktorfit.http.Body
import de.jensklingenberg.ktorfit.http.GET
import de.jensklingenberg.ktorfit.http.POST
import de.jensklingenberg.ktorfit.http.Path

Expand All @@ -12,4 +14,7 @@ interface NotificationService { // 알림 관련
@Path("notificationId") notificationId: String,
@Body status: NotificationStatusRequestDto,
): Response<Unit>

@GET("notifications")
suspend fun getNotifications(): Response<List<NotificationPageResponseDto>>
Comment on lines +18 to +19
Copy link
Copy Markdown

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

🌐 Web query:

Ktorfit REST API pagination response type pattern

💡 Result:

Ktorfit does not have a built-in specific "pagination response type pattern" like Retrofit's PagedList or dedicated pagination classes. Instead, pagination is handled by defining custom data classes that match the REST API's paginated response structure (e.g., containing items list, total count, current page, total pages) and using @Query parameters for page/size in the API interface methods. Example pattern from official docs and examples: 1. Define a paginated response data class: @kotlinx.serialization.Serializable data class PaginatedResponse( val data: List, val currentPage: Int, val totalPages: Int, val totalItems: Int ) 2. API interface method with pagination queries: interface ApiService { @GET("items") suspend fun getItems( @Query("page") page: Int, @Query("size") size: Int ): PaginatedResponse } 3. Usage relies on Ktor client's ContentNegotiation (e.g., JSON) for deserialization. Custom ResponseConverter can wrap responses if needed (e.g., for error handling), but standard serialization handles typical paginated JSON. This mirrors Retrofit patterns but leverages Kotlin serialization. No official pagination converter/factory exists in Ktorfit docs (up to v2.7.2, 2026). For infinite lists, combine with Flow via FlowConverterFactory.

Citations:


🏁 Script executed:

# Find the NotificationPageResponseDto definition
find . -type f -name "*.kt" | xargs grep -l "NotificationPageResponseDto" | head -20

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 589


🏁 Script executed:

# Search for the DTO definition in the codebase
rg "data class NotificationPageResponseDto|class NotificationPageResponseDto" -A 10

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 893


🏁 Script executed:

# Check for other Response types in the same file to understand the pattern
rg "Response<" composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/api/fcm/NotificationService.kt -B 2 -A 2

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 313


🏁 Script executed:

# Look for related pagination or response DTOs
rg "PageResponseDto|PagedResponse|PaginatedResponse" --type kt

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 101


🏁 Script executed:

# Check for other API endpoints to see pagination pattern
rg "@GET" composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/api -B 1 -A 3 | head -50

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 5282


🏁 Script executed:

# Look for Response<List patterns in the codebase
rg "Response<List<" composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/api

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 1249


🏁 Script executed:

# Verify NoticesResponseDto to confirm the pattern difference
rg "data class NoticesResponseDto" -A 10

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 1636


반환 타입 수정 필요: List<NotificationPageResponseDto>NotificationPageResponseDto

NotificationPageResponseDto는 이미 한 페이지의 알림 데이터를 담는 컨테이너(totalPages, currentPageNumber, currentElements, notifications)인데, Response<List<NotificationPageResponseDto>>로 감싸면 List[Page[items]] 구조가 돼서 안 맞습니다.

다른 API들과 비교하면 개별 항목을 반환할 때만 Response<List<ItemDto>>를 쓰고, 페이지네이션이 있을 때는 Response<PageContainerDto> 패턴을 써야 해요. 여기서는 Response<NotificationPageResponseDto>로 수정하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/api/fcm/NotificationService.kt`
around lines 18 - 19, The getNotifications() API in NotificationService
currently returns Response<List<NotificationPageResponseDto>> but
NotificationPageResponseDto already models a paginated container (totalPages,
currentPageNumber, currentElements, notifications); change the signature of
suspend fun getNotifications() to return Response<NotificationPageResponseDto>
instead of a List wrapper, and update any call sites or imports that expect the
old List-based type to use the single NotificationPageResponseDto container.

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import com.konkuk.medicarecall.data.api.elders.createStatisticsService
import com.konkuk.medicarecall.data.api.elders.createSubscribeService
import com.konkuk.medicarecall.data.api.fcm.FcmUpdateService
import com.konkuk.medicarecall.data.api.fcm.FcmValidationService
import com.konkuk.medicarecall.data.api.fcm.NotificationService
import com.konkuk.medicarecall.data.api.fcm.createFcmUpdateService
import com.konkuk.medicarecall.data.api.fcm.createFcmValidationService
import com.konkuk.medicarecall.data.api.fcm.createNotificationService
import com.konkuk.medicarecall.data.api.member.MemberRegisterService
import com.konkuk.medicarecall.data.api.member.SettingService
import com.konkuk.medicarecall.data.api.member.createMemberRegisterService
Expand Down Expand Up @@ -120,4 +122,8 @@ class ApiModule {
@Single
fun provideFcmUpdateService(ktorfit: Ktorfit): FcmUpdateService =
ktorfit.createFcmUpdateService()

@Single
fun provideNotificationService(ktorfit: Ktorfit): NotificationService =
ktorfit.createNotificationService()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.konkuk.medicarecall.data.dto.response

import kotlinx.serialization.Serializable

@Serializable
data class NotificationPageResponseDto(
val totalPages: Int,
val currentPageNumber: Int,
val currentElements: Int,
val notifications: List<NotificationResponseDto>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.konkuk.medicarecall.data.dto.response

import kotlinx.serialization.Serializable

@Serializable
data class NotificationResponseDto(
val id: Int,
val title: String,
val body: String,
val isRead: Boolean,
val createdAt: String,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.konkuk.medicarecall.data.repository

import com.konkuk.medicarecall.data.dto.response.NotificationPageResponseDto

interface NotificationRepository {
suspend fun changeNotificationState(id: String): Result<Unit>
suspend fun testNotification(): Result<Unit>
suspend fun getNotifications(): Result<List<NotificationPageResponseDto>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.konkuk.medicarecall.data.repositoryimpl

import com.konkuk.medicarecall.data.api.fcm.NotificationService
import com.konkuk.medicarecall.data.dto.response.NotificationPageResponseDto
import com.konkuk.medicarecall.data.repository.NotificationRepository
import com.konkuk.medicarecall.data.util.handleResponse
import org.koin.core.annotation.Single

@Single
class NotificationRepositoryImpl(
private val notificationService: NotificationService,
) : NotificationRepository {
override suspend fun changeNotificationState(id: String): Result<Unit> {
TODO("Not yet implemented")
}

override suspend fun testNotification(): Result<Unit> {
TODO("Not yet implemented")
}
Comment on lines +13 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if TODO methods are called anywhere in the codebase

# Search for changeNotificationState calls
echo "=== changeNotificationState 호출 검색 ==="
rg -n "changeNotificationState" --type=kt

echo ""
echo "=== testNotification 호출 검색 ==="
rg -n "testNotification" --type=kt

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 215


🏁 Script executed:

#!/bin/bash
# Search for changeNotificationState and testNotification calls (corrected syntax)

echo "=== changeNotificationState 호출/정의 검색 ==="
rg "changeNotificationState" --type-add 'kotlin:*.kt' -tkotlin -B2 -A2

echo ""
echo "=== testNotification 호출/정의 검색 ==="
rg "testNotification" --type-add 'kotlin:*.kt' -tkotlin -B2 -A2

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 4057


🏁 Script executed:

#!/bin/bash
# Also search without type restriction
echo "=== changeNotificationState (모든 파일) ==="
rg "changeNotificationState" -B2 -A2

echo ""
echo "=== testNotification (모든 파일) ==="
rg "testNotification" -B2 -A2

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 4055


🏁 Script executed:

#!/bin/bash
# Read the actual file to verify current state
echo "=== NotificationRepositoryImpl.kt 파일 내용 ==="
cat -n "composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/repositoryimpl/NotificationRepositoryImpl.kt"

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 1188


TODO 메서드들이 현재 호출되지 않아 즉각적인 문제는 없지만, 향후 구현 필요

changeNotificationState()testNotification() 메서드가 TODO()로 남아있고 호출 시 NotImplementedError를 발생시키긴 하지만, 코드베이스 전체를 검색한 결과 이 메서드들은 현재 어느 곳에서도 호출되지 않고 있습니다. 따라서 지금은 런타임 충돌 위험이 없습니다. 다만 인터페이스 계약을 완성하기 위해 언젠가는 반드시 구현되어야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/data/repositoryimpl/NotificationRepositoryImpl.kt`
around lines 13 - 19, NotificationRepositoryImpl currently leaves
changeNotificationState(id: String) and testNotification() as TODOs which will
throw NotImplementedError if ever called; implement these methods in
NotificationRepositoryImpl by providing the real business logic (e.g., call the
appropriate data source / API client methods, update local datasource or remote
endpoint, or toggle the notification flag), wrap outcomes in a Result<Unit>
(return Result.success(Unit) on success and Result.failure(exception) on
errors), and ensure you catch and convert exceptions to failures; locate the
methods by name (changeNotificationState and testNotification) in class
NotificationRepositoryImpl and implement them consistent with other repository
methods (using existing network/data-layer helpers and coroutine patterns).


override suspend fun getNotifications():
Result<List<NotificationPageResponseDto>> = runCatching {
notificationService.getNotifications().handleResponse()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,33 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.konkuk.medicarecall.domain.model.type.AlarmType
import com.konkuk.medicarecall.resources.Res
import com.konkuk.medicarecall.resources.*
import com.konkuk.medicarecall.resources.ic_settings_back
import com.konkuk.medicarecall.ui.feature.alarm.component.AlarmItem
import com.konkuk.medicarecall.ui.feature.alarm.viewmodel.AlarmViewModel
import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar
import com.konkuk.medicarecall.ui.theme.MediCareCallTheme
import com.konkuk.medicarecall.domain.model.type.AlarmType
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.viewmodel.koinViewModel

@Composable
fun AlarmScreen(
onBack: () -> Unit,
modifier: Modifier = Modifier,
viewModel: AlarmViewModel = koinViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()

Column(
modifier = modifier
.fillMaxSize()
Expand All @@ -42,31 +52,53 @@ fun AlarmScreen(
)
},
)
AlarmItem(
AlarmType.NEW_ALARM,
"✅ 1차 케어콜이 완료되었어요. 확인해 보세요!",
"7월 8일 13:15",
)
AlarmItem(
AlarmType.READ_ALARM,
"❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!",
"7월 7일 13:15",
)
AlarmItem(
AlarmType.READ_ALARM,
"📞 김옥자 어르신 케어콜 부재중 상태입니다. 확인해 주세요!",
"7월 7일 13:15",
)
AlarmItem(
AlarmType.READ_ALARM,
"❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!",
"7월 7일 13:15",
)
AlarmItem(
AlarmType.READ_ALARM,
"✅ 1차 케어콜이 완료되었어요. 확인해 보세요!",
"7월 7일 13:15",
)
Column(
modifier = modifier.verticalScroll(scrollState),
) {
Comment on lines +55 to +57
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

내부 Column에도 Modifier를 사용하세요.

위와 동일한 이유로, 스크롤 가능한 내부 Column에도 modifier 파라미터 대신 Modifier를 사용해야 합니다.

🔧 수정 제안
        Column(
-           modifier = modifier.verticalScroll(scrollState),
+           modifier = Modifier.verticalScroll(scrollState),
        ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/alarm/screen/AlarmScreen.kt`
around lines 55 - 57, The inner scrollable Column is using the passed-in
parameter named modifier instead of the Compose root Modifier; update the Column
call that currently sets modifier = modifier.verticalScroll(scrollState) to use
Modifier.verticalScroll(scrollState) so the internal layout doesn't accidentally
inherit external modifiers—look for the Column that applies
verticalScroll(scrollState) and replace the use of the parameter "modifier" with
the top-level "Modifier".

if (uiState.errorMessage != null) {
AlarmItem(
AlarmType.READ_ALARM,
"공지사항 오류 발생",
uiState.errorMessage ?: "",
)
} else {
uiState.alarmPages.forEach { alarmPage ->
alarmPage.notifications.forEach { alarm ->
AlarmItem(
alarmType = if (alarm.isRead) AlarmType.READ_ALARM else AlarmType.NEW_ALARM,
content = alarm.body,
date = alarm.createdAt.replace("-", "."),
)
}
}

}
}
// AlarmItem(
// AlarmType.NEW_ALARM,
// "✅ 1차 케어콜이 완료되었어요. 확인해 보세요!",
// "7월 8일 13:15",
// )
// AlarmItem(
// AlarmType.READ_ALARM,
// "❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!",
// "7월 7일 13:15",
// )
// AlarmItem(
// AlarmType.READ_ALARM,
// "📞 김옥자 어르신 케어콜 부재중 상태입니다. 확인해 주세요!",
// "7월 7일 13:15",
// )
// AlarmItem(
// AlarmType.READ_ALARM,
// "❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!",
// "7월 7일 13:15",
// )
// AlarmItem(
// AlarmType.READ_ALARM,
// "✅ 1차 케어콜이 완료되었어요. 확인해 보세요!",
// "7월 7일 13:15",
// )
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.konkuk.medicarecall.ui.feature.alarm.viewmodel

import com.konkuk.medicarecall.data.dto.response.NotificationPageResponseDto

data class AlarmUiState(
val alarmPages: List<NotificationPageResponseDto> = emptyList(),
val errorMessage: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.konkuk.medicarecall.ui.feature.alarm.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.konkuk.medicarecall.data.repository.NotificationRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.koin.android.annotation.KoinViewModel

@KoinViewModel
class AlarmViewModel(
private val repository: NotificationRepository,
) : ViewModel() {
private val _uiState = MutableStateFlow(AlarmUiState())
val uiState = _uiState.asStateFlow()

init {
loadAlarms()
}

private fun loadAlarms() {
viewModelScope.launch {
repository.getNotifications()
.onSuccess { alarmPages ->
_uiState.update { it.copy(alarmPages = alarmPages) }
}
.onFailure {
_uiState.update { it.copy(errorMessage = "알림을 불러오지 못했습니다.") }
it.printStackTrace()
}
}
}
}
Loading