feat: channel routing
we can now switch between servers and channels, and there is a global drawer to do exactly that the app is really starting to take shape at this point!
This commit is contained in:
parent
dc3e71083b
commit
2a14f5fdc9
|
|
@ -16,8 +16,7 @@ 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
|
||||||
import chat.revolt.screens.chat.ChannelScreen
|
import chat.revolt.screens.chat.ChatRouterScreen
|
||||||
import chat.revolt.screens.chat.HomeScreen
|
|
||||||
import chat.revolt.screens.login.GreeterScreen
|
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
|
||||||
|
|
@ -92,12 +91,7 @@ fun AppEntrypoint() {
|
||||||
MfaScreen(navController, allowedAuthTypes, mfaTicket)
|
MfaScreen(navController, allowedAuthTypes, mfaTicket)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable("chat/home") { HomeScreen(navController) }
|
composable("chat") { ChatRouterScreen(navController) }
|
||||||
composable("chat/channel/{channelId}") { backStackEntry ->
|
|
||||||
val channelId = backStackEntry.arguments?.getString("channelId") ?: ""
|
|
||||||
|
|
||||||
ChannelScreen(navController, channelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
composable("about") { AboutScreen(navController) }
|
composable("about") { AboutScreen(navController) }
|
||||||
composable("about/oss") { AttributionScreen(navController) }
|
composable("about/oss") { AttributionScreen(navController) }
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"home" -> {
|
"home" -> {
|
||||||
navController.navigate("chat/home") {
|
navController.navigate("chat") {
|
||||||
popUpTo("splash") {
|
popUpTo("splash") {
|
||||||
inclusive = true
|
inclusive = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
package chat.revolt.screens.chat
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import chat.revolt.api.REVOLT_FILES
|
||||||
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.components.generic.RemoteImage
|
||||||
|
import chat.revolt.components.generic.drawableResource
|
||||||
|
import chat.revolt.screens.chat.views.HomeScreen
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.screens.chat.views.ChannelScreen
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class ChatRouterViewModel : ViewModel() {
|
||||||
|
private var _currentServer =
|
||||||
|
mutableStateOf(RevoltAPI.serverCache.values.firstOrNull()?.id ?: "home")
|
||||||
|
val currentServer: String
|
||||||
|
get() = _currentServer.value
|
||||||
|
|
||||||
|
fun setCurrentServer(serverId: String) {
|
||||||
|
_currentServer.value = serverId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun goToHome() {
|
||||||
|
_currentServer.value = "home"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = viewModel()) {
|
||||||
|
val channelDrawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
|
DismissibleNavigationDrawer(drawerState = channelDrawerState, drawerContent = {
|
||||||
|
ModalDrawerSheet {
|
||||||
|
Column(Modifier.fillMaxWidth()) {
|
||||||
|
Row {
|
||||||
|
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||||
|
RemoteImage(
|
||||||
|
url = drawableResource(R.drawable.ic_launcher_monochrome),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.clickable { viewModel.goToHome() },
|
||||||
|
description = "Home",
|
||||||
|
)
|
||||||
|
RevoltAPI.serverCache.values.forEach { server ->
|
||||||
|
server.icon?.let { icon ->
|
||||||
|
RemoteImage(
|
||||||
|
url = "$REVOLT_FILES/icons/${icon.id!!}/server.png?max_side=256",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.clickable { viewModel.setCurrentServer(server.id!!) },
|
||||||
|
description = "${server.name}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
if (viewModel.currentServer != "home") {
|
||||||
|
val server = RevoltAPI.serverCache[viewModel.currentServer]
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = server?.name ?: "Unknown Server",
|
||||||
|
fontWeight = FontWeight.Black,
|
||||||
|
fontSize = 24.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
server?.channels?.forEach { channelId ->
|
||||||
|
RevoltAPI.channelCache[channelId]?.let {
|
||||||
|
Text(
|
||||||
|
text = it.name ?: "Unnamed Channel",
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
scope.launch { channelDrawerState.close() }
|
||||||
|
navController.navigate("channel/${it.id}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(text = "Home not implemented!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Column(Modifier.fillMaxSize()) {
|
||||||
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
|
composable("home") {
|
||||||
|
HomeScreen(navController = navController)
|
||||||
|
}
|
||||||
|
composable("channel/{channelId}") { backStackEntry ->
|
||||||
|
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||||
|
if (channelId != null) {
|
||||||
|
ChannelScreen(navController, channelId = channelId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
package chat.revolt.screens.chat
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
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 chat.revolt.api.RevoltAPI
|
|
||||||
import chat.revolt.persistence.KVStorage
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class HomeScreenViewModel @Inject constructor(
|
|
||||||
private val kvStorage: KVStorage
|
|
||||||
) : ViewModel() {
|
|
||||||
private var _messageContent by mutableStateOf("")
|
|
||||||
val messageContent: String
|
|
||||||
get() = _messageContent
|
|
||||||
|
|
||||||
fun setMessageContent(value: String) {
|
|
||||||
_messageContent = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun logout() {
|
|
||||||
runBlocking {
|
|
||||||
kvStorage.remove("sessionToken")
|
|
||||||
RevoltAPI.logout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendMessage() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
chat.revolt.api.routes.channel.sendMessage(
|
|
||||||
"01F7ZSBSFHCAAJQ92ZGTY67HMN", // revolt lounge #general (temporarily hardcoded) FIXME
|
|
||||||
messageContent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
setMessageContent("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hiltViewModel()) {
|
|
||||||
val channelDrawerState = rememberDrawerState(DrawerValue.Closed)
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
DismissibleNavigationDrawer(drawerState = channelDrawerState, drawerContent = {
|
|
||||||
ModalDrawerSheet {
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
Text(
|
|
||||||
text = "Revolt Lounge",
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Black)
|
|
||||||
)
|
|
||||||
Divider()
|
|
||||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
|
||||||
RevoltAPI.channelCache.values
|
|
||||||
.filter { channel ->
|
|
||||||
channel.server == "01F7ZSBSFHQ8TA81725KQCSDDP"
|
|
||||||
}
|
|
||||||
.forEach { channel ->
|
|
||||||
NavigationDrawerItem(
|
|
||||||
selected = false,
|
|
||||||
label = { Text(text = "#" + channel.name) },
|
|
||||||
onClick = {
|
|
||||||
scope.launch {
|
|
||||||
channelDrawerState.close()
|
|
||||||
navController.navigate("chat/channel/${channel.id}")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Column() {
|
|
||||||
Text(
|
|
||||||
text = "Home (placeholder)",
|
|
||||||
style = MaterialTheme.typography.displaySmall.copy(
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
textAlign = TextAlign.Left,
|
|
||||||
fontSize = 24.sp
|
|
||||||
),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
)
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
viewModel.logout()
|
|
||||||
navController.navigate("login/greeting") {
|
|
||||||
popUpTo("chat/home") {
|
|
||||||
inclusive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(bottom = 30.dp, top = 5.dp, start = 20.dp, end = 20.dp)
|
|
||||||
) {
|
|
||||||
Text("Logout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package chat.revolt.screens.chat
|
package chat.revolt.screens.chat.views
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package chat.revolt.screens.chat.views
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
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 chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.persistence.KVStorage
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class HomeScreenViewModel @Inject constructor(
|
||||||
|
private val kvStorage: KVStorage
|
||||||
|
) : ViewModel() {
|
||||||
|
private var _messageContent by mutableStateOf("")
|
||||||
|
val messageContent: String
|
||||||
|
get() = _messageContent
|
||||||
|
|
||||||
|
fun setMessageContent(value: String) {
|
||||||
|
_messageContent = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
runBlocking {
|
||||||
|
kvStorage.remove("sessionToken")
|
||||||
|
RevoltAPI.logout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendMessage() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
chat.revolt.api.routes.channel.sendMessage(
|
||||||
|
"01F7ZSBSFHCAAJQ92ZGTY67HMN", // revolt lounge #general (temporarily hardcoded) FIXME
|
||||||
|
messageContent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setMessageContent("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hiltViewModel()) {
|
||||||
|
Column() {
|
||||||
|
Text(
|
||||||
|
text = "Home (placeholder)",
|
||||||
|
style = MaterialTheme.typography.displaySmall.copy(
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
textAlign = TextAlign.Left,
|
||||||
|
fontSize = 24.sp
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
viewModel.logout()
|
||||||
|
navController.navigate("login/greeting") {
|
||||||
|
popUpTo("chat") {
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 30.dp, top = 5.dp, start = 20.dp, end = 20.dp)
|
||||||
|
) {
|
||||||
|
Text("Logout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -116,7 +116,7 @@ fun LoginScreen(
|
||||||
)
|
)
|
||||||
viewModel.navigationComplete()
|
viewModel.navigationComplete()
|
||||||
} else if (viewModel.navigateTo == "home") {
|
} else if (viewModel.navigateTo == "home") {
|
||||||
navController.navigate("chat/home") {
|
navController.navigate("chat") {
|
||||||
popUpTo("login/greeting") { inclusive = true }
|
popUpTo("login/greeting") { inclusive = true }
|
||||||
}
|
}
|
||||||
viewModel.navigationComplete()
|
viewModel.navigationComplete()
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ fun MfaScreen(
|
||||||
val allowedAuthTypes = allowedAuthTypesCommaSep.split(",")
|
val allowedAuthTypes = allowedAuthTypesCommaSep.split(",")
|
||||||
|
|
||||||
if (viewModel.navigateToHome) {
|
if (viewModel.navigateToHome) {
|
||||||
navController.navigate("chat/home") {
|
navController.navigate("chat") {
|
||||||
popUpTo("login/greeting") { inclusive = true }
|
popUpTo("login/greeting") { inclusive = true }
|
||||||
}
|
}
|
||||||
viewModel.navigationComplete()
|
viewModel.navigationComplete()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue