feat: splash screen, move login branching logic out of greeter
This commit is contained in:
parent
4356f759e0
commit
a5288e4ed7
|
|
@ -12,6 +12,7 @@ import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import chat.revolt.screens.SplashScreen
|
||||||
import chat.revolt.screens.about.AboutScreen
|
import chat.revolt.screens.about.AboutScreen
|
||||||
import chat.revolt.screens.about.AttributionScreen
|
import chat.revolt.screens.about.AttributionScreen
|
||||||
import chat.revolt.screens.about.PlaceholderScreen
|
import chat.revolt.screens.about.PlaceholderScreen
|
||||||
|
|
@ -52,7 +53,7 @@ fun AppEntrypoint() {
|
||||||
|
|
||||||
AnimatedNavHost(
|
AnimatedNavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = "login/greeting",
|
startDestination = "splash",
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
slideIntoContainer(
|
slideIntoContainer(
|
||||||
AnimatedContentScope.SlideDirection.Left,
|
AnimatedContentScope.SlideDirection.Left,
|
||||||
|
|
@ -78,6 +79,8 @@ fun AppEntrypoint() {
|
||||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
) + fadeOut(animationSpec = RevoltTweenFloat)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
composable("splash") { SplashScreen(navController) }
|
||||||
|
|
||||||
composable("login/greeting") { GreeterScreen(navController) }
|
composable("login/greeting") { GreeterScreen(navController) }
|
||||||
composable("login/login") { LoginScreen(navController) }
|
composable("login/login") { LoginScreen(navController) }
|
||||||
composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry ->
|
composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry ->
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package chat.revolt.api
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import chat.revolt.api.realtime.RealtimeSocket
|
import chat.revolt.api.realtime.RealtimeSocket
|
||||||
import chat.revolt.api.routes.user.fetchSelf
|
import chat.revolt.api.routes.user.fetchSelf
|
||||||
import chat.revolt.api.schemas.*
|
import chat.revolt.api.schemas.*
|
||||||
|
|
@ -61,11 +62,11 @@ object RevoltAPI {
|
||||||
const val TOKEN_HEADER_NAME = "x-session-token"
|
const val TOKEN_HEADER_NAME = "x-session-token"
|
||||||
|
|
||||||
// FIXME discount caching solutions! LRU would be better but this is fine for now
|
// FIXME discount caching solutions! LRU would be better but this is fine for now
|
||||||
val userCache = mutableMapOf<String, User>()
|
val userCache = mutableStateMapOf<String, User>()
|
||||||
val serverCache = mutableMapOf<String, Server>()
|
val serverCache = mutableStateMapOf<String, Server>()
|
||||||
val channelCache = mutableMapOf<String, Channel>()
|
val channelCache = mutableStateMapOf<String, Channel>()
|
||||||
val emojiCache = mutableMapOf<String, Emoji>()
|
val emojiCache = mutableStateMapOf<String, Emoji>()
|
||||||
val messageCache = mutableMapOf<String, Message>()
|
val messageCache = mutableStateMapOf<String, Message>()
|
||||||
|
|
||||||
var selfId: String? = null
|
var selfId: String? = null
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package chat.revolt.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
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.R
|
||||||
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.components.generic.RemoteImage
|
||||||
|
import chat.revolt.components.generic.drawableResource
|
||||||
|
import chat.revolt.persistence.KVStorage
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SplashScreenViewModel @Inject constructor(
|
||||||
|
private val kvStorage: KVStorage
|
||||||
|
) : ViewModel() {
|
||||||
|
private var _navigateTo by mutableStateOf("")
|
||||||
|
val navigateTo: String
|
||||||
|
get() = _navigateTo
|
||||||
|
|
||||||
|
fun setNavigateTo(value: String) {
|
||||||
|
_navigateTo = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val token = kvStorage.get("sessionToken") ?: return@launch setNavigateTo("login")
|
||||||
|
|
||||||
|
val valid = RevoltAPI.checkSessionToken(token)
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
kvStorage.remove("sessionToken")
|
||||||
|
setNavigateTo("login")
|
||||||
|
} else {
|
||||||
|
RevoltAPI.loginAs(token)
|
||||||
|
setNavigateTo("home")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel = hiltViewModel()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
RemoteImage(
|
||||||
|
url = drawableResource(R.drawable.revolt_logo_wide),
|
||||||
|
description = "Revolt Logo",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(60.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewModel.navigateTo.isNotEmpty()) {
|
||||||
|
when (viewModel.navigateTo) {
|
||||||
|
"login" -> {
|
||||||
|
navController.navigate("login/greeting") {
|
||||||
|
popUpTo("splash") {
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"home" -> {
|
||||||
|
navController.navigate("chat/home") {
|
||||||
|
popUpTo("splash") {
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModel.setNavigateTo("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -105,7 +105,6 @@ fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a scrollable list of all users in user cache
|
|
||||||
Text(
|
Text(
|
||||||
text = "User cache",
|
text = "User cache",
|
||||||
style = MaterialTheme.typography.displaySmall.copy(
|
style = MaterialTheme.typography.displaySmall.copy(
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ import androidx.compose.material3.ElevatedButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -19,90 +16,12 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import chat.revolt.api.RevoltAPI
|
|
||||||
import chat.revolt.components.generic.RemoteImage
|
import chat.revolt.components.generic.RemoteImage
|
||||||
import chat.revolt.components.generic.drawableResource
|
import chat.revolt.components.generic.drawableResource
|
||||||
import chat.revolt.persistence.KVStorage
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class GreeterViewModel @Inject constructor(
|
|
||||||
private val kvStorage: KVStorage
|
|
||||||
) : ViewModel() {
|
|
||||||
private var _skipLogin by mutableStateOf(false)
|
|
||||||
val skipLogin: Boolean
|
|
||||||
get() = _skipLogin
|
|
||||||
|
|
||||||
private var _finishedLoading by mutableStateOf(false)
|
|
||||||
val finishedLoading: Boolean
|
|
||||||
get() = _finishedLoading
|
|
||||||
|
|
||||||
fun setSkipLogin(value: Boolean) {
|
|
||||||
_skipLogin = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFinishedLoading(value: Boolean) {
|
|
||||||
_finishedLoading = value
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
viewModelScope.launch {
|
|
||||||
val token = kvStorage.get("sessionToken")
|
|
||||||
if (token != null) {
|
|
||||||
val valid = RevoltAPI.checkSessionToken(token)
|
|
||||||
if (!valid) {
|
|
||||||
kvStorage.remove("sessionToken")
|
|
||||||
RevoltAPI.setSessionHeader("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RevoltAPI.isLoggedIn()) {
|
|
||||||
RevoltAPI.loginAs(token ?: "")
|
|
||||||
_skipLogin = true
|
|
||||||
}
|
|
||||||
|
|
||||||
setFinishedLoading(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GreeterScreen(navController: NavController, viewModel: GreeterViewModel = hiltViewModel()) {
|
fun GreeterScreen(navController: NavController) {
|
||||||
if (viewModel.skipLogin) {
|
|
||||||
navController.navigate("chat/home") {
|
|
||||||
popUpTo("login/greeting") {
|
|
||||||
inclusive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewModel.setSkipLogin(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!viewModel.finishedLoading) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.fillMaxHeight(),
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
RemoteImage(
|
|
||||||
url = drawableResource(R.drawable.revolt_logo_wide),
|
|
||||||
description = "Revolt Logo",
|
|
||||||
contentScale = ContentScale.Fit,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(60.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue