feat: standardise flow for dialogs (#1061)

* feat: standardise flow for dialogs

* feat: minor change
This commit is contained in:
Yogesh Choudhary Paliyal
2025-01-26 11:54:52 +05:30
committed by GitHub
parent 76e3e7eb0a
commit 7831511ed6
20 changed files with 355 additions and 264 deletions

View File

@@ -1,8 +1,6 @@
package com.yogeshpaliyal.keypass.importer package com.yogeshpaliyal.keypass.importer
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
interface AccountsImporter { interface AccountsImporter {
@@ -12,6 +10,5 @@ interface AccountsImporter {
fun allowedMimeType(): String fun allowedMimeType(): String
@Composable fun readFileGetAction(file: Uri): Action
fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit)
} }

View File

@@ -1,32 +1,10 @@
package com.yogeshpaliyal.keypass.importer package com.yogeshpaliyal.keypass.importer
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.opencsv.CSVReader
import com.opencsv.exceptions.CsvMalformedLineException
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import java.io.FileNotFoundException import com.yogeshpaliyal.keypass.ui.redux.states.RestoreChromeBackupState
class ChromeAccountImporter : AccountsImporter { class ChromeAccountImporter : AccountsImporter {
override fun getImporterTitle(): Int = R.string.google_backup override fun getImporterTitle(): Int = R.string.google_backup
@@ -36,67 +14,9 @@ class ChromeAccountImporter : AccountsImporter {
return "text/comma-separated-values" return "text/comma-separated-values"
} }
@Composable override fun readFileGetAction(
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) { file: Uri
val context = LocalContext.current ): Action {
return UpdateDialogAction(RestoreChromeBackupState(file))
LaunchedEffect(key1 = file, block = {
try {
val inputStream = context.contentResolver.openInputStream(file)
val reader = CSVReader(inputStream?.reader())
val myEntries: List<Array<String>> = reader.readAll()
val headers = myEntries[0]
val result = myEntries.drop(1).map { data ->
headers.zip(data).toMap()
}
val listOfAccounts = ArrayList<AccountModel>()
result.forEach {
listOfAccounts.add(
AccountModel(
title = it["name"],
notes = it["note"],
password = it["password"],
username = it["username"],
site = it["url"]
)
)
}
resolve(listOfAccounts)
onCompleteOrCancel(ToastAction(R.string.backup_restored))
} catch (e: CsvMalformedLineException) {
onCompleteOrCancel(ToastAction(R.string.invalid_csv_file))
} catch (e: FileNotFoundException) {
onCompleteOrCancel(ToastAction(R.string.file_not_found))
}
})
LoadingDialog()
} }
} }
@Composable
fun LoadingDialog() {
Dialog(onDismissRequest = {}) {
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.fillMaxWidth(1f)
.padding(16.dp),
Arrangement.Center,
Alignment.CenterHorizontally
) {
Text(
text = stringResource(id = R.string.restore),
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.size(16.dp))
CircularProgressIndicator()
}
}
}
@Preview()
@Composable
fun DialogPreview() {
LoadingDialog()
}

View File

@@ -1,15 +1,10 @@
package com.yogeshpaliyal.keypass.importer package com.yogeshpaliyal.keypass.importer
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import com.opencsv.CSVReader
import com.opencsv.exceptions.CsvMalformedLineException
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeePassBackupState
class KeePassAccountImporter : AccountsImporter { class KeePassAccountImporter : AccountsImporter {
override fun getImporterTitle(): Int = R.string.keepass_backup override fun getImporterTitle(): Int = R.string.keepass_backup
@@ -19,40 +14,9 @@ class KeePassAccountImporter : AccountsImporter {
return "text/comma-separated-values" return "text/comma-separated-values"
} }
@Composable override fun readFileGetAction(
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) { file: Uri
val context = LocalContext.current ): Action {
return UpdateDialogAction(RestoreKeePassBackupState(file))
LaunchedEffect(key1 = file, block = {
try {
val inputStream = context.contentResolver.openInputStream(file)
val reader = CSVReader(inputStream?.reader())
val myEntries: List<Array<String>> = reader.readAll()
val headers = myEntries[0]
val result = myEntries.drop(1).map { data ->
headers.zip(data).toMap()
}
val listOfAccounts = ArrayList<AccountModel>()
result.forEach {
listOfAccounts.add(
AccountModel(
title = it["Title"],
notes = it["Notes"],
password = it["Password"],
username = it["Username"],
site = it["URL"],
tags = it["Group"],
secret = if (it["TOTP"].isNullOrBlank()) null else it["TOTP"]
)
)
}
resolve(listOfAccounts)
onCompleteOrCancel(ToastAction(R.string.backup_restored))
} catch (e: CsvMalformedLineException) {
onCompleteOrCancel(ToastAction(R.string.invalid_csv))
}
})
LoadingDialog()
} }
} }

View File

@@ -1,30 +1,9 @@
package com.yogeshpaliyal.keypass.importer package com.yogeshpaliyal.keypass.importer
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.common.dbhelper.restoreBackup
import com.yogeshpaliyal.common.utils.BACKUP_KEY_LENGTH
import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import kotlinx.coroutines.launch
import org.reduxkotlin.compose.rememberTypedDispatcher
class KeyPassAccountImporter : AccountsImporter { class KeyPassAccountImporter : AccountsImporter {
override fun getImporterTitle(): Int { override fun getImporterTitle(): Int {
@@ -37,86 +16,9 @@ class KeyPassAccountImporter : AccountsImporter {
return "*/*" return "*/*"
} }
@Composable override fun readFileGetAction(
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) { file: Uri
RestoreDialog( ): Action {
selectedFile = file, return UpdateDialogAction(com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeyPassBackupState(file))
hideDialog = {
onCompleteOrCancel(null)
},
saveAccounts = resolve
)
} }
} }
@Composable
fun RestoreDialog(
selectedFile: Uri,
hideDialog: () -> Unit,
saveAccounts: (list: List<AccountModel>) -> Unit
) {
val (keyphrase, setKeyPhrase) = remember {
mutableStateOf("")
}
val dispatchAction = rememberTypedDispatcher<Action>()
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
AlertDialog(
onDismissRequest = {
hideDialog()
},
title = {
Text(text = stringResource(id = R.string.restore))
},
confirmButton = {
TextButton(onClick = {
if (keyphrase.isEmpty()) {
dispatchAction(ToastAction(R.string.alert_blank_keyphrase))
return@TextButton
}
if (keyphrase.length != BACKUP_KEY_LENGTH) {
dispatchAction(ToastAction(R.string.alert_invalid_keyphrase))
return@TextButton
}
coroutineScope.launch {
val result =
restoreBackup(keyphrase, context.contentResolver, selectedFile)
if (result != null) {
saveAccounts(result)
hideDialog()
dispatchAction(ToastAction(R.string.backup_restored))
} else {
dispatchAction(ToastAction(R.string.invalid_keyphrase))
}
}
}) {
Text(text = stringResource(id = R.string.restore))
}
},
dismissButton = {
TextButton(onClick = hideDialog) {
Text(text = stringResource(id = R.string.cancel))
}
},
text = {
Column(modifier = Modifier.fillMaxWidth(1f)) {
Text(text = stringResource(id = R.string.keyphrase_restore_info))
Spacer(modifier = Modifier.size(8.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(1f),
value = keyphrase,
onValueChange = setKeyPhrase,
placeholder = {
Text(text = stringResource(id = R.string.enter_keyphrase))
}
)
}
}
)
}

View File

@@ -64,13 +64,7 @@ fun BackupImporter(state: BackupImporterState, mViewModel: DashboardViewModel =
}) })
result?.let { result?.let {
state.selectedImported?.readFile(result, { state.selectedImported?.readFileGetAction(result)?.let { it1 -> dispatchAction(it1) }
setResult(null)
setRestoredAccounts(it)
}) {
it?.let(dispatchAction)
dispatchAction(StateUpdateAction(state = state.copy(selectedImported = null)))
}
} }
Scaffold(bottomBar = { Scaffold(bottomBar = {

View File

@@ -0,0 +1,40 @@
package com.yogeshpaliyal.keypass.ui.commonComponents
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun LoadingDialog(@StringRes id: Int) {
Dialog(onDismissRequest = {}) {
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium)
.fillMaxWidth(1f)
.padding(16.dp),
Arrangement.Center,
Alignment.CenterHorizontally
) {
Text(
text = stringResource(id = id),
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.size(16.dp))
CircularProgressIndicator()
}
}
}

View File

@@ -0,0 +1,65 @@
package com.yogeshpaliyal.keypass.ui.dialogs
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import com.opencsv.CSVReader
import com.opencsv.exceptions.CsvMalformedLineException
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.commonComponents.LoadingDialog
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.RestoreAccountsAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreChromeBackupState
import org.reduxkotlin.compose.rememberTypedDispatcher
import java.io.FileNotFoundException
@Composable
fun RestoreChromeBackupDialog(
state: RestoreChromeBackupState
) {
val (selectedFile) = state
val dispatchAction = rememberTypedDispatcher<Action>()
val context = LocalContext.current
val onCompleteOrCancel: (ToastAction) -> Unit = {
dispatchAction(UpdateDialogAction(null))
dispatchAction(it)
}
LaunchedEffect(key1 = selectedFile, block = {
try {
val inputStream = context.contentResolver.openInputStream(selectedFile)
val reader = CSVReader(inputStream?.reader())
val myEntries: List<Array<String>> = reader.readAll()
val headers = myEntries[0]
val result = myEntries.drop(1).map { data ->
headers.zip(data).toMap()
}
val listOfAccounts = ArrayList<AccountModel>()
result.forEach {
listOfAccounts.add(
AccountModel(
title = it["name"],
notes = it["note"],
password = it["password"],
username = it["username"],
site = it["url"]
)
)
}
dispatchAction(RestoreAccountsAction(listOfAccounts))
onCompleteOrCancel(ToastAction(R.string.backup_restored))
} catch (e: CsvMalformedLineException) {
onCompleteOrCancel(ToastAction(R.string.invalid_csv_file))
} catch (e: FileNotFoundException) {
onCompleteOrCancel(ToastAction(R.string.file_not_found))
}
})
LoadingDialog(R.string.restore)
}

View File

@@ -0,0 +1,64 @@
package com.yogeshpaliyal.keypass.ui.dialogs
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import com.opencsv.CSVReader
import com.opencsv.exceptions.CsvMalformedLineException
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.commonComponents.LoadingDialog
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.RestoreAccountsAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeePassBackupState
import org.reduxkotlin.compose.rememberTypedDispatcher
@Composable
fun RestoreKeePassBackupDialog(
state: RestoreKeePassBackupState
) {
val (selectedFile) = state
val dispatchAction = rememberTypedDispatcher<Action>()
val context = LocalContext.current
val onCompleteOrCancel: (ToastAction) -> Unit = {
dispatchAction(UpdateDialogAction(null))
dispatchAction(it)
}
LaunchedEffect(key1 = selectedFile, block = {
try {
val inputStream = context.contentResolver.openInputStream(selectedFile)
val reader = CSVReader(inputStream?.reader())
val myEntries: List<Array<String>> = reader.readAll()
val headers = myEntries[0]
val result = myEntries.drop(1).map { data ->
headers.zip(data).toMap()
}
val listOfAccounts = ArrayList<AccountModel>()
result.forEach {
listOfAccounts.add(
AccountModel(
title = it["Title"],
notes = it["Notes"],
password = it["Password"],
username = it["Username"],
site = it["URL"],
tags = it["Group"],
secret = if (it["TOTP"].isNullOrBlank()) null else it["TOTP"]
)
)
}
dispatchAction(RestoreAccountsAction(listOfAccounts))
onCompleteOrCancel(ToastAction(R.string.backup_restored))
} catch (e: CsvMalformedLineException) {
onCompleteOrCancel(ToastAction(R.string.invalid_csv))
}
})
LoadingDialog(R.string.restore)
}

View File

@@ -0,0 +1,102 @@
package com.yogeshpaliyal.keypass.ui.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
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.RestoreAccountsAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeyPassBackupState
import kotlinx.coroutines.launch
import org.reduxkotlin.compose.rememberTypedDispatcher
@Composable
fun RestoreKeyPassBackupDialog(
state: RestoreKeyPassBackupState
) {
val (keyphrase, setKeyPhrase) = remember {
mutableStateOf("")
}
val (selectedFile) = state
val dispatchAction = rememberTypedDispatcher<Action>()
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val hideDialog: () -> Unit = {
dispatchAction(UpdateDialogAction(null))
}
AlertDialog(
onDismissRequest = hideDialog,
title = {
Text(text = stringResource(id = R.string.restore))
},
confirmButton = {
TextButton(onClick = {
if (keyphrase.isEmpty()) {
dispatchAction(ToastAction(R.string.alert_blank_keyphrase))
return@TextButton
}
if (keyphrase.length != BACKUP_KEY_LENGTH) {
dispatchAction(ToastAction(R.string.alert_invalid_keyphrase))
return@TextButton
}
coroutineScope.launch {
val result =
restoreBackup(keyphrase, context.contentResolver, selectedFile)
if (result != null) {
dispatchAction(RestoreAccountsAction(result))
dispatchAction(UpdateDialogAction(null))
dispatchAction(ToastAction(R.string.backup_restored))
} else {
dispatchAction(ToastAction(R.string.invalid_keyphrase))
}
}
}) {
Text(text = stringResource(id = R.string.restore))
}
},
dismissButton = {
TextButton(onClick = hideDialog) {
Text(text = stringResource(id = R.string.cancel))
}
},
text = {
Column(modifier = Modifier.fillMaxWidth(1f)) {
Text(text = stringResource(id = R.string.keyphrase_restore_info))
Spacer(modifier = Modifier.size(8.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(1f),
value = keyphrase,
onValueChange = setKeyPhrase,
placeholder = {
Text(text = stringResource(id = R.string.enter_keyphrase))
}
)
}
}
)
}

View File

@@ -1,4 +1,4 @@
package com.yogeshpaliyal.keypass.ui.home.components package com.yogeshpaliyal.keypass.ui.dialogs
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@@ -21,7 +21,7 @@ import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogState import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.ForgotKeyPhraseState import com.yogeshpaliyal.keypass.ui.redux.states.ForgotKeyPhraseState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.reduxkotlin.compose.rememberTypedDispatcher import org.reduxkotlin.compose.rememberTypedDispatcher
@@ -40,7 +40,7 @@ fun ValidateKeyPhraseDialog() {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val hideDialog: () -> Unit = { val hideDialog: () -> Unit = {
dispatchAction(UpdateDialogState(null)) dispatchAction(UpdateDialogAction(null))
} }
AlertDialog( AlertDialog(
@@ -92,7 +92,7 @@ fun ValidateKeyPhraseDialog() {
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
TextButton(onClick = { TextButton(onClick = {
dispatchAction(UpdateDialogState(ForgotKeyPhraseState)) dispatchAction(UpdateDialogAction(ForgotKeyPhraseState))
}) { }) {
Text(text = stringResource(id = R.string.forgot_keyphrase_question)) Text(text = stringResource(id = R.string.forgot_keyphrase_question))
} }

View File

@@ -43,9 +43,11 @@ class DashboardViewModel @Inject constructor(
} }
} }
suspend fun restoreBackup( fun restoreBackup(
list: List<AccountModel> list: List<AccountModel>
) { ) {
return appDb.saveToDb(list) viewModelScope.launch {
appDb.saveToDb(list)
}
} }
} }

View File

@@ -23,12 +23,15 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.yogeshpaliyal.keypass.ui.home.components.AccountsList import com.yogeshpaliyal.keypass.ui.home.components.AccountsList
import com.yogeshpaliyal.keypass.ui.home.components.SearchBar import com.yogeshpaliyal.keypass.ui.home.components.SearchBar
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
import com.yogeshpaliyal.keypass.ui.redux.KeyPassRedux
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction
import com.yogeshpaliyal.keypass.ui.redux.actions.StateUpdateAction import com.yogeshpaliyal.keypass.ui.redux.actions.StateUpdateAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogState import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateViewModalAction
import com.yogeshpaliyal.keypass.ui.redux.states.HomeState import com.yogeshpaliyal.keypass.ui.redux.states.HomeState
import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase
import org.reduxkotlin.compose.rememberDispatcher import org.reduxkotlin.compose.rememberTypedDispatcher
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/* /*
@@ -52,12 +55,16 @@ fun Homepage(
val listOfAccountsLiveData by mViewModel.mediator.observeAsState() val listOfAccountsLiveData by mViewModel.mediator.observeAsState()
val dispatchAction = rememberDispatcher() val dispatchAction = rememberTypedDispatcher<Action>()
LaunchedEffect(tag, keyword, sortField, sortAscendingOrder, block = { LaunchedEffect(tag, keyword, sortField, sortAscendingOrder, block = {
mViewModel.queryUpdated(keyword, tag, sortField, sortAscendingOrder) mViewModel.queryUpdated(keyword, tag, sortField, sortAscendingOrder)
}) })
LaunchedEffect(KeyPassRedux, mViewModel) {
dispatchAction(UpdateViewModalAction(mViewModel))
}
LaunchedEffect(Unit, { LaunchedEffect(Unit, {
if (userSettings.backupKey == null) { if (userSettings.backupKey == null) {
return@LaunchedEffect return@LaunchedEffect
@@ -68,7 +75,7 @@ fun Homepage(
val diffInDays = TimeUnit.MILLISECONDS.toDays(diff) val diffInDays = TimeUnit.MILLISECONDS.toDays(diff)
if (diffInDays >= 7) { if (diffInDays >= 7) {
// Show the modal // Show the modal
dispatchAction(UpdateDialogState(dialogState = ValidateKeyPhrase)) dispatchAction(UpdateDialogAction(dialogState = ValidateKeyPhrase))
} }
}) })

View File

@@ -13,7 +13,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogState import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase
import org.reduxkotlin.compose.rememberTypedDispatcher import org.reduxkotlin.compose.rememberTypedDispatcher
@@ -22,7 +22,7 @@ fun ForgotKeyPhraseDialog() {
val dispatchAction = rememberTypedDispatcher<Action>() val dispatchAction = rememberTypedDispatcher<Action>()
val hideDialog: () -> Unit = { val hideDialog: () -> Unit = {
dispatchAction(UpdateDialogState(ValidateKeyPhrase)) dispatchAction(UpdateDialogAction(ValidateKeyPhrase))
} }
AlertDialog( AlertDialog(

View File

@@ -33,10 +33,13 @@ import com.yogeshpaliyal.keypass.ui.backupsImport.BackupImporter
import com.yogeshpaliyal.keypass.ui.changeDefaultPasswordLength.ChangeDefaultPasswordLengthScreen import com.yogeshpaliyal.keypass.ui.changeDefaultPasswordLength.ChangeDefaultPasswordLengthScreen
import com.yogeshpaliyal.keypass.ui.changePassword.ChangePassword import com.yogeshpaliyal.keypass.ui.changePassword.ChangePassword
import com.yogeshpaliyal.keypass.ui.detail.AccountDetailPage import com.yogeshpaliyal.keypass.ui.detail.AccountDetailPage
import com.yogeshpaliyal.keypass.ui.dialogs.RestoreChromeBackupDialog
import com.yogeshpaliyal.keypass.ui.dialogs.RestoreKeePassBackupDialog
import com.yogeshpaliyal.keypass.ui.dialogs.RestoreKeyPassBackupDialog
import com.yogeshpaliyal.keypass.ui.dialogs.ValidateKeyPhraseDialog
import com.yogeshpaliyal.keypass.ui.generate.ui.GeneratePasswordScreen import com.yogeshpaliyal.keypass.ui.generate.ui.GeneratePasswordScreen
import com.yogeshpaliyal.keypass.ui.home.Homepage import com.yogeshpaliyal.keypass.ui.home.Homepage
import com.yogeshpaliyal.keypass.ui.home.components.ForgotKeyPhraseDialog import com.yogeshpaliyal.keypass.ui.home.components.ForgotKeyPhraseDialog
import com.yogeshpaliyal.keypass.ui.home.components.ValidateKeyPhraseDialog
import com.yogeshpaliyal.keypass.ui.nav.components.DashboardBottomSheet import com.yogeshpaliyal.keypass.ui.nav.components.DashboardBottomSheet
import com.yogeshpaliyal.keypass.ui.nav.components.KeyPassBottomBar import com.yogeshpaliyal.keypass.ui.nav.components.KeyPassBottomBar
import com.yogeshpaliyal.keypass.ui.passwordHint.PasswordHintScreen import com.yogeshpaliyal.keypass.ui.passwordHint.PasswordHintScreen
@@ -55,6 +58,9 @@ import com.yogeshpaliyal.keypass.ui.redux.states.ForgotKeyPhraseState
import com.yogeshpaliyal.keypass.ui.redux.states.HomeState import com.yogeshpaliyal.keypass.ui.redux.states.HomeState
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
import com.yogeshpaliyal.keypass.ui.redux.states.PasswordGeneratorState import com.yogeshpaliyal.keypass.ui.redux.states.PasswordGeneratorState
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreChromeBackupState
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeePassBackupState
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeyPassBackupState
import com.yogeshpaliyal.keypass.ui.redux.states.SettingsState import com.yogeshpaliyal.keypass.ui.redux.states.SettingsState
import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase import com.yogeshpaliyal.keypass.ui.redux.states.ValidateKeyPhrase
import com.yogeshpaliyal.keypass.ui.settings.MySettingCompose import com.yogeshpaliyal.keypass.ui.settings.MySettingCompose
@@ -132,7 +138,6 @@ fun Dashboard() {
DisposableEffect(KeyPassRedux, context) { DisposableEffect(KeyPassRedux, context) {
dispatch(UpdateContextAction(context)) dispatch(UpdateContextAction(context))
onDispose { onDispose {
dispatch(UpdateContextAction(null)) dispatch(UpdateContextAction(null))
} }
@@ -195,7 +200,10 @@ fun CurrentPage() {
currentScreen.dialog?.let { currentScreen.dialog?.let {
when (it) { when (it) {
is ValidateKeyPhrase -> ValidateKeyPhraseDialog() is ValidateKeyPhrase -> ValidateKeyPhraseDialog()
ForgotKeyPhraseState -> ForgotKeyPhraseDialog() is ForgotKeyPhraseState -> ForgotKeyPhraseDialog()
is RestoreKeyPassBackupState -> RestoreKeyPassBackupDialog(it)
is RestoreChromeBackupState -> RestoreChromeBackupDialog(it)
is RestoreKeePassBackupState -> RestoreKeePassBackupDialog(it)
} }
} }
} }

View File

@@ -9,11 +9,13 @@ import com.yogeshpaliyal.keypass.ui.redux.actions.BottomSheetAction
import com.yogeshpaliyal.keypass.ui.redux.actions.CopyToClipboard import com.yogeshpaliyal.keypass.ui.redux.actions.CopyToClipboard
import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction
import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction 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.StateUpdateAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastActionStr import com.yogeshpaliyal.keypass.ui.redux.actions.ToastActionStr
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateContextAction import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateContextAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogState 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.intentNavigationMiddleware
import com.yogeshpaliyal.keypass.ui.redux.states.BottomSheetState import com.yogeshpaliyal.keypass.ui.redux.states.BottomSheetState
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
@@ -102,10 +104,19 @@ object KeyPassRedux {
) )
) )
} }
is UpdateDialogState -> { is UpdateDialogAction -> {
state.copy(dialog = action.dialogState) state.copy(dialog = action.dialogState)
} }
is UpdateViewModalAction -> {
state.copy(viewModel = action.viewModal)
}
is RestoreAccountsAction -> {
state.viewModel?.restoreBackup(action.accounts)
state
}
else -> state else -> state
} }
} }

View File

@@ -1,13 +1,19 @@
package com.yogeshpaliyal.keypass.ui.redux.actions package com.yogeshpaliyal.keypass.ui.redux.actions
import android.content.Context import android.content.Context
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.ui.home.DashboardViewModel
import com.yogeshpaliyal.keypass.ui.redux.states.DialogState import com.yogeshpaliyal.keypass.ui.redux.states.DialogState
import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState
sealed interface Action sealed interface Action
class UpdateContextAction(val context: Context?) : Action class UpdateContextAction(val context: Context?) : Action
class UpdateViewModalAction(val viewModal: DashboardViewModel?) : Action
data class NavigationAction(val state: ScreenState, val clearBackStack: Boolean = false) : Action data class NavigationAction(val state: ScreenState, val clearBackStack: Boolean = false) : Action
data class RestoreAccountsAction(val accounts: List<AccountModel>) : Action
data class StateUpdateAction(val state: ScreenState) : Action data class StateUpdateAction(val state: ScreenState) : Action
data class UpdateDialogState(val dialogState: DialogState? = null) : Action data class UpdateDialogAction(val dialogState: DialogState? = null) : Action

View File

@@ -1,6 +1,14 @@
package com.yogeshpaliyal.keypass.ui.redux.states package com.yogeshpaliyal.keypass.ui.redux.states
import android.net.Uri
sealed class DialogState() sealed class DialogState()
object ValidateKeyPhrase : DialogState() object ValidateKeyPhrase : DialogState()
object ForgotKeyPhraseState : DialogState() object ForgotKeyPhraseState : DialogState()
sealed class RestoreBackupState(val fileUri: Uri) : DialogState()
data class RestoreKeyPassBackupState(val fileUrii: Uri) : RestoreBackupState(fileUrii)
data class RestoreChromeBackupState(val fileUrii: Uri) : RestoreBackupState(fileUrii)
data class RestoreKeePassBackupState(val fileUrii: Uri) : RestoreBackupState(fileUrii)

View File

@@ -1,10 +1,12 @@
package com.yogeshpaliyal.keypass.ui.redux.states package com.yogeshpaliyal.keypass.ui.redux.states
import android.content.Context import android.content.Context
import com.yogeshpaliyal.keypass.ui.home.DashboardViewModel
import com.yogeshpaliyal.keypass.ui.redux.BottomSheetRoutes import com.yogeshpaliyal.keypass.ui.redux.BottomSheetRoutes
data class KeyPassState( data class KeyPassState(
val context: Context? = null, val context: Context? = null,
val viewModel: DashboardViewModel? = null,
val currentScreen: ScreenState, val currentScreen: ScreenState,
val bottomSheet: BottomSheetState? = null, val bottomSheet: BottomSheetState? = null,
val dialog: DialogState? = null, val dialog: DialogState? = null,

View File

@@ -19,7 +19,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.Feedback import androidx.compose.material.icons.rounded.Feedback
import androidx.compose.material.icons.rounded.Fingerprint import androidx.compose.material.icons.rounded.Fingerprint
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.Password import androidx.compose.material.icons.rounded.Password
import androidx.compose.material.icons.rounded.Share import androidx.compose.material.icons.rounded.Share
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
@@ -48,7 +47,7 @@ import com.yogeshpaliyal.keypass.ui.redux.actions.Action
import com.yogeshpaliyal.keypass.ui.redux.actions.IntentNavigation import com.yogeshpaliyal.keypass.ui.redux.actions.IntentNavigation
import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogState import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
import com.yogeshpaliyal.keypass.ui.redux.states.AboutState import com.yogeshpaliyal.keypass.ui.redux.states.AboutState
import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState
import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState
@@ -115,7 +114,7 @@ fun MySettingCompose() {
title = R.string.validate_keyphrase, title = R.string.validate_keyphrase,
summary = R.string.validate_keyphrase summary = R.string.validate_keyphrase
) { ) {
dispatchAction(UpdateDialogState(ValidateKeyPhrase)) dispatchAction(UpdateDialogAction(ValidateKeyPhrase))
} }
BiometricsOption() BiometricsOption()

View File

@@ -63,4 +63,4 @@ subprojects {
// commandLine = listOf("sh", "./githooks/git-init.sh") // commandLine = listOf("sh", "./githooks/git-init.sh")
//} //}
tasks.getByPath("app:assemble").dependsOn(installGitHook) //tasks.getByPath("app:assemble").dependsOn(installGitHook)