feat: revamp chat UI to be closer to web, add double drawer
This commit is contained in:
parent
738a2a832f
commit
e5ab9b3228
|
|
@ -70,6 +70,7 @@ dependencies {
|
|||
// Jetpack Compose
|
||||
implementation "androidx.compose.ui:ui"
|
||||
implementation "androidx.compose.ui:ui-util"
|
||||
implementation 'androidx.compose.material:material'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation "androidx.compose.ui:ui-tooling-preview"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package chat.revolt.components.generic
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
|
|
@ -26,7 +28,17 @@ enum class Presence {
|
|||
Idle,
|
||||
Dnd,
|
||||
Focus,
|
||||
Offline,
|
||||
Offline
|
||||
}
|
||||
|
||||
fun presenceFromStatus(status: String): Presence {
|
||||
return when (status) {
|
||||
"online" -> Presence.Online
|
||||
"idle" -> Presence.Idle
|
||||
"dnd" -> Presence.Dnd
|
||||
"focus" -> Presence.Focus
|
||||
else -> Presence.Offline
|
||||
}
|
||||
}
|
||||
|
||||
fun presenceColour(presence: Presence): Color {
|
||||
|
|
@ -50,6 +62,7 @@ fun PresenceBadge(presence: Presence, size: Dp = 16.dp) {
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun UserAvatar(
|
||||
username: String,
|
||||
|
|
@ -60,6 +73,8 @@ fun UserAvatar(
|
|||
rawUrl: String? = null,
|
||||
size: Dp = 40.dp,
|
||||
presenceSize: Dp = 16.dp,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
|
|
@ -69,19 +84,35 @@ fun UserAvatar(
|
|||
if (avatar != null) {
|
||||
RemoteImage(
|
||||
url = rawUrl ?: "$REVOLT_FILES/avatars/${avatar.id!!}/user.png",
|
||||
description = stringResource(id = R.string.avatar_alt, username),
|
||||
contentScale = ContentScale.Crop,
|
||||
description = stringResource(id = R.string.avatar_alt, username),
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(size)
|
||||
.then(
|
||||
if (onLongClick != null || onClick != null) Modifier
|
||||
.combinedClickable(
|
||||
onClick = { onClick?.invoke() },
|
||||
onLongClick = { onLongClick?.invoke() }
|
||||
)
|
||||
else Modifier
|
||||
)
|
||||
)
|
||||
} else {
|
||||
RemoteImage(
|
||||
url = "$REVOLT_BASE/users/${userId}/default_avatar",
|
||||
description = stringResource(id = R.string.avatar_alt, username),
|
||||
modifier = Modifier
|
||||
.size(size)
|
||||
.then(
|
||||
if (onLongClick != null || onClick != null) Modifier
|
||||
.combinedClickable(
|
||||
onClick = { onClick?.invoke() },
|
||||
onLongClick = { onLongClick?.invoke() }
|
||||
)
|
||||
else Modifier
|
||||
)
|
||||
.clip(CircleShape),
|
||||
description = stringResource(id = R.string.avatar_alt, username),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.RevoltTweenIntSize
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun BottomNavigation(
|
||||
navController: NavController,
|
||||
show: Boolean,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = show,
|
||||
enter = expandVertically(
|
||||
animationSpec = RevoltTweenIntSize
|
||||
),
|
||||
exit = shrinkVertically(
|
||||
animationSpec = RevoltTweenIntSize
|
||||
),
|
||||
) {
|
||||
BottomAppBar(
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
if (navController.currentDestination?.route != "chat") {
|
||||
navController.navigate("chat")
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Home,
|
||||
contentDescription = stringResource(id = R.string.home),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
if (navController.currentDestination?.route != "settings") {
|
||||
navController.navigate("settings")
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
enum class DoubleDrawerOpenState {
|
||||
Start,
|
||||
Center,
|
||||
End
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
class DoubleDrawerState(
|
||||
var initialValue: DoubleDrawerOpenState = DoubleDrawerOpenState.Center,
|
||||
val confirmStateChange: (DoubleDrawerOpenState) -> Boolean = { true }
|
||||
) {
|
||||
val swipeableState = SwipeableState<DoubleDrawerOpenState>(
|
||||
initialValue = initialValue,
|
||||
animationSpec = spring(),
|
||||
confirmStateChange = confirmStateChange
|
||||
)
|
||||
|
||||
suspend fun focusStart() {
|
||||
swipeableState.animateTo(DoubleDrawerOpenState.Start)
|
||||
}
|
||||
|
||||
suspend fun focusCenter() {
|
||||
swipeableState.animateTo(DoubleDrawerOpenState.Center)
|
||||
}
|
||||
|
||||
suspend fun focusEnd() {
|
||||
swipeableState.animateTo(DoubleDrawerOpenState.End)
|
||||
}
|
||||
|
||||
val isStart: Boolean
|
||||
get() = swipeableState.currentValue == DoubleDrawerOpenState.Start
|
||||
val isCenter: Boolean
|
||||
get() = swipeableState.currentValue == DoubleDrawerOpenState.Center
|
||||
val isEnd: Boolean
|
||||
get() = swipeableState.currentValue == DoubleDrawerOpenState.End
|
||||
|
||||
val currentValue: DoubleDrawerOpenState
|
||||
get() = swipeableState.currentValue
|
||||
|
||||
companion object {
|
||||
fun Saver(
|
||||
confirmStateChange: (DoubleDrawerOpenState) -> Boolean
|
||||
): Saver<DoubleDrawerState, DoubleDrawerOpenState> = Saver(
|
||||
save = { it.currentValue },
|
||||
restore = { DoubleDrawerState(it, confirmStateChange) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberDoubleDrawerState(
|
||||
initialValue: DoubleDrawerOpenState = DoubleDrawerOpenState.Center,
|
||||
confirmStateChange: (DoubleDrawerOpenState) -> Boolean = { true }
|
||||
): DoubleDrawerState = rememberSaveable(
|
||||
saver = DoubleDrawerState.Saver(confirmStateChange)
|
||||
) {
|
||||
DoubleDrawerState(initialValue, confirmStateChange)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun DoubleDrawer(
|
||||
state: DoubleDrawerState = rememberDoubleDrawerState(),
|
||||
startPanel: @Composable () -> Unit,
|
||||
endPanel: @Composable () -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||
val isPortrait =
|
||||
LocalContext.current.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
val drawerWeight =
|
||||
if (isPortrait) 0.9f else 0.8f
|
||||
|
||||
val offsetValue =
|
||||
(constraints.maxWidth * drawerWeight) + (LocalDensity.current.run { 16.dp.toPx() })
|
||||
val isAtOffset = abs(state.swipeableState.offset.value) == abs(offsetValue)
|
||||
|
||||
val contentCornerRadius by animateDpAsState(
|
||||
targetValue = if (isAtOffset) 16.dp else 0.dp,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.swipeable(
|
||||
state = state.swipeableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
velocityThreshold = 500.dp,
|
||||
anchors = mapOf(
|
||||
offsetValue to DoubleDrawerOpenState.Start,
|
||||
0f to DoubleDrawerOpenState.Center,
|
||||
-offsetValue to DoubleDrawerOpenState.End
|
||||
),
|
||||
reverseDirection = layoutDirection == LayoutDirection.Rtl,
|
||||
resistance = ResistanceConfig(0.5f, 0.5f)
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(drawerWeight)
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topEnd = 16.dp,
|
||||
bottomEnd = 16.dp
|
||||
)
|
||||
)
|
||||
.align(Alignment.CenterStart)
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = state.swipeableState.offset.value.roundToInt() - offsetValue.roundToInt(),
|
||||
y = 0
|
||||
)
|
||||
},
|
||||
) {
|
||||
startPanel()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(
|
||||
RoundedCornerShape(contentCornerRadius)
|
||||
)
|
||||
.align(Alignment.Center)
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = state.swipeableState.offset.value.roundToInt(),
|
||||
y = 0
|
||||
)
|
||||
},
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(drawerWeight)
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = 16.dp,
|
||||
bottomStart = 16.dp
|
||||
)
|
||||
)
|
||||
.align(Alignment.CenterEnd)
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = state.swipeableState.offset.value.roundToInt() + offsetValue.roundToInt(),
|
||||
y = 0
|
||||
)
|
||||
},
|
||||
) {
|
||||
endPanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
package chat.revolt.components.screens.chat.drawer.server
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -14,6 +14,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.components.screens.chat.ChannelIcon
|
||||
|
||||
@Composable
|
||||
fun DrawerChannel(
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
package chat.revolt.components.screens.chat.drawer.server
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
package chat.revolt.components.screens.chat.drawer.server
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
package chat.revolt.components.screens.chat.drawer.server
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -1,19 +1,24 @@
|
|||
package chat.revolt.screens.chat
|
||||
|
||||
import android.widget.Toast
|
||||
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
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -31,9 +36,17 @@ import chat.revolt.api.realtime.DisconnectionState
|
|||
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.components.generic.UserAvatar
|
||||
import chat.revolt.components.generic.presenceFromStatus
|
||||
import chat.revolt.components.screens.chat.DoubleDrawer
|
||||
import chat.revolt.components.screens.chat.drawer.server.DrawerChannel
|
||||
import chat.revolt.components.screens.chat.drawer.server.DrawerServer
|
||||
import chat.revolt.components.screens.chat.drawer.server.DrawerServerlikeIcon
|
||||
import chat.revolt.components.screens.chat.drawer.server.ServerDrawerSeparator
|
||||
import chat.revolt.components.screens.chat.rememberDoubleDrawerState
|
||||
import chat.revolt.screens.chat.sheets.ChannelInfoSheet
|
||||
import chat.revolt.screens.chat.sheets.MessageContextSheet
|
||||
import chat.revolt.screens.chat.sheets.StatusSheet
|
||||
import chat.revolt.screens.chat.views.ChannelScreen
|
||||
import chat.revolt.screens.chat.views.HomeScreen
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
|
|
@ -72,11 +85,12 @@ class ChatRouterViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialNavigationApi::class)
|
||||
@OptIn(ExperimentalMaterialNavigationApi::class)
|
||||
@Composable
|
||||
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = viewModel()) {
|
||||
val channelDrawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val drawerState = rememberDoubleDrawerState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
val navController = rememberNavController(bottomSheetNavigator)
|
||||
|
|
@ -93,56 +107,80 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
})
|
||||
}
|
||||
|
||||
DismissibleNavigationDrawer(
|
||||
drawerState = channelDrawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet(
|
||||
drawerContainerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)
|
||||
) {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Row {
|
||||
Column(
|
||||
DoubleDrawer(
|
||||
state = drawerState,
|
||||
startPanel = {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Row {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
UserAvatar(
|
||||
username = RevoltAPI.userCache[RevoltAPI.selfId]?.username
|
||||
?: "",
|
||||
presence = presenceFromStatus(
|
||||
RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence
|
||||
?: ""
|
||||
),
|
||||
userId = RevoltAPI.selfId ?: "",
|
||||
avatar = RevoltAPI.userCache[RevoltAPI.selfId]?.avatar,
|
||||
size = 48.dp,
|
||||
presenceSize = 16.dp,
|
||||
onClick = {
|
||||
viewModel.navigateToServer("home", navController)
|
||||
},
|
||||
onLongClick = {
|
||||
navController.navigate("status")
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||
2.dp
|
||||
.padding(8.dp)
|
||||
.size(48.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
|
||||
)
|
||||
)
|
||||
) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
DrawerServerlikeIcon(
|
||||
onClick = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = stringResource(id = R.string.server_plus_alt),
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(targetState = viewModel.currentServer) {
|
||||
Crossfade(targetState = viewModel.currentServer) {
|
||||
Surface(
|
||||
tonalElevation = 1.dp,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
|
|
@ -165,7 +203,7 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
onClick = {
|
||||
navController.navigate("channel/${channel.id}")
|
||||
scope.launch {
|
||||
channelDrawerState.close()
|
||||
drawerState.focusCenter()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -196,13 +234,14 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
"channelId"
|
||||
) == ch.id,
|
||||
onClick = {
|
||||
scope.launch { channelDrawerState.close() }
|
||||
scope.launch { drawerState.focusCenter() }
|
||||
navController.navigate("channel/${ch.id}") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +252,16 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
endPanel = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = "👋", fontSize = 64.sp)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
|
|
@ -248,14 +296,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
)
|
||||
}
|
||||
}
|
||||
bottomSheet("status") {
|
||||
StatusSheet(navController = navController, topNav = topNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BottomNavigation(
|
||||
navController = topNav,
|
||||
show = channelDrawerState.currentValue == DrawerValue.Open,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package chat.revolt.screens.chat.sheets
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.components.generic.SheetClickable
|
||||
|
||||
@Composable
|
||||
fun StatusSheet(
|
||||
navController: NavController,
|
||||
topNav: NavController,
|
||||
) {
|
||||
if (RevoltAPI.selfId == null || RevoltAPI.userCache[RevoltAPI.selfId] == null) {
|
||||
navController.popBackStack()
|
||||
return
|
||||
}
|
||||
|
||||
val selfUser = RevoltAPI.userCache[RevoltAPI.selfId]!!
|
||||
|
||||
Surface {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Text(text = "Logged in as @${selfUser.username} (${selfUser.id})")
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings),
|
||||
style = style
|
||||
)
|
||||
}
|
||||
) {
|
||||
topNav.navigate("settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +86,8 @@
|
|||
<string name="home">Home</string>
|
||||
<string name="logout">Log out</string>
|
||||
|
||||
<string name="server_plus_alt">Add server</string>
|
||||
|
||||
<string name="avatar_alt">%1$s\'s avatar</string>
|
||||
|
||||
<string name="channel_dm">Direct Message</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue