feat: channel info sheet (shows description)
This commit is contained in:
parent
493a542ae9
commit
9c26a7ab93
|
|
@ -1,6 +1,7 @@
|
|||
package chat.revolt.screens.chat
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
|
|
@ -31,8 +32,13 @@ import chat.revolt.api.realtime.RealtimeSocket
|
|||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
import chat.revolt.components.screens.chat.*
|
||||
import chat.revolt.screens.chat.sheets.ChannelInfoSheet
|
||||
import chat.revolt.screens.chat.views.ChannelScreen
|
||||
import chat.revolt.screens.chat.views.HomeScreen
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
||||
import com.google.accompanist.navigation.material.bottomSheet
|
||||
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ChatRouterViewModel : ViewModel() {
|
||||
|
|
@ -65,129 +71,138 @@ class ChatRouterViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialNavigationApi::class)
|
||||
@Composable
|
||||
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = viewModel()) {
|
||||
val channelDrawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = rememberNavController()
|
||||
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
val navController = rememberNavController(bottomSheetNavigator)
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
|
||||
Column {
|
||||
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
|
||||
DisconnectedNotice(
|
||||
state = RealtimeSocket.disconnectionState,
|
||||
onReconnect = {
|
||||
RealtimeSocket.updateDisconnectionState(DisconnectionState.Reconnecting)
|
||||
scope.launch { RevoltAPI.connectWS() }
|
||||
})
|
||||
}
|
||||
DismissibleNavigationDrawer(
|
||||
drawerState = channelDrawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet(
|
||||
drawerContainerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)
|
||||
) {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Row {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||
) {
|
||||
DrawerServerlikeIcon(
|
||||
onClick = {
|
||||
viewModel.navigateToServer("home", navController)
|
||||
}
|
||||
ModalBottomSheetLayout(bottomSheetNavigator = bottomSheetNavigator) {
|
||||
Column {
|
||||
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
|
||||
DisconnectedNotice(
|
||||
state = RealtimeSocket.disconnectionState,
|
||||
onReconnect = {
|
||||
RealtimeSocket.updateDisconnectionState(DisconnectionState.Reconnecting)
|
||||
scope.launch { RevoltAPI.connectWS() }
|
||||
})
|
||||
}
|
||||
|
||||
DismissibleNavigationDrawer(
|
||||
drawerState = channelDrawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet(
|
||||
drawerContainerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)
|
||||
) {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Row {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||
2.dp
|
||||
)
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Home,
|
||||
contentDescription = stringResource(id = R.string.home),
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
DrawerServerlikeIcon(
|
||||
onClick = {
|
||||
viewModel.navigateToServer("home", navController)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Home,
|
||||
contentDescription = stringResource(id = R.string.home),
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
ServerDrawerSeparator()
|
||||
|
||||
RevoltAPI.serverCache.values
|
||||
.sortedBy { it.id }
|
||||
.forEach { server ->
|
||||
if (server.name == null) return@forEach
|
||||
|
||||
DrawerServer(
|
||||
iconId = server.icon?.id,
|
||||
serverName = server.name
|
||||
) {
|
||||
viewModel.navigateToServer(
|
||||
server.id!!,
|
||||
navController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerDrawerSeparator()
|
||||
|
||||
RevoltAPI.serverCache.values
|
||||
.sortedBy { it.id }
|
||||
.forEach { server ->
|
||||
if (server.name == null) return@forEach
|
||||
|
||||
DrawerServer(
|
||||
iconId = server.icon?.id,
|
||||
serverName = server.name
|
||||
) {
|
||||
viewModel.navigateToServer(
|
||||
server.id!!,
|
||||
navController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(targetState = viewModel.currentServer) {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
if (it == "home") {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.Group }
|
||||
.forEach { channel ->
|
||||
DrawerChannel(
|
||||
name = channel.name
|
||||
?: "GDM #${channel.id}",
|
||||
channelType = ChannelType.Group,
|
||||
selected = channel.id == (navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
) ?: false),
|
||||
onClick = {
|
||||
navController.navigate("channel/${channel.id}")
|
||||
scope.launch {
|
||||
channelDrawerState.close()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val server = RevoltAPI.serverCache[it]
|
||||
|
||||
Text(
|
||||
text = server?.name
|
||||
?: stringResource(R.string.unknown),
|
||||
fontWeight = FontWeight.Black,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
server?.channels?.forEach { channelId ->
|
||||
RevoltAPI.channelCache[channelId]?.let { ch ->
|
||||
DrawerChannel(
|
||||
name = ch.name!!,
|
||||
channelType = ch.channelType!!,
|
||||
selected = navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
) == ch.id,
|
||||
onClick = {
|
||||
scope.launch { channelDrawerState.close() }
|
||||
navController.navigate("channel/${ch.id}") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
Crossfade(targetState = viewModel.currentServer) {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
if (it == "home") {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.Group }
|
||||
.forEach { channel ->
|
||||
DrawerChannel(
|
||||
name = channel.name
|
||||
?: "GDM #${channel.id}",
|
||||
channelType = ChannelType.Group,
|
||||
selected = channel.id == (navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
) ?: false),
|
||||
onClick = {
|
||||
navController.navigate("channel/${channel.id}")
|
||||
scope.launch {
|
||||
channelDrawerState.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val server = RevoltAPI.serverCache[it]
|
||||
|
||||
Text(
|
||||
text = server?.name
|
||||
?: stringResource(R.string.unknown),
|
||||
fontWeight = FontWeight.Black,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
server?.channels?.forEach { channelId ->
|
||||
RevoltAPI.channelCache[channelId]?.let { ch ->
|
||||
DrawerChannel(
|
||||
name = ch.name!!,
|
||||
channelType = ch.channelType!!,
|
||||
selected = navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
) == ch.id,
|
||||
onClick = {
|
||||
scope.launch { channelDrawerState.close() }
|
||||
navController.navigate("channel/${ch.id}") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -196,28 +211,40 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
composable("home") {
|
||||
HomeScreen(navController = topNav)
|
||||
}
|
||||
composable("channel/{channelId}") { backStackEntry ->
|
||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||
if (channelId != null) {
|
||||
ChannelScreen(navController, channelId = channelId)
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
composable("home") {
|
||||
HomeScreen(navController = topNav)
|
||||
}
|
||||
composable("channel/{channelId}") { backStackEntry ->
|
||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||
if (channelId != null) {
|
||||
ChannelScreen(
|
||||
navController = navController,
|
||||
channelId = channelId
|
||||
)
|
||||
}
|
||||
}
|
||||
bottomSheet("channel/{channelId}/info") { backStackEntry ->
|
||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||
if (channelId != null) {
|
||||
ChannelInfoSheet(
|
||||
navController = navController,
|
||||
channelId = channelId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BottomNavigation(
|
||||
navController = topNav,
|
||||
show = channelDrawerState.currentValue == DrawerValue.Open,
|
||||
)
|
||||
BottomNavigation(
|
||||
navController = topNav,
|
||||
show = channelDrawerState.currentValue == DrawerValue.Open,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package chat.revolt.screens.chat.sheets
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
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.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.chat.ChannelIcon
|
||||
|
||||
@Composable
|
||||
fun ChannelInfoSheet(
|
||||
navController: NavController,
|
||||
channelId: String,
|
||||
) {
|
||||
val channel = RevoltAPI.channelCache[channelId]
|
||||
if (channel == null) {
|
||||
navController.popBackStack()
|
||||
return
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ChannelIcon(
|
||||
channelType = channel.channelType ?: ChannelType.TextChannel,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
PageHeader(
|
||||
text = channel.name ?: channel.id ?: "",
|
||||
modifier = Modifier.offset((-4).dp, 0.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.channel_info_sheet_description),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
Text(
|
||||
text = channel.description
|
||||
?: stringResource(id = R.string.channel_info_sheet_description_empty),
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,14 +61,14 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
get() = _channel
|
||||
|
||||
private var _callback = mutableStateOf<RealtimeSocket.ChannelCallback?>(null)
|
||||
val callback: RealtimeSocket.ChannelCallback?
|
||||
private val callback: RealtimeSocket.ChannelCallback?
|
||||
get() = _callback.value
|
||||
|
||||
private var _renderableMessages = mutableStateListOf<MessageSchema>()
|
||||
val renderableMessages: List<MessageSchema>
|
||||
get() = _renderableMessages
|
||||
|
||||
fun setRenderableMessages(messages: List<MessageSchema>) {
|
||||
private fun setRenderableMessages(messages: List<MessageSchema>) {
|
||||
_renderableMessages.clear()
|
||||
_renderableMessages.addAll(messages)
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
val attachments: List<FileArgs>
|
||||
get() = _attachments
|
||||
|
||||
fun setAttachments(attachments: List<FileArgs>) {
|
||||
private fun setAttachments(attachments: List<FileArgs>) {
|
||||
_attachments.clear()
|
||||
_attachments.addAll(attachments)
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
viewModelScope.launch {
|
||||
val messages = arrayListOf<MessageSchema>()
|
||||
|
||||
if (!renderableMessages.isEmpty()) {
|
||||
if (renderableMessages.isNotEmpty()) {
|
||||
fetchMessagesFromChannel(
|
||||
channel!!.id!!,
|
||||
limit = 50,
|
||||
|
|
@ -383,7 +383,7 @@ fun ChannelScreen(
|
|||
val totalItemsNumber = layoutInfo.totalItemsCount
|
||||
val lastVisibleItemIndex =
|
||||
(layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
|
||||
val buffer = if (totalItemsNumber > 6) 6 else 0
|
||||
val buffer = 6
|
||||
|
||||
lastVisibleItemIndex > (totalItemsNumber - buffer)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ val RevoltTypography = Typography(
|
|||
),
|
||||
bodySmall = TextStyle(
|
||||
fontFamily = Inter,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
)
|
||||
|
|
@ -113,6 +113,9 @@
|
|||
<string name="copy">Copy</string>
|
||||
<string name="copied">Copied to clipboard</string>
|
||||
|
||||
<string name="channel_info_sheet_description">Channel description</string>
|
||||
<string name="channel_info_sheet_description_empty">There hasn\'t been a description set for this channel yet.</string>
|
||||
|
||||
<string name="settings_appearance">Appearance</string>
|
||||
<string name="settings_appearance_theme">Theme</string>
|
||||
<string name="settings_appearance_theme_none">System</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue