feat: sparks & settings button location spark
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
a35f550172
commit
02d6fa0dbf
|
|
@ -156,6 +156,9 @@ dependencies {
|
||||||
// AboutLibraries - automated OSS library attribution
|
// AboutLibraries - automated OSS library attribution
|
||||||
implementation "com.mikepenz:aboutlibraries-compose:$aboutlibraries_version"
|
implementation "com.mikepenz:aboutlibraries-compose:$aboutlibraries_version"
|
||||||
|
|
||||||
|
// Lottie - animated vector graphics
|
||||||
|
implementation "com.airbnb.android:lottie-compose:6.0.0"
|
||||||
|
|
||||||
// Sentry - crash reporting
|
// Sentry - crash reporting
|
||||||
implementation 'io.sentry:sentry-android:6.15.0'
|
implementation 'io.sentry:sentry-android:6.15.0'
|
||||||
implementation 'io.sentry:sentry-compose-android:6.15.0'
|
implementation 'io.sentry:sentry-compose-android:6.15.0'
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import chat.revolt.screens.login.GreeterScreen
|
||||||
import chat.revolt.screens.login.LoginScreen
|
import chat.revolt.screens.login.LoginScreen
|
||||||
import chat.revolt.screens.login.MfaScreen
|
import chat.revolt.screens.login.MfaScreen
|
||||||
import chat.revolt.screens.settings.AppearanceSettingsScreen
|
import chat.revolt.screens.settings.AppearanceSettingsScreen
|
||||||
|
import chat.revolt.screens.settings.DebugSettingsScreen
|
||||||
import chat.revolt.screens.settings.SettingsScreen
|
import chat.revolt.screens.settings.SettingsScreen
|
||||||
import chat.revolt.ui.theme.RevoltTheme
|
import chat.revolt.ui.theme.RevoltTheme
|
||||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||||
|
|
@ -111,6 +112,7 @@ fun AppEntrypoint() {
|
||||||
|
|
||||||
composable("settings") { SettingsScreen(navController) }
|
composable("settings") { SettingsScreen(navController) }
|
||||||
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
|
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
|
||||||
|
composable("settings/debug") { DebugSettingsScreen(navController) }
|
||||||
dialog("settings/feedback") { FeedbackDialog(navController) }
|
dialog("settings/feedback") { FeedbackDialog(navController) }
|
||||||
|
|
||||||
composable("about") { AboutScreen(navController) }
|
composable("about") { AboutScreen(navController) }
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ class KVStorage @Inject constructor(
|
||||||
return dataStore.data.firstOrNull()?.get(stringPreferencesKey(key))
|
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) {
|
suspend fun remove(key: String) {
|
||||||
dataStore.edit { preferences ->
|
dataStore.edit { preferences ->
|
||||||
preferences.remove(stringPreferencesKey(key))
|
preferences.remove(stringPreferencesKey(key))
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
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.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
|
@ -16,13 +17,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
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.HomeScreen
|
||||||
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
|
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
|
||||||
import chat.revolt.screens.chat.views.channel.ChannelScreen
|
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.ExperimentalMaterialNavigationApi
|
||||||
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
||||||
import com.google.accompanist.navigation.material.bottomSheet
|
import com.google.accompanist.navigation.material.bottomSheet
|
||||||
|
|
@ -85,10 +95,19 @@ class ChatRouterViewModel @Inject constructor(
|
||||||
val currentChannel: String?
|
val currentChannel: String?
|
||||||
get() = _currentChannel.value
|
get() = _currentChannel.value
|
||||||
|
|
||||||
|
private var _sidebarSparkDisplayed = mutableStateOf(true)
|
||||||
|
val sidebarSparkDisplayed: Boolean
|
||||||
|
get() = _sidebarSparkDisplayed.value
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_currentServer.value = kvStorage.get("currentServer") ?: "home"
|
_currentServer.value = kvStorage.get("currentServer") ?: "home"
|
||||||
_currentChannel.value = kvStorage.get("currentChannel")
|
_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) {
|
fun navigateToServer(serverId: String, navController: NavController) {
|
||||||
if (serverId == "home") {
|
if (serverId == "home") {
|
||||||
navController.navigate("home") {
|
navController.navigate("home") {
|
||||||
|
|
@ -155,6 +178,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||||
val navController = rememberNavController(bottomSheetNavigator)
|
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) {
|
LaunchedEffect(drawerState) {
|
||||||
snapshotFlow { drawerState.currentValue }
|
snapshotFlow { drawerState.currentValue }
|
||||||
.distinctUntilChanged()
|
.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(
|
ModalBottomSheetLayout(
|
||||||
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
|
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
bottomSheetNavigator = bottomSheetNavigator,
|
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 {
|
Column {
|
||||||
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
|
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
|
||||||
DisconnectedNotice(
|
DisconnectedNotice(
|
||||||
|
|
@ -255,7 +325,10 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = viewModel.currentServer) {
|
Crossfade(
|
||||||
|
targetState = viewModel.currentServer,
|
||||||
|
label = "Channel List"
|
||||||
|
) {
|
||||||
ChannelList(
|
ChannelList(
|
||||||
serverId = it,
|
serverId = it,
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
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.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import chat.revolt.BuildConfig
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.components.generic.PageHeader
|
import chat.revolt.components.generic.PageHeader
|
||||||
import chat.revolt.components.generic.SheetClickable
|
import chat.revolt.components.generic.SheetClickable
|
||||||
|
|
@ -78,6 +80,24 @@ fun SettingsScreen(
|
||||||
navController.navigate("about")
|
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()
|
Divider()
|
||||||
|
|
||||||
SheetClickable(
|
SheetClickable(
|
||||||
|
|
@ -109,7 +129,8 @@ fun SettingsScreen(
|
||||||
},
|
},
|
||||||
modifier = Modifier.testTag("settings_view_logout")
|
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.
|
|
@ -244,6 +244,11 @@
|
||||||
<string name="media_viewer_share_video">Share video</string>
|
<string name="media_viewer_share_video">Share video</string>
|
||||||
<string name="media_viewer_share_image">Share image</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">Appearance</string>
|
||||||
<string name="settings_appearance_theme">Theme</string>
|
<string name="settings_appearance_theme">Theme</string>
|
||||||
<string name="settings_appearance_theme_none">System</string>
|
<string name="settings_appearance_theme_none">System</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue