Improvements to the Settings Interface

This commit is contained in:
Vitor Pamplona
2023-08-14 21:25:12 -04:00
parent decd6ef627
commit 3a8a50006d
6 changed files with 172 additions and 140 deletions
@@ -35,7 +35,7 @@ import kotlinx.collections.immutable.ImmutableList
@Composable @Composable
fun TextSpinner( fun TextSpinner(
label: String, label: String?,
placeholder: String, placeholder: String,
options: ImmutableList<String>, options: ImmutableList<String>,
explainers: ImmutableList<String>? = null, explainers: ImmutableList<String>? = null,
@@ -54,7 +54,7 @@ fun TextSpinner(
value = currentText, value = currentText,
onValueChange = {}, onValueChange = {},
readOnly = true, readOnly = true,
label = { Text(label) }, label = { label?.let { Text(it) } },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.focusRequester(focusRequester) .focusRequester(focusRequester)
@@ -44,10 +44,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavBackStackEntry import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import coil.Coil import coil.Coil
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
@@ -108,7 +106,8 @@ fun AppTopBar(
navEntryState: State<NavBackStackEntry?>, navEntryState: State<NavBackStackEntry?>,
scaffoldState: ScaffoldState, scaffoldState: ScaffoldState,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit,
navPopBack: () -> Unit
) { ) {
val currentRoute by remember(navEntryState.value) { val currentRoute by remember(navEntryState.value) {
derivedStateOf { derivedStateOf {
@@ -122,7 +121,7 @@ fun AppTopBar(
} }
} }
RenderTopRouteBar(currentRoute, id, followLists, scaffoldState, accountViewModel, nav) RenderTopRouteBar(currentRoute, id, followLists, scaffoldState, accountViewModel, nav, navPopBack)
} }
@Composable @Composable
@@ -132,14 +131,15 @@ private fun RenderTopRouteBar(
followLists: FollowListViewModel, followLists: FollowListViewModel,
scaffoldState: ScaffoldState, scaffoldState: ScaffoldState,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit,
navPopBack: () -> Unit
) { ) {
when (currentRoute) { when (currentRoute) {
// Route.Profile.route -> TopBarWithBackButton(nav)
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel, nav) Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel, nav) Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Discover.base -> DiscoveryTopBar(followLists, scaffoldState, accountViewModel, nav) Route.Discover.base -> DiscoveryTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel, nav) Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Settings.base -> TopBarWithBackButton(stringResource(id = R.string.application_preferences), navPopBack)
else -> { else -> {
if (id != null) { if (id != null) {
when (currentRoute) { when (currentRoute) {
@@ -488,24 +488,23 @@ fun SimpleTextSpinner(
} }
@Composable @Composable
fun TopBarWithBackButton(navController: NavHostController) { fun TopBarWithBackButton(caption: String, popBack: () -> Unit) {
Column() { Column() {
TopAppBar( TopAppBar(
elevation = 0.dp, elevation = 0.dp,
backgroundColor = Color(0xFFFFFF), backgroundColor = Color(0xFFFFFF),
title = {}, title = {
Text(caption)
},
navigationIcon = { navigationIcon = {
IconButton( IconButton(
onClick = { onClick = popBack,
navController.popBackStack()
},
modifier = Modifier modifier = Modifier
) { ) {
Icon( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.Default.ArrowBack,
null, contentDescription = stringResource(R.string.back),
modifier = Modifier.size(28.dp), tint = MaterialTheme.colors.onSurface
tint = MaterialTheme.colors.primary
) )
} }
}, },
@@ -95,6 +95,13 @@ fun MainScreen(
} }
} }
val navPopBack = remember(navController) {
{
navController.popBackStack()
Unit
}
}
val followLists: FollowListViewModel = viewModel( val followLists: FollowListViewModel = viewModel(
key = accountViewModel.userProfile().pubkeyHex + "FollowListViewModel", key = accountViewModel.userProfile().pubkeyHex + "FollowListViewModel",
factory = FollowListViewModel.Factory(accountViewModel.account) factory = FollowListViewModel.Factory(accountViewModel.account)
@@ -201,7 +208,7 @@ fun MainScreen(
AppBottomBar(accountViewModel, navState, navBottomRow) AppBottomBar(accountViewModel, navState, navBottomRow)
}, },
topBar = { topBar = {
AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav) AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav, navPopBack)
}, },
drawerContent = { drawerContent = {
DrawerContent(nav, scaffoldState, sheetState, accountViewModel) DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
import android.content.Context import android.content.Context
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@@ -15,6 +16,7 @@ import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ExposedDropdownMenuBox import androidx.compose.material.ExposedDropdownMenuBox
import androidx.compose.material.ExposedDropdownMenuDefaults import androidx.compose.material.ExposedDropdownMenuDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextField import androidx.compose.material.TextField
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -26,10 +28,12 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
@@ -39,7 +43,10 @@ import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.parseConnectivityType import com.vitorpamplona.amethyst.model.parseConnectivityType
import com.vitorpamplona.amethyst.ui.screen.ThemeViewModel import com.vitorpamplona.amethyst.ui.screen.ThemeViewModel
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size20dp
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@@ -108,16 +115,18 @@ fun SettingsScreen(
stringResource(ConnectivityType.WIFI_ONLY.reourceId), stringResource(ConnectivityType.WIFI_ONLY.reourceId),
stringResource(ConnectivityType.NEVER.reourceId) stringResource(ConnectivityType.NEVER.reourceId)
) )
val settings = accountViewModel.account.settings
val index = settings.automaticallyShowImages.screenCode
val videoIndex = settings.automaticallyStartPlayback.screenCode
val linkIndex = settings.automaticallyShowUrlPreview.screenCode
val themeItens = persistentListOf( val themeItens = persistentListOf(
stringResource(R.string.system), stringResource(R.string.system),
stringResource(R.string.light), stringResource(R.string.light),
stringResource(R.string.dark) stringResource(R.string.dark)
) )
val settings = accountViewModel.account.settings
val showImagesIndex = settings.automaticallyShowImages.screenCode
val videoIndex = settings.automaticallyStartPlayback.screenCode
val linkIndex = settings.automaticallyShowUrlPreview.screenCode
val themeIndex = themeViewModel.theme.value ?: 0 val themeIndex = themeViewModel.theme.value ?: 0
val context = LocalContext.current val context = LocalContext.current
@@ -127,21 +136,17 @@ fun SettingsScreen(
val languageIndex = getLanguageIndex(languageEntries) val languageIndex = getLanguageIndex(languageEntries)
Column( Column(
StdPadding Modifier
.padding(top = Size10dp, start = Size20dp, end = Size20dp)
.verticalScroll(rememberScrollState()), .verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Section(stringResource(R.string.application_preferences)) SettingsRow(
R.string.language,
Row( R.string.language_description,
verticalAlignment = Alignment.CenterVertically, languageList,
modifier = Modifier.fillMaxWidth() languageIndex
) { ) {
TextSpinner(
label = stringResource(R.string.language),
placeholder = languageList[languageIndex],
options = languageList,
onSelect = {
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
val job = scope.launch(Dispatchers.IO) { val job = scope.launch(Dispatchers.IO) {
val locale = languageEntries[languageList[it]] val locale = languageEntries[languageList[it]]
@@ -152,100 +157,113 @@ fun SettingsScreen(
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it]]) val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it]])
AppCompatDelegate.setApplicationLocales(appLocale) AppCompatDelegate.setApplicationLocales(appLocale)
} }
},
modifier = Modifier
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
.weight(1f)
)
} }
Row( Spacer(modifier = HalfVertSpacer)
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth() SettingsRow(
R.string.theme,
R.string.theme_description,
themeItens,
themeIndex
) { ) {
TextSpinner(
label = stringResource(R.string.theme),
placeholder = themeItens[themeIndex],
options = themeItens,
onSelect = {
themeViewModel.onChange(it) themeViewModel.onChange(it)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
LocalPreferences.updateTheme(it) LocalPreferences.updateTheme(it)
} }
},
modifier = Modifier
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
.weight(1f)
)
} }
Row( Spacer(modifier = HalfVertSpacer)
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth() SettingsRow(
R.string.automatically_load_images_gifs,
R.string.automatically_load_images_gifs_description,
selectedItens,
showImagesIndex
) { ) {
TextSpinner(
label = stringResource(R.string.automatically_load_images_gifs),
placeholder = selectedItens[index],
options = selectedItens,
onSelect = {
val automaticallyShowImages = parseConnectivityType(it) val automaticallyShowImages = parseConnectivityType(it)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.updateAutomaticallyShowImages(automaticallyShowImages) accountViewModel.updateAutomaticallyShowImages(automaticallyShowImages)
LocalPreferences.saveToEncryptedStorage(accountViewModel.account) LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
} }
},
modifier = Modifier
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
.weight(1f)
)
} }
Row( Spacer(modifier = HalfVertSpacer)
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth() SettingsRow(
R.string.automatically_play_videos,
R.string.automatically_play_videos_description,
selectedItens,
videoIndex
) { ) {
TextSpinner(
label = stringResource(R.string.automatically_play_videos),
placeholder = selectedItens[videoIndex],
options = selectedItens,
onSelect = {
val automaticallyStartPlayback = parseConnectivityType(it) val automaticallyStartPlayback = parseConnectivityType(it)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.updateAutomaticallyStartPlayback(automaticallyStartPlayback) accountViewModel.updateAutomaticallyStartPlayback(automaticallyStartPlayback)
LocalPreferences.saveToEncryptedStorage(accountViewModel.account) LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
} }
},
modifier = Modifier
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
.weight(1f)
)
} }
Row( Spacer(modifier = HalfVertSpacer)
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth() SettingsRow(
R.string.automatically_show_url_preview,
R.string.automatically_show_url_preview_description,
selectedItens,
linkIndex
) { ) {
TextSpinner(
label = stringResource(R.string.automatically_show_url_preview),
placeholder = selectedItens[linkIndex],
options = selectedItens,
onSelect = {
val automaticallyShowUrlPreview = parseConnectivityType(it) val automaticallyShowUrlPreview = parseConnectivityType(it)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.updateAutomaticallyShowUrlPreview(automaticallyShowUrlPreview) accountViewModel.updateAutomaticallyShowUrlPreview(automaticallyShowUrlPreview)
LocalPreferences.saveToEncryptedStorage(accountViewModel.account) LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
} }
}, }
}
}
@Composable
fun SettingsRow(
name: Int,
description: Int,
selectedItens: ImmutableList<String>,
selectedIndex: Int,
onSelect: (Int) -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.weight(2.0f),
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
Text(
text = stringResource(name),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(description),
style = MaterialTheme.typography.caption,
color = Color.Gray,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
TextSpinner(
label = "",
placeholder = selectedItens[selectedIndex],
options = selectedItens,
onSelect = onSelect,
modifier = Modifier modifier = Modifier
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)) .windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
.weight(1f) .weight(1f)
) )
} }
} }
}
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
+12 -4
View File
@@ -493,12 +493,12 @@
<string name="system">System</string> <string name="system">System</string>
<string name="light">Light</string> <string name="light">Light</string>
<string name="dark">Dark</string> <string name="dark">Dark</string>
<string name="application_preferences">Application preferences</string> <string name="application_preferences">Application Preferences</string>
<string name="language">Language</string> <string name="language">Language</string>
<string name="theme">Theme</string> <string name="theme">Theme</string>
<string name="automatically_load_images_gifs">Automatically load images/gifs</string> <string name="automatically_load_images_gifs">Image Preview</string>
<string name="automatically_play_videos">Automatically play videos</string> <string name="automatically_play_videos">Video Playback</string>
<string name="automatically_show_url_preview">Automatically show url preview</string> <string name="automatically_show_url_preview">URL Preview</string>
<string name="load_image">Load Image</string> <string name="load_image">Load Image</string>
<string name="spamming_users">Spammers</string> <string name="spamming_users">Spammers</string>
@@ -536,4 +536,12 @@
<string name="messages_group_descriptor">Members of this group</string> <string name="messages_group_descriptor">Members of this group</string>
<string name="messages_new_subject_message">Explanation to members</string> <string name="messages_new_subject_message">Explanation to members</string>
<string name="messages_new_subject_message_placeholder">Changing the name for the new goals.</string> <string name="messages_new_subject_message_placeholder">Changing the name for the new goals.</string>
<string name="language_description">For the App\'s Interface</string>
<string name="theme_description">Dark, Light or System theme</string>
<string name="automatically_load_images_gifs_description">Automatically load images and GIFs</string>
<string name="automatically_play_videos_description">Automatically plays videos and GIFs</string>
<string name="automatically_show_url_preview_description">Show URL previews</string>
<string name="load_image_description">When to load images</string>
</resources> </resources>
+3 -3
View File
@@ -4,10 +4,13 @@
<locale android:name="cs"/> <locale android:name="cs"/>
<locale android:name="de"/> <locale android:name="de"/>
<locale android:name="eo"/> <locale android:name="eo"/>
<locale android:name="en"/>
<locale android:name="en-GB"/>
<locale android:name="es"/> <locale android:name="es"/>
<locale android:name="fa"/> <locale android:name="fa"/>
<locale android:name="fr"/> <locale android:name="fr"/>
<locale android:name="hu"/> <locale android:name="hu"/>
<locale android:name="ja"/>
<locale android:name="nl"/> <locale android:name="nl"/>
<locale android:name="pt-BR"/> <locale android:name="pt-BR"/>
<locale android:name="ru"/> <locale android:name="ru"/>
@@ -18,7 +21,4 @@
<locale android:name="zh"/> <locale android:name="zh"/>
<locale android:name="zh-HK"/> <locale android:name="zh-HK"/>
<locale android:name="zh-TW"/> <locale android:name="zh-TW"/>
<locale android:name="en"/>
<locale android:name="en-GB"/>
<locale android:name="ja"/>
</locale-config> </locale-config>