feat: sparks & settings button location spark

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-04-23 06:04:19 +02:00
parent a35f550172
commit 02d6fa0dbf
8 changed files with 198 additions and 2 deletions

View File

@ -156,6 +156,9 @@ dependencies {
// AboutLibraries - automated OSS library attribution
implementation "com.mikepenz:aboutlibraries-compose:$aboutlibraries_version"
// Lottie - animated vector graphics
implementation "com.airbnb.android:lottie-compose:6.0.0"
// Sentry - crash reporting
implementation 'io.sentry:sentry-android:6.15.0'
implementation 'io.sentry:sentry-compose-android:6.15.0'

View File

@ -26,6 +26,7 @@ import chat.revolt.screens.login.GreeterScreen
import chat.revolt.screens.login.LoginScreen
import chat.revolt.screens.login.MfaScreen
import chat.revolt.screens.settings.AppearanceSettingsScreen
import chat.revolt.screens.settings.DebugSettingsScreen
import chat.revolt.screens.settings.SettingsScreen
import chat.revolt.ui.theme.RevoltTheme
import com.google.accompanist.navigation.animation.AnimatedNavHost
@ -111,6 +112,7 @@ fun AppEntrypoint() {
composable("settings") { SettingsScreen(navController) }
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
composable("settings/debug") { DebugSettingsScreen(navController) }
dialog("settings/feedback") { FeedbackDialog(navController) }
composable("about") { AboutScreen(navController) }

View File

@ -29,6 +29,16 @@ class KVStorage @Inject constructor(
return dataStore.data.firstOrNull()?.get(stringPreferencesKey(key))
}
suspend fun set(key: String, value: Boolean) {
dataStore.edit { preferences ->
preferences[stringPreferencesKey(key)] = value.toString()
}
}
suspend fun getBoolean(key: String): Boolean? {
return dataStore.data.firstOrNull()?.get(stringPreferencesKey(key))?.toBoolean()
}
suspend fun remove(key: String) {
dataStore.edit { preferences ->
preferences.remove(stringPreferencesKey(key))

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -16,13 +17,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.surfaceColorAtElevation
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.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
@ -64,6 +69,11 @@ import chat.revolt.screens.chat.sheets.StatusSheet
import chat.revolt.screens.chat.views.HomeScreen
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
import chat.revolt.screens.chat.views.channel.ChannelScreen
import com.airbnb.lottie.RenderMode
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
import com.google.accompanist.navigation.material.bottomSheet
@ -85,10 +95,19 @@ class ChatRouterViewModel @Inject constructor(
val currentChannel: String?
get() = _currentChannel.value
private var _sidebarSparkDisplayed = mutableStateOf(true)
val sidebarSparkDisplayed: Boolean
get() = _sidebarSparkDisplayed.value
init {
viewModelScope.launch {
_currentServer.value = kvStorage.get("currentServer") ?: "home"
_currentChannel.value = kvStorage.get("currentChannel")
_sidebarSparkDisplayed.value = if (kvStorage.getBoolean("sidebarSpark") == null) {
false
} else {
kvStorage.getBoolean("sidebarSpark")!!
}
}
}
@ -108,6 +127,10 @@ class ChatRouterViewModel @Inject constructor(
}
}
suspend fun setSettingsHintDisplayed() {
kvStorage.set("sidebarSpark", true)
}
fun navigateToServer(serverId: String, navController: NavController) {
if (serverId == "home") {
navController.navigate("home") {
@ -155,6 +178,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
val bottomSheetNavigator = rememberBottomSheetNavigator()
val navController = rememberNavController(bottomSheetNavigator)
val showSidebarSpark = remember { mutableStateOf(false) }
val sidebarSparkComposition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.open_settings_tutorial))
val sidebarSparkProgress by animateLottieCompositionAsState(
composition = sidebarSparkComposition,
)
LaunchedEffect(drawerState) {
snapshotFlow { drawerState.currentValue }
.distinctUntilChanged()
@ -175,11 +204,52 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
}
}
LaunchedEffect(viewModel.sidebarSparkDisplayed) {
snapshotFlow { viewModel.sidebarSparkDisplayed }
.distinctUntilChanged()
.collect { displayed ->
showSidebarSpark.value = !displayed
}
}
ModalBottomSheetLayout(
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
bottomSheetNavigator = bottomSheetNavigator,
) {
if (showSidebarSpark.value) {
AlertDialog(
onDismissRequest = {},
title = {
Text(stringResource(id = R.string.spark_sidebar_settings_tutorial))
},
text = {
Column {
LottieAnimation(
composition = sidebarSparkComposition,
progress = { sidebarSparkProgress },
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
renderMode = RenderMode.HARDWARE
)
Text(stringResource(id = R.string.spark_sidebar_settings_tutorial_description_1))
Text(stringResource(id = R.string.spark_sidebar_settings_tutorial_description_2))
}
},
confirmButton = {
TextButton(onClick = {
scope.launch {
viewModel.setSettingsHintDisplayed()
}
showSidebarSpark.value = false
}) {
Text(stringResource(id = R.string.spark_sidebar_settings_tutorial_acknowledge))
}
}
)
}
Column {
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
DisconnectedNotice(
@ -255,7 +325,10 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
}
}
Crossfade(targetState = viewModel.currentServer) {
Crossfade(
targetState = viewModel.currentServer,
label = "Channel List"
) {
ChannelList(
serverId = it,
drawerState = drawerState,

View File

@ -0,0 +1,82 @@
package chat.revolt.screens.settings
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import chat.revolt.components.generic.PageHeader
import chat.revolt.persistence.KVStorage
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class DebugSettingsScreenViewModel @Inject constructor(
private val kvStorage: KVStorage,
) : ViewModel() {
fun forgetSidebarSparkShown() {
viewModelScope.launch {
kvStorage.remove("sidebarSpark")
}
}
fun forgetAllSparks() {
this.forgetSidebarSparkShown()
}
}
@Composable
fun DebugSettingsScreen(
navController: NavController,
viewModel: DebugSettingsScreenViewModel = hiltViewModel()
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
PageHeader(
text = "Debug",
showBackButton = true,
onBackButtonClicked = {
navController.popBackStack()
})
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(20.dp)
) {
Text(
text = "Sparks",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(bottom = 10.dp)
)
Row(
modifier = Modifier.horizontalScroll(rememberScrollState())
) {
TextButton(onClick = { viewModel.forgetSidebarSparkShown() }) {
Text("Forget sidebar spark")
}
ElevatedButton(onClick = { viewModel.forgetAllSparks() }) {
Text("Forget all sparks")
}
}
}
}
}

View File

@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
@ -18,6 +19,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import chat.revolt.BuildConfig
import chat.revolt.R
import chat.revolt.components.generic.PageHeader
import chat.revolt.components.generic.SheetClickable
@ -78,6 +80,24 @@ fun SettingsScreen(
navController.navigate("about")
}
if (BuildConfig.DEBUG) {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Settings,
contentDescription = "Debug",
modifier = modifier
)
},
label = { textStyle ->
Text(text = "Debug", style = textStyle)
},
modifier = Modifier.testTag("settings_view_debug")
) {
navController.navigate("settings/debug")
}
}
Divider()
SheetClickable(
@ -109,7 +129,8 @@ fun SettingsScreen(
},
modifier = Modifier.testTag("settings_view_logout")
) {
Toast.makeText(navController.context, "Not implemented yet", Toast.LENGTH_SHORT).show()
Toast.makeText(navController.context, "Not implemented yet", Toast.LENGTH_SHORT)
.show()
}
}
}

Binary file not shown.

View File

@ -244,6 +244,11 @@
<string name="media_viewer_share_video">Share video</string>
<string name="media_viewer_share_image">Share image</string>
<string name="spark_sidebar_settings_tutorial">The settings are in the sidebar</string>
<string name="spark_sidebar_settings_tutorial_description_1">You can open the sidebar by swiping from the left edge of the screen.</string>
<string name="spark_sidebar_settings_tutorial_description_2">Then long tap your profile picture to open the settings.</string>
<string name="spark_sidebar_settings_tutorial_acknowledge">Got it</string>
<string name="settings_appearance">Appearance</string>
<string name="settings_appearance_theme">Theme</string>
<string name="settings_appearance_theme_none">System</string>