Skip to content

Commit

Permalink
Merge pull request #90 from everymeals/feature/get_restaurant
Browse files Browse the repository at this point in the history
[feature/get_restaurant] 대학 주변 식당 API 연결 및 코드 리팩터링
  • Loading branch information
SsongSik authored Jan 20, 2024
2 parents c862b4a + cc2d288 commit 5a05a02
Show file tree
Hide file tree
Showing 21 changed files with 651 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.everymeal.everymeal_android.di

import com.everymeal.data.service.auth.AuthApi
import com.everymeal.data.service.onboarding.OnboardingApi
import com.everymeal.data.service.restaurant.RestaurantApi
import com.everymeal.everymeal_android.BuildConfig
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
Expand All @@ -11,6 +12,7 @@ import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
Expand All @@ -26,7 +28,9 @@ object NetworkModule {
@Provides
@Singleton
fun provideClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
return OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(100, TimeUnit.SECONDS)
.readTimeout(100, TimeUnit.SECONDS)
.build()
Expand All @@ -53,4 +57,10 @@ object NetworkModule {
fun provideAuthApi(retrofit: Retrofit): AuthApi {
return retrofit.create(AuthApi::class.java)
}

@Provides
@Singleton
fun provideRestaurantApi(retrofit: Retrofit): RestaurantApi {
return retrofit.create(RestaurantApi::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import com.everymeal.data.datasource.local.LocalDataSource
import com.everymeal.data.datasource.local.LocalDataSourceImpl
import com.everymeal.data.datasource.onboarding.OnboardingDataSource
import com.everymeal.data.datasource.onboarding.OnboardingDataSourceImpl
import com.everymeal.data.datasource.restaurant.RestaurantDataSource
import com.everymeal.data.datasource.restaurant.RestaurantDataSourceImpl
import com.everymeal.data.repository.local.LocalRepositoryImpl
import com.everymeal.data.repository.DefaultAuthRepository
import com.everymeal.data.repository.onboarding.OnboardingRepositoryImpl
import com.everymeal.data.repository.restaurant.RestaurantRepositoryImpl
import com.everymeal.domain.repository.local.LocalRepository
import com.everymeal.domain.repository.auth.AuthRepository
import com.everymeal.domain.repository.onboarding.OnboardingRepository
import com.everymeal.domain.repository.restaurant.RestaurantRepository
import com.everymeal.presentation.ui.home.Restaurant
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -57,4 +62,16 @@ abstract class RepositoryModule {
abstract fun bindAuthRepository(
defaultAuthRepository: DefaultAuthRepository
): AuthRepository

@Singleton
@Binds
abstract fun bindRestaurantDataSource(
restaurantDataSourceImpl: RestaurantDataSourceImpl
): RestaurantDataSource

@Singleton
@Binds
abstract fun bindRestaurantRepository(
restaurantRepositoryImpl: RestaurantRepositoryImpl
): RestaurantRepository
}
4 changes: 4 additions & 0 deletions data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ dependencies {

//DataStore
implementation(libs.data.store)

// Paging
implementation(libs.paging.compose)
implementation(libs.paging.runtime)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.everymeal.data.datasource.restaurant

import androidx.paging.PagingData
import com.everymeal.data.model.restaruant.RestaurantResponse
import kotlinx.coroutines.flow.Flow

interface RestaurantDataSource {
suspend fun getUnivRestaurant(
campusIdx: Int,
order: String,
group: String? = null,
grade: String? = null,
): Flow<PagingData<RestaurantResponse>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.everymeal.data.datasource.restaurant

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.everymeal.data.model.restaruant.RestaurantResponse
import com.everymeal.data.service.restaurant.RestaurantApi
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

const val PAGING_SIZE = 20
const val STARTING_PAGE_INDEX = 0

class RestaurantDataSourceImpl @Inject constructor(
private val restaurantApi: RestaurantApi
) : RestaurantDataSource {
override suspend fun getUnivRestaurant(
campusIdx: Int,
order: String,
group: String?,
grade: String?
) : Flow<PagingData<RestaurantResponse>> {
val pagingSourceFactory = { RestaurantPagingSource(restaurantApi, campusIdx, order, group, grade) }
return Pager(
config = PagingConfig(
initialLoadSize = PAGING_SIZE,
pageSize = PAGING_SIZE,
enablePlaceholders = false,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.everymeal.data.datasource.restaurant

import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.everymeal.data.model.restaruant.RestaurantResponse
import com.everymeal.data.service.restaurant.RestaurantApi
import retrofit2.HttpException
import java.io.IOException

class RestaurantPagingSource (
private val restaurantApi: RestaurantApi,
private val campusIdx: Int,
private val order: String,
private val group: String?,
private val grade: String?
) : PagingSource<Int, RestaurantResponse>() {

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

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, RestaurantResponse> {
val position = params.key ?: STARTING_PAGE_INDEX

return try {
val response = restaurantApi.getUnivRestaurant(
campusIdx = campusIdx,
order = order,
group = group,
grade = grade,
offset = position,
limit = PAGING_SIZE
)
val restaurants = response.data.content
LoadResult.Page(
data = restaurants,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (restaurants.isEmpty()) null else position + 1
)
} catch (exception: IOException) {
LoadResult.Error(exception)
} catch (exception: HttpException) {
LoadResult.Error(exception)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.everymeal.data.model.restaruant

import com.everymeal.domain.model.restaurant.RestaurantDataEntity
import kotlinx.serialization.Serializable

@Serializable
data class GetUnivRestaurantResponse(
val content: List<RestaurantResponse>,
val pageable: Pageable,
val totalPages: Int,
val totalElements: Int,
val last: Boolean,
val size: Int,
val number: Int,
val sort: Sort,
val numberOfElements: Int,
val first: Boolean,
val empty: Boolean
)

@Serializable
data class RestaurantResponse(
val idx: Int,
val name: String,
val address: String,
val phoneNumber: String,
val categoryDetail: String,
val distance: Int,
val grade: Float,
val reviewCount: Int,
val recommendedCount: Int,
val images: List<String>?,
val isLiked: Boolean
)

@Serializable
data class Pageable(
val sort: Sort,
val offset: Int,
val pageNumber: Int,
val pageSize: Int,
val paged: Boolean,
val unpaged: Boolean
)

@Serializable
data class Sort(
val empty: Boolean,
val sorted: Boolean,
val unsorted: Boolean
)

fun RestaurantResponse.toEntity(): RestaurantDataEntity {
return RestaurantDataEntity(
idx = this.idx,
name = this.name,
address = this.address,
phoneNumber = this.phoneNumber,
categoryDetail = this.categoryDetail,
distance = this.distance,
grade = this.grade,
reviewCount = this.reviewCount,
recommendedCount = this.recommendedCount,
images = this.images,
isLiked = this.isLiked
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.everymeal.data.repository.restaurant

import androidx.paging.PagingData
import com.everymeal.data.datasource.restaurant.RestaurantDataSource
import com.everymeal.domain.model.restaurant.RestaurantDataEntity
import com.everymeal.domain.repository.restaurant.RestaurantRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import androidx.paging.map
import com.everymeal.data.model.restaruant.toEntity
import javax.inject.Inject

class RestaurantRepositoryImpl @Inject constructor(
private val restaurantDataSource: RestaurantDataSource
) : RestaurantRepository {
override suspend fun getUnivRestaurant(
campusIdx: Int,
order: String,
group: String?,
grade: String?
) : Flow<PagingData<RestaurantDataEntity>> {
return restaurantDataSource.getUnivRestaurant(campusIdx, order, group, grade)
.map { pagingData ->
pagingData.map { it.toEntity() }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.everymeal.data.service.restaurant

import com.everymeal.data.model.BaseResponse
import com.everymeal.data.model.restaruant.GetUnivRestaurantResponse
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface RestaurantApi {

@GET("/api/v1/stores/campus/{campusIdx}")
suspend fun getUnivRestaurant(
@Path("campusIdx") campusIdx: Int,
@Query("offset") offset: Int,
@Query("limit") limit: Int,
@Query("order") order: String,
@Query("group") group: String? = null,
@Query("grade") grade: String? = null,
): BaseResponse<GetUnivRestaurantResponse>
}
2 changes: 2 additions & 0 deletions domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ dependencies {

// Coroutines
implementation(libs.kotlin.coroutines)

implementation("androidx.paging:paging-common:3.2.0-rc01")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.everymeal.domain.model.restaurant

data class GetUnivRestaurantEntity(
val data: List<RestaurantDataEntity>
)

data class RestaurantDataEntity(
val idx: Int,
val name: String,
val address: String,
val phoneNumber: String,
val categoryDetail: String,
val distance: Int,
val grade: Float,
val reviewCount: Int,
val recommendedCount: Int,
val images: List<String>?,
val isLiked: Boolean
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.everymeal.domain.repository.restaurant

import androidx.paging.PagingData
import com.everymeal.domain.model.restaurant.RestaurantDataEntity
import kotlinx.coroutines.flow.Flow

interface RestaurantRepository {

suspend fun getUnivRestaurant(
campusIdx: Int,
order: String,
group: String? = null,
grade: String? = null,
) : Flow<PagingData<RestaurantDataEntity>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.everymeal.domain.usecase.restaurant

import androidx.paging.PagingData
import com.everymeal.domain.model.restaurant.RestaurantDataEntity
import com.everymeal.domain.repository.restaurant.RestaurantRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class GetUnivRestaurantUseCase @Inject constructor(
private val restaurantRepository: RestaurantRepository
) {
suspend operator fun invoke(
campusIdx: Int,
order: String,
group: String?,
grade: String?
) : Flow<PagingData<RestaurantDataEntity>> {
return restaurantRepository.getUnivRestaurant(campusIdx, order, group, grade)
}
}
8 changes: 8 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ accompanist = "0.33.0-alpha"
data-store = "1.0.0"
kotlin-coroutines = "1.7.3"

paging-compose = "3.2.0-rc01"
paging-runtime = "3.1.0"

[libraries]
agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil-compose" }
Expand Down Expand Up @@ -78,6 +81,11 @@ data-store = { module = "androidx.datastore:datastore-preferences", version.ref
#kotlin-coroutines
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }

#Paging
paging-runtime = { module = "androidx.paging:paging-runtime", version = "paging-runtime" }
paging-compose = { module = "androidx.paging:paging-compose", version = "paging-compose" }
paging-common = { module = "androidx.paging:paging-common", version = "paging-compose" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
Expand Down
Loading

0 comments on commit 5a05a02

Please sign in to comment.