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.AttributionScreen
|
||||
import chat.revolt.screens.about.PlaceholderScreen
|
||||
import chat.revolt.screens.chat.ChannelScreen
|
||||
import chat.revolt.screens.chat.HomeScreen
|
||||
import chat.revolt.screens.chat.ChatRouterScreen
|
||||
import chat.revolt.screens.login.GreeterScreen
|
||||
import chat.revolt.screens.login.LoginScreen
|
||||
import chat.revolt.screens.login.MfaScreen
|
||||
|
|
@ -92,12 +91,7 @@ fun AppEntrypoint() {
|
|||
MfaScreen(navController, allowedAuthTypes, mfaTicket)
|
||||
}
|
||||
|
||||
composable("chat/home") { HomeScreen(navController) }
|
||||
composable("chat/channel/{channelId}") { backStackEntry ->
|
||||
val channelId = backStackEntry.arguments?.getString("channelId") ?: ""
|
||||
|
||||
ChannelScreen(navController, channelId)
|
||||
}
|
||||
composable("chat") { ChatRouterScreen(navController) }
|
||||
|
||||
composable("about") { AboutScreen(navController) }
|
||||
composable("about/oss") { AttributionScreen(navController) }
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel
|
|||
}
|
||||
}
|
||||
"home" -> {
|
||||
navController.navigate("chat/home") {
|
||||
navController.navigate("chat") {
|
||||
popUpTo("splash") {
|
||||
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 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()
|
||||
} else if (viewModel.navigateTo == "home") {
|
||||
navController.navigate("chat/home") {
|
||||
navController.navigate("chat") {
|
||||
popUpTo("login/greeting") { inclusive = true }
|
||||
}
|
||||
viewModel.navigationComplete()
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ fun MfaScreen(
|
|||
val allowedAuthTypes = allowedAuthTypesCommaSep.split(",")
|
||||
|
||||
if (viewModel.navigateToHome) {
|
||||
navController.navigate("chat/home") {
|
||||
navController.navigate("chat") {
|
||||
popUpTo("login/greeting") { inclusive = true }
|
||||
}
|
||||
viewModel.navigationComplete()
|
||||
|
|
|
|||
Loading…
Reference in New Issue