diff --git a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt index f2040ee7..20618cc1 100644 --- a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt +++ b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt @@ -29,4 +29,6 @@ interface UserRepository { suspend fun setLastNotificationSyncedEnabled(isEnabled: Boolean) suspend fun updateNotificationSettings(notificationEnabled: Boolean): Result + + suspend fun resetNotificationData() } diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/FirebaseModule.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/FirebaseModule.kt index 525dce21..9c5f5f65 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/FirebaseModule.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/FirebaseModule.kt @@ -1,6 +1,8 @@ package com.ninecraft.booket.core.data.impl.di import com.google.firebase.Firebase +import com.google.firebase.installations.FirebaseInstallations +import com.google.firebase.installations.installations import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.messaging import com.google.firebase.remoteconfig.FirebaseRemoteConfig @@ -32,4 +34,8 @@ internal object FirebaseModule { @Singleton @Provides fun provideFirebaseMessaging(): FirebaseMessaging = Firebase.messaging + + @Singleton + @Provides + fun provideFirebaseInstallation(): FirebaseInstallations = Firebase.installations } diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt index 08cce48c..9e560650 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt @@ -2,7 +2,6 @@ package com.ninecraft.booket.core.data.impl.repository import com.ninecraft.booket.core.common.utils.runSuspendCatching import com.ninecraft.booket.core.data.api.repository.AuthRepository -import com.ninecraft.booket.core.datastore.api.datasource.NotificationDataSource import com.ninecraft.booket.core.datastore.api.datasource.TokenDataSource import com.ninecraft.booket.core.model.AutoLoginState import com.ninecraft.booket.core.model.UserState @@ -16,7 +15,6 @@ private const val KAKAO_PROVIDER_TYPE = "KAKAO" internal class DefaultAuthRepository @Inject constructor( private val service: ReedService, private val tokenDataSource: TokenDataSource, - private val notificationDataSource: NotificationDataSource, ) : AuthRepository { override suspend fun login(accessToken: String) = runSuspendCatching { val response = service.login( @@ -31,7 +29,6 @@ internal class DefaultAuthRepository @Inject constructor( override suspend fun logout() = runSuspendCatching { service.logout() clearTokens() - clearNotificationDataStore() } override suspend fun withdraw() = runSuspendCatching { @@ -64,8 +61,4 @@ internal class DefaultAuthRepository @Inject constructor( val accessToken = tokenDataSource.getAccessToken() return if (accessToken.isBlank()) UserState.Guest else UserState.LoggedIn } - - private suspend fun clearNotificationDataStore() { - notificationDataSource.clearNotificationDataStore() - } } diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultUserRepository.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultUserRepository.kt index 3d915d61..8efe5d8f 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultUserRepository.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultUserRepository.kt @@ -1,12 +1,13 @@ package com.ninecraft.booket.core.data.impl.repository +import com.google.firebase.installations.FirebaseInstallations import com.google.firebase.messaging.FirebaseMessaging import com.ninecraft.booket.core.common.utils.runSuspendCatching import com.ninecraft.booket.core.data.api.repository.UserRepository import com.ninecraft.booket.core.data.impl.mapper.toModel import com.ninecraft.booket.core.datastore.api.datasource.NotificationDataSource import com.ninecraft.booket.core.datastore.api.datasource.OnboardingDataSource -import com.ninecraft.booket.core.network.request.FcmTokenRequest +import com.ninecraft.booket.core.network.request.DeviceRegistrationRequest import com.ninecraft.booket.core.network.request.NotificationSettingsRequest import com.ninecraft.booket.core.network.request.TermsAgreementRequest import com.ninecraft.booket.core.network.service.ReedService @@ -21,6 +22,7 @@ internal class DefaultUserRepository @Inject constructor( private val onboardingDataSource: OnboardingDataSource, private val notificationDataSource: NotificationDataSource, private val firebaseMessaging: FirebaseMessaging, + private val firebaseInstallations: FirebaseInstallations, ) : UserRepository { override suspend fun agreeTerms(termsAgreed: Boolean) = runSuspendCatching { service.agreeTerms(TermsAgreementRequest(termsAgreed)).toModel() @@ -45,12 +47,12 @@ internal class DefaultUserRepository @Inject constructor( return@runSuspendCatching } - updateFcmToken(newToken) + registerDevice(newToken) setFcmToken(newToken) } override suspend fun syncFcmToken(fcmToken: String): Result = runSuspendCatching { - updateFcmToken(fcmToken) + registerDevice(fcmToken) setFcmToken(fcmToken) } @@ -73,6 +75,15 @@ internal class DefaultUserRepository @Inject constructor( service.updateNotificationSettings(NotificationSettingsRequest(notificationEnabled)).toModel() } + override suspend fun resetNotificationData() { + try { + deleteRemoteFcmToken() + clearNotificationDataStore() + } catch (e: Exception) { + Logger.e("Failed to reset notification data: ${e.message}") + } + } + private suspend fun getRemoteFcmToken(): String { return try { firebaseMessaging.token.await() @@ -88,7 +99,25 @@ internal class DefaultUserRepository @Inject constructor( notificationDataSource.setFcmToken(fcmToken) } - private suspend fun updateFcmToken(fcmToken: String) { - service.updateFcmToken(FcmTokenRequest(fcmToken)) + private suspend fun getDeviceId(): String { + return try { + firebaseInstallations.id.await() + } catch (e: Exception) { + Logger.e("Failed to fetch device ID: ${e.message}") + throw e + } + } + + private suspend fun registerDevice(fcmToken: String) { + val deviceId = getDeviceId() + service.upsertDevice(DeviceRegistrationRequest(deviceId, fcmToken)) + } + + private suspend fun deleteRemoteFcmToken() { + firebaseMessaging.deleteToken().await() + } + + private suspend fun clearNotificationDataStore() { + notificationDataSource.clearNotificationDataStore() } } diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/FcmTokenRequest.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/DeviceRegistrationRequest.kt similarity index 68% rename from core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/FcmTokenRequest.kt rename to core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/DeviceRegistrationRequest.kt index 7ad8220a..be82c4cd 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/FcmTokenRequest.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/request/DeviceRegistrationRequest.kt @@ -4,7 +4,9 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class FcmTokenRequest( +data class DeviceRegistrationRequest( + @SerialName("deviceId") + val deviceId: String, @SerialName("fcmToken") val fcmToken: String, ) diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt index b6247a64..f3926e64 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt @@ -1,7 +1,7 @@ package com.ninecraft.booket.core.network.service import com.ninecraft.booket.core.network.request.BookUpsertRequest -import com.ninecraft.booket.core.network.request.FcmTokenRequest +import com.ninecraft.booket.core.network.request.DeviceRegistrationRequest import com.ninecraft.booket.core.network.request.LoginRequest import com.ninecraft.booket.core.network.request.NotificationSettingsRequest import com.ninecraft.booket.core.network.request.RecordRegisterRequest @@ -53,8 +53,8 @@ interface ReedService { @GET("api/v1/users/me") suspend fun getUserProfile(): UserProfileResponse - @PUT("api/v1/users/me/fcm-token") - suspend fun updateFcmToken(@Body fcmTokenRequest: FcmTokenRequest): UserProfileResponse + @PUT("api/v1/users/me/devices") + suspend fun upsertDevice(@Body deviceRegistrationRequest: DeviceRegistrationRequest): UserProfileResponse @PUT("api/v1/users/me/notification-settings") suspend fun updateNotificationSettings(@Body notificationSettingsRequest: NotificationSettingsRequest): UserProfileResponse diff --git a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt index eb296d45..003a09bc 100644 --- a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt +++ b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt @@ -10,6 +10,7 @@ import com.ninecraft.booket.core.common.constants.WebViewConstants import com.ninecraft.booket.core.common.utils.handleException import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository +import com.ninecraft.booket.core.data.api.repository.UserRepository import com.ninecraft.booket.core.model.UserState import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.NotificationScreen @@ -34,6 +35,7 @@ import kotlinx.coroutines.launch class SettingsPresenter @AssistedInject constructor( @Assisted val navigator: Navigator, private val authRepository: AuthRepository, + private val userRepository: UserRepository, private val remoteConfigRepository: RemoteConfigRepository, private val analyticsHelper: AnalyticsHelper, ) : Presenter { @@ -62,6 +64,7 @@ class SettingsPresenter @AssistedInject constructor( isLoading = true authRepository.logout() .onSuccess { + userRepository.resetNotificationData() analyticsHelper.logEvent(SETTINGS_LOGOUT_COMPLETE) navigator.resetRoot(LoginScreen()) } @@ -91,6 +94,7 @@ class SettingsPresenter @AssistedInject constructor( isLoading = true authRepository.withdraw() .onSuccess { + userRepository.resetNotificationData() analyticsHelper.logEvent(SETTINGS_WITHDRAWAL_COMPLETE) navigator.resetRoot(LoginScreen()) }