Skip to content

Commit

Permalink
[FEAT/#306] 배너 조회 API 연동
Browse files Browse the repository at this point in the history
  • Loading branch information
arinming committed Jan 4, 2025
1 parent 608733b commit 7e52504
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.terning.data.search.datasource
import com.terning.core.network.BaseResponse
import com.terning.data.search.dto.request.SearchRequestDto
import com.terning.data.search.dto.response.SearchAnnouncementResponseDto
import com.terning.data.search.dto.response.SearchBannersResponseDto
import com.terning.data.search.dto.response.SearchResultResponseDto

interface SearchDataSource {
suspend fun getSearch(request: SearchRequestDto): BaseResponse<SearchResultResponseDto>
suspend fun getSearchViews(): BaseResponse<SearchAnnouncementResponseDto>
suspend fun getSearchScraps(): BaseResponse<SearchAnnouncementResponseDto>
suspend fun getSearchBanners(): BaseResponse<SearchBannersResponseDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.terning.core.network.BaseResponse
import com.terning.data.search.datasource.SearchDataSource
import com.terning.data.search.dto.request.SearchRequestDto
import com.terning.data.search.dto.response.SearchAnnouncementResponseDto
import com.terning.data.search.dto.response.SearchBannersResponseDto
import com.terning.data.search.dto.response.SearchResultResponseDto
import com.terning.data.search.service.SearchService
import javax.inject.Inject
Expand All @@ -26,4 +27,7 @@ class SearchDataSourceImpl @Inject constructor(

override suspend fun getSearchScraps(): BaseResponse<SearchAnnouncementResponseDto> =
searchService.getSearchScrapsList()

override suspend fun getSearchBanners(): BaseResponse<SearchBannersResponseDto> =
searchService.getSearchBannerList()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.terning.data.search.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SearchBannersResponseDto(
@SerialName("banners")
val banners: List<BannerDto>,
) {
@Serializable
data class BannerDto(
@SerialName("imageUrl")
val imageUrl: String,
@SerialName("link")
val link: String,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.terning.data.search.mapper

import com.terning.data.search.dto.response.SearchBannersResponseDto
import com.terning.domain.search.entity.SearchBanner

fun SearchBannersResponseDto.toSearchBannerList(): List<SearchBanner> {
return banners.map {
SearchBanner(
imageRes = it.imageUrl,
url = it.link,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.terning.data.search.repositoryimpl

import com.terning.data.search.datasource.SearchDataSource
import com.terning.data.search.dto.request.SearchRequestDto
import com.terning.data.search.mapper.toSearchBannerList
import com.terning.data.search.mapper.toSearchPopularAnnouncementList
import com.terning.data.search.mapper.toSearchResultList
import com.terning.domain.search.entity.SearchBanner
import com.terning.domain.search.entity.SearchPopularAnnouncement
import com.terning.domain.search.entity.SearchResult
import com.terning.domain.search.repository.SearchRepository
Expand Down Expand Up @@ -39,4 +41,9 @@ class SearchRepositoryImpl @Inject constructor(
runCatching {
searchDataSource.getSearchScraps().result.toSearchPopularAnnouncementList()
}

override suspend fun getSearchBannersList(): Result<List<SearchBanner>> =
kotlin.runCatching {
searchDataSource.getSearchBanners().result.toSearchBannerList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.terning.data.search.service

import com.terning.core.network.BaseResponse
import com.terning.data.search.dto.response.SearchAnnouncementResponseDto
import com.terning.data.search.dto.response.SearchBannersResponseDto
import com.terning.data.search.dto.response.SearchResultResponseDto
import retrofit2.http.GET
import retrofit2.http.Query
Expand All @@ -20,4 +21,7 @@ interface SearchService {

@GET("api/v1/search/scraps")
suspend fun getSearchScrapsList(): BaseResponse<SearchAnnouncementResponseDto>

@GET("api/v1/search/banners")
suspend fun getSearchBannerList(): BaseResponse<SearchBannersResponseDto>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.terning.domain.search.entity

data class SearchBanner(
val imageRes: Int,
val url: String
val imageRes: String,
val url: String,
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.terning.domain.search.repository

import com.terning.domain.search.entity.SearchBanner
import com.terning.domain.search.entity.SearchPopularAnnouncement
import com.terning.domain.search.entity.SearchResult

Expand All @@ -10,6 +11,8 @@ interface SearchRepository {
page: Int,
size: Int,
): Result<List<SearchResult>>

suspend fun getSearchViewsList(): Result<List<SearchPopularAnnouncement>>
suspend fun getSearchScrapsList(): Result<List<SearchPopularAnnouncement>>
suspend fun getSearchBannersList(): Result<List<SearchBanner>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
Expand Down Expand Up @@ -47,6 +46,7 @@ fun SearchRoute(
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current

val bannerState by viewModel.bannerState.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)
val viewState by viewModel.viewState.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)
val scrapState by viewModel.scrapState.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

Expand All @@ -55,6 +55,7 @@ fun SearchRoute(
LaunchedEffect(key1 = true) {
viewModel.getSearchViews()
viewModel.getSearchScraps()
viewModel.getSearchBanners()
}

LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
Expand All @@ -68,6 +69,11 @@ fun SearchRoute(
}
}

val bannerList = when (bannerState.searchBannersList) {
is UiState.Success -> (bannerState.searchBannersList as UiState.Success<List<com.terning.domain.search.entity.SearchBanner>>).data.toImmutableList()
else -> emptyList()
}

val searchViewsList = when (viewState.searchViewsList) {
is UiState.Success -> (viewState.searchViewsList as UiState.Success<List<com.terning.domain.search.entity.SearchPopularAnnouncement>>).data.toImmutableList()
else -> emptyList()
Expand All @@ -80,7 +86,7 @@ fun SearchRoute(

SearchScreen(
paddingValues = paddingValues,
bannerList = SearchViewModel.bannerList,
bannerList = bannerList,
searchViewsList = searchViewsList,
searchScrapsList = searchScrapsList,
navigateToSearchProcess = {
Expand All @@ -97,7 +103,7 @@ fun SearchRoute(
name = "quest_banner"
)
CustomTabsIntent.Builder().build()
.launchUrl(context, SearchViewModel.bannerList[pageIndex].url.toUri())
.launchUrl(context, bannerList[pageIndex].url.toUri())
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.terning.core.designsystem.state.UiState
import com.terning.feature.search.R
import com.terning.feature.search.search.model.SearchBannerListState
import com.terning.feature.search.search.model.SearchScrapsListState
import com.terning.feature.search.search.model.SearchViewsListState
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -27,12 +28,20 @@ class SearchViewModel @Inject constructor(
MutableStateFlow(SearchScrapsListState())
val scrapState: StateFlow<SearchScrapsListState> = _scrapState.asStateFlow()

private val _bannerState: MutableStateFlow<SearchBannerListState> =
MutableStateFlow(SearchBannerListState())
val bannerState: StateFlow<SearchBannerListState> = _bannerState.asStateFlow()

private val _sideEffect: MutableSharedFlow<SearchSideEffect> = MutableSharedFlow()
val sideEffect = _sideEffect.asSharedFlow()

private val _bannerList: MutableStateFlow<List<com.terning.domain.search.entity.SearchBanner>> =
MutableStateFlow(emptyList())

init {
getSearchViews()
getSearchScraps()
getSearchBanners()
}

fun getSearchViews() {
Expand Down Expand Up @@ -61,20 +70,17 @@ class SearchViewModel @Inject constructor(
}
}

companion object {
val bannerList: List<com.terning.domain.search.entity.SearchBanner> = listOf(
com.terning.domain.search.entity.SearchBanner(
imageRes = R.drawable.img_ad_1,
url = "https://www.instagram.com/p/DBWCO97TRds/?igsh=bDhjMGxlMGliNDc2"
),
com.terning.domain.search.entity.SearchBanner(
imageRes = R.drawable.img_ad_2,
url = "https://www.instagram.com/terning_official/"
),
com.terning.domain.search.entity.SearchBanner(
imageRes = R.drawable.img_ad_3,
url = "https://forms.gle/4btEwEbUQ3JSjTKP7"
)
)
fun getSearchBanners() {
viewModelScope.launch {
searchRepository.getSearchBannersList()
.onSuccess { searchBannersList ->
_bannerState.value = _bannerState.value.copy(
searchBannersList = UiState.Success(searchBannersList)
)
_bannerList.value = searchBannersList
}.onFailure {
_sideEffect.emit(SearchSideEffect.Toast(R.string.server_failure))
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.terning.feature.search.search.component

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -15,8 +14,8 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.terning.core.designsystem.extension.noRippleClickable
import com.terning.core.designsystem.theme.Grey200
import kotlinx.coroutines.delay
Expand All @@ -29,16 +28,16 @@ fun ImageSlider(
) {
val pagerState = rememberPagerState(
initialPage = 0,
pageCount = { Int.MAX_VALUE }
pageCount = { if (images.isEmpty()) 0 else Int.MAX_VALUE }
)
val autoScroll = remember { mutableStateOf(true) }

LaunchedEffect(autoScroll.value) {
if (autoScroll.value) {
while (true) {
delay(2500)
if (!pagerState.isScrollInProgress) {
val nextPage = pagerState.currentPage + 1
if (!pagerState.isScrollInProgress && images.isNotEmpty()) {
val nextPage = (pagerState.currentPage + 1) % images.size
pagerState.animateScrollToPage(nextPage)
}
}
Expand All @@ -51,30 +50,32 @@ fun ImageSlider(
.background(Grey200),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = modifier,
contentAlignment = Alignment.BottomCenter
) {
HorizontalPager(
state = pagerState,
if (images.isNotEmpty()) {
Box(
modifier = modifier,
beyondViewportPageCount = 1
) { currentPage ->
val pageIndex = currentPage % images.size
Image(
painter = painterResource(id = images[pageIndex].imageRes),
contentDescription = null,
modifier = modifier
.fillMaxWidth()
.height(112.dp)
.noRippleClickable { onAdvertisementClick(pageIndex) },
contentScale = ContentScale.Crop,
contentAlignment = Alignment.BottomCenter
) {
HorizontalPager(
state = pagerState,
modifier = modifier,
beyondViewportPageCount = 1
) { currentPage ->
val pageIndex = currentPage % images.size
AsyncImage(
model = images[pageIndex].url,
contentDescription = null,
modifier = modifier
.fillMaxWidth()
.height(112.dp)
.noRippleClickable { onAdvertisementClick(pageIndex) },
contentScale = ContentScale.Crop
)
}
DotsIndicator(
pageCount = images.size,
currentPage = pagerState.currentPage % images.size
)
}
DotsIndicator(
pageCount = images.size,
currentPage = pagerState.currentPage % images.size
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.search.search.model

import com.terning.core.designsystem.state.UiState
import com.terning.domain.search.entity.SearchBanner

data class SearchBannerListState(
var searchBannersList: UiState<List<SearchBanner>> = UiState.Loading,
)

0 comments on commit 7e52504

Please sign in to comment.