feat: bottom navigation when drawer is open, placeholder settings screen
This commit is contained in:
parent
89d372d65c
commit
2e6e20939f
|
|
@ -12,6 +12,7 @@ import androidx.compose.material3.*
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import chat.revolt.screens.SplashScreen
|
||||
import chat.revolt.screens.about.AboutScreen
|
||||
import chat.revolt.screens.about.AttributionScreen
|
||||
|
|
@ -20,6 +21,7 @@ import chat.revolt.screens.chat.ChatRouterScreen
|
|||
import chat.revolt.screens.login.GreeterScreen
|
||||
import chat.revolt.screens.login.LoginScreen
|
||||
import chat.revolt.screens.login.MfaScreen
|
||||
import chat.revolt.screens.settings.SettingsScreen
|
||||
import chat.revolt.ui.theme.RevoltTheme
|
||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
|
|
@ -44,6 +46,7 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
val RevoltTweenInt: FiniteAnimationSpec<IntOffset> = tween(400, easing = EaseInOutExpo)
|
||||
val RevoltTweenIntSize: FiniteAnimationSpec<IntSize> = tween(400, easing = EaseInOutExpo)
|
||||
val RevoltTweenFloat: FiniteAnimationSpec<Float> = tween(400, easing = EaseInOutExpo)
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
|
|
@ -93,6 +96,8 @@ fun AppEntrypoint() {
|
|||
|
||||
composable("chat") { ChatRouterScreen(navController) }
|
||||
|
||||
composable("settings") { SettingsScreen(navController) }
|
||||
|
||||
composable("about") { AboutScreen(navController) }
|
||||
composable("about/oss") { AttributionScreen(navController) }
|
||||
composable("about/placeholder") { PlaceholderScreen(navController) }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package chat.revolt.api.realtime
|
|||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import chat.revolt.api.REVOLT_WEBSOCKET
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.RevoltHttp
|
||||
|
|
@ -118,9 +119,7 @@ object RealtimeSocket {
|
|||
|
||||
RevoltAPI.messageCache[messageFrame.id!!] = messageFrame
|
||||
|
||||
channelCallbacks[messageFrame.channel]?.forEach { callback ->
|
||||
callback.onMessage(messageFrame)
|
||||
}
|
||||
channelCallbacks[messageFrame.channel]?.onMessage(messageFrame)
|
||||
}
|
||||
"ChannelStartTyping" -> {
|
||||
val typingFrame =
|
||||
|
|
@ -130,9 +129,7 @@ object RealtimeSocket {
|
|||
"Received channel start typing frame for ${typingFrame.id} from ${typingFrame.user}."
|
||||
)
|
||||
|
||||
channelCallbacks[typingFrame.id]?.forEach { callback ->
|
||||
callback.onStartTyping(typingFrame)
|
||||
}
|
||||
channelCallbacks[typingFrame.id]?.onStartTyping(typingFrame)
|
||||
}
|
||||
"ChannelStopTyping" -> {
|
||||
val typingFrame =
|
||||
|
|
@ -142,9 +139,7 @@ object RealtimeSocket {
|
|||
"Received channel stop typing frame for ${typingFrame.id} from ${typingFrame.user}."
|
||||
)
|
||||
|
||||
channelCallbacks[typingFrame.id]?.forEach { callback ->
|
||||
callback.onStopTyping(typingFrame)
|
||||
}
|
||||
channelCallbacks[typingFrame.id]?.onStopTyping(typingFrame)
|
||||
}
|
||||
"UserUpdate" -> {
|
||||
val userUpdateFrame =
|
||||
|
|
@ -178,18 +173,16 @@ object RealtimeSocket {
|
|||
fun onMessage(message: MessageFrame)
|
||||
}
|
||||
|
||||
private val channelCallbacks: MutableMap<String, List<ChannelCallback>> = mutableStateMapOf()
|
||||
private val channelCallbacks: SnapshotStateMap<String, ChannelCallback> = mutableStateMapOf()
|
||||
|
||||
fun registerChannelCallback(channelId: String, callback: ChannelCallback) {
|
||||
val callbacks = channelCallbacks[channelId] ?: emptyList()
|
||||
channelCallbacks[channelId] = callbacks + callback
|
||||
channelCallbacks[channelId] = callback
|
||||
|
||||
Log.d("RealtimeSocket", "Registered channel callback for $channelId.")
|
||||
}
|
||||
|
||||
fun unregisterChannelCallback(channelId: String, callback: ChannelCallback) {
|
||||
val callbacks = channelCallbacks[channelId] ?: emptyList()
|
||||
channelCallbacks[channelId] = callbacks - callback
|
||||
channelCallbacks.remove(channelId, callback)
|
||||
|
||||
Log.d("RealtimeSocket", "Unregistered channel callback for $channelId")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
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.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 {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package chat.revolt.screens.chat
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -37,6 +36,7 @@ import chat.revolt.api.realtime.RealtimeSocket
|
|||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
import chat.revolt.components.generic.RemoteImage
|
||||
import chat.revolt.components.screens.chat.BottomNavigation
|
||||
import chat.revolt.components.screens.chat.DrawerChannel
|
||||
import chat.revolt.screens.chat.views.ChannelScreen
|
||||
import chat.revolt.screens.chat.views.HomeScreen
|
||||
|
|
@ -80,7 +80,7 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
val navController = rememberNavController()
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
|
||||
Column() {
|
||||
Column {
|
||||
AnimatedVisibility(visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected) {
|
||||
DisconnectedNotice(
|
||||
state = RealtimeSocket.disconnectionState,
|
||||
|
|
@ -174,7 +174,8 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.Group }
|
||||
.forEach { channel ->
|
||||
DrawerChannel(
|
||||
name = channel.name ?: "GDM #${channel.id}",
|
||||
name = channel.name
|
||||
?: "GDM #${channel.id}",
|
||||
channelType = ChannelType.Group,
|
||||
selected = channel.id == (navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
|
|
@ -192,7 +193,8 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
val server = RevoltAPI.serverCache[it]
|
||||
|
||||
Text(
|
||||
text = server?.name ?: stringResource(R.string.unknown),
|
||||
text = server?.name
|
||||
?: stringResource(R.string.unknown),
|
||||
fontWeight = FontWeight.Black,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
|
|
@ -245,5 +247,10 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
BottomNavigation(
|
||||
navController = topNav,
|
||||
show = channelDrawerState.currentValue == DrawerValue.Open,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -78,10 +78,10 @@ fun GreeterScreen(navController: NavController) {
|
|||
.padding(horizontal = 20.dp, vertical = 30.dp)
|
||||
) {
|
||||
ElevatedButton(
|
||||
onClick = { navController.navigate("about") },
|
||||
onClick = { navController.navigate("about/placeholder") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.about))
|
||||
Text(text = stringResource(R.string.signup))
|
||||
}
|
||||
|
||||
Button(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package chat.revolt.screens.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
navController: NavController
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
PageHeader(stringResource(id = R.string.settings))
|
||||
ElevatedButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.back))
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
navController.navigate("about")
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.about))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,4 +106,5 @@
|
|||
<string name="tap_to_retry">Tap to retry</string>
|
||||
|
||||
<string name="scroll_to_bottom">Scroll to bottom</string>
|
||||
<string name="settings">Settings</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue