Skip to content

Commit

Permalink
Merge pull request #320 from teamterning/feat/#306-search
Browse files Browse the repository at this point in the history
  • Loading branch information
arinming authored Jan 10, 2025
2 parents ed9c027 + a4d4f6f commit 86e087f
Show file tree
Hide file tree
Showing 20 changed files with 169 additions and 124 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(
imageUrl = 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 imageUrl: 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 @@ -4,8 +4,8 @@ import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.background
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 All @@ -30,22 +30,26 @@ import com.terning.core.designsystem.state.UiState
import com.terning.core.designsystem.theme.Black
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.domain.search.entity.SearchBanner
import com.terning.domain.search.entity.SearchPopularAnnouncement
import com.terning.feature.search.R
import com.terning.feature.search.search.component.ImageSlider
import com.terning.feature.search.search.component.InternListType
import com.terning.feature.search.search.component.SearchInternList
import okhttp3.internal.toImmutableList
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList

@Composable
fun SearchRoute(
modifier: Modifier,
paddingValues: PaddingValues,
navigateToSearchProcess: () -> Unit,
navigateToIntern: (Long) -> Unit,
viewModel: SearchViewModel = hiltViewModel(),
) {
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 @@ -54,32 +58,38 @@ fun SearchRoute(
LaunchedEffect(key1 = true) {
viewModel.getSearchViews()
viewModel.getSearchScraps()
viewModel.getSearchBanners()
}

LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle)
.collect { sideEffect ->
when (sideEffect) {
is SearchSideEffect.Toast -> {
is SearchSideEffect.ShowToast -> {
sideEffect.message
}
}
}
}

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

val searchViewsList = when (viewState.searchViewsList) {
is UiState.Success -> (viewState.searchViewsList as UiState.Success<List<com.terning.domain.search.entity.SearchPopularAnnouncement>>).data.toImmutableList()
else -> emptyList()
is UiState.Success -> (viewState.searchViewsList as UiState.Success<List<SearchPopularAnnouncement>>).data.toImmutableList()
else -> emptyList<SearchPopularAnnouncement>().toImmutableList()
}

val searchScrapsList = when (scrapState.searchScrapsList) {
is UiState.Success -> (scrapState.searchScrapsList as UiState.Success<List<com.terning.domain.search.entity.SearchPopularAnnouncement>>).data.toImmutableList()
else -> emptyList()
is UiState.Success -> (scrapState.searchScrapsList as UiState.Success<List<SearchPopularAnnouncement>>).data.toImmutableList()
else -> emptyList<SearchPopularAnnouncement>().toImmutableList()
}

SearchScreen(
modifier = modifier,
bannerList = SearchViewModel.bannerList,
paddingValues = paddingValues,
bannerList = bannerList,
searchViewsList = searchViewsList,
searchScrapsList = searchScrapsList,
navigateToSearchProcess = {
Expand All @@ -96,25 +106,25 @@ fun SearchRoute(
name = "quest_banner"
)
CustomTabsIntent.Builder().build()
.launchUrl(context, SearchViewModel.bannerList[pageIndex].url.toUri())
.launchUrl(context, bannerList[pageIndex].url.toUri())
}
)
}

@Composable
fun SearchScreen(
modifier: Modifier = Modifier,
bannerList: List<com.terning.domain.search.entity.SearchBanner>,
searchViewsList: List<com.terning.domain.search.entity.SearchPopularAnnouncement>,
searchScrapsList: List<com.terning.domain.search.entity.SearchPopularAnnouncement>,
paddingValues: PaddingValues,
bannerList: ImmutableList<SearchBanner>,
searchViewsList: ImmutableList<SearchPopularAnnouncement>,
searchScrapsList: ImmutableList<SearchPopularAnnouncement>,
navigateToSearchProcess: () -> Unit,
navigateToIntern: (Long) -> Unit,
onAdvertisementClick: (Int) -> Unit,
) {
Column(
modifier = modifier
.fillMaxSize()
modifier = Modifier
.background(White)
.padding(paddingValues)
) {
TerningImage(
painter = R.drawable.ic_terning_logo_typo,
Expand Down Expand Up @@ -145,7 +155,7 @@ fun SearchScreen(
LazyColumn {
item {
ImageSlider(
images = bannerList,
searchBanners = bannerList,
onAdvertisementClick = onAdvertisementClick,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package com.terning.feature.search.search
import androidx.annotation.StringRes

sealed class SearchSideEffect {
data class Toast(@StringRes val message: Int) : SearchSideEffect()
data class ShowToast(@StringRes val message: Int) : SearchSideEffect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.terning.feature.search.search
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 @@ -29,12 +29,17 @@ 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()

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

fun getSearchViews() {
Expand All @@ -45,7 +50,7 @@ class SearchViewModel @Inject constructor(
searchViewsList = UiState.Success(searchViewsList)
)
}.onFailure {
_sideEffect.emit(SearchSideEffect.Toast(DesignSystemR.string.server_failure))
_sideEffect.emit(SearchSideEffect.ShowToast(DesignSystemR.string.server_failure))
}
}
}
Expand All @@ -58,25 +63,21 @@ class SearchViewModel @Inject constructor(
searchScrapsList = UiState.Success(searchScrapsList)
)
}.onFailure {
_sideEffect.emit(SearchSideEffect.Toast(DesignSystemR.string.server_failure))
_sideEffect.emit(SearchSideEffect.ShowToast(DesignSystemR.string.server_failure))
}
}
}

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)
)
}.onFailure {
_sideEffect.emit(SearchSideEffect.ShowToast(DesignSystemR.string.server_failure))
}
}
}
}
}
Loading

0 comments on commit 86e087f

Please sign in to comment.