compose: dispenser: Migrate dispensers logic to compose
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Composable to display dispenser URL in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param url URL of the dispenser
|
||||
* @param onClick Callback when this URL is clicked
|
||||
* @param onClear Callback when the clear button is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DispenserListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
url: String,
|
||||
onClick: () -> Unit = {},
|
||||
onClear: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.weight(1F),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_server),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = url,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
TextButton(onClick = onClear) {
|
||||
Text(
|
||||
text = stringResource(R.string.remove),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DispenserListItemPreview() {
|
||||
DispenserListItem(url = "https://auroraoss.com/api/auth")
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.aurora.store.compose.ui.blacklist.BlacklistScreen
|
||||
import com.aurora.store.compose.ui.commons.PermissionRationaleScreen
|
||||
import com.aurora.store.compose.ui.details.AppDetailsScreen
|
||||
import com.aurora.store.compose.ui.dev.DevProfileScreen
|
||||
import com.aurora.store.compose.ui.dispenser.DispenserScreen
|
||||
import com.aurora.store.compose.ui.downloads.DownloadsScreen
|
||||
import com.aurora.store.compose.ui.favourite.FavouriteScreen
|
||||
import com.aurora.store.compose.ui.onboarding.OnboardingScreen
|
||||
@@ -135,6 +136,10 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
onNavigateToSplash = { activity?.startActivity(splashIntent) }
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Dispenser> {
|
||||
DispenserScreen(onNavigateUp = ::onNavigateUp)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,4 +54,7 @@ sealed class Screen : NavKey, Parcelable {
|
||||
|
||||
@Serializable
|
||||
data object Spoof : Screen()
|
||||
|
||||
@Serializable
|
||||
data object Dispenser : Screen()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.dispenser
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.extensions.copyToClipBoard
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.DispenserListItem
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.preview.PreviewTemplate
|
||||
import com.aurora.store.viewmodel.dispenser.DispenserViewModel
|
||||
|
||||
@Composable
|
||||
fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hiltViewModel()) {
|
||||
val dispensers by viewModel.dispensers.collectAsStateWithLifecycle()
|
||||
|
||||
var shouldShowInputDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (shouldShowInputDialog) {
|
||||
InputDispenserDialog(
|
||||
onAdd = { url ->
|
||||
viewModel.addDispenser(url)
|
||||
shouldShowInputDialog = false
|
||||
},
|
||||
onDismiss = { shouldShowInputDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
var shouldRemoveDispenser : String? by rememberSaveable { mutableStateOf(null) }
|
||||
shouldRemoveDispenser?.let { url ->
|
||||
RemoveDispenserDialog(
|
||||
url = url,
|
||||
onRemove = {
|
||||
viewModel.removeDispenser(url)
|
||||
shouldRemoveDispenser = null
|
||||
},
|
||||
onDismiss = { shouldRemoveDispenser = null }
|
||||
)
|
||||
}
|
||||
|
||||
ScreenContent(
|
||||
dispensers = dispensers,
|
||||
onNavigateUp = onNavigateUp,
|
||||
onAddDispenser = { shouldShowInputDialog = true },
|
||||
onRemoveDispenser = { url -> shouldRemoveDispenser = url }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
onNavigateUp: () -> Unit = {},
|
||||
dispensers: Set<String> = emptySet(),
|
||||
onAddDispenser: () -> Unit = {},
|
||||
onRemoveDispenser: (url: String) -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.pref_dispenser_title),
|
||||
onNavigateUp = onNavigateUp
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (dispensers.isNotEmpty()) {
|
||||
FloatingActionButton(onClick = onAddDispenser) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_add),
|
||||
contentDescription = stringResource(R.string.add_dispenser_title)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
if (dispensers.isEmpty()) {
|
||||
Error(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_server),
|
||||
message = stringResource(R.string.no_dispensers_available),
|
||||
actionMessage = stringResource(R.string.add_dispenser_title),
|
||||
onAction = onAddDispenser
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
) {
|
||||
items(items = dispensers.toList(), key = { url -> url }) { url ->
|
||||
DispenserListItem(
|
||||
url = url,
|
||||
onClick = { context.copyToClipBoard(url) },
|
||||
onClear = { onRemoveDispenser(url) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DispenserScreenPreview() {
|
||||
val dispensers = List(10) { "https://auroraoss.com/api/auth/$it" }
|
||||
PreviewTemplate {
|
||||
ScreenContent(dispensers = dispensers.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DispenserScreenEmptyPreview() {
|
||||
PreviewTemplate {
|
||||
ScreenContent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.dispenser
|
||||
|
||||
import android.util.Patterns
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
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.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aurora.store.R
|
||||
import kotlinx.coroutines.android.awaitFrame
|
||||
|
||||
/**
|
||||
* Dialog for adding a new token dispenser
|
||||
* @param onAdd Callback on adding a new dispenser
|
||||
* @param onDismiss Callback on dismiss
|
||||
*/
|
||||
@Composable
|
||||
fun InputDispenserDialog(onAdd: (url: String) -> Unit = {}, onDismiss: () -> Unit = {}) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
var url by remember { mutableStateOf(TextFieldValue()) }
|
||||
|
||||
LaunchedEffect(focusRequester) {
|
||||
awaitFrame()
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
title = { Text(text = stringResource(R.string.add_dispenser_title)) },
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
) {
|
||||
Text(text = stringResource(R.string.add_dispenser_summary))
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester),
|
||||
value = url,
|
||||
placeholder = { Text(text = stringResource(R.string.add_dispenser_hint)) },
|
||||
onValueChange = { url = it },
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
singleLine = true,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
)
|
||||
}
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = { onAdd(url.text) },
|
||||
enabled = Patterns.WEB_URL.matcher(url.text).matches()
|
||||
) {
|
||||
Text(text = stringResource(R.string.add))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InputDispenserDialogPreview() {
|
||||
InputDispenserDialog()
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.dispenser
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Dialog for removing a token dispenser
|
||||
* @param onRemove Callback on removing a dispenser
|
||||
* @param onDismiss Callback on dismiss
|
||||
*/
|
||||
@Composable
|
||||
fun RemoveDispenserDialog(url: String, onRemove: () -> Unit = {}, onDismiss: () -> Unit = {}) {
|
||||
AlertDialog(
|
||||
title = { Text(text = stringResource(R.string.remove_dispenser_title)) },
|
||||
text = {
|
||||
Text(text = stringResource(R.string.remove_dispenser_summary, url))
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onRemove) {
|
||||
Text(text = stringResource(R.string.remove))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun RemoveDispenserDialogPreview() {
|
||||
RemoveDispenserDialog(url = "https://auroraoss.com/api/auth/")
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.aurora.store.view.epoxy.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.airbnb.epoxy.CallbackProp
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.databinding.ViewDispenserBinding
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class DispenserView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewDispenserBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp
|
||||
fun url(url: String) {
|
||||
binding.url.text = url
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun copy(onClickListener: OnClickListener?) {
|
||||
binding.url.setOnClickListener(onClickListener)
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun clear(onClickListener: OnClickListener?) {
|
||||
binding.btnAction.setOnClickListener(onClickListener)
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.aurora.store.view.ui.dispenser
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.extensions.copyToClipBoard
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.FragmentDispenserBinding
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
|
||||
import com.aurora.store.view.epoxy.views.DispenserViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DispenserFragment : BaseFragment<FragmentDispenserBinding>(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private val dispensers: Set<String>
|
||||
get() = Preferences.getStringSet(requireContext(), PREFERENCE_DISPENSER_URLS)
|
||||
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sharedPreferences = Preferences.getPrefs(view.context)
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
binding.addFab.setOnClickListener {
|
||||
findNavController().navigate(R.id.inputDispenserDialog)
|
||||
}
|
||||
|
||||
setupDispensers()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == PREFERENCE_DISPENSER_URLS) setupDispensers()
|
||||
}
|
||||
|
||||
private fun setupDispensers() {
|
||||
if (dispensers.isNotEmpty()) {
|
||||
binding.noDispensersTextView.visibility = View.GONE
|
||||
|
||||
binding.epoxyRecycler.visibility = View.VISIBLE
|
||||
binding.epoxyRecycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
dispensers.forEachIndexed { index, url ->
|
||||
add(
|
||||
DispenserViewModel_()
|
||||
.id(index)
|
||||
.url(url)
|
||||
.copy { _ -> requireContext().copyToClipBoard(url) }
|
||||
.clear { _ ->
|
||||
findNavController().navigate(
|
||||
DispenserFragmentDirections
|
||||
.actionDispenserFragmentToRemoveDispenserDialog(url)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.epoxyRecycler.visibility = View.GONE
|
||||
binding.noDispensersTextView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.aurora.store.view.ui.dispenser
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.aurora.extensions.showKeyboard
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
|
||||
import com.aurora.store.util.save
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class InputDispenserDialog: DialogFragment() {
|
||||
|
||||
private val textInputLayout: TextInputLayout?
|
||||
get() = dialog?.findViewById(R.id.textInputLayout)
|
||||
|
||||
private val dispensers: Set<String>
|
||||
get() = Preferences.getStringSet(requireContext(), PREFERENCE_DISPENSER_URLS)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val view = layoutInflater.inflate(R.layout.dialog_text_input_edit_text, null)
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.add_dispenser_title)
|
||||
.setMessage(R.string.add_dispenser_summary)
|
||||
.setView(view)
|
||||
.setPositiveButton(getString(R.string.add)) { _, _ -> saveDispenserUrl() }
|
||||
.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dialog?.dismiss()}
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
textInputLayout?.editText?.apply {
|
||||
hint = requireContext().getString(R.string.add_dispenser_hint)
|
||||
showKeyboard()
|
||||
}
|
||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
}
|
||||
|
||||
private fun saveDispenserUrl() {
|
||||
val url = textInputLayout?.editText?.text?.toString()
|
||||
if (!url.isNullOrEmpty() && Patterns.WEB_URL.matcher(url).matches()) {
|
||||
val newSet = dispensers.toMutableSet().apply {
|
||||
add(url)
|
||||
}
|
||||
save(PREFERENCE_DISPENSER_URLS, newSet)
|
||||
} else {
|
||||
toast(R.string.add_dispenser_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.aurora.store.view.ui.dispenser
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
|
||||
import com.aurora.store.util.save
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RemoveDispenserDialog : DialogFragment() {
|
||||
|
||||
private val args: RemoveDispenserDialogArgs by navArgs()
|
||||
|
||||
private val dispensers: Set<String>
|
||||
get() = Preferences.getStringSet(requireContext(), PREFERENCE_DISPENSER_URLS)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.remove_dispenser_title)
|
||||
.setMessage(getString(R.string.remove_dispenser_summary, args.url))
|
||||
.setPositiveButton(getString(R.string.remove)) { _, _ -> removeDispenserUrl() }
|
||||
.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dialog?.dismiss() }
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun removeDispenserUrl() {
|
||||
val newSet = dispensers.toMutableSet().apply {
|
||||
remove(args.url)
|
||||
}
|
||||
save(PREFERENCE_DISPENSER_URLS, newSet)
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,11 @@ import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.extensions.runOnUiThread
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_MICROG_AUTH
|
||||
@@ -45,7 +47,7 @@ class NetworkPreference : BasePreferenceFragment() {
|
||||
|
||||
findPreference<Preference>(Preferences.PREFERENCE_DISPENSER_URLS)?.apply {
|
||||
setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.dispenserFragment)
|
||||
requireContext().navigate(Screen.Dispenser)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.viewmodel.dispenser
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.toMutableSet
|
||||
|
||||
@HiltViewModel
|
||||
class DispenserViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) : ViewModel() {
|
||||
|
||||
private val sharedPreferences = Preferences.getPrefs(context)
|
||||
|
||||
private var _dispensers: Set<String>
|
||||
set(value) = sharedPreferences.edit { putStringSet(PREFERENCE_DISPENSER_URLS, value) }
|
||||
get() = sharedPreferences.getStringSet(PREFERENCE_DISPENSER_URLS, emptySet()) ?: emptySet()
|
||||
|
||||
val dispensers: StateFlow<Set<String>>
|
||||
get() {
|
||||
return callbackFlow {
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, changedKey ->
|
||||
if (changedKey == PREFERENCE_DISPENSER_URLS) trySend(_dispensers)
|
||||
}
|
||||
|
||||
trySend(_dispensers)
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
|
||||
awaitClose {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptySet())
|
||||
}
|
||||
|
||||
fun addDispenser(url: String) {
|
||||
_dispensers = _dispensers.toMutableSet().apply {
|
||||
add(url)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeDispenser(url: String) {
|
||||
_dispensers = _dispensers.toMutableSet().apply {
|
||||
remove(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user