aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/app/passwordstore/ui/editor/EditorDestination.kt9
-rw-r--r--app/src/main/java/app/passwordstore/ui/list/StoreActions.kt34
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt6
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt2
-rw-r--r--app/src/main/java/app/passwordstore/util/settings/GitSettings.kt6
-rw-r--r--app/src/main/java/app/passwordstore/util/settings/PreferenceKeys.kt1
-rw-r--r--app/src/main/res/values-it/strings.xml3
-rw-r--r--app/src/main/res/values/strings.xml3
-rw-r--r--app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt17
9 files changed, 76 insertions, 5 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/editor/EditorDestination.kt b/app/src/main/java/app/passwordstore/ui/editor/EditorDestination.kt
index 8e433957..617d804a 100644
--- a/app/src/main/java/app/passwordstore/ui/editor/EditorDestination.kt
+++ b/app/src/main/java/app/passwordstore/ui/editor/EditorDestination.kt
@@ -35,7 +35,9 @@ import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.ui.crypto.copyPasswordToClipboard
import app.passwordstore.ui.crypto.getPGPIdentifiers
+import app.passwordstore.ui.git.base.BaseGitActivity
import app.passwordstore.ui.list.FolderPicker
+import app.passwordstore.ui.list.commitAndPush
import app.passwordstore.util.extensions.commitChange
import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.isInsideRepository
@@ -148,12 +150,15 @@ fun EditorDestination(
}
// ponytail: commit failures are not shown, as for the list's move and delete. A commit cut
// short by recreation runs again; one that already went through finds nothing to commit.
- activity.commitChange(
+ val message =
activity.getString(
if (original != null) R.string.git_commit_edit_text else R.string.git_commit_add_text,
saved.file.relativeTo(root).path.removeSuffix(".gpg"),
)
- )
+ // ponytail: AutofillSaveActivity closes right after saving, which would cancel a push, so its
+ // saves stay local until the next push or sync.
+ if (activity is BaseGitActivity) activity.commitAndPush(message)
+ else activity.commitChange(message)
keyboard?.hide()
focusManager.clearFocus()
viewModel.left()
diff --git a/app/src/main/java/app/passwordstore/ui/list/StoreActions.kt b/app/src/main/java/app/passwordstore/ui/list/StoreActions.kt
index cff0964c..94a234c1 100644
--- a/app/src/main/java/app/passwordstore/ui/list/StoreActions.kt
+++ b/app/src/main/java/app/passwordstore/ui/list/StoreActions.kt
@@ -6,7 +6,9 @@
package app.passwordstore.ui.list
import android.content.Context
+import androidx.compose.material3.SnackbarDuration
import androidx.core.content.edit
+import androidx.lifecycle.lifecycleScope
import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.ui.crypto.getLongName
@@ -20,12 +22,17 @@ import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.isInsideRepository
import app.passwordstore.util.extensions.listFilesRecursively
import app.passwordstore.util.extensions.sharedPrefs
+import app.passwordstore.util.extensions.snackbar
import app.passwordstore.util.settings.PasswordSortOrder
import app.passwordstore.util.settings.PreferenceKeys
+import com.github.michaelbull.result.onFailure
+import com.github.michaelbull.result.onSuccess
import java.io.File
import java.nio.file.Files
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
+import logcat.asLog
import logcat.logcat
/** Shared preferences file of the "recently used" sort order, keyed by base64 of the path. */
@@ -64,6 +71,27 @@ internal fun File.deleteWithoutFollowingLinks(): Boolean {
return delete()
}
+/**
+ * Commits the store and, when the user asked for it, pushes without holding up the caller. A failed
+ * push only says so: the commit stays local, and [BaseGitActivity.promptOnErrorHandler] would
+ * delete the saved credentials over what may just be a missing network.
+ *
+ * ponytail: a push cut off by recreation is not resumed; the next one sends everything. An SSH key
+ * behind biometrics prompts on each change.
+ */
+suspend fun BaseGitActivity.commitAndPush(message: String) {
+ commitChange(message).onSuccess {
+ if (gitSettings.pushAfterCommit && gitSettings.url != null) {
+ lifecycleScope.launch {
+ launchGitOperation(BaseGitActivity.GitOp.PUSH).onFailure {
+ logcat(ERROR) { it.asLog() }
+ snackbar(getString(R.string.git_push_after_commit_failed), SnackbarDuration.Long)
+ }
+ }
+ }
+ }
+}
+
/** Deletes [files] (entries or whole folders), drops their autofill matches and commits. */
suspend fun BaseGitActivity.deleteItems(files: List<File>) {
val root = PasswordRepository.getRepositoryDirectory()
@@ -72,7 +100,7 @@ suspend fun BaseGitActivity.deleteItems(files: List<File>) {
files.forEach { it.deleteWithoutFollowingLinks() }
AutofillMatcher.updateMatches(applicationContext, delete = deleted)
}
- commitChange(
+ commitAndPush(
getString(
R.string.git_commit_remove_text,
files.joinToString(", ") { it.toRelativeString(root) },
@@ -116,14 +144,14 @@ suspend fun BaseGitActivity.moveItems(files: List<File>, target: File) {
val relative = "${target.absolutePath}/".replace(repo, "").replace("/+".toRegex(), "/")
getString(R.string.git_commit_move_multiple_text, relative)
}
- commitChange(message)
+ commitAndPush(message)
}
/** Renames the folder [from] to [to], keeps its recently-used time, then commits. */
suspend fun BaseGitActivity.renameFolder(from: File, to: File) {
if (!moveFile(from, to)) return
moveHistory(from, to)
- commitChange(getString(R.string.git_commit_move_text, from.name, to.name))
+ commitAndPush(getString(R.string.git_commit_move_text, from.name, to.name))
}
/** Carries the "recently used" time of [from] over to [to]. */
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
index ed78608f..a7bbe30a 100644
--- a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
+++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt
@@ -258,6 +258,12 @@ fun SettingsSectionScreen(
else R.string.pref_rebase_on_pull_summary
),
)
+ PreferenceSwitch(
+ title = stringResource(R.string.pref_push_after_commit_title),
+ checked = state.pushAfterCommit,
+ onCheckedChange = { onBoolean(PreferenceKeys.PUSH_AFTER_COMMIT, it) },
+ summary = stringResource(R.string.pref_push_after_commit_summary),
+ )
if (state.isGitRepo) {
PreferenceAction(
title = stringResource(R.string.pref_edit_git_server_settings),
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt
index ef604f1f..cfc576ae 100644
--- a/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt
+++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt
@@ -55,6 +55,7 @@ data class SettingsState(
val showPassword: Boolean,
val copyOnDecrypt: Boolean,
val rebaseOnPull: Boolean,
+ val pushAfterCommit: Boolean,
val isGitRepo: Boolean,
val httpsRemote: Boolean,
val canShowSshKey: Boolean,
@@ -205,6 +206,7 @@ constructor(
showPassword = prefs.getBoolean(PreferenceKeys.SHOW_PASSWORD, true),
copyOnDecrypt = prefs.getBoolean(PreferenceKeys.COPY_ON_DECRYPT, false),
rebaseOnPull = gitSettings.rebaseOnPull,
+ pushAfterCommit = gitSettings.pushAfterCommit,
isGitRepo = PasswordRepository.isGitRepo(),
httpsRemote = gitSettings.url?.startsWith("https") == true,
canShowSshKey = SshKey.canShowSshPublicKey,
diff --git a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
index 004028c4..0052f79c 100644
--- a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
+++ b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
@@ -125,6 +125,12 @@ constructor(
settings.edit { putBoolean(PreferenceKeys.REBASE_ON_PULL, value) }
}
+ var pushAfterCommit
+ get() = settings.getBoolean(PreferenceKeys.PUSH_AFTER_COMMIT, false)
+ set(value) {
+ settings.edit { putBoolean(PreferenceKeys.PUSH_AFTER_COMMIT, value) }
+ }
+
sealed class UpdateConnectionSettingsResult {
class MissingUsername(val newProtocol: Protocol) : UpdateConnectionSettingsResult()
diff --git a/app/src/main/java/app/passwordstore/util/settings/PreferenceKeys.kt b/app/src/main/java/app/passwordstore/util/settings/PreferenceKeys.kt
index ecc7a51c..9fd2658d 100644
--- a/app/src/main/java/app/passwordstore/util/settings/PreferenceKeys.kt
+++ b/app/src/main/java/app/passwordstore/util/settings/PreferenceKeys.kt
@@ -86,6 +86,7 @@ object PreferenceKeys {
const val PROXY_PASSWORD = "proxy_password"
const val REBASE_ON_PULL = "rebase_on_pull"
+ const val PUSH_AFTER_COMMIT = "push_after_commit"
const val DICEWARE_SEPARATOR = "diceware_separator"
const val DICEWARE_LENGTH = "diceware_length"
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 07f63cd9..b4569746 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -288,6 +288,9 @@
<string name="pref_rebase_on_pull_title">Rebase al pull</string>
<string name="pref_rebase_on_pull_summary">Durante pull o sincronizzazione, crea un merge commit con le modifiche del remote</string>
<string name="pref_rebase_on_pull_summary_on">Durante pull o sincronizzazione, esegui il rebase dei commit che non sono sul remote</string>
+ <string name="pref_push_after_commit_title">Push dopo ogni modifica</string>
+ <string name="pref_push_after_commit_summary">Esegui il push sul repository remoto dopo aver salvato, spostato o eliminato nell\'app</string>
+ <string name="git_push_after_commit_failed">Salvato sul dispositivo, ma push non riuscito. Sincronizza per riprovare</string>
<string name="pref_disable_sync_on_pull_title">Disattiva la sincronizzazione trascinando</string>
<string name="pref_disable_sync_on_pull_summary">Trascinare verso il basso l\'elenco delle password non avvia una sincronizzazione Git</string>
<string name="pref_import_pgp_key_title">Importa chiave PGP</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c8782fad..fa9fb0f3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -126,6 +126,9 @@
<string name="pref_rebase_on_pull_title">Rebase on pull</string>
<string name="pref_rebase_on_pull_summary">When pulling or syncing, create a merge commit with upstream changes</string>
<string name="pref_rebase_on_pull_summary_on">When pulling or syncing, rebase commits that are not present in the remote repository</string>
+ <string name="pref_push_after_commit_title">Push after each change</string>
+ <string name="pref_push_after_commit_summary">Push to the remote repository after saving, moving or deleting in the app</string>
+ <string name="git_push_after_commit_failed">Saved on this device, but not pushed. Sync to try again</string>
<string name="pref_disable_sync_on_pull_title">Disable pull to sync action</string>
<string name="pref_disable_sync_on_pull_summary">Prevent pulling down on the password list from triggering a Git sync</string>
<string name="pref_import_pgp_key_title">Import PGP key</string>
diff --git a/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt
index 6b30c248..111e7f51 100644
--- a/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt
+++ b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt
@@ -9,7 +9,9 @@ import app.passwordstore.util.extensions.getString
import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
import kotlin.test.assertNull
+import kotlin.test.assertTrue
class GitSettingsTest {
@@ -24,4 +26,19 @@ class GitSettingsTest {
assertEquals("proxy-user", proxy.getString(PreferenceKeys.PROXY_USERNAME))
assertNull(settings.getString(PreferenceKeys.PROXY_USERNAME))
}
+
+ @Test
+ fun pushAfterCommitIsOptIn() {
+ val settings = SPMockBuilder().createSharedPreferences()
+ val gitSettings =
+ GitSettings(
+ settings,
+ SPMockBuilder().createSharedPreferences(),
+ SPMockBuilder().createSharedPreferences(),
+ "/nonexistent",
+ )
+ assertFalse(gitSettings.pushAfterCommit)
+ gitSettings.pushAfterCommit = true
+ assertTrue(settings.getBoolean(PreferenceKeys.PUSH_AFTER_COMMIT, false))
+ }
}