Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign Deposit & Withdraw UI #73

Merged
merged 5 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ dependencies {
implementation 'androidx.activity:activity-compose:1.8.2'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0"
implementation "androidx.navigation:navigation-compose:2.7.6"
implementation "androidx.navigation:navigation-compose:2.7.7"
// Jetpack compose.
implementation "androidx.compose.ui:ui"
implementation "androidx.compose.ui:ui-tooling-preview"
Expand Down Expand Up @@ -116,8 +116,11 @@ dependencies {
// Coil Image loading library.
implementation "io.coil-kt:coil-compose:2.4.0"
// Material 3 calender / Date picker.
implementation 'com.maxkeppeler.sheets-compose-dialogs:core:1.1.0'
implementation 'com.maxkeppeler.sheets-compose-dialogs:calendar:1.1.0'
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"
// Lottie animations.
implementation "com.airbnb.android:lottie-compose:4.1.0"
// Bio-metric authentication.
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/java/com/starry/greenstash/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ import com.starry.greenstash.ui.screens.settings.viewmodels.SettingsViewModel
import com.starry.greenstash.ui.screens.settings.viewmodels.ThemeMode
import com.starry.greenstash.ui.theme.GreenStashTheme
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.toToast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
import java.util.concurrent.Executor
Expand Down Expand Up @@ -104,7 +103,6 @@ class MainActivity : AppCompatActivity() {
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
getString(R.string.auth_successful).toToast(this@MainActivity)
// make app contents visible after successful authentication.
setAppContents()
mainViewModel.appUnlocked = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ interface GoalDao {
@Query("SELECT * FROM saving_goal WHERE goalId = :goalId")
suspend fun getGoalWithTransactionById(goalId: Long): GoalWithTransactions?

@Transaction
@Query("SELECT * FROM saving_goal WHERE goalId = :goalId")
fun getGoalWithTransactionByIdAsFlow(goalId: Long): Flow<GoalWithTransactions?>

@Transaction
@Query(
"SELECT * FROM saving_goal ORDER BY " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import androidx.navigation.navArgument
import com.starry.greenstash.ui.screens.backups.BackupScreen
import com.starry.greenstash.ui.screens.home.composables.HomeScreen
import com.starry.greenstash.ui.screens.info.composables.GoalInfoScreen
import com.starry.greenstash.ui.screens.input.composables.CongratsScreen
import com.starry.greenstash.ui.screens.input.composables.DWScreen
import com.starry.greenstash.ui.screens.input.composables.InputScreen
import com.starry.greenstash.ui.screens.settings.composables.AboutScreen
import com.starry.greenstash.ui.screens.settings.composables.OSLScreen
Expand Down Expand Up @@ -117,6 +119,29 @@ fun NavGraph(
HomeScreen(navController)
}

/** Deposit Withdraw Screen */
composable(
route = Screens.DWScreen.route,
enterTransition = { enterTransition() },
exitTransition = { exitTransition() },
popEnterTransition = { popEnterTransition() },
popExitTransition = { popExitTransition() },
arguments = listOf(
navArgument(DW_GOAL_ID_ARG_KEY) {
type = NavType.StringType
},
),
) { backStackEntry ->
val goalId = backStackEntry.arguments!!.getString(DW_GOAL_ID_ARG_KEY)!!
val transactionType =
backStackEntry.arguments!!.getString(DW_TRANSACTION_TYPE_ARG_KEY)!!
DWScreen(
goalId = goalId,
transactionTypeName = transactionType,
navController = navController
)
}

/** Goal Info Screen */
composable(
route = Screens.GoalInfoScreen.route,
Expand All @@ -131,7 +156,7 @@ fun NavGraph(
),
) { backStackEntry ->
val goalId = backStackEntry.arguments!!.getString(GOAL_INFO_ARG_KEY)!!
GoalInfoScreen(goalId = goalId, navController)
GoalInfoScreen(goalId = goalId, navController = navController)
}

/** Input Screen */
Expand All @@ -148,7 +173,18 @@ fun NavGraph(
})
) { backStackEntry ->
val editGoalId = backStackEntry.arguments!!.getString(EDIT_GOAL_ARG_KEY)
InputScreen(editGoalId, navController)
InputScreen(editGoalId = editGoalId, navController = navController)
}

/** Goal Achieved Screen */
composable(
route = Screens.CongratsScreen.route,
enterTransition = { enterTransition() },
exitTransition = { exitTransition() },
popEnterTransition = { popEnterTransition() },
popExitTransition = { popExitTransition() },
) {
CongratsScreen(navController = navController)
}

/** Backup Screen */
Expand All @@ -159,7 +195,7 @@ fun NavGraph(
popEnterTransition = { popEnterTransition() },
popExitTransition = { popExitTransition() },
) {
BackupScreen(navController)
BackupScreen(navController = navController)
}

/** Settings Screen */
Expand All @@ -170,7 +206,7 @@ fun NavGraph(
popEnterTransition = { popEnterTransition() },
popExitTransition = { popExitTransition() },
) {
SettingsScreen(navController)
SettingsScreen(navController = navController)
}

/** Open Source Licenses Screen */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,21 @@

package com.starry.greenstash.ui.navigation

const val DW_GOAL_ID_ARG_KEY = "dwGoal"
const val DW_TRANSACTION_TYPE_ARG_KEY = "dwTransactionType"
const val EDIT_GOAL_ARG_KEY = "editGoal"
const val GOAL_INFO_ARG_KEY = "goalId"

sealed class Screens(val route: String) {

data object DWScreen :
Screens("deposit_withdraw_screen/{$DW_GOAL_ID_ARG_KEY}/{$DW_TRANSACTION_TYPE_ARG_KEY}") {
fun withGoalId(goalId: String, trasactionType: String): String {
return route.replace("{$DW_GOAL_ID_ARG_KEY}", goalId)
.replace("{$DW_TRANSACTION_TYPE_ARG_KEY}", trasactionType)
}
}

data object InputScreen : Screens("input_screen?$EDIT_GOAL_ARG_KEY={$EDIT_GOAL_ARG_KEY}") {
fun withGoalToEdit(goalId: String): String {
return route.replace("{$EDIT_GOAL_ARG_KEY}", goalId)
Expand All @@ -41,6 +52,7 @@ sealed class Screens(val route: String) {
}
}

data object CongratsScreen : Screens("goal_achieved_screen")
data object AboutScreen : Screens("about_screen")
data object OSLScreen : Screens("osl_screen")
data object WelcomeScreen : Screens("welcome_screen")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fun GoalItem(
onEditClicked: () -> Unit,
onDeleteClicked: () -> Unit,
) {
val progress by animateFloatAsState(targetValue = goalProgress)
val progress by animateFloatAsState(targetValue = goalProgress, label = "goal progress")

Card(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,19 @@ import android.content.Context
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.navigation.NavController
import com.starry.greenstash.R
import com.starry.greenstash.database.core.GoalWithTransactions
import com.starry.greenstash.database.transaction.TransactionType
import com.starry.greenstash.ui.navigation.Screens
import com.starry.greenstash.ui.screens.home.viewmodels.BottomSheetType
import com.starry.greenstash.ui.screens.home.viewmodels.HomeViewModel
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.validateAmount
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch

Expand All @@ -63,19 +57,13 @@ fun GoalLazyColumnItem(
viewModel: HomeViewModel,
item: GoalWithTransactions,
snackBarHostState: SnackbarHostState,
bottomSheetState: ModalBottomSheetState,
bottomSheetType: MutableState<BottomSheetType>,
navController: NavController
) {
val coroutineScope = rememberCoroutineScope()
val progressPercent =
((item.getCurrentlySavedAmount() / item.goal.targetAmount) * 100).toInt()

val openDeleteDialog = remember { mutableStateOf(false) }
val openDepositDialog = remember { mutableStateOf(false) }
val openWithdrawDialog = remember { mutableStateOf(false) }

val hapticFeedback = LocalHapticFeedback.current

GoalItem(title = item.goal.title,
primaryText = viewModel.goalTextUtil.buildPrimaryText(context, progressPercent, item),
Expand All @@ -88,7 +76,12 @@ fun GoalLazyColumnItem(
snackBarHostState.showSnackbar(context.getString(R.string.goal_already_achieved))
}
} else {
openDepositDialog.value = true
navController.navigate(
Screens.DWScreen.withGoalId(
goalId = item.goal.goalId.toString(),
trasactionType = TransactionType.Deposit.name
)
)
}
},
onWithdrawClicked = {
Expand All @@ -97,7 +90,12 @@ fun GoalLazyColumnItem(
snackBarHostState.showSnackbar(context.getString(R.string.withdraw_button_error))
}
} else {
openWithdrawDialog.value = true
navController.navigate(
Screens.DWScreen.withGoalId(
goalId = item.goal.goalId.toString(),
trasactionType = TransactionType.Withdraw.name
)
)
}
},
onInfoClicked = {
Expand All @@ -116,55 +114,13 @@ fun GoalLazyColumnItem(
},
onDeleteClicked = { openDeleteDialog.value = true })

ActionDialogs(
HomeDialogs(
openDeleteDialog = openDeleteDialog,
openDepositDialog = openDepositDialog,
openWithdrawDialog = openWithdrawDialog,
onDeleteConfirmed = {
viewModel.deleteGoal(item.goal)
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.goal_delete_success))
}
}, onDepositConfirmed = { amount, notes ->
if (!amount.validateAmount()) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.amount_empty_err))
}
} else if (item.getCurrentlySavedAmount() >= item.goal.targetAmount) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.goal_already_achieved))
}
} else {
val amountDouble = Utils.roundDecimal(amount.toDouble())
viewModel.deposit(item.goal, amountDouble, notes, onGoalAchieved = {
// Show a congratulations message when goal is achieved.
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
coroutineScope.launch {
bottomSheetType.value = BottomSheetType.GOAL_ACHIEVED
if (!bottomSheetState.isVisible) {
bottomSheetState.show()
}
}
})
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.deposit_successful))
}
}
}, onWithdrawConfirmed = { amount, notes ->
if (!amount.validateAmount()) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.amount_empty_err))
}
} else {
val amountDouble = Utils.roundDecimal(amount.toDouble())
if (amountDouble > item.getCurrentlySavedAmount()) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.withdraw_overflow_error))
}
} else {
viewModel.withdraw(item.goal, amountDouble, notes)
}
}
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fun MainAppBar(
) {
Crossfade(
targetState = searchWidgetState,
animationSpec = tween(durationMillis = 300)
animationSpec = tween(durationMillis = 300), label = "searchbar cross-fade"
) {
when (it) {
SearchWidgetState.CLOSED -> {
Expand Down
Loading
Loading