diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8e2be33fe..b97597095 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,4 @@ -import java.util.Properties +import java.util.* plugins { alias(libs.plugins.android.application) @@ -21,7 +21,7 @@ android { applicationId = "com.hmoa.app" minSdk = 26 targetSdk = 34 - versionCode = 33 + versionCode = 35 versionName = "1.2.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -90,6 +90,7 @@ dependencies { implementation(project(":core-repository")) implementation(project(":core-common")) + implementation(libs.app.update.ktx) implementation(libs.bootpay) //부트페이 implementation(libs.bundles.kakao.login) // kakao implementation(libs.bundles.hilt) // hilt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cdfe52f31..2848e56b9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ android:usesCleartextTraffic="true" tools:targetApi="31"> @@ -64,7 +64,7 @@ @@ -72,4 +72,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/com/hmoa/app/FcmAppService.kt b/app/src/main/java/com/hmoa/app/service/FcmAppService.kt similarity index 69% rename from app/src/main/java/com/hmoa/app/FcmAppService.kt rename to app/src/main/java/com/hmoa/app/service/FcmAppService.kt index 10828577e..236698f7f 100644 --- a/app/src/main/java/com/hmoa/app/FcmAppService.kt +++ b/app/src/main/java/com/hmoa/app/service/FcmAppService.kt @@ -1,4 +1,4 @@ -package com.hmoa.app +package com.hmoa.app.service import android.app.Notification import android.app.NotificationChannel @@ -8,28 +8,36 @@ import android.content.Context import android.content.Intent import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.hmoa.app.view.MainActivity class FcmAppService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) } + override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) - if(remoteMessage.data.isNotEmpty()){ + if (remoteMessage.data.isNotEmpty()) { sendNotification(remoteMessage.data) } } - private fun sendNotification(fcmData : Map){ + + private fun sendNotification(fcmData: Map) { val CHANNEL_DEFAULT_IMPORTANCE = "channelId" val ONGOING_NOTIFICATION = 1 - val notificationIntent = Intent(this, MainActivity::class.java).apply{ + val notificationIntent = Intent(this, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - putExtra("deeplink",fcmData["deeplink"]) - putExtra("id",fcmData["id"]) + putExtra("deeplink", fcmData["deeplink"]) + putExtra("id", fcmData["id"]) } - val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + val pendingIntent = PendingIntent.getActivity( + this, + 0, + notificationIntent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) val notification = Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE) .setContentTitle(fcmData["title"]) @@ -41,9 +49,10 @@ class FcmAppService : FirebaseMessagingService() { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val channel = NotificationChannel(CHANNEL_DEFAULT_IMPORTANCE, "HMOA Channel", NotificationManager.IMPORTANCE_DEFAULT) + val channel = + NotificationChannel(CHANNEL_DEFAULT_IMPORTANCE, "HMOA Channel", NotificationManager.IMPORTANCE_DEFAULT) notificationManager.createNotificationChannel(channel) notificationManager.notify(ONGOING_NOTIFICATION, notification) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/hmoa/app/MainActivity.kt b/app/src/main/java/com/hmoa/app/view/MainActivity.kt similarity index 73% rename from app/src/main/java/com/hmoa/app/MainActivity.kt rename to app/src/main/java/com/hmoa/app/view/MainActivity.kt index dabd29521..3b4ec8e22 100644 --- a/app/src/main/java/com/hmoa/app/MainActivity.kt +++ b/app/src/main/java/com/hmoa/app/view/MainActivity.kt @@ -1,4 +1,4 @@ -package com.hmoa.app +package com.hmoa.app.view import android.Manifest import android.content.Intent @@ -8,6 +8,8 @@ import android.os.Build import android.os.Bundle import android.util.Log import androidx.activity.compose.setContent +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Box @@ -32,19 +34,23 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import com.google.android.material.snackbar.Snackbar +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.UpdateAvailability import com.google.firebase.messaging.FirebaseMessaging +import com.hmoa.app.BuildConfig import com.hmoa.app.navigation.SetUpNavGraph +import com.hmoa.app.viewmodel.AppViewModel import com.hmoa.core_common.permissions import com.hmoa.core_designsystem.BottomScreen import com.hmoa.core_designsystem.component.HomeTopBar import com.hmoa.core_designsystem.component.MainBottomBar -import com.hmoa.core_domain.entity.navigation.AuthenticationRoute -import com.hmoa.core_domain.entity.navigation.CommunityRoute -import com.hmoa.core_domain.entity.navigation.HPediaRoute -import com.hmoa.core_domain.entity.navigation.HomeRoute -import com.hmoa.core_domain.entity.navigation.MagazineRoute -import com.hmoa.core_domain.entity.navigation.PerfumeRoute -import com.hmoa.core_domain.entity.navigation.UserInfoRoute +import com.hmoa.core_domain.entity.navigation.* import com.hmoa.feature_brand.navigation.navigateToBrandSearch import com.hmoa.feature_fcm.navigateToAlarmScreen import com.hmoa.feature_home.navigation.navigateToHome @@ -86,10 +92,94 @@ class MainActivity : AppCompatActivity() { BottomScreen.Magazine.name, BottomScreen.MyPage.name ) + private lateinit var appUpdateManager: AppUpdateManager + private val activityResultLauncher = + registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult -> + handleUpdateResult(result) + } + + // Displays the snackbar notification and call to action. + fun popupSnackbarForCompleteUpdate() { + Snackbar.make( + findViewById(com.hmoa.core_designsystem.R.drawable.ic_fab), + "새로운 업데이트 다운로드가 완료되었습니다.", + Snackbar.LENGTH_INDEFINITE + ).apply { + setAction("재시작") { appUpdateManager.completeUpdate() } + show() + } + } + + private fun checkImmediateUpdateAvailability() { + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + if (shouldTriggerImmediateUpdate(appUpdateInfo)) { + Log.d("checkUpdateAvailability", "ImemediateUpdate") + startImmediateUpdate(appUpdateInfo) + } + }.addOnFailureListener { e -> + Log.e("UpdateFlow", "Failed to check update availability", e) + } + } + + private fun checkFlexibleUpdateAvailability() { + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + if (shouldTriggerFlexibleUpdate(appUpdateInfo)) { + Log.d("checkUpdateAvailability", "ImemediateUpdate") + startFlexibleUpdate(appUpdateInfo) + if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) { + popupSnackbarForCompleteUpdate() + } + } + }.addOnFailureListener { e -> + Log.e("UpdateFlow", "Failed to check update availability", e) + } + } + + + private fun shouldTriggerImmediateUpdate(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + } + + private fun shouldTriggerFlexibleUpdate(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + } + + + private fun startImmediateUpdate(appUpdateInfo: AppUpdateInfo) { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + activityResultLauncher, + AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build() + ) + } + + private fun startFlexibleUpdate(appUpdateInfo: AppUpdateInfo) { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + activityResultLauncher, + AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build() + ) + } + + private fun handleUpdateResult(result: ActivityResult) { + if (result.resultCode == RESULT_OK) { + Log.d("UpdateFlow", "Update flow completed successfully!") + } else { + Log.e("UpdateFlow", "Update flow failed! Result code: ${result.resultCode}") + // 필요시 재시도 로직 추가 가능 + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen() WindowCompat.setDecorFitsSystemWindows(window, false) + + appUpdateManager = AppUpdateManagerFactory.create(this) + checkImmediateUpdateAvailability() + requestNotificationPermission() BootpayAnalytics.init(this, BuildConfig.BOOTPAY_APPLICATION_ID) @@ -162,6 +252,11 @@ class MainActivity : AppCompatActivity() { } } + override fun onResume() { + super.onResume() + checkFlexibleUpdateAvailability() + } + //firebase 초기 토큰 처리 private fun initializeFirebaseSetting(fcmToken: String?, onSaveFcmToken: (token: String) -> Unit) { FirebaseMessaging.getInstance().token.addOnSuccessListener { @@ -273,4 +368,4 @@ class MainActivity : AppCompatActivity() { lifecycleScope.launch { viewModel.saveNotificationEnabled(true) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/hmoa/app/AppViewModel.kt b/app/src/main/java/com/hmoa/app/viewmodel/AppViewModel.kt similarity index 93% rename from app/src/main/java/com/hmoa/app/AppViewModel.kt rename to app/src/main/java/com/hmoa/app/viewmodel/AppViewModel.kt index daaaf2b65..6b1adb339 100644 --- a/app/src/main/java/com/hmoa/app/AppViewModel.kt +++ b/app/src/main/java/com/hmoa/app/viewmodel/AppViewModel.kt @@ -1,4 +1,4 @@ -package com.hmoa.app +package com.hmoa.app.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -7,8 +7,6 @@ import com.hmoa.core_domain.repository.LoginRepository import com.hmoa.core_model.request.FCMTokenSaveRequestDto import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -43,4 +41,4 @@ class AppViewModel @Inject constructor( suspend fun saveNotificationEnabled(isEnabled: Boolean) = viewModelScope.launch { fcmRepository.saveNotificationEnabled(isEnabled) } -} \ No newline at end of file +} diff --git a/feature-home/src/main/java/com/hmoa/feature_home/PerfumeNameSearchPagingSource.kt b/feature-home/src/main/java/com/hmoa/feature_home/PerfumeNameSearchPagingSource.kt deleted file mode 100644 index e42c5731d..000000000 --- a/feature-home/src/main/java/com/hmoa/feature_home/PerfumeNameSearchPagingSource.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.hmoa.feature_home - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.hmoa.core_domain.repository.SearchRepository -import com.hmoa.core_model.response.PerfumeNameSearchResponseDto - -class PerfumeNameSearchPagingSource( - val searchRepository: SearchRepository, - val word: String -) : PagingSource() { - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { - state.closestPageToPosition(it)?.prevKey?.plus(1) - ?: state.closestPageToPosition(it)?.nextKey?.minus(1) - } - } - - override suspend fun load(params: LoadParams): LoadResult { - val pageNumber = params.key ?: 0 - try { - val response = searchRepository.getPerfumeName(page = pageNumber, searchWord = word) - - if (response.errorMessage != null) { - throw Exception(response.errorMessage!!.message) - } - - val prevKey = if (pageNumber > 0) pageNumber - 1 else null - val nextKey = if (response.data!!.isEmpty()) null else pageNumber + 1 - - return LoadResult.Page( - data = response.data!!, - prevKey = prevKey, - nextKey = nextKey - ) - } catch (e: Exception) { - return LoadResult.Error(e) - } - } - -} \ No newline at end of file diff --git a/feature-home/src/main/java/com/hmoa/feature_home/PerfumePagingSource.kt b/feature-home/src/main/java/com/hmoa/feature_home/PerfumePagingSource.kt new file mode 100644 index 000000000..d5ee53887 --- /dev/null +++ b/feature-home/src/main/java/com/hmoa/feature_home/PerfumePagingSource.kt @@ -0,0 +1,36 @@ +package com.hmoa.feature_home + +import androidx.paging.PagingSource +import androidx.paging.PagingState + +class PerfumePagingSource( + private val fetcher: suspend (pageNumber: Int) -> R, + private val mapper: (R) -> List +) : PagingSource() { + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { + state.closestPageToPosition(it)?.prevKey?.plus(1) + ?: state.closestPageToPosition(it)?.nextKey?.minus(1) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + val pageNumber = params.key ?: 0 + return try { + val response = fetcher(pageNumber) + val data = mapper(response) + + val prevKey = if (pageNumber > 0) pageNumber - 1 else null + val nextKey = if (data.isEmpty()) null else pageNumber + 1 + + LoadResult.Page( + data = data, + prevKey = prevKey, + nextKey = nextKey + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } +} diff --git a/feature-home/src/main/java/com/hmoa/feature_home/PerfumeSearchPagingSource.kt b/feature-home/src/main/java/com/hmoa/feature_home/PerfumeSearchPagingSource.kt deleted file mode 100644 index d10a2ae3f..000000000 --- a/feature-home/src/main/java/com/hmoa/feature_home/PerfumeSearchPagingSource.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.hmoa.feature_home - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.hmoa.core_domain.repository.SearchRepository -import com.hmoa.core_model.response.PerfumeSearchResponseDto - -class PerfumeSearchPagingSource( - val searchRepository: SearchRepository, - val word: String -) : PagingSource() { - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { - state.closestPageToPosition(it)?.prevKey?.plus(1) - ?: state.closestPageToPosition(it)?.nextKey?.minus(1) - } - } - - override suspend fun load(params: LoadParams): LoadResult { - val pageNumber = params.key ?: 0 - try { - val response = searchRepository.getPerfume(page = pageNumber, searchWord = word) - - if (response.errorMessage != null) { - throw Exception(response.errorMessage!!.message) - } - - val prevKey = if (pageNumber > 0) pageNumber - 1 else null - val nextKey = if (response.data!!.isEmpty()) null else pageNumber + 1 - - return LoadResult.Page( - data = response.data!!, - prevKey = prevKey, - nextKey = nextKey - ) - } catch (e: Exception) { - return LoadResult.Error(e) - } - } - - -} \ No newline at end of file diff --git a/feature-home/src/main/java/com/hmoa/feature_home/screen/PerfumeSearchScreen.kt b/feature-home/src/main/java/com/hmoa/feature_home/screen/PerfumeSearchScreen.kt index fd45d1f21..b0ad7ceba 100644 --- a/feature-home/src/main/java/com/hmoa/feature_home/screen/PerfumeSearchScreen.kt +++ b/feature-home/src/main/java/com/hmoa/feature_home/screen/PerfumeSearchScreen.kt @@ -138,7 +138,7 @@ fun PerfumeNameSearchResultList( LazyColumn(modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp)) { items(perfumeNameList?.itemSnapshotList?.items ?: emptyList()) { Text( - text = it!!.perfumeName ?: "", + text = it.perfumeName, modifier = Modifier.clickable { onPerfumeSearchResultClick(it.perfumeName) } .padding(vertical = 10.dp).fillMaxWidth(), style = TextStyle(fontWeight = FontWeight.Normal, fontSize = 14.sp) @@ -156,14 +156,14 @@ fun PerfumeSearchResultList( columns = GridCells.Fixed(2), horizontalArrangement = Arrangement.Center ) { - items(perfumeList?.itemSnapshotList?.items ?: emptyList()) { + items(perfumeList?.itemSnapshotList?.items ?: emptyList(), key = { it.perfumeId }) { Column(modifier = Modifier.clickable { onPerfumeSearchResultClick(it.perfumeName) } .padding(bottom = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) { PerfumeItemView( - imageUrl = it?.perfumeImageUrl ?: "", - perfumeName = it?.perfumeName ?: "", - brandName = it?.brandName ?: "", + imageUrl = it.perfumeImageUrl, + perfumeName = it.perfumeName, + brandName = it.brandName, containerWidth = 160, containerHeight = 160, imageWidth = 0.7f, diff --git a/feature-home/src/main/java/com/hmoa/feature_home/viewmodel/PerfumeSearchViewmodel.kt b/feature-home/src/main/java/com/hmoa/feature_home/viewmodel/PerfumeSearchViewmodel.kt index 10f3ffe74..0afe14dc7 100644 --- a/feature-home/src/main/java/com/hmoa/feature_home/viewmodel/PerfumeSearchViewmodel.kt +++ b/feature-home/src/main/java/com/hmoa/feature_home/viewmodel/PerfumeSearchViewmodel.kt @@ -11,8 +11,7 @@ import com.hmoa.core_domain.entity.data.PerfumeSearchViewType import com.hmoa.core_domain.repository.SearchRepository import com.hmoa.core_model.response.PerfumeNameSearchResponseDto import com.hmoa.core_model.response.PerfumeSearchResponseDto -import com.hmoa.feature_home.PerfumeNameSearchPagingSource -import com.hmoa.feature_home.PerfumeSearchPagingSource +import com.hmoa.feature_home.PerfumePagingSource import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -37,9 +36,16 @@ class PerfumeSearchViewmodel @Inject constructor(private val searchRepository: S getPagingPerfumeNameSearchResults() } - fun perfumeNameSearchPagingSource(word: String) = PerfumeNameSearchPagingSource(searchRepository, word) + fun perfumeNameSearchPagingSource(word: String) = + PerfumePagingSource( + fetcher = { pageNumber -> searchRepository.getPerfumeName(pageNumber, word) }, + mapper = { it.data ?: emptyList() } + ) - fun perfumeSearchPagingSource(word: String) = PerfumeSearchPagingSource(searchRepository, word) + fun perfumeSearchPagingSource(word: String) = PerfumePagingSource( + fetcher = { pageNumber -> searchRepository.getPerfume(page = pageNumber, searchWord = word) }, + mapper = { it.data ?: emptyList() } + ) fun getPagingPerfumeNameSearchResults(): Flow>? { if (_perfumeNameSearchWordState.value != null) { diff --git a/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLatestPagingSource.kt b/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLatestPagingSource.kt deleted file mode 100644 index b7fbd2a64..000000000 --- a/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLatestPagingSource.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.hmoa.feature_perfume - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.hmoa.core_domain.repository.PerfumeCommentRepository -import com.hmoa.core_model.response.PerfumeCommentResponseDto - -class PerfumeCommentLatestPagingSource( - private val perfumeCommentRepository: PerfumeCommentRepository, - private val perfumeId: Int, -) : PagingSource() { - private var commentCounts = 0 - private var cursor = 0 - - override suspend fun load(params: LoadParams): LoadResult { - val pageNumber = params.key ?: 0 - try { - val response = - perfumeCommentRepository.getPerfumeCommentsLatest( - pageNumber, - cursor = cursor, - perfumeId = perfumeId - ) - commentCounts = response.commentCount - cursor = response.comments.get(response.comments.lastIndex).id - val prevKey = if (pageNumber > 0) pageNumber - 1 else null - val nextKey = if (response.lastPage) null else pageNumber + 1 - - return LoadResult.Page( - data = response.comments, - prevKey = prevKey, - nextKey = nextKey - ) - } catch (e: Exception) { - return LoadResult.Error(e) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { - state.closestPageToPosition(it)?.prevKey?.plus(1) - ?: state.closestPageToPosition(it)?.nextKey?.minus(1) - } - } -} \ No newline at end of file diff --git a/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLikePagingSource.kt b/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentPagingSource.kt similarity index 74% rename from feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLikePagingSource.kt rename to feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentPagingSource.kt index efe33afdd..988ce3817 100644 --- a/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentLikePagingSource.kt +++ b/feature-perfume/src/main/java/com/hmoa/feature_perfume/PerfumeCommentPagingSource.kt @@ -2,12 +2,11 @@ package com.hmoa.feature_perfume import androidx.paging.PagingSource import androidx.paging.PagingState -import com.hmoa.core_domain.repository.PerfumeCommentRepository +import com.hmoa.core_model.response.PerfumeCommentGetResponseDto import com.hmoa.core_model.response.PerfumeCommentResponseDto -class PerfumeCommentLikePagingSource( - private val perfumeCommentRepository: PerfumeCommentRepository, - private val perfumeId: Int, +class PerfumeCommentPagingSource( + private val fetcher: suspend (pageNumber: Int, cursor: Int) -> PerfumeCommentGetResponseDto, ) : PagingSource() { private var commentCounts = 0 private var cursor = 0 @@ -15,11 +14,7 @@ class PerfumeCommentLikePagingSource( override suspend fun load(params: LoadParams): LoadResult { val pageNumber = params.key ?: 0 try { - val response = - perfumeCommentRepository.getPerfumeCommentsLikest( - page = pageNumber.toString(), - perfumeId = perfumeId.toString() - ) + val response = fetcher(pageNumber, cursor) commentCounts = response.commentCount cursor = response.comments.get(response.comments.lastIndex).id val prevKey = if (pageNumber > 0) pageNumber - 1 else null @@ -41,4 +36,4 @@ class PerfumeCommentLikePagingSource( ?: state.closestPageToPosition(it)?.nextKey?.minus(1) } } -} \ No newline at end of file +} diff --git a/feature-perfume/src/main/java/com/hmoa/feature_perfume/viewmodel/PerfumeCommentViewmodel.kt b/feature-perfume/src/main/java/com/hmoa/feature_perfume/viewmodel/PerfumeCommentViewmodel.kt index dac39c292..77c437d41 100644 --- a/feature-perfume/src/main/java/com/hmoa/feature_perfume/viewmodel/PerfumeCommentViewmodel.kt +++ b/feature-perfume/src/main/java/com/hmoa/feature_perfume/viewmodel/PerfumeCommentViewmodel.kt @@ -7,15 +7,14 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn +import com.hmoa.core_domain.entity.data.SortType import com.hmoa.core_domain.repository.LoginRepository import com.hmoa.core_domain.repository.PerfumeCommentRepository import com.hmoa.core_domain.repository.ReportRepository import com.hmoa.core_domain.usecase.UpdateLikePerfumeCommentUseCase -import com.hmoa.core_domain.entity.data.SortType import com.hmoa.core_model.request.TargetRequestDto import com.hmoa.core_model.response.PerfumeCommentResponseDto -import com.hmoa.feature_perfume.PerfumeCommentLatestPagingSource -import com.hmoa.feature_perfume.PerfumeCommentLikePagingSource +import com.hmoa.feature_perfume.PerfumeCommentPagingSource import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* @@ -77,15 +76,21 @@ class PerfumeCommentViewmodel @Inject constructor( _unLoginedErrorState.update { true } } - fun latestPerfumeCommentPagingSource(perfumeId: Int) = PerfumeCommentLatestPagingSource( - perfumeId = perfumeId, - perfumeCommentRepository = perfumeCommentRepository - ) + fun latestPerfumeCommentPagingSource(perfumeId: Int) = PerfumeCommentPagingSource(fetcher = { pageNumber, cursor -> + perfumeCommentRepository.getPerfumeCommentsLatest( + pageNumber, + cursor = cursor, + perfumeId = perfumeId + ) + }) + + fun likePerfumeCommentPagingSource(perfumeId: Int) = PerfumeCommentPagingSource(fetcher = { pageNumber, cursor -> + perfumeCommentRepository.getPerfumeCommentsLikest( + pageNumber.toString(), + perfumeId = perfumeId.toString() + ) + }) - fun likePerfumeCommentPagingSource(perfumeId: Int) = PerfumeCommentLikePagingSource( - perfumeId = perfumeId, - perfumeCommentRepository = perfumeCommentRepository - ) fun getPagingLatestPerfumeComments(perfumeId: Int?): Flow>? { if (perfumeId != null) { @@ -152,4 +157,4 @@ class PerfumeCommentViewmodel @Inject constructor( data object Error : PerfumeCommentUiState } -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c032c0233..84901f190 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,6 +36,7 @@ appcompat-version = "1.6.1" splash-screen-version = "1.2.0-alpha01" constraintlayout-version = "2.1.4" lifecycle-version = "2.5.1" +app-update-version = "2.1.0" room-version = "2.6.1" datastore-version = "1.0.0" @@ -60,78 +61,79 @@ hilt-test-version = "2.44" junit-jupiter-version = "5.3.1" [libraries] -bootpay = {group = "io.github.bootpay", name = "android", version.ref = "bootpay-version"} -kakao-all = {group = "com.kakao.sdk", name = "v2-all", version.ref = "kakao-version"} -kakao-user = {group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-version"} -kakao-talk = {group = "com.kakao.sdk", name = "v2-talk", version.ref = "kakao-version"} -kakao-share = {group = "com.kakao.sdk", name = "v2-share", version.ref = "kakao-version"} -kakao-freind = {group = "com.kakao.sdk", name = "v2-friend", version.ref = "kakao-version"} -kakao-cert = {group = "com.kakao.sdk", name = "v2-cert", version.ref = "kakao-version"} -hilt-android = {group = "com.google.dagger", name = "hilt-android", version.ref = "hilt-version"} -hilt-compiler = {group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt-version"} -hilt-android-compiler = {group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt-version"} -hilt-navigation-compose = {group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-nav-compose-version"} -hilt-viewmodel = {group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt-viewmodel-version"} -ui-tooling-preview = {group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "ui-version"} -ui-tooling = {group = "androidx.compose.ui", name = "ui-tooling", version.ref = "ui-tooling-version"} -ui = {group = "androidx.compose.ui", name = "ui", version.ref = "ui-version"} -navigation-compose = {group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation-compose-version"} -core-ktx = {group = "androidx.core", name = "core-ktx", version.ref = "core-ktx-version"} -appcompat = {group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat-version"} -splash-screen = {group = "androidx.core", name = "core-splashscreen", version.ref = "splash-screen-version"} -google-material = {group = "com.google.android.material", name = "material", version.ref = "google-material-version"} -material = {group = "androidx.compose.material", name = "material", version.ref = "material-version"} -material3 = {group = "androidx.compose.material3", name = "material3", version.ref = "metarial3-version"} -firebase-messaging = {group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebase-messaging-version"} -firebase-analytics = {group = "com.google.firebase", name = "firebase-analytics", version.ref = "firebase-analytics-version"} -play-service-auth = {group = "com.google.android.gms", name = "play-services-auth", version.ref = "play-service-auth-version"} -google-services = {group = "com.google.gms", name = "google-services", version.ref = "google-services-version"} -open-licenses = {group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "oss-licenses-version"} -room-ktx = {group = "androidx.room", name = "room-ktx", version.ref = "room-version"} -room-compiler = {group = "androidx.room", name = "room-compiler", version.ref = "room-version"} -datastore = {group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore-version"} -junit = {group = "junit", name = "junit", version.ref = "junit-version"} -junit-ktx = {group = "androidx.test.ext", name = "junit-ktx", version.ref = "junit-ktx-version"} -junit-ext = {group = "androidx.test.ext", name = "junit", version.ref = "junit-ext-version"} -mockito-core = {group = "org.mockito", name = "mockito-core", version.ref = "mockito-version"} -mockito-inline = {group = "org.mockito", name = "mockito-inline", version.ref = "mockito-version"} -mockito-android = {group = "org.mockito", name = "mockito-android", version.ref = "mockito-version"} -mockito-kotlin = {group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockito-kotlin-version"} -espresso = {group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-version"} -coroutine-test = {group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutine-test-version"} -reflect = {group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin-reflect-version"} -hilt-android-test = {group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt-test-version"} -sandwich = {group = "com.github.skydoves", name="sandwich", version.ref = "sandwich-version"} -kotlinx-serialization-json = {group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-version"} -coil-gif = {group = "io.coil-kt", name = "coil-gif", version.ref = "coil-version"} -coil-compose = {group = "io.coil-kt", name = "coil-compose", version.ref = "coil-version"} -landscapist-glide = {group = "com.github.skydoves", name = "landscapist-glide", version.ref = "landscapist-glide-version"} -bumptech-glide = {group = "com.github.bumptech.glide", name = "glide", version.ref = "bumptech-glide-version"} -accommpanist-pager = {group = "com.google.accompanist", name = "accompanist-pager", version.ref = "paging-version"} -retrofit = {group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit-version"} -retrofit-gson = {group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit-version"} -logging-interceptor = {group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp3-version"} -lifecycle-viewmodel-ktx = {group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle-version"} -lifecycle-viewmodel-compose = {group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle-version"} -lifecycle-runtime = {group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-version"} -constraintlayout = {group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout-version"} -junit-jupiter-api = {group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit-jupiter-version"} -junit-jupiter-engine = {group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit-jupiter-version"} -paging-compose = {group = "androidx.paging", name = "paging-compose", version.ref = "paging-compose-version"} -core-gson = {group = "com.google.code.gson", name = "gson", version.ref = "gson-version"} -ui-test-junit4 = {group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "junit4-version"} -ui-test-manifest = {group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "junit4-version"} -kotlinx-collections-immutable = {group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable-version"} -testing = {group = "org.testing", name = "testing", version.ref = "testing-version"} +app-update-ktx = { group = "com.google.android.play", name = "app-update-ktx", version.ref = "app-update-version" } +bootpay = { group = "io.github.bootpay", name = "android", version.ref = "bootpay-version" } +kakao-all = { group = "com.kakao.sdk", name = "v2-all", version.ref = "kakao-version" } +kakao-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-version" } +kakao-talk = { group = "com.kakao.sdk", name = "v2-talk", version.ref = "kakao-version" } +kakao-share = { group = "com.kakao.sdk", name = "v2-share", version.ref = "kakao-version" } +kakao-freind = { group = "com.kakao.sdk", name = "v2-friend", version.ref = "kakao-version" } +kakao-cert = { group = "com.kakao.sdk", name = "v2-cert", version.ref = "kakao-version" } +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt-version" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt-version" } +hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt-version" } +hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-nav-compose-version" } +hilt-viewmodel = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt-viewmodel-version" } +ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "ui-version" } +ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "ui-tooling-version" } +ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui-version" } +navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation-compose-version" } +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx-version" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat-version" } +splash-screen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splash-screen-version" } +google-material = { group = "com.google.android.material", name = "material", version.ref = "google-material-version" } +material = { group = "androidx.compose.material", name = "material", version.ref = "material-version" } +material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "metarial3-version" } +firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebase-messaging-version" } +firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics", version.ref = "firebase-analytics-version" } +play-service-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "play-service-auth-version" } +google-services = { group = "com.google.gms", name = "google-services", version.ref = "google-services-version" } +open-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "oss-licenses-version" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room-version" } +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room-version" } +datastore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore-version" } +junit = { group = "junit", name = "junit", version.ref = "junit-version" } +junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "junit-ktx-version" } +junit-ext = { group = "androidx.test.ext", name = "junit", version.ref = "junit-ext-version" } +mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito-version" } +mockito-inline = { group = "org.mockito", name = "mockito-inline", version.ref = "mockito-version" } +mockito-android = { group = "org.mockito", name = "mockito-android", version.ref = "mockito-version" } +mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockito-kotlin-version" } +espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-version" } +coroutine-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutine-test-version" } +reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin-reflect-version" } +hilt-android-test = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt-test-version" } +sandwich = { group = "com.github.skydoves", name = "sandwich", version.ref = "sandwich-version" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-version" } +coil-gif = { group = "io.coil-kt", name = "coil-gif", version.ref = "coil-version" } +coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil-version" } +landscapist-glide = { group = "com.github.skydoves", name = "landscapist-glide", version.ref = "landscapist-glide-version" } +bumptech-glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "bumptech-glide-version" } +accommpanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "paging-version" } +retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit-version" } +retrofit-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit-version" } +logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp3-version" } +lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle-version" } +lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle-version" } +lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-version" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout-version" } +junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit-jupiter-version" } +junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit-jupiter-version" } +paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging-compose-version" } +core-gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson-version" } +ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "junit4-version" } +ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "junit4-version" } +kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable-version" } +testing = { group = "org.testing", name = "testing", version.ref = "testing-version" } [plugins] -android-application = {id = "com.android.application", version.ref = "android-application"} -kotlin-android = {id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android"} -kotlin-jvm = {id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-jvm"} -android-library = {id = "com.android.library", version.ref = "android-library"} -hilt-android = {id = "com.google.dagger.hilt.android", version.ref = "hilt-android"} -kotlin-plugin-serialization = {id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization"} -google-services = {id = "com.google.gms.google-services", version.ref = "plugin-google-services"} +android-application = { id = "com.android.application", version.ref = "android-application" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-jvm" } +android-library = { id = "com.android.library", version.ref = "android-library" } +hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt-android" } +kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization" } +google-services = { id = "com.google.gms.google-services", version.ref = "plugin-google-services" } [bundles] kakao-login = ["kakao-all", "kakao-user", "kakao-talk", "kakao-cert", "kakao-share", "kakao-freind"] @@ -145,4 +147,4 @@ coil = ["coil-gif", "coil-compose"] glide = ["landscapist-glide", "bumptech-glide"] retrofit = ["retrofit", "retrofit-gson"] lifecycle = ["lifecycle-runtime", "lifecycle-viewmodel-compose", "lifecycle-viewmodel-ktx"] -android-test-mockito = ["mockito-core", "mockito-android", "mockito-kotlin"] \ No newline at end of file +android-test-mockito = ["mockito-core", "mockito-android", "mockito-kotlin"]