- Makes sure to offer a None option on the PushNotification Distributor List

- Makes sure to not ask again if the person has already replied.
- Makes sure to not ask if there is no Unified Push app installed.
- Moves the code to a Component instead of a Custom Screen.
- Adds i18n messaging.
- Uses the existing Spinner Dialog for consistency
This commit is contained in:
Vitor Pamplona
2023-10-26 18:53:42 -04:00
parent 3b93198219
commit 56af44b00b
12 changed files with 179 additions and 143 deletions
@@ -0,0 +1,110 @@
package com.vitorpamplona.amethyst.ui.components
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsRow
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Composable
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
if (!sharedPreferencesViewModel.sharedPrefs.dontShowPushNotificationSelector) {
val context = LocalContext.current
var distributorPresent by remember {
mutableStateOf(PushDistributorHandler.savedDistributorExists())
}
if (!distributorPresent) {
LoadDistributors() { currentDistributor, list, readableListWithExplainer ->
if (!readableListWithExplainer.isEmpty()) {
SpinnerSelectionDialog(
title = stringResource(id = R.string.select_push_server),
options = readableListWithExplainer,
onSelect = { index ->
if (list[index] == "None") {
PushDistributorHandler.forceRemoveDistributor(context)
sharedPreferencesViewModel.dontShowPushNotificationSelector()
} else {
val fullDistributorName = list[index]
PushDistributorHandler.saveDistributor(fullDistributorName)
}
distributorPresent = true
Log.d("Amethyst", "NotificationScreen: Distributor registered.")
},
onDismiss = {
distributorPresent = true
Log.d("Amethyst", "NotificationScreen: Distributor dialog dismissed.")
}
)
}
}
} else {
val currentDistributor = PushDistributorHandler.getSavedDistributor()
PushDistributorHandler.saveDistributor(currentDistributor)
}
}
}
@Composable
fun LoadDistributors(
onInner: @Composable (String, ImmutableList<String>, ImmutableList<TitleExplainer>) -> Unit
) {
val currentDistributor = PushDistributorHandler.getSavedDistributor().ifBlank { null } ?: "None"
val list = remember {
PushDistributorHandler.getInstalledDistributors().plus("None").toImmutableList()
}
val readableListWithExplainer =
PushDistributorHandler.formattedDistributorNames()
.mapIndexed { index, name ->
TitleExplainer(
name,
stringResource(id = R.string.push_server_uses_app_explainer, list[index])
)
}
.plus(
TitleExplainer(
stringResource(id = R.string.push_server_none),
stringResource(id = R.string.push_server_none_explainer)
)
)
.toImmutableList()
onInner(
currentDistributor,
list,
readableListWithExplainer
)
}
@Composable
fun PushNotificationSettingsRow(sharedPreferencesViewModel: SharedPreferencesViewModel) {
val context = LocalContext.current
LoadDistributors() { currentDistributor, list, readableListWithExplainer ->
SettingsRow(
R.string.push_server_title,
R.string.push_server_explainer,
selectedItens = readableListWithExplainer,
selectedIndex = list.indexOf(currentDistributor)
) { index ->
if (list[index] == "None") {
sharedPreferencesViewModel.dontShowPushNotificationSelector()
PushDistributorHandler.forceRemoveDistributor(context)
} else {
PushDistributorHandler.saveDistributor(list[index])
}
}
}
}
@@ -1,117 +0,0 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.window.Dialog
import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.material3.Material3RichText
import com.halilibo.richtext.ui.resolveDefaults
import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Composable
fun CustomNotificationScreen(
notifFeedViewModel: NotificationViewModel,
userReactionsStatsModel: UserReactionsViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val pushHandler = PushDistributorHandler
var distributorPresent by remember {
mutableStateOf(pushHandler.savedDistributorExists())
}
val list = pushHandler.getInstalledDistributors()
val readableList = pushHandler.formattedDistributorNames()
if (!distributorPresent) {
SelectPushDistributor(
distrbutorList = readableList.toImmutableList(),
onDistributorSelected = { index, name ->
val fullDistributorName = list[index]
pushHandler.saveDistributor(fullDistributorName)
Log.d("Amethyst", "NotificationScreen: Distributor registered.")
},
onDismiss = {
distributorPresent = true
Log.d("Amethyst", "NotificationScreen: Distributor dialog dismissed.")
}
)
} else {
val currentDistributor = pushHandler.getSavedDistributor()
pushHandler.saveDistributor(currentDistributor)
}
NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel,
nav = nav
)
}
@Composable
fun SelectPushDistributor(
distrbutorList: ImmutableList<String>,
onDistributorSelected: (Int, String) -> Unit,
onDismiss: () -> Unit
) {
Dialog(onDismissRequest = onDismiss) {
Surface(
modifier = Modifier
.border(
width = Dp.Hairline,
color = MaterialTheme.colorScheme.background,
shape = CircleShape
)
.fillMaxWidth()
) {
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
) {
Box(modifier = Modifier.align(CenterHorizontally)) {
Material3RichText(
style = RichTextStyle().resolveDefaults()
) {
Markdown(content = "### Select a distributor")
}
}
distrbutorList.forEachIndexed { index, distributor ->
TextButton(
onClick = {
onDistributorSelected(index, distributor)
onDismiss()
},
modifier = HalfPadding.fillMaxWidth()
) {
Material3RichText(
style = RichTextStyle().resolveDefaults()
) {
Markdown(content = distributor)
}
}
}
}
}
}
}
@@ -370,7 +370,8 @@ object LocalPreferences {
automaticallyStartPlayback,
automaticallyShowUrlPreview,
automaticallyHideNavigationBars,
automaticallyShowProfilePictures
automaticallyShowProfilePictures,
false
)
}
}
@@ -11,7 +11,8 @@ data class Settings(
val automaticallyStartPlayback: ConnectivityType = ConnectivityType.ALWAYS,
val automaticallyShowUrlPreview: ConnectivityType = ConnectivityType.ALWAYS,
val automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS,
val automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.ALWAYS
val automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.ALWAYS,
val dontShowPushNotificationSelector: Boolean = false
)
enum class ThemeType(val screenCode: Int, val resourceId: Int) {
@@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.vitorpamplona.amethyst.ui.theme.Font14SP
@@ -85,11 +86,13 @@ fun TextSpinner(
@Composable
fun SpinnerSelectionDialog(
title: String? = null,
options: ImmutableList<TitleExplainer>,
onDismiss: () -> Unit,
onSelect: (Int) -> Unit
) {
SpinnerSelectionDialog(
title = title,
options = options,
onSelect = onSelect,
onDismiss = onDismiss
@@ -104,8 +107,7 @@ fun SpinnerSelectionDialog(
Spacer(modifier = Modifier.height(5.dp))
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier
.fillMaxWidth()
modifier = Modifier.fillMaxWidth()
) {
Text(text = it, color = Color.Gray, fontSize = Font14SP)
}
@@ -115,6 +117,7 @@ fun SpinnerSelectionDialog(
@Composable
fun <T> SpinnerSelectionDialog(
title: String? = null,
options: ImmutableList<T>,
onSelect: (Int) -> Unit,
onDismiss: () -> Unit,
@@ -126,14 +129,31 @@ fun <T> SpinnerSelectionDialog(
shape = RoundedCornerShape(5.dp)
) {
LazyColumn() {
title?.let {
item {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp, 16.dp),
horizontalArrangement = Arrangement.Center
) {
Text(
text = title,
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Bold
)
}
Divider(color = Color.LightGray, thickness = 0.25.dp)
}
}
itemsIndexed(options) { index, item ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp, 16.dp)
.clickable {
onSelect(index)
}
.padding(16.dp, 16.dp)
) {
Column() {
onRenderItem(item)
@@ -38,13 +38,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChatroomListScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChatroomScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChatroomScreenByAuthor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CommunityScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CustomNotificationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.DiscoverScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.GeoHashScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HashtagScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HiddenUsersScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HomeScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LoadRedirectScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.NotificationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ProfileScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsScreen
@@ -157,9 +157,10 @@ fun AppNavigation(
Route.Notification.let { route ->
composable(route.route, route.arguments, content = {
CustomNotificationScreen(
NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
sharedPreferencesViewModel = sharedPreferencesViewModel,
accountViewModel = accountViewModel,
nav = nav
)
@@ -30,6 +30,7 @@ class SettingsState() {
var automaticallyShowUrlPreview by mutableStateOf(ConnectivityType.ALWAYS)
var automaticallyHideNavigationBars by mutableStateOf(BooleanType.ALWAYS)
var automaticallyShowProfilePictures by mutableStateOf(ConnectivityType.ALWAYS)
var dontShowPushNotificationSelector by mutableStateOf<Boolean>(false)
var isOnMobileData: State<Boolean> = mutableStateOf(false)
@@ -152,6 +153,13 @@ class SharedPreferencesViewModel : ViewModel() {
}
}
fun dontShowPushNotificationSelector() {
if (!sharedPrefs.dontShowPushNotificationSelector) {
sharedPrefs.dontShowPushNotificationSelector = true
saveSharedSettings()
}
}
fun updateConnectivityStatusState(isOnMobileDataState: State<Boolean>) {
if (sharedPrefs.isOnMobileData != isOnMobileDataState) {
sharedPrefs.isOnMobileData = isOnMobileDataState
@@ -177,7 +185,8 @@ class SharedPreferencesViewModel : ViewModel() {
sharedPrefs.automaticallyStartPlayback,
sharedPrefs.automaticallyShowUrlPreview,
sharedPrefs.automaticallyHideNavigationBars,
sharedPrefs.automaticallyShowProfilePictures
sharedPrefs.automaticallyShowProfilePictures,
sharedPrefs.dontShowPushNotificationSelector
)
)
}
@@ -47,6 +47,7 @@ import com.patrykandpatrick.vico.core.chart.line.LineChart
import com.patrykandpatrick.vico.core.chart.values.ChartValues
import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
import com.vitorpamplona.amethyst.ui.components.SelectNotificationProvider
import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.OneGiga
import com.vitorpamplona.amethyst.ui.note.OneKilo
@@ -57,6 +58,7 @@ import com.vitorpamplona.amethyst.ui.note.showCount
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
import com.vitorpamplona.amethyst.ui.screen.RefresheableCardView
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.RoyalBlue
import com.vitorpamplona.amethyst.ui.theme.chartStyle
@@ -69,9 +71,12 @@ import kotlin.math.roundToInt
fun NotificationScreen(
notifFeedViewModel: NotificationViewModel,
userReactionsStatsModel: UserReactionsViewModel,
sharedPreferencesViewModel: SharedPreferencesViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
SelectNotificationProvider(sharedPreferencesViewModel)
WatchAccountForNotifications(notifFeedViewModel, accountViewModel)
CheckifItNeedsToRequestNotificationPermission()
@@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.model.ThemeType
import com.vitorpamplona.amethyst.model.parseBooleanType
import com.vitorpamplona.amethyst.model.parseConnectivityType
import com.vitorpamplona.amethyst.model.parseThemeType
import com.vitorpamplona.amethyst.ui.components.PushNotificationSettingsRow
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer
@@ -217,6 +218,10 @@ fun SettingsScreen(
) {
sharedPreferencesViewModel.updateAutomaticallyHideNavBars(parseBooleanType(it))
}
Spacer(modifier = HalfVertSpacer)
PushNotificationSettingsRow(sharedPreferencesViewModel)
}
}
+7
View File
@@ -645,4 +645,11 @@
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json">Unable to create a lightning invoice before sending the zap. Element pr not found in the resulting JSON.</string>
<string name="read_only_user">Read-only user</string>
<string name="no_reactions_setup">No reactions setup</string>
<string name="select_push_server">Select a Push Notification distributor</string>
<string name="push_server_title">Push Notification</string>
<string name="push_server_explainer">From installed UnifiedPush apps</string>
<string name="push_server_none">None</string>
<string name="push_server_none_explainer">Disables Push Notifications</string>
<string name="push_server_uses_app_explainer">Uses app %1$s</string>
</resources>
@@ -0,0 +1,12 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
@Composable
fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesViewModel) {
}
@Composable
fun PushNotificationSettingsRow(sharedPreferencesViewModel: SharedPreferencesViewModel) {
}
@@ -1,18 +0,0 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
@Composable
fun CustomNotificationScreen(
notifFeedViewModel: NotificationViewModel,
userReactionsStatsModel: UserReactionsViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) = NotificationScreen(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel,
nav = nav
)