feat: theme picker and settings sync 🔥🔥🔥🔥🔥
This commit is contained in:
parent
63227d8d0f
commit
023ac6e5cb
|
|
@ -75,6 +75,7 @@ dependencies {
|
|||
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-navigation-animation:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-flowlayout:$accompanist_version"
|
||||
|
||||
// KTOR - HTTP+WebSocket Library
|
||||
implementation "io.ktor:ktor-client-core:$ktor_version"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.screens.SplashScreen
|
||||
import chat.revolt.screens.about.AboutScreen
|
||||
import chat.revolt.screens.about.AttributionScreen
|
||||
|
|
@ -21,6 +22,7 @@ import chat.revolt.screens.chat.ChatRouterScreen
|
|||
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.SettingsScreen
|
||||
import chat.revolt.ui.theme.RevoltTheme
|
||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
|
|
@ -33,14 +35,7 @@ class MainActivity : ComponentActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
RevoltTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
AppEntrypoint()
|
||||
}
|
||||
}
|
||||
AppEntrypoint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,52 +49,62 @@ val RevoltTweenFloat: FiniteAnimationSpec<Float> = tween(400, easing = EaseInOut
|
|||
fun AppEntrypoint() {
|
||||
val navController = rememberAnimatedNavController()
|
||||
|
||||
AnimatedNavHost(
|
||||
navController = navController,
|
||||
startDestination = "splash",
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentScope.SlideDirection.Left,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeIn(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOutOfContainer(
|
||||
AnimatedContentScope.SlideDirection.Left,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentScope.SlideDirection.Right,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeIn(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutOfContainer(
|
||||
AnimatedContentScope.SlideDirection.Right,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
||||
}
|
||||
RevoltTheme(
|
||||
requestedTheme = GlobalState.theme,
|
||||
) {
|
||||
composable("splash") { SplashScreen(navController) }
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
AnimatedNavHost(
|
||||
navController = navController,
|
||||
startDestination = "splash",
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentScope.SlideDirection.Left,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeIn(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOutOfContainer(
|
||||
AnimatedContentScope.SlideDirection.Left,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIntoContainer(
|
||||
AnimatedContentScope.SlideDirection.Right,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeIn(animationSpec = RevoltTweenFloat)
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutOfContainer(
|
||||
AnimatedContentScope.SlideDirection.Right,
|
||||
animationSpec = RevoltTweenInt
|
||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
||||
}
|
||||
) {
|
||||
composable("splash") { SplashScreen(navController) }
|
||||
|
||||
composable("login/greeting") { GreeterScreen(navController) }
|
||||
composable("login/login") { LoginScreen(navController) }
|
||||
composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry ->
|
||||
val mfaTicket = backStackEntry.arguments?.getString("mfaTicket") ?: ""
|
||||
val allowedAuthTypes =
|
||||
backStackEntry.arguments?.getString("allowedAuthTypes") ?: ""
|
||||
composable("login/greeting") { GreeterScreen(navController) }
|
||||
composable("login/login") { LoginScreen(navController) }
|
||||
composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry ->
|
||||
val mfaTicket = backStackEntry.arguments?.getString("mfaTicket") ?: ""
|
||||
val allowedAuthTypes =
|
||||
backStackEntry.arguments?.getString("allowedAuthTypes") ?: ""
|
||||
|
||||
MfaScreen(navController, allowedAuthTypes, mfaTicket)
|
||||
MfaScreen(navController, allowedAuthTypes, mfaTicket)
|
||||
}
|
||||
|
||||
composable("chat") { ChatRouterScreen(navController) }
|
||||
|
||||
composable("settings") { SettingsScreen(navController) }
|
||||
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
|
||||
|
||||
composable("about") { AboutScreen(navController) }
|
||||
composable("about/oss") { AttributionScreen(navController) }
|
||||
composable("about/placeholder") { PlaceholderScreen(navController) }
|
||||
}
|
||||
}
|
||||
|
||||
composable("chat") { ChatRouterScreen(navController) }
|
||||
|
||||
composable("settings") { SettingsScreen(navController) }
|
||||
|
||||
composable("about") { AboutScreen(navController) }
|
||||
composable("about/oss") { AttributionScreen(navController) }
|
||||
composable("about/placeholder") { PlaceholderScreen(navController) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import chat.revolt.api.realtime.frames.sendable.PingFrame
|
|||
import io.ktor.client.plugins.websocket.*
|
||||
import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import java.util.*
|
||||
|
||||
enum class DisconnectionState {
|
||||
Disconnected,
|
||||
|
|
@ -64,7 +63,7 @@ object RealtimeSocket {
|
|||
suspend fun sendPing() {
|
||||
if (disconnectionState != DisconnectionState.Connected) return
|
||||
|
||||
val pingPacket = PingFrame("Ping", Calendar.getInstance().timeInMillis.toInt())
|
||||
val pingPacket = PingFrame("Ping", System.currentTimeMillis())
|
||||
socket?.send(RevoltJson.encodeToString(PingFrame.serializer(), pingPacket))
|
||||
Log.d("RealtimeSocket", "Sent ping frame with ${pingPacket.data}")
|
||||
}
|
||||
|
|
@ -167,7 +166,7 @@ object RealtimeSocket {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun invalidateAllChannelStates() {
|
||||
channelCallbacks.forEach { (_, callback) ->
|
||||
callback.onStateInvalidate()
|
||||
|
|
@ -189,8 +188,8 @@ object RealtimeSocket {
|
|||
Log.d("RealtimeSocket", "Registered channel callback for $channelId.")
|
||||
}
|
||||
|
||||
fun unregisterChannelCallback(channelId: String, callback: ChannelCallback) {
|
||||
channelCallbacks.remove(channelId, callback)
|
||||
fun unregisterChannelCallback(channelId: String) {
|
||||
channelCallbacks.remove(channelId)
|
||||
|
||||
Log.d("RealtimeSocket", "Unregistered channel callback for $channelId")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ data class BulkFrame(
|
|||
@Serializable
|
||||
data class PongFrame(
|
||||
val type: String = "Pong",
|
||||
val data: Int
|
||||
val data: Long
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ data class AuthorizationFrame(
|
|||
@Serializable
|
||||
data class PingFrame(
|
||||
val type: String,
|
||||
val data: Int
|
||||
val data: Long
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package chat.revolt.api.routes.sync
|
||||
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.RevoltHttp
|
||||
import chat.revolt.api.RevoltJson
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
|
||||
@Serializable
|
||||
data class SyncedSetting(val timestamp: Long, val value: String)
|
||||
|
||||
suspend fun getKeys(vararg keys: String): Map<String, SyncedSetting> {
|
||||
val response = RevoltHttp.post("/sync/settings/fetch") {
|
||||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
|
||||
// format: {"keys": ["key1", "key2"]}
|
||||
setBody(
|
||||
RevoltJson.encodeToString(
|
||||
MapSerializer(
|
||||
String.serializer(),
|
||||
ListSerializer(String.serializer())
|
||||
),
|
||||
mapOf("keys" to keys.toList())
|
||||
)
|
||||
)
|
||||
}.bodyAsText()
|
||||
|
||||
return RevoltJson.decodeFromString(
|
||||
MapSerializer(
|
||||
String.serializer(),
|
||||
JsonArray.serializer()
|
||||
),
|
||||
response
|
||||
).mapValues { (_, value) ->
|
||||
SyncedSetting(
|
||||
timestamp = value[0].toString().toLong(),
|
||||
value = value[1]
|
||||
.toString()
|
||||
.removeSurrounding("\"")
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\\\", "\\") // the revolt API is so scuffed i can't even make this up
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setKey(key: String, value: String) {
|
||||
RevoltHttp.post("/sync/settings/set") {
|
||||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
|
||||
parameter("timestamp", System.currentTimeMillis())
|
||||
|
||||
// format: {"key": "value"}
|
||||
setBody(
|
||||
RevoltJson.encodeToString(
|
||||
MapSerializer(
|
||||
String.serializer(),
|
||||
String.serializer()
|
||||
),
|
||||
mapOf(key to value)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package chat.revolt.api.schemas
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OrderingSettings(
|
||||
val servers: List<String> = emptyList(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AndroidSpecificSettings(
|
||||
/**
|
||||
* The theme to use for the app.
|
||||
* Can be one of { None, Revolt, Light, M3Dynamic, Amoled }
|
||||
*/
|
||||
var theme: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package chat.revolt.api.settings
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.revolt.ui.theme.Theme
|
||||
|
||||
object GlobalState {
|
||||
private var _theme = mutableStateOf(Theme.Revolt)
|
||||
val theme
|
||||
get() = _theme.value
|
||||
|
||||
fun setTheme(theme: Theme) {
|
||||
_theme.value = theme
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package chat.revolt.api.settings
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.api.routes.sync.getKeys
|
||||
import chat.revolt.api.routes.sync.setKey
|
||||
import chat.revolt.api.schemas.AndroidSpecificSettings
|
||||
import chat.revolt.api.schemas.OrderingSettings
|
||||
|
||||
object SyncedSettings {
|
||||
private val _ordering = mutableStateOf(OrderingSettings())
|
||||
private val _android = mutableStateOf(AndroidSpecificSettings("None"))
|
||||
|
||||
val ordering: OrderingSettings
|
||||
get() = _ordering.value
|
||||
val android: AndroidSpecificSettings
|
||||
get() = _android.value
|
||||
|
||||
suspend fun fetch() {
|
||||
val settings = getKeys("ordering", "android")
|
||||
|
||||
settings["ordering"]?.let {
|
||||
_ordering.value = RevoltJson.decodeFromString(
|
||||
OrderingSettings.serializer(),
|
||||
it.value
|
||||
)
|
||||
}
|
||||
|
||||
settings["android"]?.let {
|
||||
_android.value = RevoltJson.decodeFromString(
|
||||
AndroidSpecificSettings.serializer(),
|
||||
it.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateOrdering(value: OrderingSettings) {
|
||||
_ordering.value = value
|
||||
setKey("ordering", RevoltJson.encodeToString(OrderingSettings.serializer(), value))
|
||||
}
|
||||
|
||||
suspend fun updateAndroid(value: AndroidSpecificSettings) {
|
||||
_android.value = value
|
||||
setKey("android", RevoltJson.encodeToString(AndroidSpecificSettings.serializer(), value))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,66 @@
|
|||
package chat.revolt.components.generic
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.revolt.R
|
||||
|
||||
@Composable
|
||||
fun PageHeader(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
showBackButton: Boolean = false,
|
||||
onBackButtonClicked: () -> Unit = {},
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Left,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
modifier = modifier
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (showBackButton) {
|
||||
IconButton(onClick = onBackButtonClicked) {
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Left,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
modifier = modifier
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PageHeaderPreview() {
|
||||
PageHeader(text = "Page Header")
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PageHeaderPreviewWithBackButton() {
|
||||
PageHeader(text = "Page Header", showBackButton = true)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -32,7 +31,9 @@ fun Weblink(text: String, url: String, modifier: Modifier = Modifier) {
|
|||
fun AnyLink(text: String, action: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = text,
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package chat.revolt.components.screens.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SettingsCategory(
|
||||
icon: @Composable (Modifier) -> Unit,
|
||||
label: @Composable (TextStyle) -> Unit,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(bottom = 8.dp)) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(all = 4.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
icon(Modifier.padding(end = 16.dp))
|
||||
label(
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SettingsCategoryPreview() {
|
||||
SettingsCategory(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = Icons.Default.Person,
|
||||
contentDescription = "Account"
|
||||
)
|
||||
},
|
||||
label = { textStyle ->
|
||||
Text(text = "Account", style = textStyle)
|
||||
},
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package chat.revolt.components.screens.settings.appearance
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ThemeChip(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color,
|
||||
text: String,
|
||||
selected: Boolean = false,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable(onClick = onClick)
|
||||
.then(modifier)
|
||||
.then(
|
||||
if (selected)
|
||||
Modifier
|
||||
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f))
|
||||
else Modifier
|
||||
)
|
||||
.padding(4.dp)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(color)
|
||||
.height(60.dp)
|
||||
.fillMaxWidth(1f)
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -21,8 +22,12 @@ import androidx.lifecycle.viewModelScope
|
|||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.screens.splash.DisconnectedScreen
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import chat.revolt.ui.theme.RevoltColorScheme
|
||||
import chat.revolt.ui.theme.Theme
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -82,18 +87,29 @@ class SplashScreenViewModel @Inject constructor(
|
|||
setNavigateTo("login")
|
||||
} else {
|
||||
RevoltAPI.loginAs(token)
|
||||
loadSettings()
|
||||
setNavigateTo("home")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
viewModelScope.launch {
|
||||
SyncedSettings.fetch()
|
||||
SyncedSettings.android.theme?.let { GlobalState.setTheme(Theme.valueOf(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
checkLoggedInState()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel = hiltViewModel()) {
|
||||
fun SplashScreen(
|
||||
navController: NavController,
|
||||
viewModel: SplashScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
if (!viewModel.isConnected) {
|
||||
DisconnectedScreen(
|
||||
onRetry = {
|
||||
|
|
@ -105,6 +121,7 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel
|
|||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = RevoltColorScheme.background)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -50,7 +49,9 @@ fun VersionItem(
|
|||
Row(modifier) {
|
||||
Text(
|
||||
text = key,
|
||||
color = Color(0xccffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Bold
|
||||
|
|
@ -60,7 +61,9 @@ fun VersionItem(
|
|||
)
|
||||
Text(
|
||||
text = value,
|
||||
color = Color(0xccffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal
|
||||
|
|
@ -117,7 +120,9 @@ fun AboutScreen(
|
|||
if (viewModel.root.value == null) {
|
||||
Text(
|
||||
text = stringResource(R.string.loading),
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.ui.theme.DarkColorScheme
|
||||
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
|
||||
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
|
||||
|
||||
|
|
@ -39,10 +38,10 @@ fun AttributionScreen(navController: NavController) {
|
|||
.fillMaxSize()
|
||||
.weight(1f),
|
||||
colors = LibraryDefaults.libraryColors(
|
||||
backgroundColor = DarkColorScheme.background,
|
||||
contentColor = DarkColorScheme.onBackground,
|
||||
badgeBackgroundColor = DarkColorScheme.primary,
|
||||
badgeContentColor = DarkColorScheme.onPrimary
|
||||
backgroundColor = MaterialTheme.colorScheme.background,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
badgeBackgroundColor = MaterialTheme.colorScheme.primary,
|
||||
badgeContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
)
|
||||
Button(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -49,10 +48,8 @@ fun PlaceholderScreen(
|
|||
|
||||
Text(
|
||||
text = stringResource(R.string.comingsoon_body),
|
||||
color = Color(0xaaffffff),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||
|
|
|
|||
|
|
@ -64,9 +64,9 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
val channel: Channel?
|
||||
get() = _channel
|
||||
|
||||
private var _callbacks = mutableStateOf<RealtimeSocket.ChannelCallback?>(null)
|
||||
val callbacks: RealtimeSocket.ChannelCallback?
|
||||
get() = _callbacks.value
|
||||
private var _callback = mutableStateOf<RealtimeSocket.ChannelCallback?>(null)
|
||||
val callback: RealtimeSocket.ChannelCallback?
|
||||
get() = _callback.value
|
||||
|
||||
private var _renderableMessages = mutableStateListOf<MessageSchema>()
|
||||
val renderableMessages: List<MessageSchema>
|
||||
|
|
@ -164,8 +164,8 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
private fun registerCallback() {
|
||||
_callbacks.value = ChannelScreenCallback()
|
||||
RealtimeSocket.registerChannelCallback(channel!!.id!!, callbacks!!)
|
||||
_callback.value = ChannelScreenCallback()
|
||||
RealtimeSocket.registerChannelCallback(channel!!.id!!, callback!!)
|
||||
}
|
||||
|
||||
fun fetchMessages() {
|
||||
|
|
@ -399,9 +399,7 @@ fun ChannelScreen(
|
|||
|
||||
DisposableEffect(channelId) {
|
||||
onDispose {
|
||||
viewModel.callbacks?.let {
|
||||
RealtimeSocket.unregisterChannelCallback(channelId, it)
|
||||
}
|
||||
RealtimeSocket.unregisterChannelCallback(channelId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -84,7 +83,9 @@ fun GreeterScreen(navController: NavController) {
|
|||
|
||||
Text(
|
||||
text = stringResource(R.string.login_onboarding_body),
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||
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.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
|
|
@ -169,7 +168,9 @@ fun MfaScreen(
|
|||
|
||||
Text(
|
||||
text = stringResource(R.string.mfa_interstitial_lead),
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
|
@ -206,7 +207,9 @@ fun MfaScreen(
|
|||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.mfa_totp_lead),
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
|
@ -244,7 +247,9 @@ fun MfaScreen(
|
|||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.mfa_recovery_lead),
|
||||
color = Color(0xaaffffff),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
package chat.revolt.screens.settings
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
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.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.appearance.ThemeChip
|
||||
import chat.revolt.ui.theme.Theme
|
||||
import chat.revolt.ui.theme.systemSupportsDynamicColors
|
||||
import com.google.accompanist.flowlayout.FlowRow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppearanceSettingsScreenViewModel : ViewModel() {
|
||||
fun saveNewTheme(theme: Theme) {
|
||||
viewModelScope.launch {
|
||||
val android = SyncedSettings.android
|
||||
android.theme = theme.toString()
|
||||
SyncedSettings.updateAndroid(android)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppearanceSettingsScreen(
|
||||
navController: NavController,
|
||||
viewModel: AppearanceSettingsScreenViewModel = viewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
fun setNewTheme(theme: Theme) {
|
||||
GlobalState.setTheme(theme)
|
||||
viewModel.saveNewTheme(theme)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings_appearance),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
})
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_theme),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
|
||||
|
||||
FlowRow(
|
||||
mainAxisSpacing = 10.dp,
|
||||
crossAxisSpacing = 10.dp,
|
||||
) {
|
||||
ThemeChip(
|
||||
color = Color(0xff172333),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_revolt),
|
||||
selected = GlobalState.theme == Theme.Revolt,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
setNewTheme(Theme.Revolt)
|
||||
}
|
||||
|
||||
ThemeChip(
|
||||
color = Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_light),
|
||||
selected = GlobalState.theme == Theme.Light,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
setNewTheme(Theme.Light)
|
||||
}
|
||||
|
||||
ThemeChip(
|
||||
color = Color(0xff000000),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_amoled),
|
||||
selected = GlobalState.theme == Theme.Amoled,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
setNewTheme(Theme.Amoled)
|
||||
}
|
||||
|
||||
ThemeChip(
|
||||
color = if (isSystemInDarkTheme()) Color(0xff172333) else Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_none),
|
||||
selected = GlobalState.theme == Theme.None,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
setNewTheme(Theme.None)
|
||||
}
|
||||
|
||||
if (systemSupportsDynamicColors()) {
|
||||
ThemeChip(
|
||||
color = dynamicDarkColorScheme(LocalContext.current).primary,
|
||||
text = stringResource(id = R.string.settings_appearance_theme_m3dynamic),
|
||||
selected = GlobalState.theme == Theme.M3Dynamic,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
setNewTheme(Theme.M3Dynamic)
|
||||
}
|
||||
} else {
|
||||
ThemeChip(
|
||||
color = Color(0xffa0a0a0),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_m3dynamic_unsupported),
|
||||
selected = false,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.settings_appearance_theme_m3dynamic_unsupported_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,20 @@ package chat.revolt.screens.settings
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.SettingsCategory
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
|
|
@ -21,20 +25,51 @@ fun SettingsScreen(
|
|||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
PageHeader(stringResource(id = R.string.settings))
|
||||
ElevatedButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.back))
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
})
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(10.dp)
|
||||
) {
|
||||
SettingsCategory(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_palette_24dp),
|
||||
contentDescription =
|
||||
stringResource(id = R.string.settings_appearance),
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { textStyle ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance),
|
||||
style = textStyle
|
||||
)
|
||||
})
|
||||
{
|
||||
navController.navigate("settings/appearance")
|
||||
}
|
||||
|
||||
SettingsCategory(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = stringResource(id = R.string.about),
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { textStyle ->
|
||||
Text(text = stringResource(id = R.string.about), style = textStyle)
|
||||
})
|
||||
{
|
||||
navController.navigate("about")
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.about))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package chat.revolt.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
|
@ -3,10 +3,7 @@ package chat.revolt.ui.theme
|
|||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -15,43 +12,78 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.ViewCompat
|
||||
|
||||
const val FORCE_ANDROID_DEFAULTS = false
|
||||
|
||||
const val FOREGROUND = 0xffffffff
|
||||
|
||||
val DarkColorScheme = darkColorScheme(
|
||||
val RevoltColorScheme = darkColorScheme(
|
||||
primary = Color(0xfffe4654),
|
||||
onPrimary = Color(FOREGROUND),
|
||||
onPrimary = Color(0xffffffff),
|
||||
secondary = Color(0xfffd6671),
|
||||
onSecondary = Color(FOREGROUND),
|
||||
onSecondary = Color(0xffffffff),
|
||||
tertiary = Color(0xffff6667),
|
||||
onTertiary = Color(FOREGROUND),
|
||||
onTertiary = Color(0xffffffff),
|
||||
background = Color(0xff101823),
|
||||
onBackground = Color(FOREGROUND),
|
||||
onBackground = Color(0xffffffff),
|
||||
surfaceVariant = Color(0xff172333),
|
||||
onSurfaceVariant = Color(FOREGROUND),
|
||||
onSurfaceVariant = Color(0xffffffff),
|
||||
surface = Color(0xff111a26),
|
||||
onSurface = Color(FOREGROUND),
|
||||
onSurface = Color(0xffffffff),
|
||||
)
|
||||
|
||||
val AmoledColorScheme = RevoltColorScheme.copy(
|
||||
background = Color(0xff000000),
|
||||
onBackground = Color(0xffffffff),
|
||||
surfaceVariant = Color(0xff131313),
|
||||
onSurfaceVariant = Color(0xffffffff),
|
||||
surface = Color(0xff212121),
|
||||
onSurface = Color(0xffffffff),
|
||||
)
|
||||
|
||||
val LightColorScheme = lightColorScheme(
|
||||
primary = Color(0xfffe4654),
|
||||
onPrimary = Color(0xffffffff),
|
||||
secondary = Color(0xfffd6671),
|
||||
onSecondary = Color(0xffffffff),
|
||||
tertiary = Color(0xffff6667),
|
||||
onTertiary = Color(0xffffffff),
|
||||
background = Color(0xffffffff),
|
||||
onBackground = Color(0xff000000),
|
||||
surfaceVariant = Color(0xffe6e6e6),
|
||||
onSurfaceVariant = Color(0xff000000),
|
||||
surface = Color(0xffdddddd),
|
||||
onSurface = Color(0xff000000),
|
||||
)
|
||||
|
||||
enum class Theme {
|
||||
None,
|
||||
Revolt,
|
||||
Light,
|
||||
M3Dynamic,
|
||||
Amoled,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RevoltTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = false,
|
||||
requestedTheme: Theme,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val systemInDarkTheme = isSystemInDarkTheme()
|
||||
val m3Supported = systemSupportsDynamicColors()
|
||||
|
||||
val colorScheme = when {
|
||||
dynamicColor && darkTheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
dynamicDarkColorScheme(context)
|
||||
}
|
||||
dynamicColor && !darkTheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
dynamicLightColorScheme(context)
|
||||
}
|
||||
else -> DarkColorScheme
|
||||
m3Supported && requestedTheme == Theme.M3Dynamic && systemInDarkTheme -> dynamicDarkColorScheme(
|
||||
context
|
||||
)
|
||||
m3Supported && requestedTheme == Theme.M3Dynamic && !systemInDarkTheme -> dynamicLightColorScheme(
|
||||
context
|
||||
)
|
||||
requestedTheme == Theme.Revolt -> RevoltColorScheme
|
||||
requestedTheme == Theme.Light -> LightColorScheme
|
||||
requestedTheme == Theme.Amoled -> AmoledColorScheme
|
||||
requestedTheme == Theme.None && systemInDarkTheme -> RevoltColorScheme
|
||||
requestedTheme == Theme.None && !systemInDarkTheme -> LightColorScheme
|
||||
else -> RevoltColorScheme
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
|
|
@ -62,15 +94,13 @@ fun RevoltTheme(
|
|||
}
|
||||
}
|
||||
|
||||
if (FORCE_ANDROID_DEFAULTS) {
|
||||
MaterialTheme(
|
||||
content = content
|
||||
)
|
||||
} else {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = RevoltTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = RevoltTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
fun systemSupportsDynamicColors(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ val RevoltTypography = Typography(
|
|||
),
|
||||
displaySmall = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 36.sp
|
||||
),
|
||||
|
||||
|
|
@ -49,39 +49,39 @@ val RevoltTypography = Typography(
|
|||
),
|
||||
headlineSmall = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 22.sp
|
||||
),
|
||||
titleMedium = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp
|
||||
),
|
||||
titleSmall = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
),
|
||||
|
||||
labelLarge = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 14.sp
|
||||
),
|
||||
labelMedium = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 12.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 11.sp
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
<path
|
||||
android:pathData="M24.98,12.56c0,2.79 -1.52,4.46 -4.76,4.46L14.84,17.02L14.84,8.2L20.22,8.2C23.46,8.2 24.98,9.93 24.98,12.56ZM0.98,1.01 L6.18,8.22L6.18,36.58h8.67L14.84,23.15h2.08l7.4,13.43h9.78l-8.21,-14.09A10.35,10.35 0,0 0,33.8 12.21c0,-6.18 -4.36,-11.2 -13.07,-11.2ZM61.01,1.01L39.22,1.01L39.22,36.58L61.01,36.58L61.01,29.64L47.89,29.64v-7.8L59.49,21.84L59.49,15.15L47.89,15.15L47.89,8.21L61.01,8.21ZM82,27.87 L73.18,1.01L63.95,1.01L76.57,36.58L87.42,36.58L100.03,1.01L90.86,1.01ZM138.65,18.69c0,-10.69 -8.06,-18.19 -18.19,-18.19 -10.09,0 -18.3,7.5 -18.3,18.19a17.9,17.9 0,0 0,18.3 18.24A17.82,17.82 0,0 0,138.65 18.69ZM111.03,18.69c0,-6.34 3.65,-10.34 9.43,-10.34 5.68,0 9.38,4 9.38,10.34 0,6.23 -3.7,10.34 -9.38,10.34C114.68,29.03 111.03,24.93 111.03,18.69ZM143.47,1.01L143.47,36.58L163.49,36.58v-6.95L152.13,29.63L152.13,1.01ZM165.71,8.21h9.43L175.14,36.58h8.67L183.81,8.2h9.43v-7.2L165.71,1.01Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#fff"
|
||||
android:strokeColor="#fff"/>
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
|
|||
|
|
@ -107,4 +107,14 @@
|
|||
|
||||
<string name="scroll_to_bottom">Scroll to bottom</string>
|
||||
<string name="settings">Settings</string>
|
||||
|
||||
<string name="settings_appearance">Appearance</string>
|
||||
<string name="settings_appearance_theme">Theme</string>
|
||||
<string name="settings_appearance_theme_none">System</string>
|
||||
<string name="settings_appearance_theme_revolt">Revolt</string>
|
||||
<string name="settings_appearance_theme_light">Light</string>
|
||||
<string name="settings_appearance_theme_amoled">Pure Black</string>
|
||||
<string name="settings_appearance_theme_m3dynamic">Material You</string>
|
||||
<string name="settings_appearance_theme_m3dynamic_unsupported">Material You (unsupported)</string>
|
||||
<string name="settings_appearance_theme_m3dynamic_unsupported_toast">Material You is not supported on this device.</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue