feat: batch actions (#1062)

This commit is contained in:
Yogesh Choudhary Paliyal
2025-01-26 14:37:59 +05:30
committed by GitHub
parent 7831511ed6
commit 35cae1f1b0
9 changed files with 146 additions and 60 deletions

View File

@@ -20,6 +20,7 @@ import com.yogeshpaliyal.common.dbhelper.restoreBackup
import com.yogeshpaliyal.common.utils.BACKUP_KEY_LENGTH
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.BatchActions
import com.yogeshpaliyal.keypass.ui.redux.actions.RestoreAccountsAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
@@ -68,9 +69,13 @@ fun RestoreKeyPassBackupDialog(
restoreBackup(keyphrase, context.contentResolver, selectedFile)
if (result != null) {
dispatchAction(RestoreAccountsAction(result))
dispatchAction(UpdateDialogAction(null))
dispatchAction(ToastAction(R.string.backup_restored))
dispatchAction(
BatchActions(
RestoreAccountsAction(result),
UpdateDialogAction(null),
ToastAction(R.string.backup_restored)
)
)
} else {
dispatchAction(ToastAction(R.string.invalid_keyphrase))
}

View File

@@ -24,6 +24,7 @@ import com.yogeshpaliyal.keypass.ui.commonComponents.DefaultBottomAppBar
import com.yogeshpaliyal.keypass.ui.commonComponents.KeyPassInputField
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.BatchActions
import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import kotlinx.coroutines.launch
@@ -54,8 +55,7 @@ fun PasswordHintScreen() {
Button(modifier = Modifier.fillMaxWidth(1f), onClick = {
coroutineScope.launch {
context.setPasswordHint(passwordHint)
dispatchAction(ToastAction(R.string.hint_change_success))
dispatchAction(GoBackAction)
dispatchAction(BatchActions(GoBackAction, ToastAction(R.string.hint_change_success)))
}
}) {
Text(text = stringResource(id = R.string.change_app_hint))
@@ -64,8 +64,7 @@ fun PasswordHintScreen() {
OutlinedButton(modifier = Modifier.fillMaxWidth(1f), onClick = {
coroutineScope.launch {
context.setPasswordHint(null)
dispatchAction(ToastAction(R.string.hint_removed_success))
dispatchAction(GoBackAction)
dispatchAction(BatchActions(GoBackAction, ToastAction(R.string.hint_removed_success)))
}
}) {
Text(text = stringResource(id = R.string.remove_app_hint))

View File

@@ -1,22 +1,16 @@
package com.yogeshpaliyal.keypass.ui.redux
import android.content.ClipData
import android.content.ClipboardManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.BatchActions
import com.yogeshpaliyal.keypass.ui.redux.actions.BottomSheetAction
import com.yogeshpaliyal.keypass.ui.redux.actions.CopyToClipboard
import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction
import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction
import com.yogeshpaliyal.keypass.ui.redux.actions.RestoreAccountsAction
import com.yogeshpaliyal.keypass.ui.redux.actions.StateUpdateAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastActionStr
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateContextAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateViewModalAction
import com.yogeshpaliyal.keypass.ui.redux.middlewares.intentNavigationMiddleware
import com.yogeshpaliyal.keypass.ui.redux.middlewares.utilityMiddleware
import com.yogeshpaliyal.keypass.ui.redux.states.BottomSheetState
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState
@@ -32,7 +26,17 @@ object KeyPassRedux {
fun getLastScreen() = arrPages.lastOrNull()
private val reducer: Reducer<KeyPassState> = { state, action ->
when (action) {
if (action is BatchActions) {
action.actions.fold(state) { acc, act ->
handleAction(act, acc)
}
} else {
handleAction(action, state)
}
}
private fun handleAction(action: Any, state: KeyPassState): KeyPassState {
return when (action) {
is NavigationAction -> {
if (action.clearBackStack) {
arrPages.clear()
@@ -49,43 +53,10 @@ object KeyPassRedux {
state.copy(currentScreen = action.state)
}
is CopyToClipboard -> {
state.context?.let {
val clipboard = ContextCompat.getSystemService(
it,
ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", action.password)
clipboard?.setPrimaryClip(clip)
Toast.makeText(
it,
R.string.copied_to_clipboard,
Toast.LENGTH_SHORT
)
.show()
}
state
}
is UpdateContextAction -> {
state.copy(context = action.context)
}
is ToastAction -> {
state.context?.let {
Toast.makeText(it, action.text, Toast.LENGTH_SHORT).show()
}
state
}
is ToastActionStr -> {
state.context?.let {
Toast.makeText(it, action.text, Toast.LENGTH_SHORT).show()
}
state
}
is GoBackAction -> {
val lastItem = arrPages.removeLastOrNull()
if (lastItem != null) {
@@ -104,6 +75,7 @@ object KeyPassRedux {
)
)
}
is UpdateDialogAction -> {
state.copy(dialog = action.dialogState)
}
@@ -125,6 +97,6 @@ object KeyPassRedux {
createStore(
reducer,
generateDefaultState(),
applyMiddleware(intentNavigationMiddleware)
applyMiddleware(utilityMiddleware, intentNavigationMiddleware)
)
}

View File

@@ -8,12 +8,45 @@ import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState
sealed interface Action
/**
* Batch Multiples Actions to send to ingest in one go
*/
class BatchActions(vararg val actions: Action) : Action
/**
* Used to update context value in redux store
*/
class UpdateContextAction(val context: Context?) : Action
/**
* Used to update ViewModal in redux store
*/
class UpdateViewModalAction(val viewModal: DashboardViewModel?) : Action
/**
* Used to navigate from 1 state to another
* @param state: ScreenState New State where to navigate
* @param clearBackStack: Boolean Clear BackStack or not
*/
data class NavigationAction(val state: ScreenState, val clearBackStack: Boolean = false) : Action
/**
* Used to send Accounts list which we want to restore
*/
data class RestoreAccountsAction(val accounts: List<AccountModel>) : Action
/**
* Used to update Screen State in redux store
*/
data class StateUpdateAction(val state: ScreenState) : Action
/**
* used to update dialog state of the app
* @param dialogState: DialogState? New Dialog State or send null to dismiss the dialog
*/
data class UpdateDialogAction(val dialogState: DialogState? = null) : Action
/**
* Used to go back to previous screen
*/
object GoBackAction : Action

View File

@@ -3,6 +3,9 @@ package com.yogeshpaliyal.keypass.ui.redux.actions
import android.os.Bundle
import com.yogeshpaliyal.keypass.ui.redux.BottomSheetRoutes
/**
* Bottom Sheet action to open bottom sheet for different routes
*/
sealed class BottomSheetAction(
val route: String,
val globalIsBottomSheetOpen: Boolean,

View File

@@ -1,7 +1,10 @@
package com.yogeshpaliyal.keypass.ui.redux.actions
/**
* Intent Navigation actions, used to navigate to different activities
* handled in {@link IntentNavigationMiddleware}
*/
sealed interface IntentNavigation : Action {
object GeneratePassword : IntentNavigation
object BackupActivity : IntentNavigation
object ShareApp : IntentNavigation
}

View File

@@ -1,9 +1,10 @@
package com.yogeshpaliyal.keypass.ui.redux.actions
import androidx.annotation.StringRes
import com.yogeshpaliyal.keypass.R
data class ToastAction(@StringRes val text: Int) : Action
data class ToastActionStr(val text: String) : Action
data class CopyToClipboard(val password: String) : Action
interface UtilityAction : Action
object GoBackAction : Action
data class ToastAction(@StringRes val text: Int) : UtilityAction
data class ToastActionStr(val text: String) : UtilityAction
data class CopyToClipboard(val password: String, @StringRes val successMessage: Int = R.string.copied_to_clipboard) : UtilityAction

View File

@@ -4,23 +4,35 @@ import android.content.Intent
import com.yogeshpaliyal.keypass.BuildConfig
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.generate.GeneratePasswordActivity
import com.yogeshpaliyal.keypass.ui.redux.actions.BatchActions
import com.yogeshpaliyal.keypass.ui.redux.actions.IntentNavigation
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
import org.reduxkotlin.Store
import org.reduxkotlin.middleware
/**
* Middleware to handle intent navigation
*/
val intentNavigationMiddleware = middleware<KeyPassState> { store, next, action ->
val state = store.state
if (action is BatchActions) {
action.actions.forEach {
store.handleAction(it, state)
}
} else {
store.handleAction(action, state)
}
next(action)
}
private fun Store<KeyPassState>.handleAction(action: Any, state: KeyPassState) {
when (action) {
is IntentNavigation.GeneratePassword -> {
val intent = Intent(state.context, GeneratePasswordActivity::class.java)
state.context?.startActivity(intent)
}
is IntentNavigation.BackupActivity -> {
// BackupActivity.start(state.context)
}
is IntentNavigation.ShareApp -> {
val sendIntent = Intent()
sendIntent.action = Intent.ACTION_SEND
@@ -37,5 +49,4 @@ val intentNavigationMiddleware = middleware<KeyPassState> { store, next, action
)
}
}
next(action)
}

View File

@@ -0,0 +1,59 @@
package com.yogeshpaliyal.keypass.ui.redux.middlewares
import android.content.ClipData
import android.content.ClipboardManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.yogeshpaliyal.keypass.ui.redux.actions.BatchActions
import com.yogeshpaliyal.keypass.ui.redux.actions.CopyToClipboard
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastActionStr
import com.yogeshpaliyal.keypass.ui.redux.actions.UtilityAction
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
import org.reduxkotlin.Store
import org.reduxkotlin.middleware
/**
* Middle ware to handle utility functions like copy to clipboard and toast
*/
val utilityMiddleware = middleware<KeyPassState> { store, next, action ->
val state = store.state
if (action is BatchActions) {
action.actions.forEach {
store.handleAction(it, state)
}
} else {
store.handleAction(action, state)
}
next(action)
}
private fun Store<KeyPassState>.handleAction(action: Any, state: KeyPassState) {
if (action is UtilityAction) {
when (action) {
is ToastActionStr -> {
state.context?.let {
Toast.makeText(it, action.text, Toast.LENGTH_SHORT).show()
}
}
is ToastAction -> {
state.context?.let {
Toast.makeText(it, action.text, Toast.LENGTH_SHORT).show()
}
}
is CopyToClipboard -> {
state.context?.let {
val clipboard = ContextCompat.getSystemService(
it,
ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", action.password)
clipboard?.setPrimaryClip(clip)
dispatch(ToastAction(action.successMessage))
}
}
}
}
}