Skip to content

Commit

Permalink
Swipe to dismiss
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbel committed Mar 2, 2024
1 parent f473ebd commit 0d158a7
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.material.icons.filled.MovieFilter
import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.FileDownload
import androidx.compose.material.icons.outlined.Fingerprint
import androidx.compose.material.icons.outlined.GridView
Expand Down Expand Up @@ -46,6 +47,7 @@ object MoviesIcons {
val ArrowBack = Icons.AutoMirrored.Outlined.ArrowBack
val Check = Icons.Outlined.Check
val Close = Icons.Outlined.Close
val Delete = Icons.Outlined.Delete
val Info = Icons.Outlined.Info
val FileDownload = Icons.Outlined.FileDownload
val Fingerprint = Icons.Outlined.Fingerprint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ val AmoledColorScheme = darkColorScheme(
surfaceVariant = amoledSurface,
onSurfaceVariant = Color.White,
error = Color.White,
errorContainer = Color.Black,
onErrorContainer = Color.White,
outline = Color.White
)
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fun DetailsToolbar(
Text(
text = movieTitle,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
style = MaterialTheme.typography.titleLarge.copy(onContainerColor)
)
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.michaelbel.movies.search.ui

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -14,6 +15,9 @@ import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
Expand All @@ -23,6 +27,7 @@ import androidx.compose.ui.unit.dp
import org.michaelbel.movies.common.theme.AppTheme
import org.michaelbel.movies.persistence.database.entity.MovieDb
import org.michaelbel.movies.persistence.database.entity.SuggestionDb
import org.michaelbel.movies.search.ui.common.SwipeToDismiss
import org.michaelbel.movies.search_impl.R
import org.michaelbel.movies.ui.compose.iconbutton.BackIcon
import org.michaelbel.movies.ui.compose.iconbutton.CloseIcon
Expand Down Expand Up @@ -94,16 +99,26 @@ fun SearchToolbar(
.height(48.dp)
)

val movies by remember { mutableStateOf(searchHistoryMovies) }

LazyColumn {
items(searchHistoryMovies) { movie ->
SearchRecentResult(
text = movie.title,
onRemoveClick = { onHistoryMovieRemoveClick(movie.movieId) },
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.clickable { onInputText(movie.title) }
)
items(movies) { movieDb ->
SwipeToDismiss(
item = movieDb,
onDelete = { deletedMovieDb ->
onHistoryMovieRemoveClick(deletedMovieDb.movieId)
}
) { swipedMovieDb ->
SearchRecentResult(
text = swipedMovieDb.title,
onRemoveClick = { onHistoryMovieRemoveClick(swipedMovieDb.movieId) },
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.background(MaterialTheme.colorScheme.inversePrimary)
.clickable { onInputText(swipedMovieDb.title) }
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.michaelbel.movies.search.ui.common

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
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.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import org.michaelbel.movies.ui.accessibility.MoviesContentDescription
import org.michaelbel.movies.ui.icons.MoviesIcons

@Composable
internal fun <T> SwipeToDismiss(
item: T,
onDelete: (T) -> Unit,
modifier: Modifier = Modifier,
duration: Long = 500L,
content: @Composable (T) -> Unit
) {
val hapticFeedback = LocalHapticFeedback.current
var removed by rememberSaveable { mutableStateOf(false) }
val state = rememberSwipeToDismissBoxState(
confirmValueChange = { value ->
if (value == SwipeToDismissBoxValue.EndToStart) {
removed = true
true
} else {
false
}
}
)

LaunchedEffect(removed) {
if (removed) {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
delay(duration)
onDelete(item)
}
}

AnimatedVisibility(
visible = !removed,
modifier = modifier,
exit = shrinkVertically(animationSpec = tween(durationMillis = duration.toInt()), shrinkTowards = Alignment.Top) + fadeOut()
) {
SwipeToDismissBox(
state = state,
enableDismissFromStartToEnd = false,
enableDismissFromEndToStart = true,
backgroundContent = {
val color = if (state.dismissDirection == SwipeToDismissBoxValue.EndToStart) MaterialTheme.colorScheme.errorContainer else Color.Transparent

Box(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(16.dp),
contentAlignment = Alignment.CenterEnd
) {
Icon(
imageVector = MoviesIcons.Delete,
contentDescription = MoviesContentDescription.None,
tint = MaterialTheme.colorScheme.onErrorContainer
)
}
},
content = {
content(item)
}
)
}
}

0 comments on commit 0d158a7

Please sign in to comment.