Improvements to the Settings Interface
This commit is contained in:
@@ -35,7 +35,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun TextSpinner(
|
||||
label: String,
|
||||
label: String?,
|
||||
placeholder: String,
|
||||
options: ImmutableList<String>,
|
||||
explainers: ImmutableList<String>? = null,
|
||||
@@ -54,7 +54,7 @@ fun TextSpinner(
|
||||
value = currentText,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text(label) },
|
||||
label = { label?.let { Text(it) } },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester)
|
||||
|
||||
@@ -44,10 +44,8 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.map
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import coil.Coil
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
@@ -108,7 +106,8 @@ fun AppTopBar(
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
scaffoldState: ScaffoldState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit
|
||||
) {
|
||||
val currentRoute by remember(navEntryState.value) {
|
||||
derivedStateOf {
|
||||
@@ -122,7 +121,7 @@ fun AppTopBar(
|
||||
}
|
||||
}
|
||||
|
||||
RenderTopRouteBar(currentRoute, id, followLists, scaffoldState, accountViewModel, nav)
|
||||
RenderTopRouteBar(currentRoute, id, followLists, scaffoldState, accountViewModel, nav, navPopBack)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -132,14 +131,15 @@ private fun RenderTopRouteBar(
|
||||
followLists: FollowListViewModel,
|
||||
scaffoldState: ScaffoldState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit
|
||||
) {
|
||||
when (currentRoute) {
|
||||
// Route.Profile.route -> TopBarWithBackButton(nav)
|
||||
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||
Route.Discover.base -> DiscoveryTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel, nav)
|
||||
Route.Settings.base -> TopBarWithBackButton(stringResource(id = R.string.application_preferences), navPopBack)
|
||||
else -> {
|
||||
if (id != null) {
|
||||
when (currentRoute) {
|
||||
@@ -488,24 +488,23 @@ fun SimpleTextSpinner(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TopBarWithBackButton(navController: NavHostController) {
|
||||
fun TopBarWithBackButton(caption: String, popBack: () -> Unit) {
|
||||
Column() {
|
||||
TopAppBar(
|
||||
elevation = 0.dp,
|
||||
backgroundColor = Color(0xFFFFFF),
|
||||
title = {},
|
||||
title = {
|
||||
Text(caption)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onClick = popBack,
|
||||
modifier = Modifier
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
tint = MaterialTheme.colors.onSurface
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -95,6 +95,13 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val navPopBack = remember(navController) {
|
||||
{
|
||||
navController.popBackStack()
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
val followLists: FollowListViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "FollowListViewModel",
|
||||
factory = FollowListViewModel.Factory(accountViewModel.account)
|
||||
@@ -201,7 +208,7 @@ fun MainScreen(
|
||||
AppBottomBar(accountViewModel, navState, navBottomRow)
|
||||
},
|
||||
topBar = {
|
||||
AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav)
|
||||
AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav, navPopBack)
|
||||
},
|
||||
drawerContent = {
|
||||
DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
|
||||
|
||||
+133
-115
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -15,6 +16,7 @@ import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ExposedDropdownMenuBox
|
||||
import androidx.compose.material.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -26,10 +28,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.sp
|
||||
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.ui.screen.ThemeViewModel
|
||||
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.toImmutableList
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
@@ -108,16 +115,18 @@ fun SettingsScreen(
|
||||
stringResource(ConnectivityType.WIFI_ONLY.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(
|
||||
stringResource(R.string.system),
|
||||
stringResource(R.string.light),
|
||||
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 context = LocalContext.current
|
||||
@@ -127,123 +136,132 @@ fun SettingsScreen(
|
||||
val languageIndex = getLanguageIndex(languageEntries)
|
||||
|
||||
Column(
|
||||
StdPadding
|
||||
Modifier
|
||||
.padding(top = Size10dp, start = Size20dp, end = Size20dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Section(stringResource(R.string.application_preferences))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
SettingsRow(
|
||||
R.string.language,
|
||||
R.string.language_description,
|
||||
languageList,
|
||||
languageIndex
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(R.string.language),
|
||||
placeholder = languageList[languageIndex],
|
||||
options = languageList,
|
||||
onSelect = {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val job = scope.launch(Dispatchers.IO) {
|
||||
val locale = languageEntries[languageList[it]]
|
||||
accountViewModel.account.settings.preferredLanguage = locale
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
job.join()
|
||||
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it]])
|
||||
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val job = scope.launch(Dispatchers.IO) {
|
||||
val locale = languageEntries[languageList[it]]
|
||||
accountViewModel.account.settings.preferredLanguage = locale
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
job.join()
|
||||
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it]])
|
||||
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
|
||||
SettingsRow(
|
||||
R.string.theme,
|
||||
R.string.theme_description,
|
||||
themeItens,
|
||||
themeIndex
|
||||
) {
|
||||
themeViewModel.onChange(it)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
LocalPreferences.updateTheme(it)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
|
||||
SettingsRow(
|
||||
R.string.automatically_load_images_gifs,
|
||||
R.string.automatically_load_images_gifs_description,
|
||||
selectedItens,
|
||||
showImagesIndex
|
||||
) {
|
||||
val automaticallyShowImages = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyShowImages(automaticallyShowImages)
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
|
||||
SettingsRow(
|
||||
R.string.automatically_play_videos,
|
||||
R.string.automatically_play_videos_description,
|
||||
selectedItens,
|
||||
videoIndex
|
||||
) {
|
||||
val automaticallyStartPlayback = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyStartPlayback(automaticallyStartPlayback)
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = HalfVertSpacer)
|
||||
|
||||
SettingsRow(
|
||||
R.string.automatically_show_url_preview,
|
||||
R.string.automatically_show_url_preview_description,
|
||||
selectedItens,
|
||||
linkIndex
|
||||
) {
|
||||
val automaticallyShowUrlPreview = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyShowUrlPreview(automaticallyShowUrlPreview)
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(R.string.theme),
|
||||
placeholder = themeItens[themeIndex],
|
||||
options = themeItens,
|
||||
onSelect = {
|
||||
themeViewModel.onChange(it)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
LocalPreferences.updateTheme(it)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(R.string.automatically_load_images_gifs),
|
||||
placeholder = selectedItens[index],
|
||||
options = selectedItens,
|
||||
onSelect = {
|
||||
val automaticallyShowImages = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyShowImages(automaticallyShowImages)
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(R.string.automatically_play_videos),
|
||||
placeholder = selectedItens[videoIndex],
|
||||
options = selectedItens,
|
||||
onSelect = {
|
||||
val automaticallyStartPlayback = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyStartPlayback(automaticallyStartPlayback)
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(R.string.automatically_show_url_preview),
|
||||
placeholder = selectedItens[linkIndex],
|
||||
options = selectedItens,
|
||||
onSelect = {
|
||||
val automaticallyShowUrlPreview = parseConnectivityType(it)
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.updateAutomaticallyShowUrlPreview(automaticallyShowUrlPreview)
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
TextSpinner(
|
||||
label = "",
|
||||
placeholder = selectedItens[selectedIndex],
|
||||
options = selectedItens,
|
||||
onSelect = onSelect,
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -493,12 +493,12 @@
|
||||
<string name="system">System</string>
|
||||
<string name="light">Light</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="theme">Theme</string>
|
||||
<string name="automatically_load_images_gifs">Automatically load images/gifs</string>
|
||||
<string name="automatically_play_videos">Automatically play videos</string>
|
||||
<string name="automatically_show_url_preview">Automatically show url preview</string>
|
||||
<string name="automatically_load_images_gifs">Image Preview</string>
|
||||
<string name="automatically_play_videos">Video Playback</string>
|
||||
<string name="automatically_show_url_preview">URL Preview</string>
|
||||
<string name="load_image">Load Image</string>
|
||||
|
||||
<string name="spamming_users">Spammers</string>
|
||||
@@ -536,4 +536,12 @@
|
||||
<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_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>
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
<locale android:name="cs"/>
|
||||
<locale android:name="de"/>
|
||||
<locale android:name="eo"/>
|
||||
<locale android:name="en"/>
|
||||
<locale android:name="en-GB"/>
|
||||
<locale android:name="es"/>
|
||||
<locale android:name="fa"/>
|
||||
<locale android:name="fr"/>
|
||||
<locale android:name="hu"/>
|
||||
<locale android:name="ja"/>
|
||||
<locale android:name="nl"/>
|
||||
<locale android:name="pt-BR"/>
|
||||
<locale android:name="ru"/>
|
||||
@@ -18,7 +21,4 @@
|
||||
<locale android:name="zh"/>
|
||||
<locale android:name="zh-HK"/>
|
||||
<locale android:name="zh-TW"/>
|
||||
<locale android:name="en"/>
|
||||
<locale android:name="en-GB"/>
|
||||
<locale android:name="ja"/>
|
||||
</locale-config>
|
||||
|
||||
Reference in New Issue
Block a user