Skip to content

Commit

Permalink
Update copy when deleting passwords to consider whether sync enabled …
Browse files Browse the repository at this point in the history
…or not (#4445)

Task/Issue URL:
https://app.asana.com/0/488551667048375/1206638124888258/f

### Description
Copy changes around deleting passwords (now showing a different message
when sync is enabled vs disabled).

Translations will come in
#4446

# Steps to test this PR

## Sync Disabled

### single saved password
- [ ] Disable sync (or leave it disabled if off already)
- [ ] Visit password management screen (overflow -> passwords)
- [ ] Add a single saved password (so you only have 1 total). 
- [ ] Return to password management screen
- [ ] Then choose overflow -> Delete All Passwords
- [ ] Verify the dialog title is **Are you sure you want to delete this
password?**
- [ ] Verify the dialog message says "password" (singular) and "account"
(singular)
- [ ] Verify the dialog message says it'll be "deleted from this device"
- [ ] Cancel the prompt. Click on the overflow of the saved password,
and choose **Delete**. Ensure the last two checks you just did are still
valid for this scenario.


### multiple saved passwords
- [ ] Add a second saved password (so you now have 2 total), and return
to password management screen
- [ ] Then choose overflow -> Delete All Passwords
- [ ] Verify the dialog title is **Are you sure you want to delete 2
passwords**
- [ ] Verify the dialog message says "password**s**" and "account**s**"
- [ ] Verify the dialog message says it'll be "deleted from this device"


## Sync Enabled

### multiple saved passwords
- [ ] Enable sync
- [ ] Visit password management screen (overflow -> passwords). Ensure
you have > 1 saved password.
- [ ] Then choose overflow -> Delete All Passwords
- [ ] Verify the dialog title is **Are you sure you want to delete 2
passwords**
- [ ] Verify the dialog message says "password**s**" and "account**s**"
- [ ] Verify the dialog message says it'll be "deleted from all synced
devices"

### single saved password
- [ ] Enable sync
- [ ] Visit password management screen (overflow -> passwords). Ensure
you have exactly 1 saved password.
- [ ] Then choose overflow -> Delete All Passwords
- [ ] Verify the dialog title is **Are you sure you want to delete this
password?**
- [ ] Verify the dialog message says "password" (singular) and "account"
(singular)
- [ ] Verify the dialog message says it'll be "deleted from all synced
devices"
  • Loading branch information
CDRussell authored May 28, 2024
1 parent 42ee60c commit a12b7a6
Show file tree
Hide file tree
Showing 30 changed files with 259 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ class AutofillManagementCredentialsMode : DuckDuckGoFragment(R.layout.fragment_a
@Inject
lateinit var browserNav: BrowserNav

@Inject
lateinit var stringBuilder: AutofillManagementStringBuilder

// we need to revert the toolbar title when this fragment is destroyed, so will track its initial value
private var initialActionBarTitle: String? = null

Expand Down Expand Up @@ -176,21 +179,28 @@ class AutofillManagementCredentialsMode : DuckDuckGoFragment(R.layout.fragment_a

private fun launchDeleteLoginConfirmationDialog() {
this.context?.let {
TextAlertDialogBuilder(it)
.setTitle(R.string.autofillDeleteLoginDialogTitle)
.setMessage(R.string.credentialManagementDeletePasswordConfirmationMessage)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteCurrentCredentials()
viewModel.onExitCredentialMode()
}
},
)
.show()
lifecycleScope.launch(dispatchers.io()) {
val dialogTitle = stringBuilder.stringForDeletePasswordDialogConfirmationTitle(numberToDelete = 1)
val dialogMessage = stringBuilder.stringForDeletePasswordDialogConfirmationMessage(numberToDelete = 1)

withContext(dispatchers.main()) {
TextAlertDialogBuilder(it)
.setTitle(dialogTitle)
.setMessage(dialogMessage)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteCurrentCredentials()
viewModel.onExitCredentialMode()
}
},
)
.show()
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import com.duckduckgo.di.scopes.FragmentScope
import com.duckduckgo.mobile.android.R as CommonR
import javax.inject.Inject
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@InjectWith(FragmentScope::class)
class AutofillManagementListMode : DuckDuckGoFragment(R.layout.fragment_autofill_management_list_mode) {
Expand Down Expand Up @@ -100,6 +101,9 @@ class AutofillManagementListMode : DuckDuckGoFragment(R.layout.fragment_autofill
@Inject
lateinit var deviceAuthenticator: DeviceAuthenticator

@Inject
lateinit var stringBuilder: AutofillManagementStringBuilder

val viewModel by lazy {
ViewModelProvider(requireActivity(), viewModelFactory)[AutofillSettingsViewModel::class.java]
}
Expand Down Expand Up @@ -350,61 +354,54 @@ class AutofillManagementListMode : DuckDuckGoFragment(R.layout.fragment_autofill

private fun launchDeleteLoginConfirmationDialog(loginCredentials: LoginCredentials) {
this.context?.let {
TextAlertDialogBuilder(it)
.setTitle(R.string.autofillDeleteLoginDialogTitle)
.setMessage(R.string.credentialManagementDeletePasswordConfirmationMessage)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteCredentials(loginCredentials)
}
},
)
.show()
lifecycleScope.launch(dispatchers.io()) {
val dialogTitle = stringBuilder.stringForDeletePasswordDialogConfirmationTitle(numberToDelete = 1)
val dialogMessage = stringBuilder.stringForDeletePasswordDialogConfirmationMessage(numberToDelete = 1)

withContext(dispatchers.main()) {
TextAlertDialogBuilder(it)
.setTitle(dialogTitle)
.setMessage(dialogMessage)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteCredentials(loginCredentials)
}
},
)
.show()
}
}
}
}

private fun launchDeleteAllLoginsConfirmationDialog(numberToDelete: Int) {
val displayStrings = getDisplayStringsForDeletingAllLogins(numberToDelete)

this.context?.let {
TextAlertDialogBuilder(it)
.setTitle(displayStrings.first)
.setMessage(displayStrings.second)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.setCancellable(true)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteAllPasswordsConfirmed()
}
},
)
.show()
}
}

/**
* Returns a pair of strings for the title and message of the delete all logins confirmation dialog.
*
* The strings will change depending on if there is only one login to delete or multiple.
*/
private fun getDisplayStringsForDeletingAllLogins(numberToDelete: Int): Pair<String, String> {
return if (numberToDelete == 1) {
Pair(
getString(R.string.autofillDeleteLoginDialogTitle),
getString(R.string.credentialManagementDeletePasswordConfirmationMessage),
)
} else {
Pair(
resources.getQuantityString(R.plurals.credentialManagementDeleteAllPasswordsConfirmationTitle, numberToDelete, numberToDelete),
getString(R.string.credentialManagementDeleteAllPasswordsConfirmationMessage),
)
lifecycleScope.launch(dispatchers.io()) {
val dialogTitle = stringBuilder.stringForDeletePasswordDialogConfirmationTitle(numberToDelete)
val dialogMessage = stringBuilder.stringForDeletePasswordDialogConfirmationMessage(numberToDelete)

withContext(dispatchers.main()) {
TextAlertDialogBuilder(it)
.setTitle(dialogTitle)
.setMessage(dialogMessage)
.setDestructiveButtons(true)
.setPositiveButton(R.string.autofillDeleteLoginDialogDelete)
.setNegativeButton(R.string.autofillDeleteLoginDialogCancel)
.setCancellable(true)
.addEventListener(
object : TextAlertDialogBuilder.EventListener() {
override fun onPositiveButtonClicked() {
viewModel.onDeleteAllPasswordsConfirmed()
}
},
)
.show()
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.ui.credential.management.viewing

import android.content.Context
import android.content.res.Resources
import com.duckduckgo.autofill.impl.R
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.FragmentScope
import com.duckduckgo.sync.api.DeviceSyncState
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.withContext

interface AutofillManagementStringBuilder {
fun stringForDeletePasswordDialogConfirmationTitle(numberToDelete: Int): String
suspend fun stringForDeletePasswordDialogConfirmationMessage(numberToDelete: Int): String
}

@ContributesBinding(FragmentScope::class)
class AutofillManagementStringBuilderImpl @Inject constructor(
private val context: Context,
private val deviceSyncState: DeviceSyncState,
private val dispatchers: DispatcherProvider,
) : AutofillManagementStringBuilder {

override fun stringForDeletePasswordDialogConfirmationTitle(numberToDelete: Int): String {
if (numberToDelete == 1) {
return context.getString(R.string.credentialManagementDeleteAllPasswordsDialogConfirmationTitleSingular)
}

return context.resources.getQuantityString(
R.plurals.credentialManagementDeleteAllPasswordsDialogConfirmationTitlePlural,
numberToDelete,
numberToDelete,
)
}

override suspend fun stringForDeletePasswordDialogConfirmationMessage(numberToDelete: Int): String {
val firstMessage = context.resources.deleteAllPasswordsWarning(numberToDelete)
val secondMessage = if (numberToDelete == 1) {
context.resources.getString(R.string.credentialManagementDeleteAllSecondInstructionSingular)
} else {
context.resources.getQuantityString(R.plurals.credentialManagementDeleteAllSecondInstructionPlural, numberToDelete)
}
return "$firstMessage $secondMessage"
}

private suspend fun Resources.deleteAllPasswordsWarning(numberToDelete: Int): String {
return withContext(dispatchers.io()) {
return@withContext if (deviceSyncState.isUserSignedInOnDevice()) {
if (numberToDelete == 1) {
getString(R.string.credentialManagementDeleteAllPasswordsFirstInstructionSyncedSingular)
} else {
getQuantityString(R.plurals.credentialManagementDeleteAllPasswordsFirstInstructionSyncedPlural, numberToDelete)
}
} else {
if (numberToDelete == 1) {
getString(R.string.credentialManagementDeleteAllPasswordsDialogFirstInstructionNotSyncedSingular)
} else {
getQuantityString(R.plurals.credentialManagementDeleteAllPasswordsDialogFirstInstructionNotSyncedPlural, numberToDelete)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@
<string name="saveLoginDialogSubtitle">Паролите се съхраняват сигурно на Вашето устройство.</string>
<string name="autofillManagementAddLogin">Добавяне на парола</string>
<string name="credentialManagementEditLoginTitleHint">Заглавие</string>
<string name="autofillDeleteLoginDialogTitle">Сигурни ли сте, че искате да изтриете тази парола?</string>
<string name="autofillManagementDeletedConfirmation">Паролата е изтрита</string>
<string name="credentialManagementEditLastUpdated" instruction="Placeholder is a date">Последна актуализация %1$s</string>
<string name="autofillDisableAutofillPromptTitle">Искате ли да продължите да запазвате пароли?</string>
Expand All @@ -152,13 +151,6 @@
<string name="credentialManagementClearNeverForThisSiteDialogNegativeButton">Отмени</string>

<string name="credentialManagementDeleteAllPasswordsMenu">Изтриване на всички пароли</string>
<plurals name="credentialManagementDeleteAllPasswordsConfirmationTitle" instruction="Placeholder is a number representing how many passwords will be deleted">
<item quantity="one">Сигурни ли сте, че искате да изтриете %1$d парола?</item>
<item quantity="other">Сигурни ли сте, че искате да изтриете %1$d пароли?</item>
</plurals>

<string name="credentialManagementDeletePasswordConfirmationMessage">Вашата парола ще бъде изтрита от всички синхронизирани устройства. Уверете се, че все още имате друг начин за достъп до акаунта си.</string>
<string name="credentialManagementDeleteAllPasswordsConfirmationMessage">Вашите пароли ще бъдат изтрити от всички синхронизирани устройства. Уверете се, че все още имате друг начин за достъп до Вашите акаунти.</string>

<plurals name="credentialManagementDeleteAllPasswordsSnackbarConfirmation" instruction="Placeholder is a number representing how many passwords were deleted">
<item quantity="one">%1$d изтрита парола</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@
<string name="saveLoginDialogSubtitle">Hesla se bezpečně ukládají do tvého zařízení.</string>
<string name="autofillManagementAddLogin">Přidat heslo</string>
<string name="credentialManagementEditLoginTitleHint">Název</string>
<string name="autofillDeleteLoginDialogTitle">Opravdu chceš smazat tohle heslo?</string>
<string name="autofillManagementDeletedConfirmation">Heslo smazáno</string>
<string name="credentialManagementEditLastUpdated" instruction="Placeholder is a date">Poslední aktualizace %1$s</string>
<string name="autofillDisableAutofillPromptTitle">Chceš dál ukládat hesla?</string>
Expand All @@ -152,15 +151,6 @@
<string name="credentialManagementClearNeverForThisSiteDialogNegativeButton">Zrušit</string>

<string name="credentialManagementDeleteAllPasswordsMenu">Smazat všechna hesla</string>
<plurals name="credentialManagementDeleteAllPasswordsConfirmationTitle" instruction="Placeholder is a number representing how many passwords will be deleted">
<item quantity="one">Opravdu chceš smazat %1$d heslo?</item>
<item quantity="few">Opravdu chceš smazat %1$d hesla?</item>
<item quantity="many">Opravdu chceš smazat %1$d hesla?</item>
<item quantity="other">Opravdu chceš smazat %1$d hesel?</item>
</plurals>

<string name="credentialManagementDeletePasswordConfirmationMessage">Tvoje heslo se smaže ze všech synchronizovaných zařízení. Zkontroluj si předtím, že se i tak dostaneš ke svému účtu.</string>
<string name="credentialManagementDeleteAllPasswordsConfirmationMessage">Tvoje hesla se smažou ze všech synchronizovaných zařízení. Zkontroluj si předtím, že se k účtům i tak dostaneš.</string>

<plurals name="credentialManagementDeleteAllPasswordsSnackbarConfirmation" instruction="Placeholder is a number representing how many passwords were deleted">
<item quantity="one">%1$d smazané heslo</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@
<string name="saveLoginDialogSubtitle">Adgangskoder gemmes sikkert på din enhed.</string>
<string name="autofillManagementAddLogin">Tilføj adgangskode</string>
<string name="credentialManagementEditLoginTitleHint">Titel</string>
<string name="autofillDeleteLoginDialogTitle">Er du sikker på, at du vil slette denne adgangskode?</string>
<string name="autofillManagementDeletedConfirmation">Adgangskode slettet</string>
<string name="credentialManagementEditLastUpdated" instruction="Placeholder is a date">Sidst opdateret %1$s</string>
<string name="autofillDisableAutofillPromptTitle">Vil du fortsætte med at gemme adgangskoder?</string>
Expand All @@ -152,13 +151,6 @@
<string name="credentialManagementClearNeverForThisSiteDialogNegativeButton">Annuller</string>

<string name="credentialManagementDeleteAllPasswordsMenu">Slet alle adgangskoder</string>
<plurals name="credentialManagementDeleteAllPasswordsConfirmationTitle" instruction="Placeholder is a number representing how many passwords will be deleted">
<item quantity="one">Er du sikker på, at du vil slette %1$d adgangskode?</item>
<item quantity="other">Er du sikker på, at du vil slette %1$d adgangskoder?</item>
</plurals>

<string name="credentialManagementDeletePasswordConfirmationMessage">Din adgangskode slettes fra alle synkroniserede enheder. Husk at sikre, at du stadig har adgang til din konto.</string>
<string name="credentialManagementDeleteAllPasswordsConfirmationMessage">Dine adgangskoder slettes fra alle synkroniserede enheder. Husk at sikre, at du stadig har adgang til dine konti.</string>

<plurals name="credentialManagementDeleteAllPasswordsSnackbarConfirmation" instruction="Placeholder is a number representing how many passwords were deleted">
<item quantity="one">%1$d adgangskode slettet</item>
Expand Down
Loading

0 comments on commit a12b7a6

Please sign in to comment.