From 4e56e072fe3ade45931a044fcecc058adb8eebe8 Mon Sep 17 00:00:00 2001 From: arinming Date: Sat, 11 Jan 2025 18:41:02 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[FEAT/#328]=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=86=8C=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/search/build.gradle.kts | 1 + .../data/search/mapper/SearchResultMapper.kt | 28 +++++------ .../search/pagingsource/SearchPagingSource.kt | 49 +++++++++++++++++++ .../repositoryimpl/SearchRepositoryImpl.kt | 36 ++++++++------ domain/search/build.gradle.kts | 6 ++- .../domain/search/entity/SearchResult.kt | 1 + .../search/repository/SearchRepository.kt | 9 ++-- gradle/libs.versions.toml | 6 +++ 8 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt diff --git a/data/search/build.gradle.kts b/data/search/build.gradle.kts index 4db4ee6f..9c130ee0 100644 --- a/data/search/build.gradle.kts +++ b/data/search/build.gradle.kts @@ -11,4 +11,5 @@ android { dependencies { // domain implementation(projects.domain.search) + implementation(libs.androidx.paging.common.android) } \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/mapper/SearchResultMapper.kt b/data/search/src/main/java/com/terning/data/search/mapper/SearchResultMapper.kt index 13f5ab0a..7cd2b517 100644 --- a/data/search/src/main/java/com/terning/data/search/mapper/SearchResultMapper.kt +++ b/data/search/src/main/java/com/terning/data/search/mapper/SearchResultMapper.kt @@ -3,18 +3,16 @@ package com.terning.data.search.mapper import com.terning.data.search.dto.response.SearchResultResponseDto import com.terning.domain.search.entity.SearchResult -fun SearchResultResponseDto.toSearchResultList(): List { - return announcements.map { - SearchResult( - internshipAnnouncementId = it.internshipAnnouncementId, - title = it.title, - dDay = it.dDay, - workingPeriod = it.workingPeriod, - companyImage = it.companyImage, - isScrapped = it.isScrapped, - deadline = it.deadline, - startYearMonth = it.startYearMonth, - color = it.color - ) - } -} \ No newline at end of file +fun SearchResultResponseDto.SearchAnnouncementDto.toSearchResultList(totalCount: Int): SearchResult = + SearchResult( + totalCount = totalCount, + internshipAnnouncementId = this.internshipAnnouncementId, + title = this.title, + dDay = this.dDay, + workingPeriod = this.workingPeriod, + companyImage = this.companyImage, + isScrapped = this.isScrapped, + deadline = this.deadline, + startYearMonth = this.startYearMonth, + color = this.color, + ) \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt new file mode 100644 index 00000000..fe4fc6d9 --- /dev/null +++ b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt @@ -0,0 +1,49 @@ +package com.terning.data.search.pagingsource + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.terning.data.search.datasource.SearchDataSource +import com.terning.data.search.dto.request.SearchRequestDto +import com.terning.data.search.dto.response.SearchResultResponseDto + +class SearchPagingSource( + private val query: String, + private val sortBy: String, + private val dataSource: SearchDataSource, +) : PagingSource>() { + + override suspend fun load(params: LoadParams): LoadResult> { + return try { + val nextParamKey = params.key ?: 0 + + val response = dataSource.getSearch( + request = SearchRequestDto( + keyword = query, + sortBy = sortBy, + page = nextParamKey, + size = params.loadSize + ) + ) + val totalCount = response.result.totalCount + val hasNextPage = response.result.hasNext + + LoadResult.Page( + data = response.result.announcements.map { + Pair(totalCount, it) + }, + prevKey = null, + nextKey = if (hasNextPage) nextParamKey + 1 else null + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override fun getRefreshKey(state: PagingState>): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } + +} \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt index 69ce5e55..ae1c6652 100644 --- a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt +++ b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt @@ -1,34 +1,40 @@ package com.terning.data.search.repositoryimpl +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.map 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.data.search.pagingsource.SearchPagingSource 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 +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import javax.inject.Inject class SearchRepositoryImpl @Inject constructor( private val searchDataSource: SearchDataSource, ) : SearchRepository { - override suspend fun getSearchList( + + override fun getSearchList( query: String, sortBy: String, - page: Int, - size: Int, - ): Result> { - return runCatching { - searchDataSource.getSearch( - SearchRequestDto( - keyword = query, - sortBy = sortBy, - page = page, - size = size - ) - ).result.toSearchResultList() + ): Flow> { + return Pager( + PagingConfig(pageSize = 10) + ) { + SearchPagingSource( + query = query, + sortBy = sortBy, + dataSource = searchDataSource + ) + }.flow.map { pagedData -> + pagedData.map { it.second.toSearchResultList(it.first) } } } @@ -43,7 +49,7 @@ class SearchRepositoryImpl @Inject constructor( } override suspend fun getSearchBannersList(): Result> = - kotlin.runCatching { + runCatching { searchDataSource.getSearchBanners().result.toSearchBannerList() } } \ No newline at end of file diff --git a/domain/search/build.gradle.kts b/domain/search/build.gradle.kts index 0fde5b10..ba29fee6 100644 --- a/domain/search/build.gradle.kts +++ b/domain/search/build.gradle.kts @@ -1,3 +1,7 @@ plugins { alias(libs.plugins.terning.kotlin) -} \ No newline at end of file +} +dependencies { + implementation(libs.androidx.constraintlayout.core) + implementation(libs.androidx.constraintlayout) +} diff --git a/domain/search/src/main/java/com/terning/domain/search/entity/SearchResult.kt b/domain/search/src/main/java/com/terning/domain/search/entity/SearchResult.kt index 5c66f40f..95c74d62 100644 --- a/domain/search/src/main/java/com/terning/domain/search/entity/SearchResult.kt +++ b/domain/search/src/main/java/com/terning/domain/search/entity/SearchResult.kt @@ -1,6 +1,7 @@ package com.terning.domain.search.entity data class SearchResult( + val totalCount: Int, val internshipAnnouncementId: Long, val title: String, val dDay: String, diff --git a/domain/search/src/main/java/com/terning/domain/search/repository/SearchRepository.kt b/domain/search/src/main/java/com/terning/domain/search/repository/SearchRepository.kt index 9600fda8..c99a557b 100644 --- a/domain/search/src/main/java/com/terning/domain/search/repository/SearchRepository.kt +++ b/domain/search/src/main/java/com/terning/domain/search/repository/SearchRepository.kt @@ -1,16 +1,17 @@ package com.terning.domain.search.repository +import androidx.paging.PagingData import com.terning.domain.search.entity.SearchBanner import com.terning.domain.search.entity.SearchPopularAnnouncement import com.terning.domain.search.entity.SearchResult +import kotlinx.coroutines.flow.Flow interface SearchRepository { - suspend fun getSearchList( + + fun getSearchList( query: String, sortBy: String, - page: Int, - size: Int, - ): Result> + ): Flow> suspend fun getSearchViewsList(): Result> suspend fun getSearchScrapsList(): Result> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fab201d8..5b7d6ad7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -97,6 +97,9 @@ amplitude = "1.17.3" ## Paging paging = "3.3.2" +pagingCommonAndroid = "3.3.5" +constraintlayoutCore = "1.1.0" +constraintlayout = "2.2.0" [libraries] @@ -183,6 +186,9 @@ amplitude = { group = "com.amplitude", name = "analytics-android", version.ref = paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" } paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "paging" } paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" } +androidx-paging-common-android = { group = "androidx.paging", name = "paging-common-android", version.ref = "pagingCommonAndroid" } +androidx-constraintlayout-core = { group = "androidx.constraintlayout", name = "constraintlayout-core", version.ref = "constraintlayoutCore" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } [bundles] retrofit =[ From 459e0f2b2c59695b5d771a1f4b84e0d4f3878504 Mon Sep 17 00:00:00 2001 From: arinming Date: Sat, 11 Jan 2025 19:45:36 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[FEAT/#328]=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=9D=84=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domain/search/build.gradle.kts | 8 +- feature/search/build.gradle.kts | 4 + .../searchprocess/SearchProcessRoute.kt | 31 ++++--- .../searchprocess/SearchProcessViewModel.kt | 93 ++++++++++++------- .../models/SearchProcessState.kt | 5 +- 5 files changed, 88 insertions(+), 53 deletions(-) diff --git a/domain/search/build.gradle.kts b/domain/search/build.gradle.kts index ba29fee6..8c7175e6 100644 --- a/domain/search/build.gradle.kts +++ b/domain/search/build.gradle.kts @@ -1,7 +1,9 @@ plugins { alias(libs.plugins.terning.kotlin) } + dependencies { - implementation(libs.androidx.constraintlayout.core) - implementation(libs.androidx.constraintlayout) -} + implementation(libs.paging.common) + implementation(libs.coroutines.core) + implementation(libs.coroutines.test) +} \ No newline at end of file diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index ebec97e7..f22d9d72 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -14,4 +14,8 @@ dependencies { // feature implementation(projects.feature.dialog) + + // paging + implementation(libs.paging.runtime) + implementation(libs.paging.compose) } \ No newline at end of file diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index 3c3bd52c..c62382ac 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import androidx.navigation.NavHostController +import androidx.paging.compose.collectAsLazyPagingItems import com.terning.core.analytics.EventType import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet import com.terning.core.designsystem.component.button.SortingButton @@ -61,17 +62,22 @@ fun SearchProcessRoute( viewModel: SearchProcessViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() - val internSearchResultData by viewModel.internSearchResultData.collectAsStateWithLifecycle() val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current + val searchResultList = viewModel.internSearchResultFlow.collectAsLazyPagingItems() + + val searchResultTotal = remember(searchResultList.loadState.refresh) { + if (searchResultList.itemCount > 0) { + searchResultList[0]?.totalCount ?: 0 + } else { + 0 + } + } + LaunchedEffect(true) { - viewModel.getSearchList( - keyword = state.text, - page = 0, - size = 100 - ) + viewModel.getSearchListFlow() } LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { @@ -96,18 +102,14 @@ fun SearchProcessRoute( navigateToIntern = { viewModel.navigateIntern(it) }, navigateToBack = { navController.navigateUp() }, state = state, - internSearchResultData = internSearchResultData, + internSearchResultData = searchResultList.itemSnapshotList.items, + searchResultTotal = searchResultTotal, updateText = { viewModel.updateText(it) }, onSearchAction = { viewModel.updateQuery(state.text) - viewModel.getSearchList( - keyword = state.text, - sortBy = state.currentSortBy, - page = 0, - size = 100, - ) + viewModel.getSearchListFlow() viewModel.updateShowSearchResults(true) viewModel.updateExistSearchResults() }, @@ -158,6 +160,7 @@ fun SearchProcessScreen( navigateToBack: () -> Unit, state: SearchProcessState = SearchProcessState(), internSearchResultData: List = emptyList(), + searchResultTotal: Int = 0, updateText: (String) -> Unit = {}, onSearchAction: () -> Unit = {}, onSortButtonClick: () -> Unit = {}, @@ -241,7 +244,7 @@ fun SearchProcessScreen( ) Spacer(modifier = Modifier.padding(start = 3.dp)) Text( - text = internSearchResultData.size.toString(), + text = searchResultTotal.toString(), // Display total search results here style = TerningTheme.typography.button3, color = TerningMain, ) diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt index f38e59db..4cad3737 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt @@ -2,20 +2,27 @@ package com.terning.feature.search.searchprocess import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.map import com.terning.core.designsystem.type.SortBy import com.terning.domain.search.entity.SearchResult import com.terning.feature.search.searchprocess.models.SearchProcessState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject -import com.terning.core.designsystem.R as DesignSystemR @HiltViewModel @@ -26,30 +33,47 @@ class SearchProcessViewModel @Inject constructor( private val _state = MutableStateFlow(SearchProcessState()) val state: StateFlow = _state.asStateFlow() + private val _sideEffect = MutableSharedFlow() val sideEffect: SharedFlow = _sideEffect.asSharedFlow() - private val _internSearchResultData = MutableStateFlow>(emptyList()) - val internSearchResultData: StateFlow> = - _internSearchResultData.asStateFlow() - - fun getSearchList( - keyword: String, - sortBy: Int = 0, - page: Int, - size: Int, - ) { - viewModelScope.launch { - searchRepository.getSearchList(keyword, SortBy.entries[sortBy].type, page, size) - .onSuccess { results -> - _internSearchResultData.value = results - } - .onFailure { - _sideEffect.emit(SearchProcessSideEffect.ShowToast(DesignSystemR.string.server_failure)) - } + private val scrapStateFlow: MutableStateFlow> = + MutableStateFlow(emptyMap()) + + private val _internSearchResultFlow: MutableStateFlow>> = + MutableStateFlow(flow { }) + + @OptIn(ExperimentalCoroutinesApi::class) + val internSearchResultFlow: Flow> = combine( + _internSearchResultFlow.flatMapLatest { + it.cachedIn(viewModelScope) + }, scrapStateFlow + ) { paging, scrapState -> + paging.map { intern -> + val isScrapped = scrapState[intern.internshipAnnouncementId] ?: intern.isScrapped + intern.copy( + isScrapped = isScrapped + ) } } + fun getSearchListFlow() { + refreshScrapStateFlow() + refreshSearchListFlow() + } + + private fun refreshScrapStateFlow() { + scrapStateFlow.value = emptyMap() + } + + private fun refreshSearchListFlow() { + _internSearchResultFlow.value = searchRepository.getSearchList( + query = _state.value.keyword, + sortBy = SortBy.entries[_state.value.currentSortBy].type + ).cachedIn(viewModelScope) + } + + fun updateSearchResult( internshipId: Long, title: String, @@ -72,7 +96,8 @@ class SearchProcessViewModel @Inject constructor( isScrapped = isScrapped, deadline = deadline, startYearMonth = startYearMonth, - color = color ?: "" + color = color ?: "", + totalCount = 0 ) ) } @@ -91,7 +116,7 @@ class SearchProcessViewModel @Inject constructor( } fun updateExistSearchResults() { - _state.update { it.copy(existSearchResults = _internSearchResultData.value.isNotEmpty()) } + _state.update { it.copy(existSearchResults = false) } } fun updateScrapDialogVisible(visible: Boolean) { @@ -103,22 +128,22 @@ class SearchProcessViewModel @Inject constructor( } fun updateSortBy(newSortBy: Int) { - _state.value = _state.value.copy(currentSortBy = newSortBy) - getSearchList( - keyword = _state.value.keyword, - sortBy = newSortBy, - page = 0, - size = 100 - ) + _state.update { + it.copy(currentSortBy = newSortBy) + } + refreshSearchListFlow() } fun updateSearchResultScrapStatus(internshipId: Long, isScrapped: Boolean) { - _internSearchResultData.value = _internSearchResultData.value.map { searchResult -> - if (searchResult.internshipAnnouncementId == internshipId) { - searchResult.copy(isScrapped = isScrapped) - } else { - searchResult - } + scrapStateFlow.update { currentMap -> + currentMap + (internshipId to isScrapped) + } + _state.update { + it.copy( + searchResult = it.searchResult.copy( + isScrapped = isScrapped + ) + ) } } diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt index 8c87e3fc..0fade448 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt @@ -19,15 +19,16 @@ data class SearchProcessState( val selectedInternIndex: Int = 0, val sheetState: Boolean = false, val changeFilterState: Boolean = false, - val searchResult: com.terning.domain.search.entity.SearchResult = com.terning.domain.search.entity.SearchResult( + val searchResult: SearchResult = SearchResult( internshipAnnouncementId = 0, title = "", - companyImage = "", dDay = "", workingPeriod = "", + companyImage = "", isScrapped = false, deadline = "", startYearMonth = "", color = "", + totalCount = 0, ), ) From 477399698d518e17d9d237fa13d9bbf493bbd2df Mon Sep 17 00:00:00 2001 From: arinming Date: Sat, 11 Jan 2025 20:29:42 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[FEAT/#328]=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repositoryimpl/SearchRepositoryImpl.kt | 2 +- .../searchprocess/SearchProcessRoute.kt | 91 +++++++++++-------- .../searchprocess/SearchProcessViewModel.kt | 6 +- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt index ae1c6652..eae73cf8 100644 --- a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt +++ b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt @@ -26,7 +26,7 @@ class SearchRepositoryImpl @Inject constructor( sortBy: String, ): Flow> { return Pager( - PagingConfig(pageSize = 10) + PagingConfig(pageSize = 20) ) { SearchPagingSource( query = query, diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index c62382ac..45fc5344 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -21,6 +22,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource @@ -32,6 +34,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import androidx.navigation.NavHostController +import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.terning.core.analytics.EventType import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet @@ -76,10 +79,6 @@ fun SearchProcessRoute( } } - LaunchedEffect(true) { - viewModel.getSearchListFlow() - } - LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { sideEffect -> @@ -144,11 +143,17 @@ fun SearchProcessRoute( workingPeriod = it.workingPeriod, companyImage = it.companyImage, isScrapped = it.isScrapped, - color = it.color + color = it.color, + totalCount = searchResultTotal ) }, onSortChange = { viewModel.updateSortBy(it) + }, + onEndOfListReached = { + if (searchResultList.loadState.append is LoadState.NotLoading) { + searchResultList.retry() + } } ) } @@ -169,6 +174,7 @@ fun SearchProcessScreen( onDismissSheet: () -> Unit = {}, onScrapButtonClicked: (SearchResult) -> Unit, onSortChange: (Int) -> Unit = {}, + onEndOfListReached: () -> Unit = {}, ) { val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current @@ -223,6 +229,7 @@ fun SearchProcessScreen( .addFocusCleaner(focusManager), onSearchAction = onSearchAction ) + if (state.showSearchResults) { Column( modifier = Modifier @@ -244,7 +251,7 @@ fun SearchProcessScreen( ) Spacer(modifier = Modifier.padding(start = 3.dp)) Text( - text = searchResultTotal.toString(), // Display total search results here + text = searchResultTotal.toString(), style = TerningTheme.typography.button3, color = TerningMain, ) @@ -268,7 +275,12 @@ fun SearchProcessScreen( top = 12.dp, bottom = 20.dp, ), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), + reverseLayout = false, + state = rememberLazyListState(), + modifier = Modifier.onGloballyPositioned { + + } ) { items(internSearchResultData.size) { index -> SearchResultInternItem( @@ -331,44 +343,43 @@ fun SearchProcessScreen( color = Grey400, ) } - } } } + } - if (state.sheetState) { - SortingBottomSheet( - currentSortBy = state.currentSortBy, - onDismiss = onDismissSheet, - newSortBy = currentSortBy, - onSortChange = onSortChange - ) - } + if (state.sheetState) { + SortingBottomSheet( + currentSortBy = state.currentSortBy, + onDismiss = onDismissSheet, + newSortBy = currentSortBy, + onSortChange = onSortChange + ) + } - if (state.isScrapDialogVisible) { - val searchResult = state.searchResult - if (searchResult.isScrapped) { - ScrapCancelDialog( - internshipAnnouncementId = searchResult.internshipAnnouncementId, - onDismissRequest = { isScrapped -> - onDismissCancelDialog(isScrapped, searchResult) - } - ) - } else { - ScrapDialog( - title = searchResult.title, - scrapColor = CalRed, - deadline = searchResult.deadline, - startYearMonth = searchResult.startYearMonth, - workingPeriod = searchResult.workingPeriod, - internshipAnnouncementId = searchResult.internshipAnnouncementId, - companyImage = searchResult.companyImage, - isScrapped = false, - onDismissRequest = { isScrapped -> - onDismissScrapDialog(isScrapped, searchResult) - } - ) - } + if (state.isScrapDialogVisible) { + val searchResult = state.searchResult + if (searchResult.isScrapped) { + ScrapCancelDialog( + internshipAnnouncementId = searchResult.internshipAnnouncementId, + onDismissRequest = { isScrapped -> + onDismissCancelDialog(isScrapped, searchResult) + } + ) + } else { + ScrapDialog( + title = searchResult.title, + scrapColor = CalRed, + deadline = searchResult.deadline, + startYearMonth = searchResult.startYearMonth, + workingPeriod = searchResult.workingPeriod, + internshipAnnouncementId = searchResult.internshipAnnouncementId, + companyImage = searchResult.companyImage, + isScrapped = false, + onDismissRequest = { isScrapped -> + onDismissScrapDialog(isScrapped, searchResult) + } + ) } } } diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt index 4cad3737..39cf68c9 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt @@ -7,6 +7,7 @@ import androidx.paging.cachedIn import androidx.paging.map import com.terning.core.designsystem.type.SortBy import com.terning.domain.search.entity.SearchResult +import com.terning.domain.search.repository.SearchRepository import com.terning.feature.search.searchprocess.models.SearchProcessState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -27,7 +28,7 @@ import javax.inject.Inject @HiltViewModel class SearchProcessViewModel @Inject constructor( - private val searchRepository: com.terning.domain.search.repository.SearchRepository, + private val searchRepository: SearchRepository, ) : ViewModel() { private val _state = MutableStateFlow(SearchProcessState()) @@ -84,6 +85,7 @@ class SearchProcessViewModel @Inject constructor( deadline: String, startYearMonth: String, color: String?, + totalCount: Int, ) { _state.update { it.copy( @@ -97,7 +99,7 @@ class SearchProcessViewModel @Inject constructor( deadline = deadline, startYearMonth = startYearMonth, color = color ?: "", - totalCount = 0 + totalCount = totalCount ) ) } From e5e8b3fd72e54737819bd8dc988b46dae9272389 Mon Sep 17 00:00:00 2001 From: arinming Date: Sat, 11 Jan 2025 21:19:44 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[FEAT/#328]=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/pagingsource/SearchPagingSource.kt | 5 +++-- .../repositoryimpl/SearchRepositoryImpl.kt | 2 +- .../searchprocess/SearchProcessRoute.kt | 22 +++++-------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt index fe4fc6d9..78222531 100644 --- a/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt +++ b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt @@ -27,12 +27,14 @@ class SearchPagingSource( val totalCount = response.result.totalCount val hasNextPage = response.result.hasNext + val nextKey = if (hasNextPage) nextParamKey + 1 else null + LoadResult.Page( data = response.result.announcements.map { Pair(totalCount, it) }, prevKey = null, - nextKey = if (hasNextPage) nextParamKey + 1 else null + nextKey = nextKey ) } catch (e: Exception) { LoadResult.Error(e) @@ -45,5 +47,4 @@ class SearchPagingSource( anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) } } - } \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt index eae73cf8..0e683ea3 100644 --- a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt +++ b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt @@ -26,7 +26,7 @@ class SearchRepositoryImpl @Inject constructor( sortBy: String, ): Flow> { return Pager( - PagingConfig(pageSize = 20) + PagingConfig(pageSize = 100) ) { SearchPagingSource( query = query, diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index 45fc5344..69246583 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -22,7 +21,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource @@ -34,9 +32,9 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import androidx.navigation.NavHostController -import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.terning.core.analytics.EventType +import com.terning.core.analytics.LocalTracker import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet import com.terning.core.designsystem.component.button.SortingButton import com.terning.core.designsystem.component.item.InternItemWithShadow @@ -78,7 +76,7 @@ fun SearchProcessRoute( 0 } } - + LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { sideEffect -> @@ -150,11 +148,6 @@ fun SearchProcessRoute( onSortChange = { viewModel.updateSortBy(it) }, - onEndOfListReached = { - if (searchResultList.loadState.append is LoadState.NotLoading) { - searchResultList.retry() - } - } ) } @@ -174,13 +167,12 @@ fun SearchProcessScreen( onDismissSheet: () -> Unit = {}, onScrapButtonClicked: (SearchResult) -> Unit, onSortChange: (Int) -> Unit = {}, - onEndOfListReached: () -> Unit = {}, ) { val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current val currentSortBy = remember { mutableIntStateOf(state.currentSortBy) } - val amplitudeTracker = com.terning.core.analytics.LocalTracker.current + val amplitudeTracker = LocalTracker.current LaunchedEffect(Unit) { focusRequester.requestFocus() @@ -275,12 +267,9 @@ fun SearchProcessScreen( top = 12.dp, bottom = 20.dp, ), + modifier = Modifier + .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp), - reverseLayout = false, - state = rememberLazyListState(), - modifier = Modifier.onGloballyPositioned { - - } ) { items(internSearchResultData.size) { index -> SearchResultInternItem( @@ -384,7 +373,6 @@ fun SearchProcessScreen( } } - @Composable private fun SearchResultInternItem( intern: SearchResult, From a88bca6dc0f726372f8f45d0d567325692d28478 Mon Sep 17 00:00:00 2001 From: arinming Date: Sun, 12 Jan 2025 15:53:55 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[FIX/#328]=20gradle=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/search/build.gradle.kts | 2 ++ domain/search/build.gradle.kts | 2 -- gradle/libs.versions.toml | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/data/search/build.gradle.kts b/data/search/build.gradle.kts index 9c130ee0..e35b48c8 100644 --- a/data/search/build.gradle.kts +++ b/data/search/build.gradle.kts @@ -11,5 +11,7 @@ android { dependencies { // domain implementation(projects.domain.search) + + // paging implementation(libs.androidx.paging.common.android) } \ No newline at end of file diff --git a/domain/search/build.gradle.kts b/domain/search/build.gradle.kts index 8c7175e6..bb560997 100644 --- a/domain/search/build.gradle.kts +++ b/domain/search/build.gradle.kts @@ -4,6 +4,4 @@ plugins { dependencies { implementation(libs.paging.common) - implementation(libs.coroutines.core) - implementation(libs.coroutines.test) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b7d6ad7..5a492139 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -98,8 +98,6 @@ amplitude = "1.17.3" ## Paging paging = "3.3.2" pagingCommonAndroid = "3.3.5" -constraintlayoutCore = "1.1.0" -constraintlayout = "2.2.0" [libraries] @@ -187,8 +185,6 @@ paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.r paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "paging" } paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" } androidx-paging-common-android = { group = "androidx.paging", name = "paging-common-android", version.ref = "pagingCommonAndroid" } -androidx-constraintlayout-core = { group = "androidx.constraintlayout", name = "constraintlayout-core", version.ref = "constraintlayoutCore" } -androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } [bundles] retrofit =[ From d5cbc53259bea084aaffa05d0859ebaf568f9d79 Mon Sep 17 00:00:00 2001 From: arinming Date: Sun, 12 Jan 2025 17:30:11 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[FEAT/#328]=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=20=EA=B0=9C=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/search/datasourceimpl/SearchDataSourceImpl.kt | 1 - .../com/terning/data/search/dto/request/SearchRequestDto.kt | 2 -- .../terning/data/search/pagingsource/SearchPagingSource.kt | 5 ++--- .../data/search/repositoryimpl/SearchRepositoryImpl.kt | 5 ++++- .../feature/search/searchprocess/SearchProcessViewModel.kt | 1 - 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/data/search/src/main/java/com/terning/data/search/datasourceimpl/SearchDataSourceImpl.kt b/data/search/src/main/java/com/terning/data/search/datasourceimpl/SearchDataSourceImpl.kt index 9b4136bb..399460e5 100644 --- a/data/search/src/main/java/com/terning/data/search/datasourceimpl/SearchDataSourceImpl.kt +++ b/data/search/src/main/java/com/terning/data/search/datasourceimpl/SearchDataSourceImpl.kt @@ -19,7 +19,6 @@ class SearchDataSourceImpl @Inject constructor( request.keyword, request.sortBy, request.page, - request.size ) override suspend fun getSearchViews(): BaseResponse = diff --git a/data/search/src/main/java/com/terning/data/search/dto/request/SearchRequestDto.kt b/data/search/src/main/java/com/terning/data/search/dto/request/SearchRequestDto.kt index 6df6bcbc..d84a8f08 100644 --- a/data/search/src/main/java/com/terning/data/search/dto/request/SearchRequestDto.kt +++ b/data/search/src/main/java/com/terning/data/search/dto/request/SearchRequestDto.kt @@ -11,6 +11,4 @@ data class SearchRequestDto( val sortBy: String, @SerialName("page") val page: Int, - @SerialName("size") - val size: Int, ) \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt index 78222531..867c7bf1 100644 --- a/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt +++ b/data/search/src/main/java/com/terning/data/search/pagingsource/SearchPagingSource.kt @@ -21,7 +21,6 @@ class SearchPagingSource( keyword = query, sortBy = sortBy, page = nextParamKey, - size = params.loadSize ) ) val totalCount = response.result.totalCount @@ -43,8 +42,8 @@ class SearchPagingSource( override fun getRefreshKey(state: PagingState>): Int? { return state.anchorPosition?.let { anchorPosition -> - val anchorPage = state.closestPageToPosition(anchorPosition) - anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) + ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) } } } \ No newline at end of file diff --git a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt index 0e683ea3..d8c122d6 100644 --- a/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt +++ b/data/search/src/main/java/com/terning/data/search/repositoryimpl/SearchRepositoryImpl.kt @@ -26,7 +26,10 @@ class SearchRepositoryImpl @Inject constructor( sortBy: String, ): Flow> { return Pager( - PagingConfig(pageSize = 100) + PagingConfig( + pageSize = 10, + enablePlaceholders = false + ) ) { SearchPagingSource( query = query, diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt index 39cf68c9..374b47a8 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt @@ -74,7 +74,6 @@ class SearchProcessViewModel @Inject constructor( ).cachedIn(viewModelScope) } - fun updateSearchResult( internshipId: Long, title: String, From 5ffc166575ad12810424d31c36ddbe2627cace4a Mon Sep 17 00:00:00 2001 From: arinming Date: Mon, 13 Jan 2025 16:01:25 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[FEAT/#328]=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../searchprocess/SearchProcessRoute.kt | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index 69246583..6b6f384b 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -1,5 +1,6 @@ package com.terning.feature.search.searchprocess +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -58,25 +59,15 @@ import com.terning.feature.search.searchprocess.models.SearchProcessState @Composable fun SearchProcessRoute( paddingValues: PaddingValues, + viewModel: SearchProcessViewModel = hiltViewModel(), navController: NavHostController, navigateIntern: (Long) -> Unit, - viewModel: SearchProcessViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current - val searchResultList = viewModel.internSearchResultFlow.collectAsLazyPagingItems() - - val searchResultTotal = remember(searchResultList.loadState.refresh) { - if (searchResultList.itemCount > 0) { - searchResultList[0]?.totalCount ?: 0 - } else { - 0 - } - } - LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { sideEffect -> @@ -99,8 +90,6 @@ fun SearchProcessRoute( navigateToIntern = { viewModel.navigateIntern(it) }, navigateToBack = { navController.navigateUp() }, state = state, - internSearchResultData = searchResultList.itemSnapshotList.items, - searchResultTotal = searchResultTotal, updateText = { viewModel.updateText(it) }, @@ -142,23 +131,23 @@ fun SearchProcessRoute( companyImage = it.companyImage, isScrapped = it.isScrapped, color = it.color, - totalCount = searchResultTotal + totalCount = it.totalCount ) }, onSortChange = { viewModel.updateSortBy(it) }, + viewModel = viewModel ) } +@OptIn(ExperimentalFoundationApi::class) @Composable fun SearchProcessScreen( paddingValues: PaddingValues, navigateToIntern: (Long) -> Unit, navigateToBack: () -> Unit, state: SearchProcessState = SearchProcessState(), - internSearchResultData: List = emptyList(), - searchResultTotal: Int = 0, updateText: (String) -> Unit = {}, onSearchAction: () -> Unit = {}, onSortButtonClick: () -> Unit = {}, @@ -167,6 +156,7 @@ fun SearchProcessScreen( onDismissSheet: () -> Unit = {}, onScrapButtonClicked: (SearchResult) -> Unit, onSortChange: (Int) -> Unit = {}, + viewModel: SearchProcessViewModel, ) { val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current @@ -174,6 +164,16 @@ fun SearchProcessScreen( val amplitudeTracker = LocalTracker.current + val searchResultList = viewModel.internSearchResultFlow.collectAsLazyPagingItems() + + val searchResultTotal = remember(searchResultList.loadState.refresh) { + if (searchResultList.itemCount > 0) { + searchResultList[0]?.totalCount ?: 0 + } else { + 0 + } + } + LaunchedEffect(Unit) { focusRequester.requestFocus() } @@ -261,7 +261,7 @@ fun SearchProcessScreen( } } - if (internSearchResultData.isNotEmpty()) { + if (searchResultList.itemCount > 0) { LazyColumn( contentPadding = PaddingValues( top = 12.dp, @@ -271,20 +271,22 @@ fun SearchProcessScreen( .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - items(internSearchResultData.size) { index -> - SearchResultInternItem( - intern = internSearchResultData[index], - navigateToIntern = navigateToIntern, - onScrapButtonClicked = { - amplitudeTracker.track( - type = EventType.CLICK, - name = "quest_scrap" - ) - with(internSearchResultData[index]) { - onScrapButtonClicked(this) + items(searchResultList.itemCount, key = { it }) { index -> + searchResultList[index]?.run { + SearchResultInternItem( + intern = searchResultList.itemSnapshotList.items[index], + navigateToIntern = navigateToIntern, + onScrapButtonClicked = { + amplitudeTracker.track( + type = EventType.CLICK, + name = "quest_scrap" + ) + with(searchResultList.itemSnapshotList.items[index]) { + onScrapButtonClicked(this) + } } - } - ) + ) + } } } } else { From 08689a6b4b0a6b214c5399c398755af2a0783af4 Mon Sep 17 00:00:00 2001 From: arinming Date: Mon, 13 Jan 2025 16:02:15 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[DEL/#328]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terning/feature/search/searchprocess/SearchProcessRoute.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index 6b6f384b..f88a8571 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -1,6 +1,5 @@ package com.terning.feature.search.searchprocess -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -141,7 +140,6 @@ fun SearchProcessRoute( ) } -@OptIn(ExperimentalFoundationApi::class) @Composable fun SearchProcessScreen( paddingValues: PaddingValues, From 4e3ab37c8e761a8784cafec5214c7f358cac765a Mon Sep 17 00:00:00 2001 From: arinming Date: Mon, 13 Jan 2025 18:06:19 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[FEAT/#328]=20=ED=82=A4=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=20=ED=8F=AC=EC=BB=A4=EC=8B=B1=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/searchprocess/SearchProcessRoute.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index f88a8571..521b2433 100644 --- a/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/search/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -16,7 +16,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -159,6 +162,7 @@ fun SearchProcessScreen( val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current val currentSortBy = remember { mutableIntStateOf(state.currentSortBy) } + var isInitialFocusSet by rememberSaveable { mutableStateOf(false) } val amplitudeTracker = LocalTracker.current @@ -172,10 +176,15 @@ fun SearchProcessScreen( } } + LaunchedEffect(Unit) { - focusRequester.requestFocus() + if (!isInitialFocusSet) { + focusRequester.requestFocus() + isInitialFocusSet = true + } } + Column( modifier = Modifier .background(White)