From 2e6e20939fe2b82e15b4032370f2bcbbb4477d43 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 11 Jan 2023 01:24:44 +0100 Subject: [PATCH] feat: bottom navigation when drawer is open, placeholder settings screen --- app/src/main/java/chat/revolt/MainActivity.kt | 5 ++ .../revolt/api/realtime/RealtimeSocket.kt | 21 ++---- .../screens/chat/BottomNavigation.kt | 68 +++++++++++++++++++ .../revolt/screens/chat/ChatRouterScreen.kt | 17 +++-- .../revolt/screens/login/GreeterScreen.kt | 4 +- .../revolt/screens/settings/SettingsScreen.kt | 40 +++++++++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/chat/revolt/components/screens/chat/BottomNavigation.kt create mode 100644 app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt diff --git a/app/src/main/java/chat/revolt/MainActivity.kt b/app/src/main/java/chat/revolt/MainActivity.kt index af3bb9b4..3d969cc1 100644 --- a/app/src/main/java/chat/revolt/MainActivity.kt +++ b/app/src/main/java/chat/revolt/MainActivity.kt @@ -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 = tween(400, easing = EaseInOutExpo) +val RevoltTweenIntSize: FiniteAnimationSpec = tween(400, easing = EaseInOutExpo) val RevoltTweenFloat: FiniteAnimationSpec = 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) } diff --git a/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt b/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt index 5812382b..e12d65a7 100644 --- a/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt +++ b/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt @@ -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> = mutableStateMapOf() + private val channelCallbacks: SnapshotStateMap = 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") } diff --git a/app/src/main/java/chat/revolt/components/screens/chat/BottomNavigation.kt b/app/src/main/java/chat/revolt/components/screens/chat/BottomNavigation.kt new file mode 100644 index 00000000..afa9efa7 --- /dev/null +++ b/app/src/main/java/chat/revolt/components/screens/chat/BottomNavigation.kt @@ -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), + ) + } + } + } +} diff --git a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt index 8bbfdae5..5b80be24 100644 --- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt @@ -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, + ) } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt index 7de09241..95595f48 100644 --- a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt @@ -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( diff --git a/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt new file mode 100644 index 00000000..4455c08b --- /dev/null +++ b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt @@ -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)) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65b1290a..fb98b6cf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,4 +106,5 @@ Tap to retry Scroll to bottom + Settings \ No newline at end of file