From ea4d29eef734512241cb8afd9d1b8ba9b73fbe0c Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Mon, 7 Oct 2024 20:39:41 +0900 Subject: [PATCH 1/6] feat: add DetailApi & Detail Repositoy & DTOs (cherry picked from commit ce8bed139e9da2bee31d0456f694f795e806d165) --- .../kr/ksw/visitkorea/data/di/DataModule.kt | 13 +++++ .../visitkorea/data/remote/api/DetailApi.kt | 44 +++++++++++++++ .../data/remote/dto/DetailCommonDTO.kt | 6 ++ .../data/remote/dto/DetailImageDTO.kt | 10 ++++ .../data/remote/dto/DetailIntroDTO.kt | 55 +++++++++++++++++++ .../data/repository/DetailRepository.kt | 20 +++++++ .../data/repository/DetailRepositoryImpl.kt | 36 ++++++++++++ 7 files changed, 184 insertions(+) create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailCommonDTO.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailImageDTO.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailIntroDTO.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt diff --git a/app/src/main/java/kr/ksw/visitkorea/data/di/DataModule.kt b/app/src/main/java/kr/ksw/visitkorea/data/di/DataModule.kt index da67bf7..e7f20e6 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/di/DataModule.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/di/DataModule.kt @@ -8,12 +8,15 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kr.ksw.visitkorea.data.local.databases.AreaCodeDatabase import kr.ksw.visitkorea.data.remote.api.AreaCodeApi +import kr.ksw.visitkorea.data.remote.api.DetailApi import kr.ksw.visitkorea.data.remote.api.LocationBasedListApi import kr.ksw.visitkorea.data.remote.api.RetrofitInterceptor import kr.ksw.visitkorea.data.remote.api.SearchFestivalApi import kr.ksw.visitkorea.data.remote.api.SearchKeywordApi import kr.ksw.visitkorea.data.repository.AreaCodeRepository import kr.ksw.visitkorea.data.repository.AreaCodeRepositoryImpl +import kr.ksw.visitkorea.data.repository.DetailRepository +import kr.ksw.visitkorea.data.repository.DetailRepositoryImpl import kr.ksw.visitkorea.data.repository.LocationBasedListRepository import kr.ksw.visitkorea.data.repository.LocationBasedListRepositoryImpl import kr.ksw.visitkorea.data.repository.SearchFestivalRepository @@ -98,4 +101,14 @@ object DataModule { fun provideSearchKeywordRepository(searchKeywordApi: SearchKeywordApi): SearchKeywordRepository { return SearchKeywordRepositoryImpl(searchKeywordApi) } + + @Provides + @Singleton + fun provideDetailApi(retrofit: Retrofit): DetailApi = retrofit.create(DetailApi::class.java) + + @Provides + @Singleton + fun provideDetailRepository(detailApi: DetailApi): DetailRepository { + return DetailRepositoryImpl(detailApi) + } } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt b/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt new file mode 100644 index 0000000..ec3df16 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt @@ -0,0 +1,44 @@ +package kr.ksw.visitkorea.data.remote.api + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO +import kr.ksw.visitkorea.data.remote.model.ApiResponse +import retrofit2.http.GET +import retrofit2.http.Query + +interface DetailApi { + @GET("detailCommon1") + suspend fun getDetailCommon( + @Query("numOfRows") numOfRows: Int = 10, + @Query("pageNo") pageNo: Int = 1, + @Query("defaultYN") defaultYN: String = "Y", + @Query("overviewYN") overviewYN: String = "Y", + @Query("contentId") contentId: Int + ): ApiResponse + + @GET("detailIntro1") + suspend fun getDetailIntro( + @Query("numOfRows") numOfRows: Int = 10, + @Query("pageNo") pageNo: Int = 1, + @Query("contentId") contentId: Int, + @Query("contentTypeId") contentTypeId: Int + ): ApiResponse +/* + @GET("detailInfo1") + suspend fun getDetailInfo( + @Query("numOfRows") numOfRows: Int = 10, + @Query("pageNo") pageNo: Int = 1, + @Query("contentId") contentId: Int, + @Query("contentTypeId") contentTypeId: Int + ) +*/ + @GET("detailImage1") + suspend fun getDetailImage( + @Query("numOfRows") numOfRows: Int = 10, + @Query("pageNo") pageNo: Int = 1, + @Query("subImageYN") subImageYN: String = "Y", + @Query("imageYN") imageYN: String = "Y", + @Query("contentId") contentId: Int, + ): ApiResponse +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailCommonDTO.kt b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailCommonDTO.kt new file mode 100644 index 0000000..a6d525e --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailCommonDTO.kt @@ -0,0 +1,6 @@ +package kr.ksw.visitkorea.data.remote.dto + +data class DetailCommonDTO( + val homepage: String, + val overview: String +) \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailImageDTO.kt b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailImageDTO.kt new file mode 100644 index 0000000..ffe6199 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailImageDTO.kt @@ -0,0 +1,10 @@ +package kr.ksw.visitkorea.data.remote.dto + +import com.google.gson.annotations.SerializedName + +data class DetailImageDTO( + @SerializedName("originimgurl") + val originImgUrl: String, + @SerializedName("smallimageurl") + val smallImageUrl: String +) diff --git a/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailIntroDTO.kt b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailIntroDTO.kt new file mode 100644 index 0000000..2c321c9 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/remote/dto/DetailIntroDTO.kt @@ -0,0 +1,55 @@ +package kr.ksw.visitkorea.data.remote.dto + +import com.google.gson.annotations.SerializedName + +data class DetailIntroDTO( + // TouristSpot + val parking: String?, + @SerializedName("restdate") + val restDate: String?, + @SerializedName("usetime") + val useTime: String?, + // Culture Center + @SerializedName("parkingculture") + val parkingCulture: String?, + @SerializedName("restdateculture") + val restDateCulture: String?, + @SerializedName("usefee") + val useFee: String?, + @SerializedName("usetimeculture") + val useTimeCulture: String?, + // Festival + @SerializedName("eventplace") + val eventPlace: String?, + @SerializedName("playtime") + val playTime: String?, + @SerializedName("usetimefestival") + val useTimeFestival: String?, + // Leisure Sports + @SerializedName("parkingleports") + val parkingLeports: String?, + @SerializedName("restdateleports") + val restDateLeports: String?, + @SerializedName("usefeeleports") + val useFeeLeports: String?, + @SerializedName("usetimeleports") + val useTimeLeports: String?, + // Restaurant + @SerializedName("parkingfood") + val parkingFood: String?, + @SerializedName("restdatefood") + val restDateFood: String?, + @SerializedName("opentimefood") + val openTimeFood: String?, + @SerializedName("firstmenu") + val firstMenu: String?, + // Hotel + @SerializedName("checkintime") + val checkInTime: String?, + @SerializedName("checkouttime") + val checkOutTime: String?, + @SerializedName("subfacility") + val subFacility: String?, + @SerializedName("reservationurl") + val reservationUrl: String? +) diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt new file mode 100644 index 0000000..18a30f1 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt @@ -0,0 +1,20 @@ +package kr.ksw.visitkorea.data.repository + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO + +interface DetailRepository { + suspend fun getDetailCommon( + contentId: Int + ): Result + + suspend fun getDetailIntro( + contentId: Int, + contentTypeId: Int + ): Result + + suspend fun getDetailImage( + contentId: Int + ): Result> +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt new file mode 100644 index 0000000..88c4fa7 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt @@ -0,0 +1,36 @@ +package kr.ksw.visitkorea.data.repository + +import kr.ksw.visitkorea.data.mapper.toItems +import kr.ksw.visitkorea.data.remote.api.DetailApi +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO +import javax.inject.Inject + +class DetailRepositoryImpl @Inject constructor( + private val detailApi: DetailApi +): DetailRepository { + override suspend fun getDetailCommon(contentId: Int): Result = runCatching { + detailApi.getDetailCommon( + contentId = contentId + ).toItems().first() + } + + override suspend fun getDetailIntro( + contentId: Int, + contentTypeId: Int + ): Result = runCatching { + detailApi.getDetailIntro( + contentId = contentId, + contentTypeId = contentTypeId + ).toItems().first() + } + + override suspend fun getDetailImage( + contentId: Int + ): Result> = runCatching { + detailApi.getDetailImage( + contentId = contentId + ).toItems() + } +} \ No newline at end of file From 6c329245cc4f95be17240be8e4f3ae07cbe89678 Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Tue, 8 Oct 2024 16:06:59 +0900 Subject: [PATCH 2/6] feat: add detail usecases *add constants *add detail module --- .../visitkorea/data/remote/api/DetailApi.kt | 8 ++--- .../data/repository/DetailRepository.kt | 8 ++--- .../data/repository/DetailRepositoryImpl.kt | 10 +++--- .../visitkorea/domain/common/ContentTypeId.kt | 7 ++++ .../ksw/visitkorea/domain/di/DetailModule.kt | 24 +++++++++++++ .../usecase/detail/GetDetailCommonUseCase.kt | 9 +++++ .../detail/GetDetailCommonUseCaseImpl.kt | 15 ++++++++ .../usecase/detail/GetDetailImageUseCase.kt | 9 +++++ .../GetDetailImageUseCaseUseCaseImpl.kt | 15 ++++++++ .../usecase/detail/GetDetailIntroUseCase.kt | 10 ++++++ .../detail/GetDetailIntroUseCaseImpl.kt | 20 +++++++++++ .../mapper/ToPresentationModelMapper.kt | 36 ++++++++++++++++++- .../domain/usecase/model/CommonDetail.kt | 10 ++++++ .../presentation/common/ContentType.kt | 9 ++--- 14 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/common/ContentTypeId.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCase.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCaseImpl.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCase.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCaseImpl.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/CommonDetail.kt diff --git a/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt b/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt index ec3df16..f5d1709 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/remote/api/DetailApi.kt @@ -14,15 +14,15 @@ interface DetailApi { @Query("pageNo") pageNo: Int = 1, @Query("defaultYN") defaultYN: String = "Y", @Query("overviewYN") overviewYN: String = "Y", - @Query("contentId") contentId: Int + @Query("contentId") contentId: String ): ApiResponse @GET("detailIntro1") suspend fun getDetailIntro( @Query("numOfRows") numOfRows: Int = 10, @Query("pageNo") pageNo: Int = 1, - @Query("contentId") contentId: Int, - @Query("contentTypeId") contentTypeId: Int + @Query("contentId") contentId: String, + @Query("contentTypeId") contentTypeId: String ): ApiResponse /* @GET("detailInfo1") @@ -39,6 +39,6 @@ interface DetailApi { @Query("pageNo") pageNo: Int = 1, @Query("subImageYN") subImageYN: String = "Y", @Query("imageYN") imageYN: String = "Y", - @Query("contentId") contentId: Int, + @Query("contentId") contentId: String, ): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt index 18a30f1..9a2ad2e 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt @@ -6,15 +6,15 @@ import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO interface DetailRepository { suspend fun getDetailCommon( - contentId: Int + contentId: String ): Result suspend fun getDetailIntro( - contentId: Int, - contentTypeId: Int + contentId: String, + contentTypeId: String ): Result suspend fun getDetailImage( - contentId: Int + contentId: String ): Result> } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt index 88c4fa7..b40e186 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt @@ -10,15 +10,17 @@ import javax.inject.Inject class DetailRepositoryImpl @Inject constructor( private val detailApi: DetailApi ): DetailRepository { - override suspend fun getDetailCommon(contentId: Int): Result = runCatching { + override suspend fun getDetailCommon( + contentId: String) + : Result = runCatching { detailApi.getDetailCommon( contentId = contentId ).toItems().first() } override suspend fun getDetailIntro( - contentId: Int, - contentTypeId: Int + contentId: String, + contentTypeId: String ): Result = runCatching { detailApi.getDetailIntro( contentId = contentId, @@ -27,7 +29,7 @@ class DetailRepositoryImpl @Inject constructor( } override suspend fun getDetailImage( - contentId: Int + contentId: String ): Result> = runCatching { detailApi.getDetailImage( contentId = contentId diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/common/ContentTypeId.kt b/app/src/main/java/kr/ksw/visitkorea/domain/common/ContentTypeId.kt new file mode 100644 index 0000000..4b53258 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/common/ContentTypeId.kt @@ -0,0 +1,7 @@ +package kr.ksw.visitkorea.domain.common + +const val TYPE_TOURIST_SPOT = "12" +const val TYPE_CULTURE = "14" +const val TYPE_LEiSURE = "28" +const val TYPE_RESTAURANT = "39" +const val TYPE_FESTIVAL = "15" \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt b/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt new file mode 100644 index 0000000..9e8bb32 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt @@ -0,0 +1,24 @@ +package kr.ksw.visitkorea.domain.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCase +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCaseImpl +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCase +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCaseImpl + +@Module +@InstallIn(ActivityRetainedComponent::class) +abstract class DetailModule { + @Binds + abstract fun bindGetDetailCommonUseCase( + getDetailCommonUseCase: GetDetailCommonUseCaseImpl + ): GetDetailCommonUseCase + + @Binds + abstract fun bindGetDetailIntroUseCase( + getDetailIntroUseCase: GetDetailIntroUseCaseImpl + ): GetDetailIntroUseCase +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCase.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCase.kt new file mode 100644 index 0000000..6b9061c --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCase.kt @@ -0,0 +1,9 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO + +interface GetDetailCommonUseCase { + suspend operator fun invoke( + contentId: String + ): Result +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCaseImpl.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCaseImpl.kt new file mode 100644 index 0000000..dd62d81 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailCommonUseCaseImpl.kt @@ -0,0 +1,15 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.repository.DetailRepository +import javax.inject.Inject + +class GetDetailCommonUseCaseImpl @Inject constructor( + private val detailRepository: DetailRepository +): GetDetailCommonUseCase { + override suspend fun invoke( + contentId: String + ): Result { + return detailRepository.getDetailCommon(contentId) + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt new file mode 100644 index 0000000..a0dc73f --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt @@ -0,0 +1,9 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO + +interface GetDetailImageUseCase { + suspend operator fun invoke( + contentId: String + ): Result> +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt new file mode 100644 index 0000000..73e6123 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt @@ -0,0 +1,15 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.data.repository.DetailRepository +import javax.inject.Inject + +class GetDetailImageUseCaseUseCaseImpl @Inject constructor( + private val detailRepository: DetailRepository +): GetDetailImageUseCase { + override suspend fun invoke(contentId: String): Result> { + return detailRepository.getDetailImage( + contentId = contentId + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCase.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCase.kt new file mode 100644 index 0000000..612a010 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCase.kt @@ -0,0 +1,10 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.domain.usecase.model.CommonDetail + +interface GetDetailIntroUseCase { + suspend operator fun invoke( + contentId: String, + contentTypeId: String + ): Result +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCaseImpl.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCaseImpl.kt new file mode 100644 index 0000000..29636cc --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailIntroUseCaseImpl.kt @@ -0,0 +1,20 @@ +package kr.ksw.visitkorea.domain.usecase.detail + +import kr.ksw.visitkorea.data.repository.DetailRepository +import kr.ksw.visitkorea.domain.usecase.mapper.toCommonDetail +import kr.ksw.visitkorea.domain.usecase.model.CommonDetail +import javax.inject.Inject + +class GetDetailIntroUseCaseImpl @Inject constructor( + private val detailRepository: DetailRepository +): GetDetailIntroUseCase { + override suspend fun invoke( + contentId: String, + contentTypeId: String + ): Result = runCatching { + detailRepository.getDetailIntro( + contentId, + contentTypeId + ).getOrThrow().toCommonDetail(contentTypeId) + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt index d66310d..fe2b034 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt @@ -1,8 +1,10 @@ package kr.ksw.visitkorea.domain.usecase.mapper +import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO import kr.ksw.visitkorea.data.remote.dto.LocationBasedDTO import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO import kr.ksw.visitkorea.domain.usecase.model.CommonCardModel +import kr.ksw.visitkorea.domain.usecase.model.CommonDetail import kr.ksw.visitkorea.domain.usecase.model.Festival import kr.ksw.visitkorea.domain.usecase.model.MoreCardModel import kr.ksw.visitkorea.domain.usecase.model.Restaurant @@ -10,6 +12,7 @@ import kr.ksw.visitkorea.domain.usecase.model.TouristSpot import kr.ksw.visitkorea.domain.usecase.util.toDateString import kr.ksw.visitkorea.domain.usecase.util.toDistForUi import kr.ksw.visitkorea.domain.usecase.util.toImageUrl +import kr.ksw.visitkorea.domain.common.* fun LocationBasedDTO.toTouristSpotModel(): TouristSpot = TouristSpot( address, @@ -61,4 +64,35 @@ fun LocationBasedDTO.toMoreCardModel(): MoreCardModel = MoreCardModel( dist.toDistForUi(), contentId, restaurantMap[category] -) \ No newline at end of file +) + +fun DetailIntroDTO.toCommonDetail(contentTypeId: String): CommonDetail = when(contentTypeId) { + TYPE_TOURIST_SPOT -> CommonDetail( + parking = parking, + time = useTime, + restDate = restDate + ) + TYPE_CULTURE -> CommonDetail( + parking = parkingCulture, + time = useTimeCulture, + restDate = restDateCulture, + fee = useFee + ) + TYPE_LEiSURE -> CommonDetail( + parking = parkingLeports, + time = useTimeLeports, + restDate = restDateLeports, + fee = useFeeLeports + ) + TYPE_RESTAURANT -> CommonDetail( + parking = parkingFood, + time = openTimeFood, + restDate = restDateFood, + menus = firstMenu + ) + else -> CommonDetail( + time = playTime, + fee = useTimeFestival, + place = eventPlace + ) +} diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/CommonDetail.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/CommonDetail.kt new file mode 100644 index 0000000..73ccdb3 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/CommonDetail.kt @@ -0,0 +1,10 @@ +package kr.ksw.visitkorea.domain.usecase.model + +data class CommonDetail( + val parking: String? = null, + val time: String? = null, + val restDate: String? = null, + val fee: String? = null, + val menus: String? = null, + val place: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/common/ContentType.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/common/ContentType.kt index fde523f..b653c5b 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/common/ContentType.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/common/ContentType.kt @@ -2,13 +2,14 @@ package kr.ksw.visitkorea.presentation.common import androidx.annotation.StringRes import kr.ksw.visitkorea.R +import kr.ksw.visitkorea.domain.common.* enum class ContentType( val contentTypeId: String, @StringRes val title: Int ) { - TOURIST("12", R.string.tourist_spot_title), - CULTURE("14", R.string.culture_center_title), - LEiSURE("28", R.string.leisure_sports_title), - RESTAURANT("39", R.string.restaurant_title) + TOURIST(TYPE_TOURIST_SPOT, R.string.tourist_spot_title), + CULTURE(TYPE_CULTURE, R.string.culture_center_title), + LEiSURE(TYPE_LEiSURE, R.string.leisure_sports_title), + RESTAURANT(TYPE_RESTAURANT, R.string.restaurant_title) } \ No newline at end of file From 3b93369a854309a7a8fe5447dfbd5749278c568f Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Tue, 8 Oct 2024 16:41:28 +0900 Subject: [PATCH 3/6] test: add GetDetailCommon api test (cherry picked from commit e592316e8ca5b87df78c8428f92d1e134da4a504) --- .../visitkorea/data/remote/DetailApiTest.kt | 29 +++++++++++++++ .../data/repository/FakeDetailRepository.kt | 37 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 app/src/test/java/kr/ksw/visitkorea/data/remote/DetailApiTest.kt create mode 100644 app/src/test/java/kr/ksw/visitkorea/data/repository/FakeDetailRepository.kt diff --git a/app/src/test/java/kr/ksw/visitkorea/data/remote/DetailApiTest.kt b/app/src/test/java/kr/ksw/visitkorea/data/remote/DetailApiTest.kt new file mode 100644 index 0000000..47102e7 --- /dev/null +++ b/app/src/test/java/kr/ksw/visitkorea/data/remote/DetailApiTest.kt @@ -0,0 +1,29 @@ +package kr.ksw.visitkorea.data.remote + +import kotlinx.coroutines.test.runTest +import kr.ksw.visitkorea.data.repository.FakeDetailRepository +import org.junit.Before +import org.junit.Test + +class DetailApiTest { + + private lateinit var detailRepository: FakeDetailRepository + + @Before + fun setUp() { + detailRepository = FakeDetailRepository() + } + + @Test + fun `get detail common by contentId success, result is not null`() = runTest { + val result = detailRepository.getDetailCommon("2662855") + assert(result.getOrNull() != null) + } + + @Test + fun `get detail common by contentId failed, result is null`() = runTest { + val result = detailRepository.getDetailCommon("1") + assert(result.getOrNull() == null) + } + +} \ No newline at end of file diff --git a/app/src/test/java/kr/ksw/visitkorea/data/repository/FakeDetailRepository.kt b/app/src/test/java/kr/ksw/visitkorea/data/repository/FakeDetailRepository.kt new file mode 100644 index 0000000..5779cc7 --- /dev/null +++ b/app/src/test/java/kr/ksw/visitkorea/data/repository/FakeDetailRepository.kt @@ -0,0 +1,37 @@ +package kr.ksw.visitkorea.data.repository + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.data.remote.dto.DetailIntroDTO + +class FakeDetailRepository : DetailRepository { + val detailCommons = mapOf( + "264408" to DetailCommonDTO( + "https://www.suwon.go.kr/web/visitsuwon/hs01/hs01-01/pages.do?seqNo=37", + "팔달산 정상에 자리 잡은 서장대는 정조 18년에 건설한 군사시설이다. 서장대의 아래층은 사면 3칸, 위층은 1칸으로 위로 가면서 좁아지는 형태이다. 아래층은 장수가 머물면서 군사 훈련을 지휘하고, 위층은 군사가 주변을 감시하는 용도로 썼다. 정조는 서장대에서 군사 훈련인 성조를 거행했는데 1795년의 행사 모습이 그림으로 남아 있다." + ), + "2662855" to DetailCommonDTO( + "http://www.gglakepark.or.kr", + "광교호수공원은 아름다운 수변공간인 어반레비와 함께 6개의 테마를 가진 둠벙으로 어우러져 여러 가지 새로운 문화를 담은 국내 최대의 도심속 호수공원이다. " + ), + "125531" to DetailCommonDTO( + "http://www.yongjoosa.or.kr", + "용주사는 정조가 창건한 사찰이다. 본래 이 곳은 신라 문성왕 16년(854)에 길양사를 창건하여 고려 초기에 수륙재가 봉행되었으나 잦은 병란으로 소실된 후 폐사되었었다. " + ), + ) + + override suspend fun getDetailCommon(contentId: String): Result = runCatching { + detailCommons[contentId] ?: throw NullPointerException() + } + + override suspend fun getDetailIntro( + contentId: String, + contentTypeId: String + ): Result { + TODO("Not yet implemented") + } + + override suspend fun getDetailImage(contentId: String): Result> { + TODO("Not yet implemented") + } +} \ No newline at end of file From 6703bfaf6bb1b3262430f7145740f942a48fd521 Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Tue, 8 Oct 2024 19:57:45 +0900 Subject: [PATCH 4/6] feat: add item click event to TouristSpotCard *change type of parameter in detail api *add Parcelize plugin --- app/build.gradle.kts | 1 + .../data/repository/DetailRepository.kt | 3 +- .../data/repository/DetailRepositoryImpl.kt | 3 +- .../usecase/detail/GetDetailImageUseCase.kt | 3 +- .../GetDetailImageUseCaseUseCaseImpl.kt | 8 +- .../mapper/ToPresentationModelMapper.kt | 3 +- .../domain/usecase/model/Restaurant.kt | 1 + .../presentation/common/DetailParcel.kt | 10 +++ .../presentation/detail/DetailActivity.kt | 23 ++++++ .../detail/viewmodel/DetailState.kt | 11 +++ .../detail/viewmodel/DetailViewModel.kt | 82 +++++++++++++++++++ .../home/component/TouristSpotCard.kt | 9 +- .../presentation/home/screen/HomeScreen.kt | 31 ++++++- .../home/viewmodel/HomeUiEffect.kt | 2 + .../home/viewmodel/HomeViewModel.kt | 7 ++ build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + 17 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt create mode 100644 app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 64deca8..5229abd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.dagger.hilt.android) alias(libs.plugins.kotlin.ksp) + alias(libs.plugins.kotlin.parcelize) } val properties = Properties().apply { diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt index 9a2ad2e..a6d6193 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepository.kt @@ -15,6 +15,7 @@ interface DetailRepository { ): Result suspend fun getDetailImage( - contentId: String + contentId: String, + imageYN: String ): Result> } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt index b40e186..54077d3 100644 --- a/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt +++ b/app/src/main/java/kr/ksw/visitkorea/data/repository/DetailRepositoryImpl.kt @@ -29,7 +29,8 @@ class DetailRepositoryImpl @Inject constructor( } override suspend fun getDetailImage( - contentId: String + contentId: String, + imageYN: String, ): Result> = runCatching { detailApi.getDetailImage( contentId = contentId diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt index a0dc73f..ecee7fd 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCase.kt @@ -4,6 +4,7 @@ import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO interface GetDetailImageUseCase { suspend operator fun invoke( - contentId: String + contentId: String, + imageYN: String ): Result> } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt index 73e6123..1e81fe4 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/detail/GetDetailImageUseCaseUseCaseImpl.kt @@ -7,9 +7,13 @@ import javax.inject.Inject class GetDetailImageUseCaseUseCaseImpl @Inject constructor( private val detailRepository: DetailRepository ): GetDetailImageUseCase { - override suspend fun invoke(contentId: String): Result> { + override suspend fun invoke( + contentId: String, + imageYN: String + ): Result> { return detailRepository.getDetailImage( - contentId = contentId + contentId = contentId, + imageYN = imageYN ) } } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt index fe2b034..4c53500 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt @@ -51,10 +51,11 @@ val restaurantMap = mapOf( fun LocationBasedDTO.toRestaurantModel(): Restaurant = Restaurant( address, + contentId, dist.toDistForUi(), firstImage.toImageUrl(), title, - restaurantMap[category] ?: "" + restaurantMap[category] ?: "", ) fun LocationBasedDTO.toMoreCardModel(): MoreCardModel = MoreCardModel( diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/Restaurant.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/Restaurant.kt index 6aabff1..701735a 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/Restaurant.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/model/Restaurant.kt @@ -2,6 +2,7 @@ package kr.ksw.visitkorea.domain.usecase.model data class Restaurant( val address: String = "", + val contentId: String = "", val dist: String = "", val firstImage: String = "", val title: String = "", diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt new file mode 100644 index 0000000..2476192 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt @@ -0,0 +1,10 @@ +package kr.ksw.visitkorea.presentation.common + +data class DetailParcel( + val title: String, + val firstImage: String, + val address: String, + val dist: String?, + val contentId: String, + val contentTypeId: String +) \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt new file mode 100644 index 0000000..2cc9356 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt @@ -0,0 +1,23 @@ +package kr.ksw.visitkorea.presentation.detail + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import dagger.hilt.android.AndroidEntryPoint +import kr.ksw.visitkorea.presentation.detail.viewmodel.DetailViewModel +import kr.ksw.visitkorea.presentation.ui.theme.VisitKoreaTheme + +@AndroidEntryPoint +class DetailActivity : ComponentActivity() { + private val viewModel: DetailViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + VisitKoreaTheme { + + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt new file mode 100644 index 0000000..802f775 --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt @@ -0,0 +1,11 @@ +package kr.ksw.visitkorea.presentation.detail.viewmodel + +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.domain.usecase.model.CommonDetail + +data class DetailState( + val detailCommon: DetailCommonDTO = DetailCommonDTO("",""), + val detailIntro: CommonDetail = CommonDetail(), + val images: List = emptyList() +) \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt new file mode 100644 index 0000000..81e532d --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt @@ -0,0 +1,82 @@ +package kr.ksw.visitkorea.presentation.detail.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kr.ksw.visitkorea.domain.common.TYPE_RESTAURANT +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCase +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailImageUseCase +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCase +import javax.inject.Inject + +@HiltViewModel +class DetailViewModel @Inject constructor( + private val getDetailCommonUseCase: GetDetailCommonUseCase, + private val getDetailIntroUseCase: GetDetailIntroUseCase, + private val getDetailImageUseCase: GetDetailImageUseCase +): ViewModel() { + private val _detailState = MutableStateFlow(DetailState()) + val detailState: StateFlow + get() = _detailState.asStateFlow() + + fun initDetail( + contentId: String, + contentTypeId: String + ) { + getDetailCommon(contentId) + getDetailIntro(contentId, contentTypeId) + getDetailImage(contentId, if(contentTypeId == TYPE_RESTAURANT) "N" else "Y") + } + + private fun getDetailCommon(contentId: String) { + viewModelScope.launch { + getDetailCommonUseCase(contentId).getOrNull()?.run { + _detailState.update { + it.copy( + detailCommon = this + ) + } + } + } + } + + private fun getDetailIntro( + contentId: String, + contentTypeId: String + ) { + viewModelScope.launch { + getDetailIntroUseCase( + contentId, + contentTypeId + ).getOrNull()?.run { + _detailState.update { + it.copy( + detailIntro = this + ) + } + } + } + } + + private fun getDetailImage( + contentId: String, + imageYN: String + ) { + viewModelScope.launch { + getDetailImageUseCase( + contentId, imageYN + ).getOrNull()?.run { + _detailState.update { + it.copy( + images = this + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/home/component/TouristSpotCard.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/home/component/TouristSpotCard.kt index ca34a3f..85ad12a 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/home/component/TouristSpotCard.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/home/component/TouristSpotCard.kt @@ -1,6 +1,7 @@ package kr.ksw.visitkorea.presentation.home.component import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -40,11 +41,13 @@ fun TouristSpotCard( address: String, dist: String, image: String, + onItemClick: () -> Unit ) { Card ( modifier = Modifier .width(200.dp) - .aspectRatio(0.75f), + .aspectRatio(0.75f) + .clickable(onClick = onItemClick), shape = RoundedCornerShape(24.dp), elevation = CardDefaults.elevatedCardElevation( defaultElevation = 4.dp @@ -112,7 +115,9 @@ fun TouristSpotCardPreview() { ) { TouristSpotCard( "광통교", "서울 특별시 종로구 서린동", "16m", "" - ) + ) { + + } } } } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt index c1af239..9771bec 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt @@ -44,7 +44,10 @@ import coil.compose.AsyncImage import coil.request.ImageRequest import coil.size.Size import kotlinx.coroutines.flow.collectLatest +import kr.ksw.visitkorea.domain.common.TYPE_TOURIST_SPOT import kr.ksw.visitkorea.presentation.common.ContentType +import kr.ksw.visitkorea.presentation.common.DetailParcel +import kr.ksw.visitkorea.presentation.detail.DetailActivity import kr.ksw.visitkorea.presentation.home.component.CultureCard import kr.ksw.visitkorea.presentation.home.component.MoreButton import kr.ksw.visitkorea.presentation.home.component.RestaurantCard @@ -73,20 +76,28 @@ fun HomeScreen( putExtra("contentType", effect.contentType) }) } + is HomeUiEffect.StartDetailActivity -> { + context.startActivity(Intent( + context, + DetailActivity::class.java + )) + } } } } HomeScreen( homeState = homeState, - onMoreClick = homeViewModel::startMoreActivity + onMoreClick = homeViewModel::startMoreActivity, + onItemClick = homeViewModel::startDetailActivity ) } @Composable fun HomeScreen( homeState: HomeState, - onMoreClick: (ContentType) -> Unit + onMoreClick: (ContentType) -> Unit, + onItemClick: (DetailParcel) -> Unit ) { val scrollState = rememberScrollState() Surface { @@ -186,7 +197,16 @@ fun HomeScreen( touristSpot.address, touristSpot.dist, touristSpot.firstImage - ) + ) { + onItemClick(DetailParcel( + title = touristSpot.title, + firstImage = touristSpot.firstImage, + address = touristSpot.address, + dist = touristSpot.dist, + contentId = touristSpot.contentId, + contentTypeId = TYPE_TOURIST_SPOT + )) + } } } } @@ -324,7 +344,10 @@ fun HomeScreenPreview() { homeState = HomeState( mainImage = "https://tong.visitkorea.or.kr/cms/resource/11/3094511_image2_1.jpg" ), - onMoreClick = { } + onMoreClick = { }, + onItemClick = { _ -> + + } ) } } diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeUiEffect.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeUiEffect.kt index 7b4d046..3b5e900 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeUiEffect.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeUiEffect.kt @@ -1,7 +1,9 @@ package kr.ksw.visitkorea.presentation.home.viewmodel import kr.ksw.visitkorea.presentation.common.ContentType +import kr.ksw.visitkorea.presentation.common.DetailParcel sealed class HomeUiEffect { data class StartHomeActivity(val contentType: ContentType) : HomeUiEffect() + data class StartDetailActivity(val data: DetailParcel) : HomeUiEffect() } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeViewModel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeViewModel.kt index b0e1ef2..608d3ea 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/home/viewmodel/HomeViewModel.kt @@ -16,6 +16,7 @@ import kr.ksw.visitkorea.domain.usecase.home.GetLeisureSportsForHomeUseCase import kr.ksw.visitkorea.domain.usecase.home.GetRestaurantForHomeUseCase import kr.ksw.visitkorea.domain.usecase.home.GetTouristSpotForHomeUseCase import kr.ksw.visitkorea.presentation.common.ContentType +import kr.ksw.visitkorea.presentation.common.DetailParcel import javax.inject.Inject @HiltViewModel @@ -110,4 +111,10 @@ class HomeViewModel @Inject constructor( _homeUiEffect.emit(HomeUiEffect.StartHomeActivity(contentType)) } } + + fun startDetailActivity(data: DetailParcel) { + viewModelScope.launch { + _homeUiEffect.emit(HomeUiEffect.StartDetailActivity(data)) + } + } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6433dde..9ce5d01 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,4 +4,5 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.dagger.hilt.android) apply false alias(libs.plugins.kotlin.ksp) apply false + alias(libs.plugins.kotlin.parcelize) apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb243e1..1ad4b13 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,3 +76,4 @@ android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin"} From ee8ff6d0818265ca0163113e4c200255b83460f8 Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Wed, 9 Oct 2024 14:31:34 +0900 Subject: [PATCH 5/6] feat: pass data to DetailActivity *add Parcelize annotation to DetailParcel --- app/src/main/AndroidManifest.xml | 1 + .../ksw/visitkorea/domain/di/DetailModule.kt | 7 +++++ .../mapper/ToPresentationModelMapper.kt | 12 ++++----- .../presentation/common/DetailParcel.kt | 18 ++++++++----- .../presentation/detail/DetailActivity.kt | 9 ++++++- .../detail/viewmodel/DetailState.kt | 4 +++ .../detail/viewmodel/DetailViewModel.kt | 27 +++++++++++++++---- .../presentation/home/screen/HomeScreen.kt | 4 ++- 8 files changed, 62 insertions(+), 20 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e2bbddc..c23c0f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,6 +42,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt b/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt index 9e8bb32..7ca1aed 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/di/DetailModule.kt @@ -6,6 +6,8 @@ import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityRetainedComponent import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCaseImpl +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailImageUseCase +import kr.ksw.visitkorea.domain.usecase.detail.GetDetailImageUseCaseUseCaseImpl import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCaseImpl @@ -21,4 +23,9 @@ abstract class DetailModule { abstract fun bindGetDetailIntroUseCase( getDetailIntroUseCase: GetDetailIntroUseCaseImpl ): GetDetailIntroUseCase + + @Binds + abstract fun bindGetDetailImageUseCase( + getDetailImageUseCase: GetDetailImageUseCaseUseCaseImpl + ): GetDetailImageUseCase } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt index 4c53500..b6e0aac 100644 --- a/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt +++ b/app/src/main/java/kr/ksw/visitkorea/domain/usecase/mapper/ToPresentationModelMapper.kt @@ -50,12 +50,12 @@ val restaurantMap = mapOf( ) fun LocationBasedDTO.toRestaurantModel(): Restaurant = Restaurant( - address, - contentId, - dist.toDistForUi(), - firstImage.toImageUrl(), - title, - restaurantMap[category] ?: "", + address = address, + dist = dist.toDistForUi(), + firstImage = firstImage2.toImageUrl(), + title = title, + category = restaurantMap[category] ?: "", + contentId = contentId ) fun LocationBasedDTO.toMoreCardModel(): MoreCardModel = MoreCardModel( diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt index 2476192..aeab6fa 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/common/DetailParcel.kt @@ -1,10 +1,14 @@ package kr.ksw.visitkorea.presentation.common +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class DetailParcel( - val title: String, - val firstImage: String, - val address: String, - val dist: String?, - val contentId: String, - val contentTypeId: String -) \ No newline at end of file + val title: String = "", + val firstImage: String = "", + val address: String = "", + val dist: String? = null, + val contentId: String = "", + val contentTypeId: String = "" +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt index 2cc9356..1eac280 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/DetailActivity.kt @@ -5,6 +5,8 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint +import kr.ksw.visitkorea.presentation.common.DetailParcel +import kr.ksw.visitkorea.presentation.detail.screen.DetailScreen import kr.ksw.visitkorea.presentation.detail.viewmodel.DetailViewModel import kr.ksw.visitkorea.presentation.ui.theme.VisitKoreaTheme @@ -16,8 +18,13 @@ class DetailActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { VisitKoreaTheme { - + DetailScreen(viewModel) } } + + val detail = intent.getParcelableExtra("detail") + if(detail != null) { + viewModel.initDetail(detail) + } } } \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt index 802f775..fa4d808 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt @@ -5,6 +5,10 @@ import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO import kr.ksw.visitkorea.domain.usecase.model.CommonDetail data class DetailState( + val title: String = "", + val firstImage: String = "", + val address: String = "", + val dist: String? = null, val detailCommon: DetailCommonDTO = DetailCommonDTO("",""), val detailIntro: CommonDetail = CommonDetail(), val images: List = emptyList() diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt index 81e532d..db73f21 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt @@ -12,6 +12,7 @@ import kr.ksw.visitkorea.domain.common.TYPE_RESTAURANT import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailImageUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCase +import kr.ksw.visitkorea.presentation.common.DetailParcel import javax.inject.Inject @HiltViewModel @@ -25,12 +26,28 @@ class DetailViewModel @Inject constructor( get() = _detailState.asStateFlow() fun initDetail( - contentId: String, - contentTypeId: String + detailParcel: DetailParcel ) { - getDetailCommon(contentId) - getDetailIntro(contentId, contentTypeId) - getDetailImage(contentId, if(contentTypeId == TYPE_RESTAURANT) "N" else "Y") + _detailState.update { + it.copy( + title = detailParcel.title, + firstImage = detailParcel.firstImage, + address = detailParcel.address, + dist = detailParcel.dist + ) + } + getDetailCommon(detailParcel.contentId) + getDetailIntro( + detailParcel.contentId, + detailParcel.contentTypeId + ) + getDetailImage( + detailParcel.contentId, + if(detailParcel.contentTypeId == TYPE_RESTAURANT) + "N" + else + "Y" + ) } private fun getDetailCommon(contentId: String) { diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt index 9771bec..2ff251e 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/home/screen/HomeScreen.kt @@ -80,7 +80,9 @@ fun HomeScreen( context.startActivity(Intent( context, DetailActivity::class.java - )) + ).apply { + putExtra("detail", effect.data) + }) } } } From 4a3a69a0cbfc78958e3076325c32f136ccaa1312 Mon Sep 17 00:00:00 2001 From: ksw4015 Date: Wed, 9 Oct 2024 17:00:07 +0900 Subject: [PATCH 6/6] feat: add DetailScreen (cherry picked from commit 929867d4dbf637f28331bd081afe7e978dcf5565) --- .../detail/screen/DetailScreen.kt | 398 ++++++++++++++++++ .../detail/viewmodel/DetailState.kt | 1 + .../detail/viewmodel/DetailViewModel.kt | 11 +- 3 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/kr/ksw/visitkorea/presentation/detail/screen/DetailScreen.kt diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/screen/DetailScreen.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/screen/DetailScreen.kt new file mode 100644 index 0000000..f6bdfcc --- /dev/null +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/screen/DetailScreen.kt @@ -0,0 +1,398 @@ +package kr.ksw.visitkorea.presentation.detail.screen + +import android.os.Build +import android.text.Html +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.FavoriteBorder +import androidx.compose.material.icons.outlined.LocationOn +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.text.HtmlCompat +import coil.compose.AsyncImage +import coil.request.ImageRequest +import coil.size.Size +import kr.ksw.visitkorea.data.remote.dto.DetailCommonDTO +import kr.ksw.visitkorea.data.remote.dto.DetailImageDTO +import kr.ksw.visitkorea.domain.common.TYPE_FESTIVAL +import kr.ksw.visitkorea.domain.common.TYPE_RESTAURANT +import kr.ksw.visitkorea.domain.common.TYPE_TOURIST_SPOT +import kr.ksw.visitkorea.domain.usecase.model.CommonDetail +import kr.ksw.visitkorea.presentation.component.SingleLineText +import kr.ksw.visitkorea.presentation.detail.viewmodel.DetailState +import kr.ksw.visitkorea.presentation.detail.viewmodel.DetailViewModel +import kr.ksw.visitkorea.presentation.ui.theme.VisitKoreaTheme + +@Composable +fun DetailScreen( + viewModel: DetailViewModel +) { + val detailState by viewModel.detailState.collectAsState() + DetailScreen(detailState) +} + +@Composable +private fun DetailScreen( + detailState: DetailState +) { + val scrollState = rememberScrollState() + Surface { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + ) { + Box { + AsyncImage( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1.0f) + .clip( + RoundedCornerShape( + bottomStart = 24.dp, + bottomEnd = 24.dp + ) + ) + .background(color = Color.LightGray), + model = ImageRequest + .Builder(LocalContext.current) + .data(detailState.firstImage) + .size(Size.ORIGINAL) + .build(), + colorFilter = if(detailState.firstImage.isNotEmpty()) + ColorFilter.tint(Color.LightGray, blendMode = BlendMode.Darken) + else + null, + contentDescription = "Detail Image", + contentScale = ContentScale.Crop, + ) + if(detailState.contentTypeId == TYPE_TOURIST_SPOT || + detailState.contentTypeId == TYPE_FESTIVAL) { + Icon( + Icons.Outlined.FavoriteBorder, + contentDescription = "Favorite Icon", + modifier = Modifier + .align(Alignment.TopEnd) + .padding(10.dp) + .size(24.dp), + tint = Color.Red + ) + } + } + Spacer(modifier = Modifier.height(16.dp)) + TitleView( + detailState.title, + detailState.address, + detailState.dist + ) + Spacer(modifier = Modifier.height(16.dp)) + OverView(detailState.detailCommon.overview) + Spacer(modifier = Modifier.height(16.dp)) + DetailIntroView( + contentTypeId = detailState.contentTypeId, + detailIntro = detailState.detailIntro + ) + Spacer(modifier = Modifier.height(16.dp)) + ImageViews(detailState.images) + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@Composable +private fun TitleView( + title: String, + address: String, + dist: String? +) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + SingleLineText( + modifier = Modifier + .weight(1.0f), + text = title, + fontSize = 22.sp, + fontWeight = FontWeight.SemiBold + ) + Text( + modifier = Modifier + .border( + width = 1.dp, + color = Color.Black, + shape = RoundedCornerShape(4.dp) + ) + .padding(horizontal = 4.dp), + text = "지도보기", + fontSize = 12.sp + ) + } + Spacer(modifier = Modifier.height(4.dp)) + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Bottom + ) { + Row( + modifier = Modifier + .weight(1.0f), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + modifier = Modifier + .size(20.dp), + imageVector = Icons.Outlined.LocationOn, + contentDescription = null + ) + SingleLineText( + text = address, + fontSize = 14.sp, + fontWeight = FontWeight.Medium + ) + } + dist?.run { + Text( + text = this, + fontSize = 12.sp + ) + } + } + } +} + +@Composable +private fun OverView( + overView: String +) { + var maxLines by remember { + mutableIntStateOf(6) + } + var showMore by remember { + mutableStateOf(false) + } + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + ) { + Text( + text = "개요", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = overView, + maxLines = maxLines, + overflow = TextOverflow.Ellipsis, + onTextLayout = { result -> + showMore = result.didOverflowHeight + }, + fontSize = 14.sp + ) + if(showMore) { + Text( + modifier = Modifier + .padding(vertical = 4.dp, horizontal = 8.dp) + .align(Alignment.End) + .clickable { + maxLines = Integer.MAX_VALUE + }, + text = "더보기", + color = Color.Gray, + fontSize = 14.sp + ) + } + } +} + +@Composable +private fun DetailIntroView( + contentTypeId: String, + detailIntro: CommonDetail +) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + ) { + Text( + text = if(contentTypeId == TYPE_FESTIVAL) "행사안내" else "시설안내", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.parking?.let { + DetailIntroContent( + "주차시설", + it + ) + } + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.time?.let { + DetailIntroContent( + if(contentTypeId == TYPE_RESTAURANT) "영업시간" else "이용시간", + it + ) + } + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.restDate?.let { + DetailIntroContent( + "쉬는날", + it + ) + } + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.fee?.let { + DetailIntroContent( + "이용요금", + it + ) + } + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.menus?.let { + DetailIntroContent( + "대표메뉴", + it + ) + } + Spacer(modifier = Modifier.height(4.dp)) + detailIntro.place?.let { + DetailIntroContent( + "행사장소", + it + ) + } + } +} + +@Composable +private fun DetailIntroContent( + title: String, + content: String +) { + val htmlContent = Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY) + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Text( + modifier = Modifier.width(76.dp), + text = title, + fontSize = 14.sp + ) + Text( + text = htmlContent.toString().ifEmpty { "문의" }, + fontSize = 14.sp + ) + } +} + +@Composable +private fun ImageViews( + images: List +) { + Text( + modifier = Modifier + .padding(start = 16.dp), + text = "사진", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(8.dp)) + LazyRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(horizontal = 16.dp) + ) { + items( + count = images.size, + key = { + images[it].originImgUrl + } + ) { index -> + val image = images[index].smallImageUrl + AsyncImage( + modifier = Modifier + .size(100.dp) + .clip(RoundedCornerShape(8.dp)) + .background(color = Color.LightGray), + model = ImageRequest + .Builder(LocalContext.current) + .data(image) + .size(Size.ORIGINAL) + .build(), + contentDescription = "Culture Spot Image", + contentScale = ContentScale.Crop, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun DetailScreenPreview() { + VisitKoreaTheme { + DetailScreen( + DetailState( + title = "타이틀 영역", + address = "주소 영역", + dist = "거리 표기", + detailCommon = DetailCommonDTO( + homepage = "", + overview = "수원박물관은 지하 1층, 지상 2층으로 수원역사박물관, 한국서예박물관 등 2개의 상설전시관과 특별전시실로 구성되어 있다. 오감으로 즐기고 배우는 어린이체험실, 다채로운 교육 프로그램 등이 운영되고, 소장유물은 수원지역에서 발굴된 유물, 구입 및 기증받은 유물 등으로 48,000여 점을 소장하고 있다. 수원역사박물관은 수원의 유구한 역사와 문화를 과거·현재·미래의 시점과 주제별로 구성하였으며, 크게 네 부분으로 구성된 전시관은 ‘수원의 자연환경’, ‘선사·역사시대의 변천사’, ‘수원로의 개설’, ‘60년대 수원 만나기’, ‘근대 수원의 문화’로 과거의 역사와 문화뿐만 아니라 현대의 발전하는 역동적인 수원의 모습을 다양하게 보여주고 있다. 한국서예박물관은 지방자치단체에서는 최초로 건립한 서예 전문 박물관으로, 우리나라 서예사를 한눈에 살펴볼 수 있다. 전시는 유물의 시기와 특성에 따라 각각의 전시 주제로 구성되어 있고 조선시대 사랑방이 재형 되어 있다. 중요 작품으로는 영조와 정조가 쓴 어필첩 등이 있다." + ), + detailIntro = CommonDetail( + parking = "가능", + time = "상시개방", + restDate = "연중무휴" + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt index fa4d808..78655ac 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailState.kt @@ -9,6 +9,7 @@ data class DetailState( val firstImage: String = "", val address: String = "", val dist: String? = null, + val contentTypeId: String = "", val detailCommon: DetailCommonDTO = DetailCommonDTO("",""), val detailIntro: CommonDetail = CommonDetail(), val images: List = emptyList() diff --git a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt index db73f21..9ea96d9 100644 --- a/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt +++ b/app/src/main/java/kr/ksw/visitkorea/presentation/detail/viewmodel/DetailViewModel.kt @@ -12,6 +12,7 @@ import kr.ksw.visitkorea.domain.common.TYPE_RESTAURANT import kr.ksw.visitkorea.domain.usecase.detail.GetDetailCommonUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailImageUseCase import kr.ksw.visitkorea.domain.usecase.detail.GetDetailIntroUseCase +import kr.ksw.visitkorea.domain.usecase.util.toImageUrl import kr.ksw.visitkorea.presentation.common.DetailParcel import javax.inject.Inject @@ -33,7 +34,8 @@ class DetailViewModel @Inject constructor( title = detailParcel.title, firstImage = detailParcel.firstImage, address = detailParcel.address, - dist = detailParcel.dist + dist = detailParcel.dist, + contentTypeId = detailParcel.contentTypeId ) } getDetailCommon(detailParcel.contentId) @@ -90,7 +92,12 @@ class DetailViewModel @Inject constructor( ).getOrNull()?.run { _detailState.update { it.copy( - images = this + images = this.map { image -> + image.copy( + originImgUrl = image.originImgUrl.toImageUrl(), + smallImageUrl = image.smallImageUrl.toImageUrl() + ) + } ) } }