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:
Infi 2022-12-27 04:58:52 +01:00
parent dc3e71083b
commit 2a14f5fdc9
8 changed files with 221 additions and 133 deletions

View File

@ -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) }

View File

@ -80,7 +80,7 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel
}
}
"home" -> {
navController.navigate("chat/home") {
navController.navigate("chat") {
popUpTo("splash") {
inclusive = true
}

View File

@ -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)
}
}
}
}
}
}

View File

@ -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")
}
}
}
}

View File

@ -1,4 +1,4 @@
package chat.revolt.screens.chat
package chat.revolt.screens.chat.views
import android.util.Log
import androidx.compose.animation.AnimatedVisibility

View File

@ -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")
}
}
}

View File

@ -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()

View File

@ -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()