Skip to content

Commit

Permalink
Migrate search screen to Compose
Browse files Browse the repository at this point in the history
  • Loading branch information
SIKV committed Jul 6, 2024
1 parent 1ae9856 commit b3dbaea
Show file tree
Hide file tree
Showing 13 changed files with 451 additions and 466 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.sikv.photos.compose.ui

import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource

@Composable
fun BackAction(
onBackClick: () -> Unit
) {
IconButton(
onClick = onBackClick
) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_back_24dp),
contentDescription = stringResource(id = R.string.navigate_back)
)
}
}
1 change: 1 addition & 0 deletions compose-ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
<string name="download">Download</string>
<string name="more">More</string>
<string name="switch_layout">Switch layout</string>
<string name="navigate_back">Navigate back</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class ConfigProvider @Inject constructor(
private val featureFlagProvider: FeatureFlagProvider
) {

fun getSearchSources(): Set<PhotoSource> {
val sources = mutableSetOf<PhotoSource>()
fun getSearchSources(): List<PhotoSource> {
val sources = mutableListOf<PhotoSource>()

if (featureFlagProvider.isFeatureEnabled(FeatureFlag.SEARCH_SOURCE_PEXELS)) {
sources.add(PhotoSource.PEXELS)
Expand Down
3 changes: 3 additions & 0 deletions feature/search/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies {
implementation project(':compose-ui')
implementation project(':navigation')
implementation project(':photo-list-ui')
implementation project(':photo-usecase')
implementation project(':feature:recommendations')

implementation libs.material
Expand All @@ -35,11 +36,13 @@ dependencies {
implementation libs.androidx.compose.material3
implementation libs.accompanist.themeadapter.material3
implementation libs.androidx.lifecycle.viewmodel.compose
implementation libs.androidx.lifecycle.runtime.compose

implementation libs.inject
kapt libs.hilt.compiler
implementation libs.hilt.android
implementation libs.androidx.hilt.navigation.compose

implementation libs.androidx.paging.runtime
implementation libs.androidx.paging.compose
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.sikv.photos.search

import androidx.paging.PagingData
import com.github.sikv.photos.domain.ListLayout
import com.github.sikv.photos.domain.Photo
import com.github.sikv.photos.domain.PhotoSource
import kotlinx.coroutines.flow.Flow

internal data class SearchUiState(
val query: String? = null,
val photoSources: List<PhotoSource> = emptyList(),
val photos: Map<PhotoSource, Flow<PagingData<Photo>>?> = emptyMap(),
val listLayout: ListLayout = ListLayout.GRID
)
Original file line number Diff line number Diff line change
@@ -1,65 +1,108 @@
package com.github.sikv.photos.search

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.github.sikv.photos.config.ConfigProvider
import com.github.sikv.photos.data.repository.FavoritesRepository
import com.github.sikv.photos.data.repository.FavoritesRepository2
import com.github.sikv.photos.data.repository.PhotosRepository
import com.github.sikv.photos.domain.ListLayout
import com.github.sikv.photos.domain.Photo
import com.github.sikv.photos.domain.PhotoSource
import com.github.sikv.photos.navigation.args.SearchFragmentArguments
import com.github.sikv.photos.navigation.args.fragmentArguments
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
internal class SearchViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val photosRepository: PhotosRepository,
private val favoritesRepository: FavoritesRepository,
private val favoritesRepository: FavoritesRepository2,
private val configProvider: ConfigProvider
) : ViewModel() {

private val mutableSearchQueryState = MutableStateFlow<SearchQuery?>(null)
val searchQueryState: StateFlow<SearchQuery?> = mutableSearchQueryState
private val mutableUiState = MutableStateFlow(SearchUiState())
val uiState: StateFlow<SearchUiState> = mutableUiState

fun favoriteUpdates(): Flow<FavoritesRepository.Update> {
return favoritesRepository.favoriteUpdates()
init {
val args = savedStateHandle.fragmentArguments<SearchFragmentArguments>()

mutableUiState.update { state ->
state.copy(
query = args.query,
photoSources = configProvider.getSearchSources()
)
}
}

fun isFavorite(photo: Photo): Flow<Boolean> {
return favoritesRepository.getFavorites()
.map { photos ->
photos.contains(photo)
}
}

fun toggleFavorite(photo: Photo) {
favoritesRepository.invertFavorite(photo)
viewModelScope.launch {
favoritesRepository.invertFavorite(photo)
}
}

fun requestSearch(text: String) {
mutableSearchQueryState.update {
SearchQuery(query = text)
fun switchListLayout() {
val listLayout = uiState.value.listLayout

val switchedListLayout = when (listLayout) {
ListLayout.LIST -> ListLayout.GRID
ListLayout.GRID -> ListLayout.LIST
}

mutableUiState.update { state ->
state.copy(listLayout = switchedListLayout)
}
}

fun clearSearch() {
mutableSearchQueryState.update {
null
fun onSearchQueryChange(text: String) {
mutableUiState.update { state ->
state.copy(query = text)
}
}

fun searchPhotos(photoSource: PhotoSource, searchQuery: SearchQuery): Flow<PagingData<Photo>>? {
val queryTrimmed = searchQuery.query.trim()
fun performSearch() {
val query = uiState.value.query?.trim().orEmpty()

if (query.isEmpty()) {
return
}

val mutableSearchFlows = uiState.value.photos.toMutableMap()

if (queryTrimmed.isEmpty()) {
return null
uiState.value.photoSources.forEach { photoSource ->
mutableSearchFlows[photoSource] = searchPhotos(query, photoSource)
}

mutableUiState.update { state ->
state.copy(photos = mutableSearchFlows)
}
}

private fun searchPhotos(query: String, photoSource: PhotoSource): Flow<PagingData<Photo>> {
return Pager(
config = configProvider.getPagingConfig(),
pagingSourceFactory = {
SearchPhotosPagingSource(
photosRepository,
photoSource,
queryTrimmed
query
)
}
).flow
Expand Down
Loading

0 comments on commit b3dbaea

Please sign in to comment.