From 68130aa1e1b37d21d3d3b39e74fe650e9f2d1507 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Sun, 29 Sep 2024 22:13:20 +0900 Subject: [PATCH 1/9] [feat] #172 survey popup ui --- .../src/main/res/values/strings.xml | 5 ++ .../main/java/org/sopt/home/HomeContract.kt | 1 + .../main/java/org/sopt/home/HomeFragment.kt | 9 ++ .../main/java/org/sopt/home/HomeViewModel.kt | 1 + .../org/sopt/home/SurveyDialogFragment.kt | 42 +++++++++ .../res/layout/fragment_survey_dialog.xml | 90 +++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt create mode 100644 feature/home/src/main/res/layout/fragment_survey_dialog.xml diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index a22fe03d..c4f23723 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -155,6 +155,11 @@ 님의 클립 이주의 링크 이주의 추천 사이트 + 1분 설문조사 참여하고\n스타벅스 기프티콘 받기 + 토스터 사용 피드백을 남겨주시면\n추첨을 통해 기프티콘을 드려요! + 참여하기 + 일주일간 보지 않기 + 클립의 이름은 최대 15자까지 입력 가능해요 diff --git a/feature/home/src/main/java/org/sopt/home/HomeContract.kt b/feature/home/src/main/java/org/sopt/home/HomeContract.kt index 797a17cf..be895c77 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeContract.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeContract.kt @@ -31,4 +31,5 @@ sealed interface HomeSideEffect { data object NavigateClipLink : HomeSideEffect data object NavigateWebView : HomeSideEffect data object ShowBottomSheet : HomeSideEffect + data object ShowPopupInfo : HomeSideEffect } diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index 70926045..cfd39cfe 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -37,6 +37,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. private fun initView() { initAdapter() + viewModel.showPopupInfo() } private fun collectState() { @@ -62,6 +63,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. is HomeSideEffect.NavigateClipLink -> navigateToDestination( "featureSaveLink://ClipLinkFragment/${viewModel.container.stateFlow.value.categoryId}/${viewModel.container.stateFlow.value.categoryName}", ) + is HomeSideEffect.ShowBottomSheet -> showHomeBottomSheet() is HomeSideEffect.NavigateWebView -> { val encodedURL = URLEncoder.encode(viewModel.container.stateFlow.value.url, StandardCharsets.UTF_8.toString()) @@ -69,6 +71,8 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. "featureSaveLink://webViewFragment/${0}/${false}/${false}/$encodedURL", ) } + + is HomeSideEffect.ShowPopupInfo -> showPopupInfo() } } @@ -82,6 +86,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. getWeekBestLink() } } + private fun navigateToSetting() { binding.ivHomeSetting.onThrottleClick { viewModel.navigateSetting() @@ -154,4 +159,8 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. } } } + + private fun showPopupInfo() { + SurveyDialogFragment().show(parentFragmentManager, this.tag) + } } diff --git a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt index 9326aa7f..ee19281f 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt @@ -89,6 +89,7 @@ class HomeViewModel @Inject constructor( fun navigateSearch() = intent { postSideEffect(HomeSideEffect.NavigateSearch) } fun navigateSetting() = intent { postSideEffect(HomeSideEffect.NavigateSetting) } fun showBottomSheet() = intent { postSideEffect(HomeSideEffect.ShowBottomSheet) } + fun showPopupInfo() = intent { postSideEffect(HomeSideEffect.ShowPopupInfo) } @OptIn(OrbitExperimental::class) fun navigateClipLink(categoryId: Long?, categoryName: String?) = blockingIntent { diff --git a/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt new file mode 100644 index 00000000..6741611e --- /dev/null +++ b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt @@ -0,0 +1,42 @@ +package org.sopt.home + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.View +import org.sopt.home.databinding.FragmentSurveyDialogBinding +import org.sopt.ui.base.BindingDialogFragment +import org.sopt.ui.view.onThrottleClick + +class SurveyDialogFragment : BindingDialogFragment( + { FragmentSurveyDialogBinding.inflate(it) }, +) { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_FRAME, android.R.style.Theme_Dialog) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.ivSurveyDialogClose.onThrottleClick { + dismiss() + } + + binding.btnSurveyDialog.onThrottleClick { + dismiss() + } + + binding.btnSurveyDialogSkip.onThrottleClick { + dismiss() + } + } + + override fun onStart() { + super.onStart() + val dialog = dialog + if (dialog != null) { + dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + } +} diff --git a/feature/home/src/main/res/layout/fragment_survey_dialog.xml b/feature/home/src/main/res/layout/fragment_survey_dialog.xml new file mode 100644 index 00000000..41f9b854 --- /dev/null +++ b/feature/home/src/main/res/layout/fragment_survey_dialog.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + From 3156302e8ff3c3ccde1215bc2ad24260128a67b1 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:59:44 +0900 Subject: [PATCH 2/9] [feat] #172 popup api --- .../java/org/sopt/model/home/PopupInfo.kt | 9 +++++ .../org/sopt/model/home/PopupInvisible.kt | 6 +++ .../org/sopt/remote/home/api/PopupService.kt | 25 ++++++++++++ .../datasource/RemotePopupDatasourceImpl.kt | 20 ++++++++++ .../remote/home/di/HomeDataSourceModule.kt | 8 ++++ .../sopt/remote/home/di/HomeServiceModule.kt | 7 ++++ .../home/request/RequestPopupInvisibleDto.kt | 12 ++++++ .../home/response/ResponsePopupInfoDto.kt | 36 +++++++++++++++++ .../response/ResponsePopupInvisibleDto.kt | 18 +++++++++ .../home/datasource/RemotePopupDataSource.kt | 9 +++++ .../java/org/sopt/home/di/RepositoryModule.kt | 8 ++++ .../org/sopt/home/repository/PopupRepoImpl.kt | 16 ++++++++ .../sopt/home/repository/PopupRepository.kt | 9 +++++ .../org/sopt/home/usecase/GetPopupInfo.kt | 11 ++++++ .../sopt/home/usecase/PatchPopupInvisible.kt | 12 ++++++ .../main/java/org/sopt/home/HomeContract.kt | 2 + .../main/java/org/sopt/home/HomeFragment.kt | 19 +++++++-- .../main/java/org/sopt/home/HomeViewModel.kt | 39 ++++++++++++++++++- .../org/sopt/home/SurveyDialogFragment.kt | 27 +++++++++++++ .../res/layout/fragment_survey_dialog.xml | 2 + 20 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 core/model/src/main/java/org/sopt/model/home/PopupInfo.kt create mode 100644 core/model/src/main/java/org/sopt/model/home/PopupInvisible.kt create mode 100644 data-remote/home/src/main/java/org/sopt/remote/home/api/PopupService.kt create mode 100644 data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt create mode 100644 data-remote/home/src/main/java/org/sopt/remote/home/request/RequestPopupInvisibleDto.kt create mode 100644 data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt create mode 100644 data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInvisibleDto.kt create mode 100644 data/home/src/main/java/org/sopt/home/datasource/RemotePopupDataSource.kt create mode 100644 data/home/src/main/java/org/sopt/home/repository/PopupRepoImpl.kt create mode 100644 domain/home/src/main/java/org/sopt/home/repository/PopupRepository.kt create mode 100644 domain/home/src/main/java/org/sopt/home/usecase/GetPopupInfo.kt create mode 100644 domain/home/src/main/java/org/sopt/home/usecase/PatchPopupInvisible.kt diff --git a/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt b/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt new file mode 100644 index 00000000..7c3cab90 --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt @@ -0,0 +1,9 @@ +package org.sopt.model.home + +data class PopupInfo( + val popupId: Int, + val popupImage: String, + val popupActiveStartDate: String, + val popupActiveEndDate: String, + val popupLinkUrl: String, + ) diff --git a/core/model/src/main/java/org/sopt/model/home/PopupInvisible.kt b/core/model/src/main/java/org/sopt/model/home/PopupInvisible.kt new file mode 100644 index 00000000..66c43e34 --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/home/PopupInvisible.kt @@ -0,0 +1,6 @@ +package org.sopt.model.home + +data class PopupInvisible( + val popupId: Int, + val popupHideUntil: String, +) diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/api/PopupService.kt b/data-remote/home/src/main/java/org/sopt/remote/home/api/PopupService.kt new file mode 100644 index 00000000..a3f23894 --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/api/PopupService.kt @@ -0,0 +1,25 @@ +package org.sopt.remote.home.api + +import org.sopt.network.model.response.base.BaseResponse +import org.sopt.remote.home.request.RequestPopupInvisibleDto +import org.sopt.remote.home.response.ResponsePopupInfoDto +import org.sopt.remote.home.response.ResponsePopupInvisibleDto +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PATCH + +interface PopupService { + companion object { + const val API = "api" + const val V2 = "v2" + const val POPUP = "popup" + } + + @PATCH("/$API/$V2/$POPUP") + suspend fun patchPopupInvisible( + @Body requestPopupInvisibleDto: RequestPopupInvisibleDto, + ): BaseResponse + + @GET("/$API/$V2/$POPUP") + suspend fun getPopupInfo(): BaseResponse +} diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt new file mode 100644 index 00000000..e0e1eb25 --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt @@ -0,0 +1,20 @@ +package org.sopt.remote.home.datasource + +import org.sopt.home.datasource.RemotePopupDataSource +import org.sopt.model.home.PopupInfo +import org.sopt.model.home.PopupInvisible +import org.sopt.remote.home.api.PopupService +import org.sopt.remote.home.request.RequestPopupInvisibleDto +import org.sopt.remote.home.response.toCoreModel +import javax.inject.Inject + +class RemotePopupDatasourceImpl @Inject constructor( + private val popupService: PopupService, +) : RemotePopupDataSource { + override suspend fun patchPopupInvisible(popupId: Long, hideDate: Long): PopupInvisible = + popupService.patchPopupInvisible(RequestPopupInvisibleDto(popupId, hideDate)).data!!.toCoreModel() + + + override suspend fun getPopupInfo(): List = + popupService.getPopupInfo().data!!.popupList.map { it.toCoreModel() } +} diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeDataSourceModule.kt b/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeDataSourceModule.kt index 741e3551..2ea85376 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeDataSourceModule.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeDataSourceModule.kt @@ -5,7 +5,9 @@ import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import org.sopt.home.datasource.RemoteHomeDataSource +import org.sopt.home.datasource.RemotePopupDataSource import org.sopt.remote.home.datasource.RemoteHomeDataSourceImpl +import org.sopt.remote.home.datasource.RemotePopupDatasourceImpl import javax.inject.Singleton @Module @@ -16,4 +18,10 @@ abstract class HomeDataSourceModule { abstract fun bindRemoteHomeDatasource( remoteHomeDataSourceImpl: RemoteHomeDataSourceImpl, ): RemoteHomeDataSource + + @Singleton + @Binds + abstract fun bindRemotePopupDataSource( + remotePopupDataSourceImpl: RemotePopupDatasourceImpl, + ): RemotePopupDataSource } diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeServiceModule.kt b/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeServiceModule.kt index b750cfaa..e7f289c7 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeServiceModule.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/di/HomeServiceModule.kt @@ -6,8 +6,10 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import org.sopt.network.di.AuthLinkMindRetrofit import org.sopt.remote.home.api.HomeService +import org.sopt.remote.home.api.PopupService import retrofit2.Retrofit import javax.inject.Singleton + @Module @InstallIn(SingletonComponent::class) object HomeServiceModule { @@ -16,4 +18,9 @@ object HomeServiceModule { @Provides fun provideHomeService(@AuthLinkMindRetrofit retrofit: Retrofit): HomeService = retrofit.create(HomeService::class.java) + + @Singleton + @Provides + fun providePopupService(@AuthLinkMindRetrofit retrofit: Retrofit): PopupService = + retrofit.create(PopupService::class.java) } diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/request/RequestPopupInvisibleDto.kt b/data-remote/home/src/main/java/org/sopt/remote/home/request/RequestPopupInvisibleDto.kt new file mode 100644 index 00000000..a7e19640 --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/request/RequestPopupInvisibleDto.kt @@ -0,0 +1,12 @@ +package org.sopt.remote.home.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPopupInvisibleDto( + @SerialName("popupId") + val popupId: Long, + @SerialName("hideDate") + val hideDate: Long, +) diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt new file mode 100644 index 00000000..973f6560 --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt @@ -0,0 +1,36 @@ +package org.sopt.remote.home.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.model.home.PopupInfo + +@Serializable +data class ResponsePopupInfoDto( + @SerialName("popupList") + val popupList: List +) +@Serializable +data class ResponsePopupInfo( + @SerialName("id") + val id: Int, + @SerialName("image") + val image: String, + @SerialName("activeStartDate") + val activeStartDate: String, + @SerialName("activeEndDate") + val activeEndDate: String, + @SerialName("linkUrl") + val linkUrl: String, +) + +internal fun ResponsePopupInfoDto.toCoreModel() = popupList.map { + it.toCoreModel() +} + +internal fun ResponsePopupInfo.toCoreModel() = PopupInfo( + popupId = id, + popupImage = image, + popupActiveStartDate = activeStartDate, + popupActiveEndDate = activeEndDate, + popupLinkUrl = linkUrl, +) diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInvisibleDto.kt b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInvisibleDto.kt new file mode 100644 index 00000000..a475497f --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInvisibleDto.kt @@ -0,0 +1,18 @@ +package org.sopt.remote.home.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.model.home.PopupInvisible + +@Serializable +data class ResponsePopupInvisibleDto( + @SerialName("popupId") + val popupId: Int, + @SerialName("hideUntil") + val hideUntil: String, +) + +internal fun ResponsePopupInvisibleDto.toCoreModel() = PopupInvisible( + popupId = popupId, + popupHideUntil = hideUntil, +) diff --git a/data/home/src/main/java/org/sopt/home/datasource/RemotePopupDataSource.kt b/data/home/src/main/java/org/sopt/home/datasource/RemotePopupDataSource.kt new file mode 100644 index 00000000..a6f76163 --- /dev/null +++ b/data/home/src/main/java/org/sopt/home/datasource/RemotePopupDataSource.kt @@ -0,0 +1,9 @@ +package org.sopt.home.datasource + +import org.sopt.model.home.PopupInfo +import org.sopt.model.home.PopupInvisible + +interface RemotePopupDataSource { + suspend fun patchPopupInvisible(popupId: Long, hideDate: Long): PopupInvisible + suspend fun getPopupInfo(): List +} diff --git a/data/home/src/main/java/org/sopt/home/di/RepositoryModule.kt b/data/home/src/main/java/org/sopt/home/di/RepositoryModule.kt index 397f2b75..9137c2a7 100644 --- a/data/home/src/main/java/org/sopt/home/di/RepositoryModule.kt +++ b/data/home/src/main/java/org/sopt/home/di/RepositoryModule.kt @@ -6,6 +6,8 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import org.sopt.home.repository.HomeRepoImpl import org.sopt.home.repository.HomeRepository +import org.sopt.home.repository.PopupRepoImpl +import org.sopt.home.repository.PopupRepository import javax.inject.Singleton @Module @@ -16,4 +18,10 @@ abstract class RepositoryModule { abstract fun bindHomeRepository( homeRepoImpl: HomeRepoImpl, ): HomeRepository + + @Singleton + @Binds + abstract fun bindPopupRepository( + popupRepoImpl: PopupRepoImpl, + ): PopupRepository } diff --git a/data/home/src/main/java/org/sopt/home/repository/PopupRepoImpl.kt b/data/home/src/main/java/org/sopt/home/repository/PopupRepoImpl.kt new file mode 100644 index 00000000..ef1388af --- /dev/null +++ b/data/home/src/main/java/org/sopt/home/repository/PopupRepoImpl.kt @@ -0,0 +1,16 @@ +package org.sopt.home.repository + +import org.sopt.home.datasource.RemotePopupDataSource +import org.sopt.model.home.PopupInfo +import org.sopt.model.home.PopupInvisible +import javax.inject.Inject + +class PopupRepoImpl @Inject constructor( + private val remotePopupDataSource: RemotePopupDataSource, +) : PopupRepository { + override suspend fun patchPopupInvisible(popupId: Long, hideDate: Long): Result = + runCatching { remotePopupDataSource.patchPopupInvisible(popupId, hideDate) } + + override suspend fun getPopupInfo(): Result> = + runCatching { remotePopupDataSource.getPopupInfo() } +} diff --git a/domain/home/src/main/java/org/sopt/home/repository/PopupRepository.kt b/domain/home/src/main/java/org/sopt/home/repository/PopupRepository.kt new file mode 100644 index 00000000..9b921c84 --- /dev/null +++ b/domain/home/src/main/java/org/sopt/home/repository/PopupRepository.kt @@ -0,0 +1,9 @@ +package org.sopt.home.repository + +import org.sopt.model.home.PopupInfo +import org.sopt.model.home.PopupInvisible + +interface PopupRepository { + suspend fun patchPopupInvisible(popupId: Long, hideDate: Long): Result + suspend fun getPopupInfo(): Result> +} diff --git a/domain/home/src/main/java/org/sopt/home/usecase/GetPopupInfo.kt b/domain/home/src/main/java/org/sopt/home/usecase/GetPopupInfo.kt new file mode 100644 index 00000000..163db982 --- /dev/null +++ b/domain/home/src/main/java/org/sopt/home/usecase/GetPopupInfo.kt @@ -0,0 +1,11 @@ +package org.sopt.home.usecase + +import org.sopt.home.repository.PopupRepository +import org.sopt.model.home.PopupInfo +import javax.inject.Inject + +class GetPopupInfo @Inject constructor( + private val popupRepository: PopupRepository, +) { + suspend operator fun invoke(): Result> = popupRepository.getPopupInfo() +} diff --git a/domain/home/src/main/java/org/sopt/home/usecase/PatchPopupInvisible.kt b/domain/home/src/main/java/org/sopt/home/usecase/PatchPopupInvisible.kt new file mode 100644 index 00000000..65b069ec --- /dev/null +++ b/domain/home/src/main/java/org/sopt/home/usecase/PatchPopupInvisible.kt @@ -0,0 +1,12 @@ +package org.sopt.home.usecase + +import org.sopt.home.repository.PopupRepository +import org.sopt.model.home.PopupInvisible +import javax.inject.Inject + +class PatchPopupInvisible @Inject constructor( + private val popupRepository: PopupRepository, +) { + suspend operator fun invoke(popupId: Long, hideDate: Long): Result = + popupRepository.patchPopupInvisible(popupId, hideDate) +} diff --git a/feature/home/src/main/java/org/sopt/home/HomeContract.kt b/feature/home/src/main/java/org/sopt/home/HomeContract.kt index be895c77..03e4b057 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeContract.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeContract.kt @@ -1,6 +1,7 @@ package org.sopt.home import org.sopt.model.category.Category +import org.sopt.model.home.PopupInfo import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink @@ -14,6 +15,7 @@ data class HomeState( val url: String = "", val categoryId: Long? = 0, val categoryName: String? = "전체 클립", + val popupList: List = emptyList(), ) { fun calculateProgress(): Int { if (readToastNum > allToastNum) return 0 diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index cfd39cfe..fbc554ef 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -14,6 +14,7 @@ import org.sopt.home.adapter.HomeWeekLinkAdapter import org.sopt.home.adapter.HomeWeekRecommendLinkAdapter import org.sopt.home.adapter.ItemDecoration import org.sopt.home.databinding.FragmentHomeBinding +import org.sopt.model.home.PopupInfo import org.sopt.ui.base.BindingFragment import org.sopt.ui.nav.DeepLinkUtil import org.sopt.ui.view.onThrottleClick @@ -37,7 +38,6 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. private fun initView() { initAdapter() - viewModel.showPopupInfo() } private fun collectState() { @@ -72,7 +72,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. ) } - is HomeSideEffect.ShowPopupInfo -> showPopupInfo() + is HomeSideEffect.ShowPopupInfo -> showPopupInfo(viewModel.container.stateFlow.value.popupList) } } @@ -84,6 +84,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. getMainPageUserClip() getRecommendSite() getWeekBestLink() + getPopupListInfo() } } @@ -160,7 +161,17 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. } } - private fun showPopupInfo() { - SurveyDialogFragment().show(parentFragmentManager, this.tag) + private fun showPopupInfo(popupList: List) { + popupList.forEach { + if (viewModel.checkPopupDate(it.popupActiveStartDate, it.popupActiveEndDate) + ) { + val surveyDialog = SurveyDialogFragment.newInstance( + it.popupImage, + { viewModel.navigateWebview(it.popupLinkUrl) }, + { viewModel.patchPopupInvisible(it.popupId.toLong(), 7) }, + ) + surveyDialog.show(parentFragmentManager, this.tag) + } + } } } diff --git a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt index ee19281f..ee01ba25 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt @@ -15,9 +15,14 @@ import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import org.sopt.domain.category.category.usecase.PostAddCategoryTitleUseCase import org.sopt.home.usecase.GetMainPageUserClip +import org.sopt.home.usecase.GetPopupInfo import org.sopt.home.usecase.GetRecommendSite import org.sopt.home.usecase.GetWeekBestLink +import org.sopt.home.usecase.PatchPopupInvisible import org.sopt.model.category.Category +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale import javax.inject.Inject @HiltViewModel @@ -26,6 +31,8 @@ class HomeViewModel @Inject constructor( private val getRecommendSite: GetRecommendSite, private val getWeekBestLink: GetWeekBestLink, private val postAddCategoryTitle: PostAddCategoryTitleUseCase, + private val patchPopupInvisible: PatchPopupInvisible, + private val getPopupInfo: GetPopupInfo, ) : ContainerHost, ViewModel() { override val container: Container = container(HomeState()) @@ -86,10 +93,32 @@ class HomeViewModel @Inject constructor( } } + fun getPopupListInfo() = intent { + getPopupInfo.invoke().onSuccess { + postSideEffect(HomeSideEffect.ShowPopupInfo) + reduce { + state.copy(popupList = it) + } + }.onFailure { + Log.d("getPopupListInfo", "$it") + } + } + + fun patchPopupInvisible(popupId: Long, hideDate: Long) { + viewModelScope.launch { + patchPopupInvisible.invoke(popupId, hideDate) + .onSuccess { + Log.d("patchPopupInvisible", "$it") + } + .onFailure { + Log.d("patchPopupInvisible", "$it") + } + } + } + fun navigateSearch() = intent { postSideEffect(HomeSideEffect.NavigateSearch) } fun navigateSetting() = intent { postSideEffect(HomeSideEffect.NavigateSetting) } fun showBottomSheet() = intent { postSideEffect(HomeSideEffect.ShowBottomSheet) } - fun showPopupInfo() = intent { postSideEffect(HomeSideEffect.ShowPopupInfo) } @OptIn(OrbitExperimental::class) fun navigateClipLink(categoryId: Long?, categoryName: String?) = blockingIntent { @@ -107,4 +136,12 @@ class HomeViewModel @Inject constructor( reduce { state.copy(url = url) } postSideEffect(HomeSideEffect.NavigateWebView) } + + fun checkPopupDate(activeStartDate: String, activeEndDate: String): Boolean { + val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val today = Calendar.getInstance().time + val startDate = dateFormat.parse(activeStartDate) + val endDate = dateFormat.parse(activeEndDate) + return today.after(startDate) && today.before(endDate) || today == startDate || today == endDate + } } diff --git a/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt index 6741611e..e1c4260c 100644 --- a/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt @@ -4,6 +4,7 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.View +import coil.load import org.sopt.home.databinding.FragmentSurveyDialogBinding import org.sopt.ui.base.BindingDialogFragment import org.sopt.ui.view.onThrottleClick @@ -11,23 +12,32 @@ import org.sopt.ui.view.onThrottleClick class SurveyDialogFragment : BindingDialogFragment( { FragmentSurveyDialogBinding.inflate(it) }, ) { + private var imageUrl: String? = null + private var linkUrl: () -> Unit = {} + private var handleSkip: () -> Unit = {} + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + imageUrl = arguments?.getString("imageUrl") setStyle(STYLE_NO_FRAME, android.R.style.Theme_Dialog) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setSurveyImage(imageUrl) + binding.ivSurveyDialogClose.onThrottleClick { dismiss() } binding.btnSurveyDialog.onThrottleClick { + linkUrl.invoke() dismiss() } binding.btnSurveyDialogSkip.onThrottleClick { + handleSkip.invoke() dismiss() } } @@ -39,4 +49,21 @@ class SurveyDialogFragment : BindingDialogFragment( dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } } + + private fun setSurveyImage(text: String?) = + binding.ivSurveyDialog.load(text) + + companion object { + fun newInstance(imageUrl: String, onNavigateWebView: () -> Unit, onNegativeButtonClick: () -> Unit): SurveyDialogFragment { + val args = Bundle().apply { + putString("imageUrl", imageUrl) + } + return SurveyDialogFragment().apply { + arguments = args + linkUrl = onNavigateWebView + handleSkip = onNegativeButtonClick + } + //얘는 bundle 안해도ㅗ되나? + } + } } diff --git a/feature/home/src/main/res/layout/fragment_survey_dialog.xml b/feature/home/src/main/res/layout/fragment_survey_dialog.xml index 41f9b854..e152ce04 100644 --- a/feature/home/src/main/res/layout/fragment_survey_dialog.xml +++ b/feature/home/src/main/res/layout/fragment_survey_dialog.xml @@ -78,6 +78,8 @@ android:layout_marginHorizontal="24dp" android:layout_marginTop="10dp" android:layout_marginBottom="24dp" + android:clickable="true" + android:focusable="true" android:gravity="center" android:text="@string/home_survey_skip" android:textAppearance="@style/Typography.suit.regular_14" From ecce41028c72c505052e3abaeebe8b703d14515a Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:39:09 +0900 Subject: [PATCH 3/9] [feat] #174 update dialog --- .../res/drawable/shape_white_fill_2_rect.xml | 6 + feature/home/build.gradle.kts | 1 + .../main/java/org/sopt/home/HomeContract.kt | 3 + .../main/java/org/sopt/home/HomeFragment.kt | 11 ++ .../main/java/org/sopt/home/HomeViewModel.kt | 23 ++++ .../sopt/home/MarketUpdateDialogFragment.kt | 107 ++++++++++++++++++ .../org/sopt/home/model/UpdatePriority.kt | 19 ++++ .../layout/fragment_market_update_dialog.xml | 63 +++++++++++ gradle/libs.versions.toml | 2 + 9 files changed, 235 insertions(+) create mode 100644 core/designsystem/src/main/res/drawable/shape_white_fill_2_rect.xml create mode 100644 feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt create mode 100644 feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt create mode 100644 feature/home/src/main/res/layout/fragment_market_update_dialog.xml diff --git a/core/designsystem/src/main/res/drawable/shape_white_fill_2_rect.xml b/core/designsystem/src/main/res/drawable/shape_white_fill_2_rect.xml new file mode 100644 index 00000000..ea574595 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/shape_white_fill_2_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index da8ce5d0..73c48047 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -18,4 +18,5 @@ dependencies { implementation(libs.jsoup) implementation(libs.orbit.core) implementation(libs.orbit.viewmodel) + implementation(libs.google.play.core) } diff --git a/feature/home/src/main/java/org/sopt/home/HomeContract.kt b/feature/home/src/main/java/org/sopt/home/HomeContract.kt index 03e4b057..493dd45a 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeContract.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeContract.kt @@ -1,5 +1,6 @@ package org.sopt.home +import org.sopt.home.model.UpdatePriority import org.sopt.model.category.Category import org.sopt.model.home.PopupInfo import org.sopt.model.home.RecommendLink @@ -16,6 +17,7 @@ data class HomeState( val categoryId: Long? = 0, val categoryName: String? = "전체 클립", val popupList: List = emptyList(), + val marketUpdate: UpdatePriority = UpdatePriority.EMPTY, ) { fun calculateProgress(): Int { if (readToastNum > allToastNum) return 0 @@ -34,4 +36,5 @@ sealed interface HomeSideEffect { data object NavigateWebView : HomeSideEffect data object ShowBottomSheet : HomeSideEffect data object ShowPopupInfo : HomeSideEffect + data object ShowUpdateDialog : HomeSideEffect } diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index fbc554ef..e5a9dfcb 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -1,6 +1,7 @@ package org.sopt.home import android.os.Bundle +import android.util.Log import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -14,6 +15,7 @@ import org.sopt.home.adapter.HomeWeekLinkAdapter import org.sopt.home.adapter.HomeWeekRecommendLinkAdapter import org.sopt.home.adapter.ItemDecoration import org.sopt.home.databinding.FragmentHomeBinding +import org.sopt.home.model.UpdatePriority import org.sopt.model.home.PopupInfo import org.sopt.ui.base.BindingFragment import org.sopt.ui.nav.DeepLinkUtil @@ -73,6 +75,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. } is HomeSideEffect.ShowPopupInfo -> showPopupInfo(viewModel.container.stateFlow.value.popupList) + is HomeSideEffect.ShowUpdateDialog -> showUpdateDialog(viewModel.container.stateFlow.value.marketUpdate) } } @@ -85,6 +88,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. getRecommendSite() getWeekBestLink() getPopupListInfo() + checkMarketUpdateState() } } @@ -174,4 +178,11 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. } } } + + private fun showUpdateDialog(marketUpdate: UpdatePriority) { + if (marketUpdate != UpdatePriority.EMPTY) { + val marketUpdateDialog = MarketUpdateDialogFragment.newInstance(marketUpdate, {}) + marketUpdateDialog.show(parentFragmentManager, this.tag) + } + } } diff --git a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt index ee01ba25..5c379c7a 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt @@ -1,9 +1,13 @@ package org.sopt.home +import android.content.Context import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.UpdateAvailability import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost @@ -14,6 +18,7 @@ import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import org.sopt.domain.category.category.usecase.PostAddCategoryTitleUseCase +import org.sopt.home.model.UpdatePriority import org.sopt.home.usecase.GetMainPageUserClip import org.sopt.home.usecase.GetPopupInfo import org.sopt.home.usecase.GetRecommendSite @@ -27,6 +32,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( + @ApplicationContext private val context: Context, private val getMainPageUserClip: GetMainPageUserClip, private val getRecommendSite: GetRecommendSite, private val getWeekBestLink: GetWeekBestLink, @@ -116,6 +122,23 @@ class HomeViewModel @Inject constructor( } } + fun checkMarketUpdateState() { + val appUpdateManager = AppUpdateManagerFactory.create(context) + val appUpdateTask = appUpdateManager.appUpdateInfo + + appUpdateTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + intent { + reduce { + state.copy(marketUpdate = UpdatePriority.toUpdatePriority(appUpdateInfo.updatePriority())) + } + } + } + }.addOnFailureListener { appUpdateInfo -> + Log.d("appUpdateInfo", appUpdateInfo.message.toString()) + } + } + fun navigateSearch() = intent { postSideEffect(HomeSideEffect.NavigateSearch) } fun navigateSetting() = intent { postSideEffect(HomeSideEffect.NavigateSetting) } fun showBottomSheet() = intent { postSideEffect(HomeSideEffect.ShowBottomSheet) } diff --git a/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt b/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt new file mode 100644 index 00000000..d4382e83 --- /dev/null +++ b/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt @@ -0,0 +1,107 @@ +package org.sopt.home + +import android.content.Intent +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.net.Uri +import android.os.Bundle +import android.view.View +import androidx.core.view.isInvisible +import org.sopt.home.databinding.FragmentMarketUpdateDialogBinding +import org.sopt.home.model.UpdatePriority +import org.sopt.ui.base.BindingDialogFragment +import org.sopt.ui.view.onThrottleClick +import kotlin.system.exitProcess + +class MarketUpdateDialogFragment : BindingDialogFragment( + { FragmentMarketUpdateDialogBinding.inflate(it) }, +) { + private var marketUpdatePriority: UpdatePriority? = null + private var handleUpdate: () -> Unit = {} + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_FRAME, android.R.style.Theme_Dialog) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + checkUpdatePriority(marketUpdatePriority) + binding.btnMarketUpdateDialogSkip.onThrottleClick { + dismiss() + } + binding.btnMarketUpdateDialogUpdate.onThrottleClick { + dismiss() + navigateMarket() + exitProcess(0) + } + } + + override fun onStart() { + super.onStart() + val dialog = dialog + if (dialog != null) { + dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog.setCanceledOnTouchOutside(false) + } + } + + private fun checkUpdatePriority(marketUpdatePriority: UpdatePriority?) { + when (marketUpdatePriority) { + UpdatePriority.EMPTY -> { + binding.tvMarketUpdateDialogTitle.text = "업데이트 알림" + binding.tvMarketUpdateDialogSubtitle.text = "토스터의 사용성이 개선되었어요!\n지금 바로 업데이트해보세요" + binding.btnMarketUpdateDialogUpdate.text = "지금 업데이트" + binding.btnMarketUpdateDialogSkip.isInvisible = false + } + + UpdatePriority.MINOR -> { + binding.tvMarketUpdateDialogTitle.text = "업데이트 알림" + binding.tvMarketUpdateDialogSubtitle.text = "토스터의 사용성이 개선되었어요!\n지금 바로 업데이트해보세요" + binding.btnMarketUpdateDialogUpdate.text = "지금 업데이트" + binding.btnMarketUpdateDialogSkip.isInvisible = false + } + + UpdatePriority.MAJOR -> { + binding.tvMarketUpdateDialogTitle.text = "기능 업데이트 알림" + binding.tvMarketUpdateDialogSubtitle.text = "토스터의 기능이 추가되었어요!\n지금 바로 업데이트해보세요" + binding.btnMarketUpdateDialogUpdate.text = "지금 업데이트" + binding.btnMarketUpdateDialogSkip.isInvisible = false + } + + UpdatePriority.CRITICAL -> { + binding.tvMarketUpdateDialogTitle.text = "신규 기능 업데이트 알림" + binding.tvMarketUpdateDialogSubtitle.text = "토스터의 새로운 기능을 이용하기 위해서는\n업데이트가 필요해요!\n최신 버전으로 업데이트 하시겠어요?" + binding.btnMarketUpdateDialogUpdate.text = "업데이트 하기" + binding.btnMarketUpdateDialogSkip.isInvisible = true + } + + null -> {} + } + } + + private fun navigateMarket() { + val appPackageName = "org.sopt.linkmind" + runCatching { + context?.packageManager?.getPackageInfo("com.android.vending", 0) + }.onSuccess { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName"))) + }.onFailure { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))) + } + } + + + companion object { + fun newInstance( + updatePriority: UpdatePriority, + updateButtonClick: () -> Unit, + ): MarketUpdateDialogFragment { + return MarketUpdateDialogFragment().apply { + marketUpdatePriority = updatePriority + handleUpdate = updateButtonClick + } + } + } +} diff --git a/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt b/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt new file mode 100644 index 00000000..94c8b438 --- /dev/null +++ b/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt @@ -0,0 +1,19 @@ +package org.sopt.home.model + +enum class UpdatePriority { + EMPTY, + MINOR, + MAJOR, + CRITICAL; + + companion object { + fun toUpdatePriority(value: Int): UpdatePriority { + return when(value){ + in 0..1 -> MINOR + in 2..3 -> MAJOR + in 4..5 -> CRITICAL + else -> EMPTY + } + } + } +} diff --git a/feature/home/src/main/res/layout/fragment_market_update_dialog.xml b/feature/home/src/main/res/layout/fragment_market_update_dialog.xml new file mode 100644 index 00000000..d65351ff --- /dev/null +++ b/feature/home/src/main/res/layout/fragment_market_update_dialog.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5a18d7f3..8171945e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,6 +47,7 @@ detekt = "1.23.1" kakao-login = "2.19.0" google-service = "4.4.0" +google-play-core = "1.10.2" process-pheonix = "2.1.2" preference = "1.2.1" core-splashscreen = "1.0.1" @@ -152,6 +153,7 @@ viewpager-indicator = { group = "com.tbuonomo", name = "dotsindicator", version. detekt-plugin-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } kakao-login = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-login" } +google-play-core = { group = "com.google.android.play", name = "core", version.ref = "google-play-core" } process-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "process-pheonix" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } From 0f35f6e95ca50ce508d8c340f2f0ee59a964db73 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:29:57 +0900 Subject: [PATCH 4/9] [feat] #174 alert visible state --- .../datastore/datastore/SecurityDataStore.kt | 15 ++++++ feature/home/build.gradle.kts | 1 + .../main/java/org/sopt/home/HomeFragment.kt | 8 ++- .../main/java/org/sopt/home/HomeViewModel.kt | 54 +++++++++++++------ .../sopt/home/MarketUpdateDialogFragment.kt | 10 ++-- .../org/sopt/home/SurveyDialogFragment.kt | 14 +++-- .../sopt/login/onboarding/LoginActivity.kt | 8 +++ 7 files changed, 84 insertions(+), 26 deletions(-) diff --git a/core/datastore/src/main/java/org/sopt/datastore/datastore/SecurityDataStore.kt b/core/datastore/src/main/java/org/sopt/datastore/datastore/SecurityDataStore.kt index 52f094c2..39316325 100644 --- a/core/datastore/src/main/java/org/sopt/datastore/datastore/SecurityDataStore.kt +++ b/core/datastore/src/main/java/org/sopt/datastore/datastore/SecurityDataStore.kt @@ -38,13 +38,28 @@ interface SecurityDataStore { @SetValue(KEY_FCM_ALLOWED) suspend fun setFcmAllowed(boolean: Boolean) + @GetValue(KEY_POPUP_VISIBILITY) + fun flowPopupVisibility(): Flow + + @SetValue(KEY_POPUP_VISIBILITY) + suspend fun setPopupVisibility(boolean: Boolean) + + @GetValue(KEY_MARKET_UPDATE) + fun flowMarketUpdate(): Flow + + @SetValue(KEY_MARKET_UPDATE) + suspend fun setMarketUpdate(boolean: Boolean) + @ClearValues suspend fun clearAll() + companion object { const val KEY_ACCESSTOKEN = "key-accesstoken" const val KEY_REFRESHTOKEN = "key-refreshtoken" const val KEY_DEVICETOKEN = "key-devicetoken" const val KEY_AUTOLOGIN = "key-autologin" const val KEY_FCM_ALLOWED = "key-fcm-allowed" + const val KEY_POPUP_VISIBILITY = "key-popup-dialog" + const val KEY_MARKET_UPDATE = "key-market-update" } } diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 73c48047..17332ef8 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -19,4 +19,5 @@ dependencies { implementation(libs.orbit.core) implementation(libs.orbit.viewmodel) implementation(libs.google.play.core) + implementation(projects.core.datastore) } diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index e5a9dfcb..25fed57e 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -1,7 +1,6 @@ package org.sopt.home import android.os.Bundle -import android.util.Log import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -30,6 +29,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. private lateinit var homeWeekLinkAdapter: HomeWeekLinkAdapter private lateinit var homeWeekRecommendLinkAdapter: HomeWeekRecommendLinkAdapter private val viewModel by viewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() @@ -173,6 +173,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. it.popupImage, { viewModel.navigateWebview(it.popupLinkUrl) }, { viewModel.patchPopupInvisible(it.popupId.toLong(), 7) }, + { viewModel.setPopupVisible() }, ) surveyDialog.show(parentFragmentManager, this.tag) } @@ -181,7 +182,10 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. private fun showUpdateDialog(marketUpdate: UpdatePriority) { if (marketUpdate != UpdatePriority.EMPTY) { - val marketUpdateDialog = MarketUpdateDialogFragment.newInstance(marketUpdate, {}) + val marketUpdateDialog = MarketUpdateDialogFragment.newInstance( + marketUpdate, + { viewModel.setMarketUpdateVisible() }, + ) marketUpdateDialog.show(parentFragmentManager, this.tag) } } diff --git a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt index 5c379c7a..57035cfb 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt @@ -8,6 +8,7 @@ import com.google.android.play.core.appupdate.AppUpdateManagerFactory import com.google.android.play.core.install.model.UpdateAvailability import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost @@ -17,6 +18,7 @@ import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container +import org.sopt.datastore.datastore.SecurityDataStore import org.sopt.domain.category.category.usecase.PostAddCategoryTitleUseCase import org.sopt.home.model.UpdatePriority import org.sopt.home.usecase.GetMainPageUserClip @@ -39,6 +41,7 @@ class HomeViewModel @Inject constructor( private val postAddCategoryTitle: PostAddCategoryTitleUseCase, private val patchPopupInvisible: PatchPopupInvisible, private val getPopupInfo: GetPopupInfo, + private val dataStore: SecurityDataStore, ) : ContainerHost, ViewModel() { override val container: Container = container(HomeState()) @@ -100,13 +103,15 @@ class HomeViewModel @Inject constructor( } fun getPopupListInfo() = intent { - getPopupInfo.invoke().onSuccess { - postSideEffect(HomeSideEffect.ShowPopupInfo) - reduce { - state.copy(popupList = it) + if (dataStore.flowPopupVisibility().first()) { + getPopupInfo.invoke().onSuccess { + postSideEffect(HomeSideEffect.ShowPopupInfo) + reduce { + state.copy(popupList = it) + } + }.onFailure { + Log.d("getPopupListInfo", "$it") } - }.onFailure { - Log.d("getPopupListInfo", "$it") } } @@ -123,19 +128,24 @@ class HomeViewModel @Inject constructor( } fun checkMarketUpdateState() { - val appUpdateManager = AppUpdateManagerFactory.create(context) - val appUpdateTask = appUpdateManager.appUpdateInfo - - appUpdateTask.addOnSuccessListener { appUpdateInfo -> - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { - intent { - reduce { - state.copy(marketUpdate = UpdatePriority.toUpdatePriority(appUpdateInfo.updatePriority())) + viewModelScope.launch { + if (dataStore.flowMarketUpdate().first()) { + val appUpdateManager = AppUpdateManagerFactory.create(context) + val appUpdateTask = appUpdateManager.appUpdateInfo + + appUpdateTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + intent { + postSideEffect(HomeSideEffect.ShowUpdateDialog) + reduce { + state.copy(marketUpdate = UpdatePriority.toUpdatePriority(appUpdateInfo.updatePriority())) + } + } } + }.addOnFailureListener { appUpdateInfo -> + Log.d("appUpdateInfo", appUpdateInfo.message.toString()) } } - }.addOnFailureListener { appUpdateInfo -> - Log.d("appUpdateInfo", appUpdateInfo.message.toString()) } } @@ -160,6 +170,18 @@ class HomeViewModel @Inject constructor( postSideEffect(HomeSideEffect.NavigateWebView) } + fun setPopupVisible() { + viewModelScope.launch { + dataStore.setPopupVisibility(false) + } + } + + fun setMarketUpdateVisible() { + viewModelScope.launch { + dataStore.setMarketUpdate(false) + } + } + fun checkPopupDate(activeStartDate: String, activeEndDate: String): Boolean { val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val today = Calendar.getInstance().time diff --git a/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt b/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt index d4382e83..2bb79c39 100644 --- a/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/MarketUpdateDialogFragment.kt @@ -17,7 +17,7 @@ class MarketUpdateDialogFragment : BindingDialogFragment Unit = {} + private var marketUpdateVisible: () -> Unit = {} override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -26,8 +26,9 @@ class MarketUpdateDialogFragment : BindingDialogFragment Unit, + setMarketUpdateVisible: () -> Unit, ): MarketUpdateDialogFragment { return MarketUpdateDialogFragment().apply { marketUpdatePriority = updatePriority - handleUpdate = updateButtonClick + marketUpdateVisible = setMarketUpdateVisible } } } diff --git a/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt index e1c4260c..c770e8e5 100644 --- a/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/SurveyDialogFragment.kt @@ -5,16 +5,19 @@ import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.View import coil.load +import dagger.hilt.android.AndroidEntryPoint import org.sopt.home.databinding.FragmentSurveyDialogBinding import org.sopt.ui.base.BindingDialogFragment import org.sopt.ui.view.onThrottleClick +@AndroidEntryPoint class SurveyDialogFragment : BindingDialogFragment( { FragmentSurveyDialogBinding.inflate(it) }, ) { private var imageUrl: String? = null private var linkUrl: () -> Unit = {} private var handleSkip: () -> Unit = {} + private var popupVisibility: () -> Unit = {} override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -24,7 +27,7 @@ class SurveyDialogFragment : BindingDialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + popupVisibility.invoke() setSurveyImage(imageUrl) binding.ivSurveyDialogClose.onThrottleClick { @@ -54,7 +57,12 @@ class SurveyDialogFragment : BindingDialogFragment( binding.ivSurveyDialog.load(text) companion object { - fun newInstance(imageUrl: String, onNavigateWebView: () -> Unit, onNegativeButtonClick: () -> Unit): SurveyDialogFragment { + fun newInstance( + imageUrl: String, + onNavigateWebView: () -> Unit, + onNegativeButtonClick: () -> Unit, + setPopupVisibility: () -> Unit, + ): SurveyDialogFragment { val args = Bundle().apply { putString("imageUrl", imageUrl) } @@ -62,8 +70,8 @@ class SurveyDialogFragment : BindingDialogFragment( arguments = args linkUrl = onNavigateWebView handleSkip = onNegativeButtonClick + popupVisibility = setPopupVisibility } - //얘는 bundle 안해도ㅗ되나? } } } diff --git a/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt b/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt index 8d719c0c..1413fe76 100644 --- a/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt +++ b/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt @@ -43,6 +43,7 @@ class LoginActivity : AppCompatActivity() { initCheckAutoLogin() initKakaoLoginBtnClickListener() initAuthStateObserver() + initAllPopupVisible() binding.vpOnboarding.adapter = LoginViewPagerAdapter(this) binding.indicatorOnboarding.attachTo(binding.vpOnboarding) @@ -99,6 +100,13 @@ class LoginActivity : AppCompatActivity() { } } + private fun initAllPopupVisible() { + lifecycleScope.launch { + dataStore.setPopupVisibility(true) + dataStore.setMarketUpdate(true) + } + } + companion object { @JvmStatic fun newInstance(context: Context) = Intent(context, LoginActivity::class.java).apply { From fe217c3bf20ec4173db15b00474c9ad52f0d5f56 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Fri, 18 Oct 2024 00:19:39 +0900 Subject: [PATCH 5/9] [feat] #174 amplitude --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 1 + app/src/main/java/org/sopt/linkmind/LinkMindApp.kt | 9 +++++++++ .../main/java/com/linkmind/convention/KotlinAndroid.kt | 5 +++++ gradle/libs.versions.toml | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9fe5c94b..f0d4aa53 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,4 +58,5 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.espresso.core) + implementation(libs.amplitude.analytics) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 12762fed..af711630 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + Date: Sun, 27 Oct 2024 00:12:07 +0900 Subject: [PATCH 6/9] [refactor] #174 divide function --- .../java/org/sopt/login/onboarding/LoginActivity.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt b/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt index 1413fe76..957c5df2 100644 --- a/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt +++ b/feature/login/src/main/java/org/sopt/login/onboarding/LoginActivity.kt @@ -43,7 +43,8 @@ class LoginActivity : AppCompatActivity() { initCheckAutoLogin() initKakaoLoginBtnClickListener() initAuthStateObserver() - initAllPopupVisible() + initPopupVisible() + initMarketUpdateVisible() binding.vpOnboarding.adapter = LoginViewPagerAdapter(this) binding.indicatorOnboarding.attachTo(binding.vpOnboarding) @@ -100,9 +101,14 @@ class LoginActivity : AppCompatActivity() { } } - private fun initAllPopupVisible() { + private fun initPopupVisible() { lifecycleScope.launch { dataStore.setPopupVisibility(true) + } + } + + private fun initMarketUpdateVisible() { + lifecycleScope.launch { dataStore.setMarketUpdate(true) } } From a45846e4caf351089dc7d305ed8665a7b4ff6cc7 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Sun, 27 Oct 2024 00:18:55 +0900 Subject: [PATCH 7/9] [chore] #174 ktlintformat --- app/src/main/java/org/sopt/linkmind/LinkMindApp.kt | 8 ++++---- core/model/src/main/java/org/sopt/model/home/PopupInfo.kt | 2 +- .../remote/home/datasource/RemotePopupDatasourceImpl.kt | 1 - .../org/sopt/remote/home/response/ResponsePopupInfoDto.kt | 3 ++- .../src/main/java/org/sopt/home/model/UpdatePriority.kt | 5 +++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt b/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt index ada23291..959cb240 100644 --- a/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt +++ b/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt @@ -19,10 +19,10 @@ class LinkMindApp : Application() { setDarkMode() setKakaoSdk() val amplitude = Amplitude( - Configuration( - apiKey = BuildConfig.AMPLITUDE_KEY, - context = applicationContext, - ), + Configuration( + apiKey = BuildConfig.AMPLITUDE_KEY, + context = applicationContext, + ), ) amplitude.track("Sign Up") diff --git a/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt b/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt index 7c3cab90..41dc9e5a 100644 --- a/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt +++ b/core/model/src/main/java/org/sopt/model/home/PopupInfo.kt @@ -6,4 +6,4 @@ data class PopupInfo( val popupActiveStartDate: String, val popupActiveEndDate: String, val popupLinkUrl: String, - ) +) diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt index e0e1eb25..d6536f35 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemotePopupDatasourceImpl.kt @@ -14,7 +14,6 @@ class RemotePopupDatasourceImpl @Inject constructor( override suspend fun patchPopupInvisible(popupId: Long, hideDate: Long): PopupInvisible = popupService.patchPopupInvisible(RequestPopupInvisibleDto(popupId, hideDate)).data!!.toCoreModel() - override suspend fun getPopupInfo(): List = popupService.getPopupInfo().data!!.popupList.map { it.toCoreModel() } } diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt index 973f6560..441edb2b 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponsePopupInfoDto.kt @@ -7,8 +7,9 @@ import org.sopt.model.home.PopupInfo @Serializable data class ResponsePopupInfoDto( @SerialName("popupList") - val popupList: List + val popupList: List, ) + @Serializable data class ResponsePopupInfo( @SerialName("id") diff --git a/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt b/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt index 94c8b438..533dc833 100644 --- a/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt +++ b/feature/home/src/main/java/org/sopt/home/model/UpdatePriority.kt @@ -4,11 +4,12 @@ enum class UpdatePriority { EMPTY, MINOR, MAJOR, - CRITICAL; + CRITICAL, + ; companion object { fun toUpdatePriority(value: Int): UpdatePriority { - return when(value){ + return when (value) { in 0..1 -> MINOR in 2..3 -> MAJOR in 4..5 -> CRITICAL From 6a6db7ba848e89af5350bc07e9ce0e9d5cfc6f83 Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Sun, 27 Oct 2024 00:50:18 +0900 Subject: [PATCH 8/9] [refactor] #174 refactor --- .../src/main/java/com/linkmind/convention/KotlinAndroid.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/convention/src/main/java/com/linkmind/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/com/linkmind/convention/KotlinAndroid.kt index 22b4c703..9d0d2fb8 100644 --- a/build-logic/convention/src/main/java/com/linkmind/convention/KotlinAndroid.kt +++ b/build-logic/convention/src/main/java/com/linkmind/convention/KotlinAndroid.kt @@ -31,7 +31,7 @@ internal fun Project.configureKotlinAndroid( buildConfigField( "String", "AMPLITUDE_KEY", - gradleLocalProperties(rootDir).getProperty("amplitude.Key") + gradleLocalProperties(rootDir).getProperty("amplitude.key") ) manifestPlaceholders["KAKAO_NATIVE_KEY"] = gradleLocalProperties(rootDir).getProperty("kakaoNativeKey") } From 5c970f7bf84efba6004c5463e91a838b21b2188d Mon Sep 17 00:00:00 2001 From: leeseokchan00 <112953135+leeseokchan00@users.noreply.github.com> Date: Sun, 27 Oct 2024 01:03:24 +0900 Subject: [PATCH 9/9] [feat] #174 ci --- .github/workflows/android-pull-request-ci.yml | 2 ++ app/src/main/java/org/sopt/linkmind/LinkMindApp.kt | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/android-pull-request-ci.yml b/.github/workflows/android-pull-request-ci.yml index 3d02ab19..a1f20b31 100644 --- a/.github/workflows/android-pull-request-ci.yml +++ b/.github/workflows/android-pull-request-ci.yml @@ -26,10 +26,12 @@ jobs: env: BASE_URL: ${{ secrets.BASE_URL }} KAKAO_NATIVE_KEY: ${{ secrets.KAKAO_NATIVE_KEY }} + AMPLITUDE_KEY: ${{ secrets.AMPLITUDE_KEY }} run: | echo base.url=\"$BASE_URL\" >> ./local.properties echo kakao.native.key=\"$KAKAO_NATIVE_KEY\" >> ./local.properties echo kakaoNativeKey=$KAKAO_NATIVE_KEY >> ./local.properties + echo amplitude.key=\"$AMPLITUDE_KEY\" >> ./local.properties - name: Create Google Services JSON File env: diff --git a/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt b/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt index 959cb240..0a548e58 100644 --- a/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt +++ b/app/src/main/java/org/sopt/linkmind/LinkMindApp.kt @@ -1,12 +1,10 @@ package org.sopt.linkmind import android.app.Application -import android.util.Log import androidx.appcompat.app.AppCompatDelegate import com.amplitude.android.Amplitude import com.amplitude.android.Configuration import com.kakao.sdk.common.KakaoSdk -import com.kakao.sdk.common.util.Utility import dagger.hilt.android.HiltAndroidApp import org.sopt.linkmind.BuildConfig.KAKAO_NATIVE_KEY import timber.log.Timber @@ -25,9 +23,6 @@ class LinkMindApp : Application() { ), ) amplitude.track("Sign Up") - - var keyHash = Utility.getKeyHash(this) - Log.e("키해시", keyHash) } private fun setTimber() {