-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: 알림 조회 API 연동 #26
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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=ktRepository: 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 -A2Repository: 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 -A2Repository: 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 메서드들이 현재 호출되지 않아 즉각적인 문제는 없지만, 향후 구현 필요
🤖 Prompt for AI Agents |
||
|
|
||
| override suspend fun getNotifications(): | ||
| Result<List<NotificationPageResponseDto>> = runCatching { | ||
| notificationService.getNotifications().handleResponse() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 내부 Column에도 위와 동일한 이유로, 스크롤 가능한 내부 Column에도 🔧 수정 제안 Column(
- modifier = modifier.verticalScroll(scrollState),
+ modifier = Modifier.verticalScroll(scrollState),
) {🤖 Prompt for AI Agents |
||
| 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", | ||
| // ) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| 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() | ||
| } | ||
| } | ||
| } | ||
| } |
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
🌐 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
@Queryparameters for page/size in the API interface methods. Example pattern from official docs and examples: 1. Define a paginated response data class:@kotlinx.serialization.Serializabledata 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:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 589
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 893
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 313
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 101
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 5282
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 1249
🏁 Script executed:
Repository: Medicare-Call/Medicare-Call-KMP
Length of output: 1636
반환 타입 수정 필요:
List<NotificationPageResponseDto>→NotificationPageResponseDtoNotificationPageResponseDto는 이미 한 페이지의 알림 데이터를 담는 컨테이너(totalPages, currentPageNumber, currentElements, notifications)인데,Response<List<NotificationPageResponseDto>>로 감싸면 List[Page[items]] 구조가 돼서 안 맞습니다.다른 API들과 비교하면 개별 항목을 반환할 때만
Response<List<ItemDto>>를 쓰고, 페이지네이션이 있을 때는Response<PageContainerDto>패턴을 써야 해요. 여기서는Response<NotificationPageResponseDto>로 수정하세요.🤖 Prompt for AI Agents