@@ -10,20 +10,27 @@ import androidx.compose.foundation.layout.fillMaxSize
1010import androidx.compose.foundation.layout.fillMaxWidth
1111import androidx.compose.foundation.layout.height
1212import androidx.compose.foundation.layout.padding
13+ import androidx.compose.foundation.layout.wrapContentSize
1314import androidx.compose.foundation.lazy.LazyColumn
1415import androidx.compose.foundation.lazy.items
1516import androidx.compose.foundation.lazy.rememberLazyListState
1617import androidx.compose.foundation.shape.CircleShape
1718import androidx.compose.material.icons.Icons
1819import androidx.compose.material.icons.filled.Add
20+ import androidx.compose.material.icons.filled.Delete
21+ import androidx.compose.material.icons.filled.Search
22+ import androidx.compose.material3.ExperimentalMaterial3Api
1923import androidx.compose.material3.FloatingActionButton
2024import androidx.compose.material3.Icon
2125import androidx.compose.material3.MaterialTheme
2226import androidx.compose.material3.SnackbarHostState
2327import androidx.compose.material3.Surface
28+ import androidx.compose.material3.SwipeToDismissBox
29+ import androidx.compose.material3.SwipeToDismissBoxValue
2430import androidx.compose.material3.Text
2531import androidx.compose.material3.TextField
2632import androidx.compose.material3.TextFieldDefaults
33+ import androidx.compose.material3.rememberSwipeToDismissBoxState
2734import androidx.compose.runtime.Composable
2835import androidx.compose.runtime.LaunchedEffect
2936import androidx.compose.runtime.collectAsState
@@ -36,9 +43,11 @@ import androidx.compose.ui.Alignment
3643import androidx.compose.ui.Modifier
3744import androidx.compose.ui.graphics.Brush
3845import androidx.compose.ui.graphics.Color
46+ import androidx.compose.ui.res.stringResource
3947import androidx.compose.ui.text.input.TextFieldValue
4048import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
4149import org.mrlem.android.core.feature.ui.UiModePreviews
50+ import org.mrlem.composesample.features.library.ui.R
4251import org.mrlem.composesample.theme.Theme
4352
4453@Composable
@@ -48,6 +57,7 @@ internal fun ListScreen(
4857 onItemSelect : (id: Long ) -> Unit ,
4958) {
5059 val state by viewModel.state.collectAsState()
60+ val errorMessage = stringResource(R .string.library_error)
5161
5262 LaunchedEffect (Unit ) {
5363 viewModel.effects
@@ -57,7 +67,7 @@ internal fun ListScreen(
5767 onItemSelect(effect.id)
5868
5969 is ListViewEffect .ShowError ->
60- snackbarHostState.showSnackbar(" Failed to retrieve data " )
70+ snackbarHostState.showSnackbar(errorMessage )
6171 }
6272 }
6373 }
@@ -76,8 +86,11 @@ internal fun ListScreen(
7686private fun ListScreen (
7787 state : ListViewState ,
7888 onAction : (ListViewAction ) -> Unit ,
89+ modifier : Modifier = Modifier ,
7990) {
80- Column {
91+ Column (
92+ modifier = modifier,
93+ ) {
8194 var fieldValue by remember {
8295 mutableStateOf(TextFieldValue (state.filter))
8396 }
@@ -96,7 +109,12 @@ private fun ListScreen(
96109 disabledIndicatorColor = Color .Transparent ,
97110 errorIndicatorColor = Color .Transparent ,
98111 ),
99- placeholder = { Text (" Filter articles" ) },
112+ placeholder = {
113+ Text (
114+ text = stringResource(id = R .string.library_search_action),
115+ )
116+ },
117+ trailingIcon = { Icon (imageVector = Icons .Default .Search , contentDescription = null ) },
100118 modifier = Modifier
101119 .fillMaxWidth()
102120 .padding(
@@ -115,6 +133,7 @@ private fun ListScreen(
115133 }
116134}
117135
136+ @OptIn(ExperimentalMaterial3Api ::class )
118137@Composable
119138private fun List (
120139 state : ListViewState ,
@@ -139,11 +158,51 @@ private fun List(
139158 modifier = Modifier
140159 .fillMaxSize(),
141160 ) {
142- items(state.items) {
143- ListItem (
144- viewState = it,
145- onAction = onAction,
161+ items(
162+ items = state.items,
163+ key = { item -> (item.onClickAction as ListViewAction .ItemClick ).itemId },
164+ ) { item ->
165+ val dismissState = rememberSwipeToDismissBoxState(
166+ confirmValueChange = { dismissValue ->
167+ if (dismissValue == SwipeToDismissBoxValue .EndToStart ) {
168+ val action = item.onClickAction as ListViewAction .ItemClick
169+ onAction(ListViewAction .ItemDismiss (action.itemId))
170+ true
171+ } else {
172+ false
173+ }
174+ },
146175 )
176+
177+ SwipeToDismissBox (
178+ state = dismissState,
179+ enableDismissFromStartToEnd = false ,
180+ backgroundContent = {
181+ when (dismissState.dismissDirection) {
182+ SwipeToDismissBoxValue .EndToStart ->
183+ Icon (
184+ imageVector = Icons .Default .Delete ,
185+ contentDescription = stringResource(R .string.library_remove_action),
186+ tint = MaterialTheme .colorScheme.onErrorContainer,
187+ modifier = Modifier
188+ .fillMaxSize()
189+ .background(MaterialTheme .colorScheme.errorContainer)
190+ .wrapContentSize(Alignment .CenterEnd )
191+ .padding(Theme .size.small),
192+ )
193+
194+ else -> {}
195+ }
196+ },
197+ ) {
198+ ListItem (
199+ viewState = item,
200+ onAction = onAction,
201+ modifier = Modifier
202+ .fillMaxSize()
203+ .background(MaterialTheme .colorScheme.background),
204+ )
205+ }
147206 }
148207 }
149208
@@ -176,7 +235,7 @@ private fun List(
176235 .padding(Theme .size.medium)
177236 .align(Alignment .BottomEnd ),
178237 ) {
179- Icon (imageVector = Icons .Filled .Add , contentDescription = " Import random bookmark " )
238+ Icon (imageVector = Icons .Filled .Add , contentDescription = stringResource( R .string.library_import_action) )
180239 }
181240 }
182241}
@@ -191,17 +250,20 @@ private fun Preview() {
191250 items = listOf (
192251 ListItemViewState (
193252 label = " Georges Brassens" ,
253+ onClickAction = ListViewAction .ItemClick (1L ),
194254 ),
195255 ListItemViewState (
196256 label = " Jacques Brel" ,
257+ onClickAction = ListViewAction .ItemClick (2L ),
197258 ),
198259 ListItemViewState (
199260 label = " Joe Dassin" ,
261+ onClickAction = ListViewAction .ItemClick (3L ),
200262 ),
201263 ),
202264 ),
203265 onAction = {},
204266 )
205267 }
206268 }
207- }
269+ }
0 commit comments