[fenix] For https://github.com/mozilla-mobile/fenix/issues/24850 - Display an address list for managing existing addresses
parent
3bc3ca9610
commit
0782aba4b0
@ -0,0 +1,82 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.lib.state.ext.observeAsComposableState
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.settings.address.controller.DefaultAddressManagementController
|
||||
import org.mozilla.fenix.settings.address.interactor.AddressManagementInteractor
|
||||
import org.mozilla.fenix.settings.address.interactor.DefaultAddressManagementInteractor
|
||||
import org.mozilla.fenix.settings.address.view.AddressList
|
||||
import org.mozilla.fenix.settings.autofill.AutofillAction
|
||||
import org.mozilla.fenix.settings.autofill.AutofillFragmentState
|
||||
import org.mozilla.fenix.settings.autofill.AutofillFragmentStore
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* Displays a list of saved addresses.
|
||||
*/
|
||||
class AddressManagementFragment : Fragment() {
|
||||
|
||||
private lateinit var store: AutofillFragmentStore
|
||||
private lateinit var interactor: AddressManagementInteractor
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
store = StoreProvider.get(this) {
|
||||
AutofillFragmentStore(AutofillFragmentState())
|
||||
}
|
||||
|
||||
interactor = DefaultAddressManagementInteractor(
|
||||
controller = DefaultAddressManagementController(
|
||||
navController = findNavController()
|
||||
)
|
||||
)
|
||||
|
||||
loadAddresses()
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
FirefoxTheme {
|
||||
val addresses = store.observeAsComposableState { state -> state.addresses }
|
||||
|
||||
AddressList(
|
||||
addresses = addresses.value ?: emptyList(),
|
||||
onAddressClick = interactor::onSelectAddress,
|
||||
onAddAddressButtonClick = interactor::onAddAddressButtonClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all the addresses from the autofill storage and updates the
|
||||
* [AutofillFragmentStore] with the list of addresses.
|
||||
*/
|
||||
private fun loadAddresses() {
|
||||
lifecycleScope.launch {
|
||||
val addresses = requireContext().components.core.autofillStorage.getAllAddresses()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
store.dispatch(AutofillAction.UpdateAddresses(addresses))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address.controller
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.concept.storage.Address
|
||||
import org.mozilla.fenix.settings.address.AddressManagementFragment
|
||||
import org.mozilla.fenix.settings.address.AddressManagementFragmentDirections
|
||||
import org.mozilla.fenix.settings.address.interactor.AddressManagementInteractor
|
||||
|
||||
/**
|
||||
* [AddressManagementFragment] controller. An interface that handles the view manipulation of
|
||||
* the address manager triggered by the interactor.
|
||||
*/
|
||||
interface AddressManagementController {
|
||||
|
||||
/**
|
||||
* @see [AddressManagementInteractor.onSelectAddress]
|
||||
*/
|
||||
fun handleAddressClicked(address: Address)
|
||||
|
||||
/**
|
||||
* @see [AddressManagementInteractor.onAddAddressClick]
|
||||
*/
|
||||
fun handleAddAddressButtonClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of [AddressManagementController].
|
||||
*
|
||||
* @param navController [NavController] used for navigation.
|
||||
*/
|
||||
class DefaultAddressManagementController(
|
||||
private val navController: NavController
|
||||
) : AddressManagementController {
|
||||
|
||||
override fun handleAddressClicked(address: Address) {
|
||||
navigateToAddressEditor()
|
||||
}
|
||||
|
||||
override fun handleAddAddressButtonClicked() {
|
||||
navigateToAddressEditor()
|
||||
}
|
||||
|
||||
private fun navigateToAddressEditor() {
|
||||
navController.navigate(
|
||||
AddressManagementFragmentDirections
|
||||
.actionAddressManagementFragmentToAddressEditorFragment()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address.interactor
|
||||
|
||||
import mozilla.components.concept.storage.Address
|
||||
import org.mozilla.fenix.settings.address.controller.AddressManagementController
|
||||
|
||||
/**
|
||||
* Interface for the address management interactor.
|
||||
*/
|
||||
interface AddressManagementInteractor {
|
||||
|
||||
/**
|
||||
* Navigates to the address editor to edit the selected address. Called when a user
|
||||
* taps on an address item.
|
||||
*
|
||||
* @param address The selected [Address] to edit.
|
||||
*/
|
||||
fun onSelectAddress(address: Address)
|
||||
|
||||
/**
|
||||
* Navigates to the address editor to add a new address. Called when a user
|
||||
* taps on 'Add address' button.
|
||||
*/
|
||||
fun onAddAddressButtonClick()
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of [AddressManagementInteractor].
|
||||
*
|
||||
* @param controller An instance of [AddressManagementController] which will be delegated for
|
||||
* all user interactions.
|
||||
*/
|
||||
class DefaultAddressManagementInteractor(
|
||||
private val controller: AddressManagementController
|
||||
) : AddressManagementInteractor {
|
||||
|
||||
override fun onSelectAddress(address: Address) {
|
||||
controller.handleAddressClicked(address)
|
||||
}
|
||||
|
||||
override fun onAddAddressButtonClick() {
|
||||
controller.handleAddAddressButtonClicked()
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import mozilla.components.concept.storage.Address
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.compose.list.IconListItem
|
||||
import org.mozilla.fenix.compose.list.TextListItem
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.theme.Theme
|
||||
|
||||
/**
|
||||
* A list of addresses.
|
||||
*
|
||||
* @param addresses A list of [Address] to display.
|
||||
* @param onAddressClick Invoked when the user clicks on an address.
|
||||
* @param onAddAddressButtonClick Invoked when the user clicks on the "Add address" button.
|
||||
*/
|
||||
@Composable
|
||||
fun AddressList(
|
||||
addresses: List<Address>,
|
||||
onAddressClick: (Address) -> Unit,
|
||||
onAddAddressButtonClick: () -> Unit,
|
||||
) {
|
||||
LazyColumn {
|
||||
items(addresses) { address ->
|
||||
TextListItem(
|
||||
label = address.givenName + " " + address.familyName,
|
||||
modifier = Modifier.padding(start = 56.dp),
|
||||
description = address.streetAddress,
|
||||
onClick = { onAddressClick(address) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
IconListItem(
|
||||
label = stringResource(R.string.preferences_addresses_add_address),
|
||||
beforeIconPainter = painterResource(R.drawable.ic_new),
|
||||
onClick = onAddAddressButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AddressListPreview() {
|
||||
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
|
||||
Box(Modifier.background(FirefoxTheme.colors.layer2)) {
|
||||
AddressList(
|
||||
addresses = listOf(
|
||||
Address(
|
||||
guid = "1",
|
||||
givenName = "Banana",
|
||||
additionalName = "",
|
||||
familyName = "Apple",
|
||||
organization = "Mozilla",
|
||||
streetAddress = "123 Sesame Street",
|
||||
addressLevel3 = "",
|
||||
addressLevel2 = "",
|
||||
addressLevel1 = "",
|
||||
postalCode = "90210",
|
||||
country = "US",
|
||||
tel = "+1 519 555-5555",
|
||||
email = "foo@bar.com",
|
||||
timeCreated = 0L,
|
||||
timeLastUsed = 0L,
|
||||
timeLastModified = 0L,
|
||||
timesUsed = 0L
|
||||
)
|
||||
),
|
||||
onAddressClick = {},
|
||||
onAddAddressButtonClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address.controller
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.concept.storage.Address
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.settings.address.AddressManagementFragmentDirections
|
||||
|
||||
class DefaultAddressManagementControllerTest {
|
||||
|
||||
private val navController: NavController = mockk(relaxed = true)
|
||||
|
||||
private lateinit var controller: AddressManagementController
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
controller = spyk(
|
||||
DefaultAddressManagementController(
|
||||
navController = navController
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN an address is selected THEN navigate to the address editor`() {
|
||||
val address: Address = mockk(relaxed = true)
|
||||
|
||||
controller.handleAddressClicked(address)
|
||||
|
||||
verify {
|
||||
navController.navigate(
|
||||
AddressManagementFragmentDirections
|
||||
.actionAddressManagementFragmentToAddressEditorFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the add address button is clicked THEN navigate to the address editor`() {
|
||||
controller.handleAddAddressButtonClicked()
|
||||
|
||||
verify {
|
||||
navController.navigate(
|
||||
AddressManagementFragmentDirections
|
||||
.actionAddressManagementFragmentToAddressEditorFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.settings.address.interactor
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.concept.storage.Address
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.settings.address.controller.AddressManagementController
|
||||
|
||||
class DefaultAddressManagementInteractorTest {
|
||||
|
||||
private val controller: AddressManagementController = mockk(relaxed = true)
|
||||
|
||||
private lateinit var interactor: AddressManagementInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
interactor = DefaultAddressManagementInteractor(controller)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN an address is selected THEN forward to controller handler`() {
|
||||
val address: Address = mockk(relaxed = true)
|
||||
|
||||
interactor.onSelectAddress(address)
|
||||
|
||||
verify { controller.handleAddressClicked(address) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN add address button is clicked THEN forward to controller handler`() {
|
||||
interactor.onAddAddressButtonClick()
|
||||
|
||||
verify { controller.handleAddAddressButtonClicked() }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue