mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-01-05 16:36:31 -06:00
feat: standardise flow for dialogs (#1061)
* feat: standardise flow for dialogs * feat: minor change
This commit is contained in:
committed by
GitHub
parent
76e3e7eb0a
commit
7831511ed6
@@ -1,8 +1,6 @@
|
||||
package com.yogeshpaliyal.keypass.importer
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.yogeshpaliyal.common.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
|
||||
|
||||
interface AccountsImporter {
|
||||
@@ -12,6 +10,5 @@ interface AccountsImporter {
|
||||
|
||||
fun allowedMimeType(): String
|
||||
|
||||
@Composable
|
||||
fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit)
|
||||
fun readFileGetAction(file: Uri): Action
|
||||
}
|
||||
|
||||
@@ -1,32 +1,10 @@
|
||||
package com.yogeshpaliyal.keypass.importer
|
||||
|
||||
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.ui.redux.actions.Action
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
|
||||
import java.io.FileNotFoundException
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.RestoreChromeBackupState
|
||||
|
||||
class ChromeAccountImporter : AccountsImporter {
|
||||
override fun getImporterTitle(): Int = R.string.google_backup
|
||||
@@ -36,67 +14,9 @@ class ChromeAccountImporter : AccountsImporter {
|
||||
return "text/comma-separated-values"
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
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()
|
||||
override fun readFileGetAction(
|
||||
file: Uri
|
||||
): Action {
|
||||
return UpdateDialogAction(RestoreChromeBackupState(file))
|
||||
}
|
||||
}
|
||||
|
||||
@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()
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package com.yogeshpaliyal.keypass.importer
|
||||
|
||||
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.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 {
|
||||
override fun getImporterTitle(): Int = R.string.keepass_backup
|
||||
@@ -19,40 +14,9 @@ class KeePassAccountImporter : AccountsImporter {
|
||||
return "text/comma-separated-values"
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
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()
|
||||
override fun readFileGetAction(
|
||||
file: Uri
|
||||
): Action {
|
||||
return UpdateDialogAction(RestoreKeePassBackupState(file))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,9 @@
|
||||
package com.yogeshpaliyal.keypass.importer
|
||||
|
||||
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.ui.redux.actions.Action
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
|
||||
import kotlinx.coroutines.launch
|
||||
import org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateDialogAction
|
||||
|
||||
class KeyPassAccountImporter : AccountsImporter {
|
||||
override fun getImporterTitle(): Int {
|
||||
@@ -37,86 +16,9 @@ class KeyPassAccountImporter : AccountsImporter {
|
||||
return "*/*"
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun readFile(file: Uri, resolve: (List<AccountModel>) -> Unit, onCompleteOrCancel: (action: Action?) -> Unit) {
|
||||
RestoreDialog(
|
||||
selectedFile = file,
|
||||
hideDialog = {
|
||||
onCompleteOrCancel(null)
|
||||
},
|
||||
saveAccounts = resolve
|
||||
)
|
||||
override fun readFileGetAction(
|
||||
file: Uri
|
||||
): Action {
|
||||
return UpdateDialogAction(com.yogeshpaliyal.keypass.ui.redux.states.RestoreKeyPassBackupState(file))
|
||||
}
|
||||
}
|
||||
|
||||
@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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,13 +64,7 @@ fun BackupImporter(state: BackupImporterState, mViewModel: DashboardViewModel =
|
||||
})
|
||||
|
||||
result?.let {
|
||||
state.selectedImported?.readFile(result, {
|
||||
setResult(null)
|
||||
setRestoredAccounts(it)
|
||||
}) {
|
||||
it?.let(dispatchAction)
|
||||
dispatchAction(StateUpdateAction(state = state.copy(selectedImported = null)))
|
||||
}
|
||||
state.selectedImported?.readFileGetAction(result)?.let { it1 -> dispatchAction(it1) }
|
||||
}
|
||||
|
||||
Scaffold(bottomBar = {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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.Spacer
|
||||
@@ -21,7 +21,7 @@ import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
|
||||
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 kotlinx.coroutines.launch
|
||||
import org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
@@ -40,7 +40,7 @@ fun ValidateKeyPhraseDialog() {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val hideDialog: () -> Unit = {
|
||||
dispatchAction(UpdateDialogState(null))
|
||||
dispatchAction(UpdateDialogAction(null))
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
@@ -92,7 +92,7 @@ fun ValidateKeyPhraseDialog() {
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
|
||||
TextButton(onClick = {
|
||||
dispatchAction(UpdateDialogState(ForgotKeyPhraseState))
|
||||
dispatchAction(UpdateDialogAction(ForgotKeyPhraseState))
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.forgot_keyphrase_question))
|
||||
}
|
||||
@@ -43,9 +43,11 @@ class DashboardViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun restoreBackup(
|
||||
fun restoreBackup(
|
||||
list: List<AccountModel>
|
||||
) {
|
||||
return appDb.saveToDb(list)
|
||||
viewModelScope.launch {
|
||||
appDb.saveToDb(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.SearchBar
|
||||
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.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.ValidateKeyPhrase
|
||||
import org.reduxkotlin.compose.rememberDispatcher
|
||||
import org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/*
|
||||
@@ -52,12 +55,16 @@ fun Homepage(
|
||||
|
||||
val listOfAccountsLiveData by mViewModel.mediator.observeAsState()
|
||||
|
||||
val dispatchAction = rememberDispatcher()
|
||||
val dispatchAction = rememberTypedDispatcher<Action>()
|
||||
|
||||
LaunchedEffect(tag, keyword, sortField, sortAscendingOrder, block = {
|
||||
mViewModel.queryUpdated(keyword, tag, sortField, sortAscendingOrder)
|
||||
})
|
||||
|
||||
LaunchedEffect(KeyPassRedux, mViewModel) {
|
||||
dispatchAction(UpdateViewModalAction(mViewModel))
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit, {
|
||||
if (userSettings.backupKey == null) {
|
||||
return@LaunchedEffect
|
||||
@@ -68,7 +75,7 @@ fun Homepage(
|
||||
val diffInDays = TimeUnit.MILLISECONDS.toDays(diff)
|
||||
if (diffInDays >= 7) {
|
||||
// Show the modal
|
||||
dispatchAction(UpdateDialogState(dialogState = ValidateKeyPhrase))
|
||||
dispatchAction(UpdateDialogAction(dialogState = ValidateKeyPhrase))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
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 org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
|
||||
@@ -22,7 +22,7 @@ fun ForgotKeyPhraseDialog() {
|
||||
val dispatchAction = rememberTypedDispatcher<Action>()
|
||||
|
||||
val hideDialog: () -> Unit = {
|
||||
dispatchAction(UpdateDialogState(ValidateKeyPhrase))
|
||||
dispatchAction(UpdateDialogAction(ValidateKeyPhrase))
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
|
||||
@@ -33,10 +33,13 @@ import com.yogeshpaliyal.keypass.ui.backupsImport.BackupImporter
|
||||
import com.yogeshpaliyal.keypass.ui.changeDefaultPasswordLength.ChangeDefaultPasswordLengthScreen
|
||||
import com.yogeshpaliyal.keypass.ui.changePassword.ChangePassword
|
||||
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.home.Homepage
|
||||
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.KeyPassBottomBar
|
||||
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.KeyPassState
|
||||
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.ValidateKeyPhrase
|
||||
import com.yogeshpaliyal.keypass.ui.settings.MySettingCompose
|
||||
@@ -132,7 +138,6 @@ fun Dashboard() {
|
||||
|
||||
DisposableEffect(KeyPassRedux, context) {
|
||||
dispatch(UpdateContextAction(context))
|
||||
|
||||
onDispose {
|
||||
dispatch(UpdateContextAction(null))
|
||||
}
|
||||
@@ -195,7 +200,10 @@ fun CurrentPage() {
|
||||
currentScreen.dialog?.let {
|
||||
when (it) {
|
||||
is ValidateKeyPhrase -> ValidateKeyPhraseDialog()
|
||||
ForgotKeyPhraseState -> ForgotKeyPhraseDialog()
|
||||
is ForgotKeyPhraseState -> ForgotKeyPhraseDialog()
|
||||
is RestoreKeyPassBackupState -> RestoreKeyPassBackupDialog(it)
|
||||
is RestoreChromeBackupState -> RestoreChromeBackupDialog(it)
|
||||
is RestoreKeePassBackupState -> RestoreKeePassBackupDialog(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.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.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.states.BottomSheetState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState
|
||||
@@ -102,10 +104,19 @@ object KeyPassRedux {
|
||||
)
|
||||
)
|
||||
}
|
||||
is UpdateDialogState -> {
|
||||
is UpdateDialogAction -> {
|
||||
state.copy(dialog = action.dialogState)
|
||||
}
|
||||
|
||||
is UpdateViewModalAction -> {
|
||||
state.copy(viewModel = action.viewModal)
|
||||
}
|
||||
|
||||
is RestoreAccountsAction -> {
|
||||
state.viewModel?.restoreBackup(action.accounts)
|
||||
state
|
||||
}
|
||||
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.yogeshpaliyal.keypass.ui.redux.actions
|
||||
|
||||
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.ScreenState
|
||||
|
||||
sealed interface 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 RestoreAccountsAction(val accounts: List<AccountModel>) : Action
|
||||
|
||||
data class StateUpdateAction(val state: ScreenState) : Action
|
||||
data class UpdateDialogState(val dialogState: DialogState? = null) : Action
|
||||
data class UpdateDialogAction(val dialogState: DialogState? = null) : Action
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
package com.yogeshpaliyal.keypass.ui.redux.states
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
sealed class DialogState()
|
||||
|
||||
object ValidateKeyPhrase : 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)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.yogeshpaliyal.keypass.ui.redux.states
|
||||
|
||||
import android.content.Context
|
||||
import com.yogeshpaliyal.keypass.ui.home.DashboardViewModel
|
||||
import com.yogeshpaliyal.keypass.ui.redux.BottomSheetRoutes
|
||||
|
||||
data class KeyPassState(
|
||||
val context: Context? = null,
|
||||
val viewModel: DashboardViewModel? = null,
|
||||
val currentScreen: ScreenState,
|
||||
val bottomSheet: BottomSheetState? = null,
|
||||
val dialog: DialogState? = null,
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.Feedback
|
||||
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.Share
|
||||
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.NavigationAction
|
||||
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.BackupImporterState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState
|
||||
@@ -115,7 +114,7 @@ fun MySettingCompose() {
|
||||
title = R.string.validate_keyphrase,
|
||||
summary = R.string.validate_keyphrase
|
||||
) {
|
||||
dispatchAction(UpdateDialogState(ValidateKeyPhrase))
|
||||
dispatchAction(UpdateDialogAction(ValidateKeyPhrase))
|
||||
}
|
||||
|
||||
BiometricsOption()
|
||||
|
||||
@@ -63,4 +63,4 @@ subprojects {
|
||||
// commandLine = listOf("sh", "./githooks/git-init.sh")
|
||||
//}
|
||||
|
||||
tasks.getByPath("app:assemble").dependsOn(installGitHook)
|
||||
//tasks.getByPath("app:assemble").dependsOn(installGitHook)
|
||||
|
||||
Reference in New Issue
Block a user