Skip to content

Commit

Permalink
Feature/add catalog search book screen UI (#25)
Browse files Browse the repository at this point in the history
* feat(mobile-app): Updated catalog search headline composable ui
* feat(mobile-app): Updated catalog search slots composable ui
* feat(mobile-app): Updated catalog search route ui content for book mode
  • Loading branch information
marlonlom authored Mar 18, 2024
1 parent 2d6ee72 commit 2232c43
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,44 @@
package dev.marlonlom.apps.cappajv.features.catalog_search

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.marlonlom.apps.cappajv.features.catalog_search.parts.CatalogSearchHeadline
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchInputSlot
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchResultsSlot
import dev.marlonlom.apps.cappajv.features.catalog_search.screens.CatalogSearchRouteScreen
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState
import org.koin.androidx.compose.koinViewModel
import timber.log.Timber

/**
* Catalog search route composable ui.
*
* @author marlonlom
*
* @param appState Application ui state.
* @param viewModel Catalog search viewmodel.
*/
@ExperimentalFoundationApi
@Composable
fun CatalogSearchRoute(
appState: CappajvAppState,
viewModel: CatalogSearchViewModel = koinViewModel(),
) {
val contentHorizontalPadding = when {
appState.isLandscape.not().and(appState.isMediumWidth) -> 40.dp
appState.isLandscape.not().and(appState.isExpandedWidth) -> 80.dp
else -> 20.dp
}

val queryText = rememberSaveable { viewModel.queryText }
val showClearIcon = remember {
derivedStateOf { viewModel.queryText.value.isNotEmpty() }
}

val searchResultState by viewModel.searchResult.collectAsStateWithLifecycle()

Column(
modifier = Modifier
.fillMaxWidth()
.padding(contentHorizontalPadding)
) {
CatalogSearchHeadline(appState)
CatalogSearchInputSlot(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = viewModel::onQueryTextChanged,
)
CatalogSearchResultsSlot(
appState = appState,
searchResultUiState = searchResultState,
onSearchedItemClicked = {
Timber.d("[CatalogSearchRoute] clicked item[$it] ")
},
)
}
CatalogSearchRouteScreen(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = viewModel::onQueryTextChanged,
searchResultUiState = searchResultState,
onSearchedItemClicked = {
Timber.d("[CatalogSearchRoute] clicked item[$it] ")
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import dev.marlonlom.apps.cappajv.R
import dev.marlonlom.apps.cappajv.ui.layout.DevicePosture
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState

/**
Expand All @@ -40,6 +41,12 @@ fun CatalogSearchHeadline(
else -> MaterialTheme.typography.headlineMedium
}

val maxTitleLines = when {
appState.isLandscape.and(appState.devicePosture == DevicePosture.Normal) -> 2
appState.isLandscape.and(appState.devicePosture is DevicePosture.Separating.Book) -> 2
else -> 1
}

Row(
modifier = modifier
.background(MaterialTheme.colorScheme.surface)
Expand All @@ -53,7 +60,7 @@ fun CatalogSearchHeadline(
text = stringResource(R.string.text_catalog_search_title),
style = titleTextStyle,
fontWeight = FontWeight.Bold,
maxLines = 1
maxLines = maxTitleLines
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2024 Marlonlom
* SPDX-License-Identifier: Apache-2.0
*/

package dev.marlonlom.apps.cappajv.features.catalog_search.screens

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import dev.marlonlom.apps.cappajv.features.catalog_search.CatalogSearchUiState
import dev.marlonlom.apps.cappajv.ui.layout.DevicePosture
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState
import dev.marlonlom.apps.cappajv.ui.navigation.NavigationType

/**
* Catalog search route screen content composable ui.
*
* @author marlonlom
*
* @param appState Application ui state.
* @param queryText Query text for searching.
* @param showClearIcon True/False if query text should be cleared.
* @param onSearchReady Action for query text ready for search.
* @param searchResultUiState Catalog search results ui state.
* @param onSearchedItemClicked Action for searched item clicked.
*/
@ExperimentalFoundationApi
@Composable
fun CatalogSearchRouteScreen(
appState: CappajvAppState,
queryText: MutableState<String>,
showClearIcon: State<Boolean>,
onSearchReady: () -> Unit,
searchResultUiState: CatalogSearchUiState,
onSearchedItemClicked: (Long) -> Unit,
) {
when {
appState.isLandscape
.and(appState.devicePosture is DevicePosture.Separating.Book)
.and(appState.navigationType == NavigationType.NAVIGATION_RAIL) -> {
LandscapeTwoPaneCatalogSearchScreen(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = onSearchReady,
searchResultUiState = searchResultUiState,
onSearchedItemClicked = onSearchedItemClicked
)
}

appState.isLandscape
.and(appState.devicePosture == DevicePosture.Normal)
.and(appState.navigationType == NavigationType.NAVIGATION_RAIL) -> {
LandscapeTwoPaneCatalogSearchScreen(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = onSearchReady,
searchResultUiState = searchResultUiState,
onSearchedItemClicked = onSearchedItemClicked
)
}

else -> {
DefaultPortraitCatalogSearchScreen(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = onSearchReady,
searchResultUiState = searchResultUiState,
onSearchedItemClicked = onSearchedItemClicked
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2024 Marlonlom
* SPDX-License-Identifier: Apache-2.0
*/

package dev.marlonlom.apps.cappajv.features.catalog_search.screens

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.marlonlom.apps.cappajv.features.catalog_search.CatalogSearchUiState
import dev.marlonlom.apps.cappajv.features.catalog_search.parts.CatalogSearchHeadline
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchInputSlot
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchResultsSlot
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState

/**
* Default portrait catalog search screen composable ui.
*
* @author marlonlom
*
* @param appState Application ui state.
* @param queryText Query text for searching.
* @param showClearIcon True/False if query text should be cleared.
* @param onSearchReady Action for query text ready for search.
* @param searchResultUiState Catalog search results ui state.
* @param onSearchedItemClicked Action for searched item clicked.
*/
@ExperimentalFoundationApi
@Composable
internal fun DefaultPortraitCatalogSearchScreen(
appState: CappajvAppState,
queryText: MutableState<String>,
showClearIcon: State<Boolean>,
onSearchReady: () -> Unit,
searchResultUiState: CatalogSearchUiState,
onSearchedItemClicked: (Long) -> Unit
) {
val contentHorizontalPadding = when {
appState.isLandscape.not().and(appState.isMediumWidth) -> 40.dp
appState.isLandscape.not().and(appState.isExpandedWidth) -> 80.dp
else -> 20.dp
}

Column(
modifier = Modifier
.fillMaxWidth()
.padding(contentHorizontalPadding)
) {
CatalogSearchHeadline(appState)
CatalogSearchInputSlot(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = onSearchReady,
)
CatalogSearchResultsSlot(
appState = appState,
searchResultUiState = searchResultUiState,
onSearchedItemClicked = onSearchedItemClicked,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2024 Marlonlom
* SPDX-License-Identifier: Apache-2.0
*/

package dev.marlonlom.apps.cappajv.features.catalog_search.screens

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.marlonlom.apps.cappajv.features.catalog_search.CatalogSearchUiState
import dev.marlonlom.apps.cappajv.features.catalog_search.parts.CatalogSearchHeadline
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchInputSlot
import dev.marlonlom.apps.cappajv.features.catalog_search.slots.CatalogSearchResultsSlot
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState

/**
* Landscape two-pane catalog search screen composable ui.
*
* @author marlonlom
*
* @param appState Application ui state.
* @param queryText Query text for searching.
* @param showClearIcon True/False if query text should be cleared.
* @param onSearchReady Action for query text ready for search.
* @param searchResultUiState Catalog search results ui state.
* @param onSearchedItemClicked Action for searched item clicked.
*/
@ExperimentalFoundationApi
@Composable
internal fun LandscapeTwoPaneCatalogSearchScreen(
appState: CappajvAppState,
queryText: MutableState<String>,
showClearIcon: State<Boolean>,
onSearchReady: () -> Unit,
searchResultUiState: CatalogSearchUiState,
onSearchedItemClicked: (Long) -> Unit
) {
Row {
Column(
modifier = Modifier
.fillMaxWidth(0.45f)
.fillMaxHeight()
.padding(horizontal = 20.dp),
) {
CatalogSearchHeadline(appState)
CatalogSearchInputSlot(
appState = appState,
queryText = queryText,
showClearIcon = showClearIcon,
onSearchReady = onSearchReady,
)
CatalogSearchResultsSlot(
appState = appState,
searchResultUiState = searchResultUiState,
onSearchedItemClicked = onSearchedItemClicked,
)
}
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.primaryContainer)
.fillMaxSize()
.safeContentPadding(),
) {
Text(text = "Detail")
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import dev.marlonlom.apps.cappajv.R
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState

/**
* Catalog empty result slot composable ui.
Expand All @@ -33,8 +34,13 @@ import dev.marlonlom.apps.cappajv.R
*/
@Composable
fun CatalogEmptyResultsSlot(
appState: CappajvAppState,
modifier: Modifier = Modifier,
) {
val imageSize = when {
appState.isCompactHeight -> 72.dp
else -> 120.dp
}
Column(
modifier = modifier
.fillMaxSize()
Expand All @@ -45,7 +51,7 @@ fun CatalogEmptyResultsSlot(
Image(
painter = painterResource(R.drawable.img_catalog_search_empty),
contentDescription = null,
modifier = modifier.size(120.dp),
modifier = modifier.size(imageSize),
contentScale = ContentScale.FillBounds
)
Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable
import dev.marlonlom.apps.cappajv.features.catalog_search.CatalogSearchUiState
import dev.marlonlom.apps.cappajv.ui.main.CappajvAppState
import timber.log.Timber

/**
* Catalog search results slot composable ui.
Expand All @@ -27,11 +26,11 @@ fun CatalogSearchResultsSlot(
onSearchedItemClicked: (Long) -> Unit,
) {
when (searchResultUiState) {
CatalogSearchUiState.None -> CatalogSearchWelcomeSlot()
CatalogSearchUiState.None -> CatalogSearchWelcomeSlot(appState)

CatalogSearchUiState.Searching -> CatalogSearchingSlot()

CatalogSearchUiState.Empty -> CatalogEmptyResultsSlot()
CatalogSearchUiState.Empty -> CatalogEmptyResultsSlot(appState=appState)

is CatalogSearchUiState.Success -> CatalogSuccessResultsSlot(
searchResults = searchResultUiState.results,
Expand Down
Loading

0 comments on commit 2232c43

Please sign in to comment.