-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/#76 alarm #86
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
40c53f3
8a184c6
deee932
d5ee71e
59ed154
a063178
cf7c8b2
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,5 @@ | ||
| package com.example.core.repository | ||
|
|
||
| interface AlarmRepository { | ||
| suspend fun registerFcmToken(token: String): Result<Unit> | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.example.data.api | ||
|
|
||
| import com.example.data.api.dto.BaseResponse | ||
| import com.example.data.api.dto.server.AlarmFcmTokenDTO | ||
| import retrofit2.http.Body | ||
| import retrofit2.http.POST | ||
|
|
||
| interface AlarmApi { | ||
| // FCM 토큰 등록 | ||
| @POST("alarm/fcmtoken") | ||
| suspend fun registerFcmToken( | ||
| @Body body: AlarmFcmTokenDTO | ||
| ): BaseResponse<String> | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,5 +11,6 @@ interface ServerApi : | |
| AIArticleApi, | ||
| FolderApi, | ||
| CategoryApi, | ||
| RefreshApi | ||
| RefreshApi, | ||
| AlarmApi | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.example.data.api.dto.server | ||
|
|
||
| import com.squareup.moshi.Json | ||
|
|
||
| data class AlarmFcmTokenDTO ( | ||
|
|
||
| @Json(name = "fcmToken") | ||
| val fcmToken: String | ||
|
|
||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.example.data.di.repository | ||
|
|
||
| import com.example.core.repository.AlarmRepository | ||
| import com.example.data.api.ServerApi | ||
| import com.example.data.implementation.repository.AlarmRepositoryImpl | ||
| import dagger.Module | ||
| import dagger.Provides | ||
| import dagger.hilt.InstallIn | ||
| import dagger.hilt.components.SingletonComponent | ||
| import javax.inject.Singleton | ||
|
|
||
| @Module | ||
| @InstallIn(SingletonComponent::class) | ||
| object AlarmRepositoryModule { | ||
|
|
||
| @Provides | ||
| @Singleton | ||
| fun provideAlarmRepository( | ||
| serverApi: ServerApi | ||
| ): AlarmRepository { | ||
| return AlarmRepositoryImpl(serverApi = serverApi) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||||||||||||||||||
| package com.example.data.implementation.repository | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import com.example.core.repository.AlarmRepository | ||||||||||||||||||||||||||||||||
| import com.example.data.api.ServerApi | ||||||||||||||||||||||||||||||||
| import com.example.data.api.dto.server.AlarmFcmTokenDTO | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| class AlarmRepositoryImpl( | ||||||||||||||||||||||||||||||||
| private val serverApi: ServerApi | ||||||||||||||||||||||||||||||||
| ) : AlarmRepository { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| override suspend fun registerFcmToken(token: String): Result<Unit> = runCatching { | ||||||||||||||||||||||||||||||||
| val res = serverApi.registerFcmToken(AlarmFcmTokenDTO(fcmToken = token)) | ||||||||||||||||||||||||||||||||
| if (!res.isSuccess) error(res.message) | ||||||||||||||||||||||||||||||||
|
Comment on lines
+7
to
+13
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+11
to
+14
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: # 파일 존재 여부 확인 및 내용 조회
find . -name "AlarmRepositoryImpl.kt" -type f 2>/dev/null | head -5Repository: LinkYou-2025/LinkU_Android Length of output: 156 🏁 Script executed: # suspend 함수에서 runCatching으로 Result를 반환하는 패턴 검색
rg -n "suspend.*Result.*runCatching" -t kotlin --max-count 10Repository: LinkYou-2025/LinkU_Android Length of output: 244 🏁 Script executed: # 더 유연한 패턴 검색: suspend 함수 정의 후 runCatching 사용
rg -n "suspend\s+fun.*Result" -t kotlin -A 2 | head -50Repository: LinkYou-2025/LinkU_Android Length of output: 2939 🌐 Web query:
💡 Result: Best practices:
|
||||||||||||||||||||||||||||||||
| override suspend fun registerFcmToken(token: String): Result<Unit> = runCatching { | |
| val res = serverApi.registerFcmToken(AlarmFcmTokenDTO(fcmToken = token)) | |
| if (!res.isSuccess) error(res.message) | |
| } | |
| import kotlinx.coroutines.CancellationException | |
| override suspend fun registerFcmToken(token: String): Result<Unit> = try { | |
| val res = serverApi.registerFcmToken(AlarmFcmTokenDTO(fcmToken = token)) | |
| if (!res.isSuccess) error(res.message ?: "FCM 토큰 등록 실패") | |
| Result.success(Unit) | |
| } catch (ce: CancellationException) { | |
| throw ce | |
| } catch (t: Throwable) { | |
| Result.failure(t) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@data/src/main/java/com/example/data/implementation/repository/AlarmRepositoryImpl.kt`
around lines 11 - 14, The registerFcmToken suspend function currently wraps all
exceptions with runCatching, which swallows CancellationException and breaks
structured concurrency; change the implementation in
AlarmRepositoryImpl.registerFcmToken so CancellationException is rethrown
instead of being wrapped. Concretely, replace the runCatching usage with an
explicit try/catch around serverApi.registerFcmToken(AlarmFcmTokenDTO(fcmToken =
token)), catch CancellationException and rethrow it, and in a general Exception
catch return Result.failure(e) (or, if you keep runCatching, inspect the caught
exception and rethrow if it's a CancellationException before returning
Result.failure). Ensure serverApi.registerFcmToken and AlarmFcmTokenDTO
references remain unchanged.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ import com.example.home.screen.AlarmScreen | |
| import com.example.home.screen.HomeScreen | ||
| import com.example.home.screen.SaveLinkResultScreen | ||
| import com.example.home.screen.SaveLinkScreen | ||
| import com.google.firebase.messaging.FirebaseMessaging | ||
| import java.io.File | ||
| import java.io.FileOutputStream | ||
| import java.io.InputStream | ||
|
|
@@ -74,6 +75,19 @@ fun HomeApp( | |
| popExitTransition = { ExitTransition.None } | ||
| ) { | ||
| composable("onboarding") { | ||
|
|
||
| // 홈 진입 시 FCM 토큰 등록 | ||
| LaunchedEffect(Unit) { | ||
| FirebaseMessaging.getInstance().token | ||
| .addOnSuccessListener { token -> | ||
| Log.d("HomeApp", "fcm token = $token") | ||
|
|
||
| viewModel.registerFcmToken(token) | ||
|
Comment on lines
+83
to
+84
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. FCM 토큰 원문 로그 출력은 제거해야 합니다. 🔧 제안 수정안- Log.d("HomeApp", "fcm token = $token")
+ Log.d("HomeApp", "fcm token acquired")🤖 Prompt for AI Agents |
||
| } | ||
| .addOnFailureListener { e -> | ||
| Log.e("HomeApp", "FCM 토큰 가져오기 실패", e) | ||
| } | ||
| } | ||
|
Comment on lines
+79
to
+89
Collaborator
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. 우리 await 배운거를 사용해볼 수 있겠다 지현아 |
||
|
|
||
| HomeScreen( | ||
| homeViewModel = viewModel, | ||
| userName = viewModel.userName.orEmpty().ifBlank { "링큐" }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ import com.example.core.model.LinkResultInfo | |||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.model.LinkSimpleInfo | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.model.search.RecentQuery | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.repository.AIArticleRepository | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.repository.AlarmRepository | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.repository.CategoryRepository | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.repository.LinkuRepository | ||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.core.repository.RecentSearchRepository | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -42,6 +43,7 @@ class HomeViewModel @Inject constructor( | |||||||||||||||||||||||||||||||||||||||||||||
| private val aiArticleRepository: AIArticleRepository, | ||||||||||||||||||||||||||||||||||||||||||||||
| private val categoryRepository: CategoryRepository, | ||||||||||||||||||||||||||||||||||||||||||||||
| private val recentRepository: RecentSearchRepository, | ||||||||||||||||||||||||||||||||||||||||||||||
| private val alarmRepository: AlarmRepository, | ||||||||||||||||||||||||||||||||||||||||||||||
| ) : ViewModel() { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // 자돌 로그인 하고 이 함수가 가장 먼저 실행함. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -658,4 +660,13 @@ class HomeViewModel @Inject constructor( | |||||||||||||||||||||||||||||||||||||||||||||
| Log.d("HomeViewModel", "clearRecentQuery return") | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| // ---------- search method ---------- | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // 알림 | ||||||||||||||||||||||||||||||||||||||||||||||
| fun registerFcmToken(token: String) { | ||||||||||||||||||||||||||||||||||||||||||||||
| viewModelScope.launch { | ||||||||||||||||||||||||||||||||||||||||||||||
| alarmRepository.registerFcmToken(token) | ||||||||||||||||||||||||||||||||||||||||||||||
| .onSuccess { Log.d("HomeViewModel", "FCM 토큰 등록 성공") } | ||||||||||||||||||||||||||||||||||||||||||||||
| .onFailure { e -> Log.e("HomeViewModel", "FCM 토큰 등록 실패", e) } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+665
to
+670
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. FCM 토큰 등록 실패를 로그로만 처리하면 알림 미등록 상태가 지속될 수 있습니다. Line [667]~Line [669]에서 실패를 호출자에 전달하지 않아, 일시적 네트워크 오류 시 재시도 타이밍을 잃습니다. 최소한 실패 콜백(또는 상태 노출)과 공백 토큰 방어를 추가하는 것이 안전합니다. 수정 예시- fun registerFcmToken(token: String) {
+ fun registerFcmToken(
+ token: String,
+ onFailure: (Throwable) -> Unit = {},
+ ) {
+ val normalizedToken = token.trim()
+ if (normalizedToken.isEmpty()) return
+
viewModelScope.launch {
- alarmRepository.registerFcmToken(token)
+ alarmRepository.registerFcmToken(normalizedToken)
.onSuccess { Log.d("HomeViewModel", "FCM 토큰 등록 성공") }
- .onFailure { e -> Log.e("HomeViewModel", "FCM 토큰 등록 실패", e) }
+ .onFailure { e ->
+ Log.e("HomeViewModel", "FCM 토큰 등록 실패", e)
+ onFailure(e)
+ }
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| [versions] | ||
| agp = "8.9.1" | ||
| agp = "8.13.2" | ||
|
Collaborator
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. 지현아 이거 지민이가 최근에 gradle 작업해서 한 번 꼭 develop 브랜치에서 pull 받아줘!
Collaborator
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. 여기 브랜치 풀 받아서 봤는데 적용이 안되어있어! 그래서 merge할 때 지금 충돌이 있어 |
||
| kotlin = "2.0.0" | ||
| coreKtx = "1.16.0" | ||
| junit = "4.13.2" | ||
|
|
@@ -26,7 +26,6 @@ androidx-test = "1.4.0" | |
| testng = "6.9.6" | ||
| media3CommonKtx = "1.5.1" | ||
| runtimeAndroid = "1.7.7" | ||
| firebaseMessagingKtx = "24.1.1" | ||
| navigationComposeJvmstubs = "2.9.3" | ||
| gson = "2.10.1" | ||
| media3CommonKtxVersion = "1.8.0" | ||
|
|
@@ -36,6 +35,7 @@ toolsCore = "1.0.0-alpha14" | |
| foundationVersion = "1.10.1" | ||
| foundationLayout = "1.10.0" | ||
| foundationLayoutVersion = "1.10.2" | ||
| firebaseBom = "34.9.0" | ||
|
|
||
|
|
||
|
|
||
|
|
@@ -55,8 +55,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin | |
| androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } | ||
| androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } | ||
| androidx-material3 = { group = "androidx.compose.material3", name = "material3" } | ||
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version = "2.54" } | ||
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version = "2.54" } | ||
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } | ||
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } | ||
| androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "androidx-hilt" } | ||
| androidx-hilt-navigation = {group = "androidx.hilt", name = "hilt-navigation-fragment", version.ref = "androidx-hilt"} | ||
| androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } | ||
|
|
@@ -74,7 +74,6 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio | |
| testng = { group = "org.testng", name = "testng", version.ref = "testng" } | ||
| androidx-media3-common-ktx = { group = "androidx.media3", name = "media3-common-ktx", version.ref = "media3CommonKtx" } | ||
| androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" } | ||
| firebase-messaging-ktx = { group = "com.google.firebase", name = "firebase-messaging-ktx", version.ref = "firebaseMessagingKtx" } | ||
|
|
||
| androidx-navigation-compose-jvmstubs = { group = "androidx.navigation", name = "navigation-compose-jvmstubs", version.ref = "navigationComposeJvmstubs" } | ||
| #androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidx-hilt" } | ||
|
|
@@ -91,6 +90,8 @@ androidx-compose-foundation-layout = { group = "androidx.compose.foundation", na | |
| androidx-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayoutVersion" } | ||
|
|
||
|
|
||
| firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } | ||
| firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" } | ||
|
|
||
|
|
||
| [plugins] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| #Thu Jul 03 04:13:57 KST 2025 | ||
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip | ||
| zipStoreBase=GRADLE_USER_HOME | ||
| zipStorePath=wrapper/dists |
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.
@ugmin1030 여기 커밋 보고 gradle 정리해주는게 좋을 것 같아용