@@ -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,74 +133,134 @@ private fun ListScreen(
115133 }
116134}
117135
136+ @OptIn(ExperimentalMaterial3Api ::class )
118137@Composable
119138private fun List (
120139 state : ListViewState ,
121140 modifier : Modifier = Modifier ,
122141 onAction : (ListViewAction ) -> Unit = {},
123142) {
143+ val listState = rememberLazyListState()
144+ val showShadow by remember {
145+ derivedStateOf { listState.canScrollBackward }
146+ }
147+
124148 Box (
125149 modifier = modifier,
126150 ) {
127- val listState = rememberLazyListState()
128-
129- val showShadow by remember {
130- derivedStateOf { listState.canScrollBackward }
131- }
132-
133- Box (
134- modifier = Modifier
135- .fillMaxWidth(),
151+ LazyColumn (
152+ state = listState,
153+ modifier = Modifier .fillMaxSize(),
136154 ) {
137- LazyColumn (
138- state = listState,
139- modifier = Modifier
140- .fillMaxSize(),
141- ) {
142- items(state.items) {
155+ items(
156+ items = state.items,
157+ key = { item -> (item.onClickAction as ListViewAction .ItemClick ).itemId },
158+ ) { item ->
159+ val dismissState = rememberSwipeToDismissBoxState(
160+ confirmValueChange = { dismissValue ->
161+ if (dismissValue == SwipeToDismissBoxValue .EndToStart ) {
162+ val action = item.onClickAction as ListViewAction .ItemClick
163+ onAction(ListViewAction .ItemDismiss (action.itemId))
164+ true
165+ } else {
166+ false
167+ }
168+ },
169+ )
170+
171+ SwipeToDismissBox (
172+ state = dismissState,
173+ enableDismissFromStartToEnd = false ,
174+ backgroundContent = {
175+ if (dismissState.dismissDirection == SwipeToDismissBoxValue .EndToStart ) {
176+ RemoveIcon ()
177+ }
178+ },
179+ ) {
143180 ListItem (
144- viewState = it ,
181+ viewState = item ,
145182 onAction = onAction,
183+ modifier = Modifier
184+ .fillMaxSize()
185+ .background(MaterialTheme .colorScheme.background),
146186 )
147187 }
148188 }
149-
150- AnimatedVisibility (
151- visible = showShadow,
152- enter = fadeIn(),
153- exit = fadeOut(),
154- modifier = Modifier .align(Alignment .TopCenter ),
155- ) {
156- Box (
157- modifier = Modifier
158- .fillMaxWidth()
159- .height(Theme .size.medium)
160- .background(
161- brush = Brush .verticalGradient(
162- colors = listOf (
163- MaterialTheme .colorScheme.background,
164- Color .Transparent ,
165- ),
166- ),
167- ),
168- )
169- }
170189 }
171190
172- FloatingActionButton (
173- onClick = { onAction(ListViewAction .ImportRandomClick ) },
174- shape = CircleShape ,
191+ Shadow (
192+ visible = showShadow,
193+ modifier = Modifier
194+ .align(Alignment .TopCenter ),
195+ )
196+
197+ ImportButton (
198+ onAction = onAction,
175199 modifier = Modifier
176200 .padding(Theme .size.medium)
177201 .align(Alignment .BottomEnd ),
178- ) {
179- Icon (imageVector = Icons .Filled .Add , contentDescription = " Import random bookmark" )
180- }
202+ )
203+ }
204+ }
205+
206+ @Composable
207+ private fun ImportButton (
208+ onAction : (ListViewAction ) -> Unit ,
209+ modifier : Modifier = Modifier ,
210+ ) {
211+ FloatingActionButton (
212+ onClick = { onAction(ListViewAction .ImportRandomClick ) },
213+ shape = CircleShape ,
214+ modifier = modifier,
215+ ) {
216+ Icon (imageVector = Icons .Filled .Add , contentDescription = stringResource(R .string.library_import_action))
217+ }
218+ }
219+
220+ @Composable
221+ private fun RemoveIcon () {
222+ Icon (
223+ imageVector = Icons .Default .Delete ,
224+ contentDescription = stringResource(R .string.library_remove_action),
225+ tint = MaterialTheme .colorScheme.onErrorContainer,
226+ modifier = Modifier
227+ .fillMaxSize()
228+ .background(MaterialTheme .colorScheme.errorContainer)
229+ .wrapContentSize(Alignment .CenterEnd )
230+ .padding(Theme .size.small),
231+ )
232+ }
233+
234+ @Composable
235+ private fun Shadow (
236+ visible : Boolean ,
237+ modifier : Modifier = Modifier ,
238+ ) {
239+ AnimatedVisibility (
240+ visible = visible,
241+ enter = fadeIn(),
242+ exit = fadeOut(),
243+ modifier = modifier,
244+ ) {
245+ Box (
246+ modifier = Modifier
247+ .fillMaxWidth()
248+ .height(Theme .size.medium)
249+ .background(
250+ brush = Brush .verticalGradient(
251+ colors = listOf (
252+ MaterialTheme .colorScheme.background,
253+ Color .Transparent ,
254+ ),
255+ ),
256+ ),
257+ )
181258 }
182259}
183260
184261@UiModePreviews
185262@Composable
263+ @Suppress(" MagicNumber" )
186264private fun Preview () {
187265 Theme {
188266 Surface {
@@ -191,17 +269,20 @@ private fun Preview() {
191269 items = listOf (
192270 ListItemViewState (
193271 label = " Georges Brassens" ,
272+ onClickAction = ListViewAction .ItemClick (1L ),
194273 ),
195274 ListItemViewState (
196275 label = " Jacques Brel" ,
276+ onClickAction = ListViewAction .ItemClick (2L ),
197277 ),
198278 ListItemViewState (
199279 label = " Joe Dassin" ,
280+ onClickAction = ListViewAction .ItemClick (3L ),
200281 ),
201282 ),
202283 ),
203284 onAction = {},
204285 )
205286 }
206287 }
207- }
288+ }
0 commit comments