For #24856 - Toggle the "Manage addresses" and "Add address" preference label depending on whether there are saved addresses

pull/543/head
Gabriel Luong 2 years ago committed by mergify[bot]
parent d1c43761eb
commit aff4c03d53

@ -4,6 +4,7 @@
package org.mozilla.fenix.settings.autofill package org.mozilla.fenix.settings.autofill
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.CreditCard import mozilla.components.concept.storage.CreditCard
import mozilla.components.lib.state.Action import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State import mozilla.components.lib.state.State
@ -18,14 +19,16 @@ class AutofillFragmentStore(initialState: AutofillFragmentState) :
) )
/** /**
* The state for [CreditCardsManagementFragment]. * The state used for managing autofill data.
* *
* @property addresses The list of [Address]es to display in the address list.
* @property creditCards The list of [CreditCard]s to display in the credit card list. * @property creditCards The list of [CreditCard]s to display in the credit card list.
* @property isLoading True if the credit cards are still being loaded from storage, * @property isLoading True if the addresses or credit cards are still being loaded from storage,
* otherwise false. * otherwise false.
*/ */
data class AutofillFragmentState( data class AutofillFragmentState(
val creditCards: List<CreditCard>, val addresses: List<Address> = emptyList(),
val creditCards: List<CreditCard> = emptyList(),
val isLoading: Boolean = true val isLoading: Boolean = true
) : State ) : State
@ -34,6 +37,13 @@ data class AutofillFragmentState(
* through the [autofillFragmentStateReducer]. * through the [autofillFragmentStateReducer].
*/ */
sealed class AutofillAction : Action { sealed class AutofillAction : Action {
/**
* Updates the list of addresses with the provided [addresses].
*
* @param addresses The list of [Address]es to display in the address list.
*/
data class UpdateAddresses(val addresses: List<Address>) : AutofillAction()
/** /**
* Updates the list of credit cards with the provided [creditCards]. * Updates the list of credit cards with the provided [creditCards].
* *
@ -54,6 +64,12 @@ private fun autofillFragmentStateReducer(
action: AutofillAction action: AutofillAction
): AutofillFragmentState { ): AutofillFragmentState {
return when (action) { return when (action) {
is AutofillAction.UpdateAddresses -> {
state.copy(
addresses = action.addresses,
isLoading = false
)
}
is AutofillAction.UpdateCreditCards -> { is AutofillAction.UpdateCreditCards -> {
state.copy( state.copy(
creditCards = action.creditCards, creditCards = action.creditCards,

@ -25,6 +25,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage
import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
@ -46,7 +47,8 @@ import org.mozilla.fenix.settings.requirePreference
class AutofillSettingFragment : BiometricPromptPreferenceFragment() { class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
private lateinit var store: AutofillFragmentStore private lateinit var store: AutofillFragmentStore
private var isCreditCardsListLoaded: Boolean = false
private var isAutofillStateLoaded: Boolean = false
/** /**
* List of preferences to be enabled or disabled during authentication. * List of preferences to be enabled or disabled during authentication.
@ -73,9 +75,9 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
store = StoreProvider.get(this) { store = StoreProvider.get(this) {
AutofillFragmentStore(AutofillFragmentState(creditCards = emptyList())) AutofillFragmentStore(AutofillFragmentState())
} }
loadCreditCards() loadAutofillState()
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -99,13 +101,17 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
loadCreditCards() loadAutofillState()
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
consumeFrom(store) { state -> consumeFrom(store) { state ->
if (requireComponents.settings.addressFeature) {
updateAddressPreference(state.addresses.isNotEmpty())
}
updateCardManagementPreference(state.creditCards.isNotEmpty(), findNavController()) updateCardManagementPreference(state.creditCards.isNotEmpty(), findNavController())
} }
@ -114,7 +120,7 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
isCreditCardsListLoaded = false isAutofillStateLoaded = false
} }
override fun onResume() { override fun onResume() {
@ -150,6 +156,25 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
togglePrefsEnabled(creditCardPreferences, true) togglePrefsEnabled(creditCardPreferences, true)
} }
/**
* Updates preferences visibility depending on addresses being already saved or not.
*/
@VisibleForTesting
internal fun updateAddressPreference(hasAddresses: Boolean) {
val manageAddressesPreference =
requirePreference<Preference>(R.string.pref_key_addresses_manage_addresses)
if (hasAddresses) {
manageAddressesPreference.icon = null
manageAddressesPreference.title =
getString(R.string.preferences_addresses_manage_addresses)
} else {
manageAddressesPreference.setIcon(R.drawable.ic_new)
manageAddressesPreference.title =
getString(R.string.preferences_addresses_add_address)
}
}
/** /**
* Updates preferences visibility depending on credit cards being already saved or not. * Updates preferences visibility depending on credit cards being already saved or not.
*/ */
@ -185,22 +210,25 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() {
} }
/** /**
* Fetches all the credit cards from autofillStorage and updates the [AutofillFragmentState] * Fetches all the addresses and credit cards from [AutofillCreditCardsAddressesStorage] and
* with the list of credit cards. * updates the [AutofillFragmentState].
*/ */
private fun loadCreditCards() { private fun loadAutofillState() {
if (isCreditCardsListLoaded) { if (isAutofillStateLoaded) {
return return
} }
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val addresses = requireComponents.core.autofillStorage.getAllAddresses()
val creditCards = requireComponents.core.autofillStorage.getAllCreditCards() val creditCards = requireComponents.core.autofillStorage.getAllCreditCards()
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
store.dispatch(AutofillAction.UpdateAddresses(addresses))
store.dispatch(AutofillAction.UpdateCreditCards(creditCards)) store.dispatch(AutofillAction.UpdateCreditCards(creditCards))
} }
} }
isCreditCardsListLoaded = true isAutofillStateLoaded = true
} }
/** /**

@ -45,7 +45,7 @@ class CreditCardsManagementFragment : SecureFragment() {
val view = inflater.inflate(CreditCardsManagementView.LAYOUT_ID, container, false) val view = inflater.inflate(CreditCardsManagementView.LAYOUT_ID, container, false)
store = StoreProvider.get(this) { store = StoreProvider.get(this) {
AutofillFragmentStore(AutofillFragmentState(creditCards = emptyList())) AutofillFragmentStore(AutofillFragmentState())
} }
interactor = DefaultCreditCardsManagementInteractor( interactor = DefaultCreditCardsManagementInteractor(

@ -1499,6 +1499,8 @@
<string name="preferences_credit_cards_add_credit_card">Add credit card</string> <string name="preferences_credit_cards_add_credit_card">Add credit card</string>
<!-- Preference option for managing saved credit cards --> <!-- Preference option for managing saved credit cards -->
<string name="preferences_credit_cards_manage_saved_cards">Manage saved cards</string> <string name="preferences_credit_cards_manage_saved_cards">Manage saved cards</string>
<!-- Preference option for adding an address -->
<string name="preferences_addresses_add_address">Add address</string>
<!-- Preference option for managing saved addresses --> <!-- Preference option for managing saved addresses -->
<string name="preferences_addresses_manage_addresses">Manage addresses</string> <string name="preferences_addresses_manage_addresses">Manage addresses</string>
<!-- Title of the "Add card" screen --> <!-- Title of the "Add card" screen -->

@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.autofill
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.CreditCard import mozilla.components.concept.storage.CreditCard
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
@ -20,7 +21,7 @@ class AutofillFragmentStoreTest {
@Before @Before
fun setup() { fun setup() {
state = AutofillFragmentState(creditCards = emptyList()) state = AutofillFragmentState()
store = AutofillFragmentStore(state) store = AutofillFragmentStore(state)
} }
@ -34,4 +35,15 @@ class AutofillFragmentStoreTest {
assertEquals(creditCards, store.state.creditCards) assertEquals(creditCards, store.state.creditCards)
assertFalse(store.state.isLoading) assertFalse(store.state.isLoading)
} }
@Test
fun `GIVEN a list of addresses WHEN update addresses action is dispatched THEN addresses state is updated`() = runBlocking {
assertTrue(store.state.isLoading)
val addresses: List<Address> = listOf(mockk(), mockk())
store.dispatch(AutofillAction.UpdateAddresses(addresses)).join()
assertEquals(addresses, store.state.addresses)
assertFalse(store.state.isLoading)
}
} }

@ -11,6 +11,7 @@ import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.TestCoroutineDispatcher
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.CreditCard import mozilla.components.concept.storage.CreditCard
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -35,6 +36,8 @@ class AutofillSettingFragmentTest {
@Before @Before
fun setUp() { fun setUp() {
every { testContext.components.settings } returns mockk(relaxed = true) every { testContext.components.settings } returns mockk(relaxed = true)
every { testContext.components.settings.addressFeature } returns true
every { testContext.components.settings.shouldAutofillCreditCardDetails } returns true
autofillSettingFragment = AutofillSettingFragment() autofillSettingFragment = AutofillSettingFragment()
@ -80,7 +83,7 @@ class AutofillSettingFragmentTest {
AutofillSettingFragmentDirections AutofillSettingFragmentDirections
.actionAutofillSettingFragmentToCreditCardEditorFragment() .actionAutofillSettingFragmentToCreditCardEditorFragment()
val state = AutofillFragmentState(creditCards = emptyList()) val state = AutofillFragmentState()
val store = AutofillFragmentStore(state) val store = AutofillFragmentStore(state)
autofillSettingFragment.updateCardManagementPreference( autofillSettingFragment.updateCardManagementPreference(
@ -95,4 +98,44 @@ class AutofillSettingFragmentTest {
verify { navController.navigate(directions) } verify { navController.navigate(directions) }
} }
@Test
fun `GIVEN the list of addresses is not empty WHEN fragment is displayed THEN the manage addresses preference label is 'Manage addresses'`() {
val preferenceTitle =
testContext.getString(R.string.preferences_addresses_manage_addresses)
val manageCardsPreference = autofillSettingFragment.findPreference<Preference>(
autofillSettingFragment.getPreferenceKey(R.string.pref_key_addresses_manage_addresses)
)
val addresses: List<Address> = listOf(mockk(), mockk())
val state = AutofillFragmentState(addresses = addresses)
val store = AutofillFragmentStore(state)
autofillSettingFragment.updateAddressPreference(
store.state.addresses.isNotEmpty()
)
assertNull(manageCardsPreference?.icon)
assertEquals(preferenceTitle, manageCardsPreference?.title)
}
@Test
fun `GIVEN the list of addresses is empty WHEN fragment is displayed THEN the manage addresses preference label is 'Add address'`() {
val preferenceTitle =
testContext.getString(R.string.preferences_addresses_add_address)
val manageCardsPreference = autofillSettingFragment.findPreference<Preference>(
autofillSettingFragment.getPreferenceKey(R.string.pref_key_addresses_manage_addresses)
)
val state = AutofillFragmentState()
val store = AutofillFragmentStore(state)
autofillSettingFragment.updateAddressPreference(
store.state.addresses.isNotEmpty()
)
assertNotNull(manageCardsPreference?.icon)
assertEquals(preferenceTitle, manageCardsPreference?.title)
}
} }

@ -42,7 +42,7 @@ class CreditCardsManagementViewTest {
@Test @Test
fun testUpdate() { fun testUpdate() {
creditCardsView.update(AutofillFragmentState(creditCards = emptyList())) creditCardsView.update(AutofillFragmentState())
assertTrue(componentCreditCardsBinding.progressBar.isVisible) assertTrue(componentCreditCardsBinding.progressBar.isVisible)
assertFalse(componentCreditCardsBinding.creditCardsList.isVisible) assertFalse(componentCreditCardsBinding.creditCardsList.isVisible)

Loading…
Cancel
Save