Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#13] FestivalScreen / MoreScreen 구현 #14

Merged
merged 13 commits into from
Oct 21, 2024
Merged
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
</activity>

<activity android:name=".presentation.main.MainActivity"/>
<activity android:name=".presentation.more.MoreActivity"/>
</application>

</manifest>
13 changes: 13 additions & 0 deletions app/src/main/java/kr/ksw/visitkorea/data/di/DataModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import kr.ksw.visitkorea.data.local.databases.AreaCodeDatabase
import kr.ksw.visitkorea.data.remote.api.AreaCodeApi
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.repository.AreaCodeRepository
import kr.ksw.visitkorea.data.repository.AreaCodeRepositoryImpl
import kr.ksw.visitkorea.data.repository.LocationBasedListRepository
import kr.ksw.visitkorea.data.repository.LocationBasedListRepositoryImpl
import kr.ksw.visitkorea.data.repository.SearchFestivalRepository
import kr.ksw.visitkorea.data.repository.SearchFestivalRepositoryImpl
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
Expand Down Expand Up @@ -72,4 +75,14 @@ object DataModule {
fun provideLocationBasedListRepository(locationBasedListApi: LocationBasedListApi): LocationBasedListRepository {
return LocationBasedListRepositoryImpl(locationBasedListApi)
}

@Provides
@Singleton
fun provideSearchFestivalApi(retrofit: Retrofit): SearchFestivalApi = retrofit.create(SearchFestivalApi::class.java)

@Provides
@Singleton
fun provideSearchFestivalRepository(searchFestivalApi: SearchFestivalApi): SearchFestivalRepository {
return SearchFestivalRepositoryImpl(searchFestivalApi)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ class LocationBasedPagingSource (
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LocationBasedDTO> {
val page = params.key ?: 1
val loadSize = params.loadSize
val response = locationBasedListApi.getLocationBasedListByContentType(
numOfRows = loadSize,
pageNo = page,
mapX = mapX,
mapY = mapY,
contentTypeId = contentTypeId
)
val data = response.toItems()
val data = try {
locationBasedListApi.getLocationBasedListByContentType(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try-catch 를 시용했는데, LocationBasedList 가 혹시 Retrofit 의 API 인가요 아니면 다른 레이어인가요? 제가 봤을때는 예외처리가 이미 완료된 데이터가 사용되는게 더 나을 거 같거든요. 같이 이야기해 보시죠.

numOfRows = loadSize,
pageNo = page,
mapX = mapX,
mapY = mapY,
contentTypeId = contentTypeId
).toItems()
} catch (e: Exception) {
emptyList()
}
return LoadResult.Page(
data = data,
prevKey = if(page == 1) null else page - 1,
nextKey = if(data.size == loadSize) page + 1 else null
prevKey = if(page == 1 || data.isEmpty()) null else page - 1,
nextKey = if(data.size == loadSize && data.isNotEmpty()) page + 1 else null
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package kr.ksw.visitkorea.data.paging.source

import androidx.paging.PagingSource
import androidx.paging.PagingState
import kr.ksw.visitkorea.data.mapper.toItems
import kr.ksw.visitkorea.data.remote.api.SearchFestivalApi
import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO

class SearchFestivalPagingSource(
private val searchFestivalApi: SearchFestivalApi,
private val eventStartDate: String,
private val eventEndDate: String,
private val areaCode: String?,
private val sigunguCode: String?
) : PagingSource<Int, SearchFestivalDTO>() {

override fun getRefreshKey(state: PagingState<Int, SearchFestivalDTO>): Int? {
return state.anchorPosition?.let { anchor ->
val anchorPage = state.closestPageToPosition(anchor)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, SearchFestivalDTO> {
val page = params.key ?: 1
val loadSize = params.loadSize
val response = searchFestivalApi.searchFestival(
numOfRows = loadSize,
pageNo = page,
eventStartDate = eventStartDate,
eventEndDate = eventEndDate,
areaCode = areaCode,
sigunguCode = sigunguCode
)
val data = response.toItems()
return LoadResult.Page(
data = data,
prevKey = if(page == 1) null else page - 1,
nextKey = if(data.size == loadSize)
page + 1
else null
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package kr.ksw.visitkorea.data.remote.api

import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO
import kr.ksw.visitkorea.data.remote.model.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface SearchFestivalApi {
@GET("searchFestival1")
suspend fun searchFestival(
@Query("arrange") arrange: String = "S",
@Query("numOfRows") numOfRows: Int,
@Query("pageNo") pageNo: Int,
@Query("eventStartDate") eventStartDate: String,
@Query("eventEndDate") eventEndDate: String,
@Query("areaCode") areaCode: String? = null,
@Query("sigunguCode") sigunguCode: String? = null
): ApiResponse<SearchFestivalDTO>
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프로퍼티명과 파싱할 값의 이름이 동일하다면 굳이 query annotation 을 쓰지 않아도 됩니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

몰랐었던 부분인데 감사합니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package kr.ksw.visitkorea.data.remote.dto

import com.google.gson.annotations.SerializedName

data class SearchFestivalDTO(
@SerializedName("addr1")
val address: String,
@SerializedName("areacode")
val areaCode: String,
@SerializedName("sigungucode")
val sigunguCode: String,
@SerializedName("contentid")
val contentId: String,
@SerializedName("contenttypeid")
val contentTypeId: String,
@SerializedName("eventstartdate")
val eventStartDate: String,
@SerializedName("eventenddate")
val eventEndDate: String,
@SerializedName("firstimage")
val firstImage: String,
@SerializedName("firstimage2")
val firstImage2: String,
@SerializedName("mapx")
val mapX: String,
@SerializedName("mapy")
val mapY: String,
val cat3: String,
val tel: String,
val title: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kr.ksw.visitkorea.data.repository

import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO

interface SearchFestivalRepository {
suspend operator fun invoke(
numOfRows: Int,
pageNo: Int,
eventStartDate: String,
eventEndDate: String,
areaCode: String? = null,
sigunguCode: String? = null
): Result<List<SearchFestivalDTO>>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing comma 추가하는게 좋을 거 같아요

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package kr.ksw.visitkorea.data.repository

import kr.ksw.visitkorea.data.mapper.toItems
import kr.ksw.visitkorea.data.remote.api.SearchFestivalApi
import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO
import javax.inject.Inject

class SearchFestivalRepositoryImpl @Inject constructor(
private val searchFestivalApi: SearchFestivalApi
): SearchFestivalRepository {
override suspend fun invoke(
numOfRows: Int,
pageNo: Int,
eventStartDate: String,
eventEndDate: String,
areaCode: String?,
sigunguCode: String?
): Result<List<SearchFestivalDTO>> = runCatching {
searchFestivalApi.searchFestival(
numOfRows = numOfRows,
pageNo = pageNo,
eventStartDate = eventStartDate,
eventEndDate = eventEndDate,
areaCode = areaCode,
sigunguCode = sigunguCode
).toItems()
}
}
15 changes: 15 additions & 0 deletions app/src/main/java/kr/ksw/visitkorea/domain/di/FestivalModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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.festival.GetFestivalListUseCase
import kr.ksw.visitkorea.domain.usecase.festival.GetFestivalListUseCaseImpl

@Module
@InstallIn(ActivityRetainedComponent::class)
abstract class FestivalModule {
@Binds
abstract fun bindGetFestivalListUseCase(getFestivalListUseCase: GetFestivalListUseCaseImpl): GetFestivalListUseCase
}
17 changes: 17 additions & 0 deletions app/src/main/java/kr/ksw/visitkorea/domain/di/MoreModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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.more.GetMoreListUseCase
import kr.ksw.visitkorea.domain.usecase.more.GetMoreListUseCaseImpl

@Module
@InstallIn(ActivityRetainedComponent::class)
abstract class MoreModule {
@Binds
abstract fun bindGetMoreListUseCase(
getMoreListUseCase: GetMoreListUseCaseImpl
): GetMoreListUseCase
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package kr.ksw.visitkorea.domain.usecase.festival

import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO

interface GetFestivalListUseCase {
suspend operator fun invoke(
forceFetch: Boolean,
eventStartDate: String,
eventEndDate: String,
areaCode: String?,
sigunguCode: String?
) : Result<Flow<PagingData<SearchFestivalDTO>>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package kr.ksw.visitkorea.domain.usecase.festival

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import kotlinx.coroutines.flow.Flow
import kr.ksw.visitkorea.data.paging.source.SearchFestivalPagingSource
import kr.ksw.visitkorea.data.remote.api.SearchFestivalApi
import kr.ksw.visitkorea.data.remote.dto.SearchFestivalDTO
import javax.inject.Inject

class GetFestivalListUseCaseImpl @Inject constructor(
private val searchFestivalApi: SearchFestivalApi
) : GetFestivalListUseCase {
private var pagingSource: PagingSource<Int, SearchFestivalDTO>? = null

override suspend fun invoke(
forceFetch: Boolean,
eventStartDate: String,
eventEndDate: String,
areaCode: String?,
sigunguCode: String?
): Result<Flow<PagingData<SearchFestivalDTO>>> = runCatching {
if(forceFetch &&
pagingSource != null &&
pagingSource?.invalid != true) {
pagingSource?.invalidate()
}
Pager(
config = PagingConfig(
pageSize = 20,
initialLoadSize = 20
),
pagingSourceFactory = {
SearchFestivalPagingSource(
searchFestivalApi,
eventStartDate,
eventEndDate,
areaCode,
sigunguCode
).also {
pagingSource = it
}
}
).flow
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class GetHotelListUseCaseImpl @Inject constructor(
}
Pager(
config = PagingConfig(
pageSize = 10,
initialLoadSize = 10
pageSize = 30,
initialLoadSize = 30
),
pagingSourceFactory = {
LocationBasedPagingSource(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package kr.ksw.visitkorea.domain.usecase.mapper

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.Festival
import kr.ksw.visitkorea.domain.usecase.model.MoreCardModel
import kr.ksw.visitkorea.domain.usecase.model.Restaurant
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

Expand All @@ -23,6 +27,15 @@ fun LocationBasedDTO.toCommonCardModel(): CommonCardModel = CommonCardModel(
contentId
)

fun SearchFestivalDTO.toFestival(): Festival = Festival(
address,
firstImage.toImageUrl(),
title,
contentId,
eventStartDate.toDateString(),
eventEndDate.toDateString()
)

val restaurantMap = mapOf(
"A05020100" to "한식",
"A05020200" to "서양식",
Expand All @@ -39,4 +52,13 @@ fun LocationBasedDTO.toRestaurantModel(): Restaurant = Restaurant(
firstImage.toImageUrl(),
title,
restaurantMap[category] ?: ""
)

fun LocationBasedDTO.toMoreCardModel(): MoreCardModel = MoreCardModel(
address,
firstImage.toImageUrl(),
title,
dist.toDistForUi(),
contentId,
restaurantMap[category]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kr.ksw.visitkorea.domain.usecase.model

data class Festival(
val address: String = "",
val firstImage: String = "",
val title: String = "",
val contentId: String = "",
val eventStartDate: String = "",
val eventEndDate: String = ""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing comma - 를 추가해 주는 게 좋을 것 같아요.

)
Loading
Loading