Skip to content

Commit

Permalink
Improve transaction swipe action & navigation drawer
Browse files Browse the repository at this point in the history
Signed-off-by: starry-shivam <[email protected]>
  • Loading branch information
starry-shivam committed Mar 28, 2024
1 parent 19a5dd6 commit 10e927c
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 38 deletions.
2 changes: 0 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ dependencies {
implementation 'com.maxkeppeler.sheets-compose-dialogs:core:1.3.0'
implementation 'com.maxkeppeler.sheets-compose-dialogs:calendar:1.3.0'
implementation 'com.maxkeppeler.sheets-compose-dialogs:date-time:1.3.0'
// Swipe actions.
implementation "me.saket.swipe:swipe:1.2.0"
// Taptarget compose.
implementation "com.pierfrancescosoffritti.taptargetcompose:core:1.1.0"
// Lottie animations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,18 @@ fun HomeScreenContent(
drawerState = drawerState,
gesturesEnabled = drawerState.isOpen,
drawerContent = {
ModalDrawerSheet(drawerShape = RoundedCornerShape(4.dp)) {
ModalDrawerSheet(
modifier = Modifier.width(280.dp),
drawerShape = RoundedCornerShape(4.dp)
) {
Spacer(Modifier.height(14.dp))

Text(
text = Utils.getGreeting(),
modifier = Modifier.padding(start = 16.dp, top = 12.dp),
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
fontFamily = greenstashFont,
color = MaterialTheme.colorScheme.onSurface
)

Expand Down Expand Up @@ -229,14 +233,35 @@ fun HomeScreenContent(
navController.navigate(item.route)
}
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
modifier = Modifier
.width(280.dp)
.padding(NavigationDrawerItemDefaults.ItemPadding)
)
Spacer(modifier = Modifier.height(4.dp))
}

Spacer(Modifier.weight(1f))

Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
text = stringResource(id = R.string.drawer_footer_text),
modifier = Modifier.padding(bottom = 18.dp),
fontSize = 12.sp,
fontFamily = greenstashFont,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
)
}

}
}) {

val showTapTargets = remember { mutableStateOf(false) }
LaunchedEffect(key1 = viewModel.showOnboardingTapTargets.value) {
delay(300) // Delay to prevent flickering
showTapTargets.value = viewModel.showOnboardingTapTargets.value
}
TapTargetCoordinator(
showTapTargets = viewModel.showOnboardingTapTargets.value,
showTapTargets = showTapTargets.value,
onComplete = { viewModel.onboardingTapTargetsShown() }
) {
Scaffold(modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package com.starry.greenstash.ui.screens.info.composables

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
Expand All @@ -43,35 +44,50 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.NotificationsActive
import androidx.compose.material.icons.filled.NotificationsOff
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -101,9 +117,11 @@ import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.ui.theme.greenstashNumberFont
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.getActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import me.saket.swipe.SwipeAction
import me.saket.swipe.SwipeableActionsBox
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


@ExperimentalCoroutinesApi
Expand Down Expand Up @@ -183,7 +201,7 @@ fun GoalInfoScreen(goalId: String, navController: NavController) {
Spacer(modifier = Modifier.height(6.dp))
}
if (goalData.transactions.isNotEmpty()) {
TransactionCard(goalData.transactions.reversed(), currencySymbol, viewModel)
TransactionItem(goalData.transactions.reversed(), currencySymbol, viewModel)
} else {
Column(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -385,30 +403,146 @@ fun GoalNotesCard(notesText: String) {
@ExperimentalAnimationApi
@ExperimentalMaterial3Api
@Composable
fun TransactionCard(
fun TransactionItem(
transactions: List<Transaction>,
currencySymbol: String,
viewModel: InfoViewModel
) {
val settingsVM = (LocalContext.current.getActivity() as MainActivity).settingsViewModel
transactions.forEach {
val deleteAction = SwipeAction(
icon = painterResource(
id = if (settingsVM.getCurrentTheme() == ThemeMode.Light)
R.drawable.ic_goal_delete else R.drawable.ic_goal_delete_white
),
background = Color.Red,
onSwipe = {
viewModel.deleteTransaction(it)
transactions.forEach {transaction ->
val showEditSheet = remember { mutableStateOf(false) }
val showDeleteDialog = remember { mutableStateOf(false) }

val coroutineScope = rememberCoroutineScope()
val swipeState = rememberSwipeToDismissBoxState(
confirmValueChange = { direction ->
when (direction) {
SwipeToDismissBoxValue.EndToStart -> {
coroutineScope.launch {
delay(180) // allow the swipe to settle.
withContext(Dispatchers.Main) { showEditSheet.value = true}
}
}

SwipeToDismissBoxValue.StartToEnd -> {
coroutineScope.launch {
delay(180) // allow the swipe to settle.
withContext(Dispatchers.Main) { showDeleteDialog.value = true}
}
}

SwipeToDismissBoxValue.Settled -> {}
}
false // Don't allow it to settle on dismissed state.
}
)
SwipeableActionsBox(
endActions = listOf(deleteAction),
swipeThreshold = 85.dp,
content = { TransactionItem(it, currencySymbol) }

val dismissDirection = swipeState.dismissDirection

SwipeToDismissBox(
state = swipeState,
backgroundContent = {
val color by animateColorAsState(
when (dismissDirection) {
SwipeToDismissBoxValue.EndToStart -> MaterialTheme.colorScheme.primary
SwipeToDismissBoxValue.StartToEnd -> Color.Red.copy(alpha = 0.5f)
SwipeToDismissBoxValue.Settled -> Color.Transparent
}, label = "color"
)
val alignment by remember(dismissDirection) {
derivedStateOf {
when (dismissDirection) {
SwipeToDismissBoxValue.EndToStart -> Alignment.CenterEnd
SwipeToDismissBoxValue.StartToEnd -> Alignment.CenterStart
SwipeToDismissBoxValue.Settled -> Alignment.Center
}
}
}
val icon by remember(dismissDirection) {
derivedStateOf {
when (dismissDirection) {
SwipeToDismissBoxValue.EndToStart -> R.drawable.ic_goal_edit
SwipeToDismissBoxValue.StartToEnd -> R.drawable.ic_goal_delete
// Placeholder icon, not used anywhere.
SwipeToDismissBoxValue.Settled -> R.drawable.ic_goal_info
}
}
}

val scale by animateFloatAsState(
if (swipeState.dismissDirection != SwipeToDismissBoxValue.Settled) 1f else 0.75f,
label = "scale"
)

Box(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
imageVector = ImageVector.vectorResource(id = icon),
contentDescription = null,
modifier = Modifier.scale(scale)
)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp)
.clip(shape = RoundedCornerShape(8.dp)),
enableDismissFromStartToEnd = true,
enableDismissFromEndToStart = true,
content = {
TransactionCard(transaction = transaction, currencySymbol = currencySymbol)
}
)

if (showEditSheet.value) {
ModalBottomSheet(onDismissRequest = { showEditSheet.value = false }) {
// TODO: Add edit transaction sheet

}
}

if (showDeleteDialog.value) {
AlertDialog(onDismissRequest = {
showDeleteDialog.value = false
}, title = {
Text(
text = stringResource(id = R.string.goal_delete_confirmation),
color = MaterialTheme.colorScheme.onSurface,
fontFamily = greenstashFont,
)
}, confirmButton = {
FilledTonalButton(
onClick = {
showDeleteDialog.value = false
viewModel.deleteTransaction(transaction)
},
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
)
) {
Text(stringResource(id = R.string.confirm), fontFamily = greenstashFont)
}
}, dismissButton = {
TextButton(onClick = {
showDeleteDialog.value = false
}) {
Text(stringResource(id = R.string.cancel), fontFamily = greenstashFont)
}
},
icon = {
Icon(imageVector = Icons.Rounded.Delete, contentDescription = null)
}
)
}
}


}

@ExperimentalCoroutinesApi
Expand All @@ -418,11 +552,10 @@ fun TransactionCard(
@ExperimentalFoundationApi
@ExperimentalMaterialApi
@Composable
fun TransactionItem(transaction: Transaction, currencySymbol: String) {
fun TransactionCard(transaction: Transaction, currencySymbol: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp, top = 4.dp),
.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ fun GoalCardStyle(navController: NavController) {
.fillMaxSize()
.padding(paddingValues)
) {


OutlinedCard(
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -233,7 +231,7 @@ fun GoalCardStyle(navController: NavController) {
delay(500)
showCompactTip.value = true
} else {
delay(400)
delay(600)
showCompactTip.value = false
}
}
Expand Down
13 changes: 7 additions & 6 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
<!-- Other Strings -->
<string name="confirm">Confirmar</string>
<string name="cancel">Cancelar</string>
<string name="unknown_error">Oops! something went wrong.</string>
<string name="unknown_error">¡Vaya! Algo salió mal.</string>

<!-- Navigation Drawer -->
<string name="drawer_home">Inicio</string>
<string name="drawer_backups">Respaldo</string>
<string name="drawer_settings">Ajustes</string>
<string name="drawer_footer_text">Hecho con ❤️ en India.</string>

<!-- Biometric Lock -->
<string name="bio_lock_title">Desbloquear GreenStash</string>
Expand Down Expand Up @@ -83,7 +84,7 @@
<string name="info_card_remaining_days">%s días restantes.</string>
<string name="info_card_no_deadline_set">Sin fecha objetivo.</string>
<string name="info_card_goal_achieved">Meta alcanzada. ¡Felicitaciones!</string>
<string name="info_goal_priority">Goal Priority is %s</string>
<string name="info_goal_priority">La prioridad del objetivo es %s</string>
<string name="info_reminder_status_on">Los recordatorios están activados</string>
<string name="info_reminder_status_off">Los recordatorios están desactivados</string>
<string name="info_notes_card_title">Notas</string>
Expand All @@ -95,7 +96,7 @@
<string name="input_pick_icon">Selecciona ícono para tu objetivo.</string>
<string name="input_icon_dialog">Selecciona un ícono</string>
<string name="input_pick_image">Imagen de la meta</string>
<string name="input_goal_priority">Select priority for this goal.</string>
<string name="input_goal_priority">Selecciona la prioridad para este objetivo.</string>
<string name="input_text_title">Título del Ahorro</string>
<string name="title_empty_err">¡Oops! el título está vacío.</string>
<string name="input_text_amount">Meta</string>
Expand All @@ -105,16 +106,16 @@
<string name="input_add_goal_button">Agregar Nueva Meta</string>
<string name="input_edit_goal_button">Actualizar Meta</string>
<string name="goal_saved_success">¡Buen trabajo! Meta agregada.</string>
<string name="goad_edit_success">Meta actualizada.</string>\
<string name="goal_remove_deadline">Do you want to remove deadline from this goal?</string>
<string name="goad_edit_success">Meta actualizada.</string>
<string name="goal_remove_deadline">¿Quieres quitar la fecha límite de este objetivo?</string>

<!-- Backup Screen -->
<string name="backup_screen_header">Respaldar y recuperar datos</string>
<string name="backup_screen_text">Haz una copia de seguridad de tus datos incluyendo metas, progreso actual, transacciones, etc para recuperarlos cuando quieras.</string>
<string name="backup_screen_sub_text">Nota: La copia de seguridad no incluye la configuración de la app.</string>
<string name="backup_button">Respaldar</string>
<string name="restore_button">Restaurar</string>
<string name="backup_restore_success">Backup restored successfully!</string>
<string name="backup_restore_success">¡La copia de seguridad se ha restaurado correctamente!</string>

<!-- Settings Screen -->
<string name="settings_screen_header">Configuración</string>
Expand Down
Loading

0 comments on commit 10e927c

Please sign in to comment.