diff --git a/app/src/main/java/chat/revolt/api/RevoltAPI.kt b/app/src/main/java/chat/revolt/api/RevoltAPI.kt index c3937bd5..3fd34cb3 100644 --- a/app/src/main/java/chat/revolt/api/RevoltAPI.kt +++ b/app/src/main/java/chat/revolt/api/RevoltAPI.kt @@ -53,18 +53,20 @@ import kotlinx.serialization.json.Json import java.net.SocketException import chat.revolt.api.schemas.Channel as ChannelSchema +private const val USE_ALPHA_API = false + val REVOLT_BASE = - if (BuildConfig.USE_ALPHA_API) "https://alpha.revolt.chat/api" else "https://api.revolt.chat/0.8" + if (USE_ALPHA_API) "https://alpha.revolt.chat/api" else "https://api.revolt.chat/0.8" const val REVOLT_SUPPORT = "https://support.revolt.chat" const val REVOLT_MARKETING = "https://revolt.chat" val REVOLT_FILES = - if (BuildConfig.USE_ALPHA_API) "https://alpha.revolt.chat/autumn" else "https://cdn.revoltusercontent.com" + if (USE_ALPHA_API) "https://alpha.revolt.chat/autumn" else "https://cdn.revoltusercontent.com" val REVOLT_JANUARY = - if (BuildConfig.USE_ALPHA_API) "https://alpha.revolt.chat/january" else "https://jan.revolt.chat" + if (USE_ALPHA_API) "https://alpha.revolt.chat/january" else "https://jan.revolt.chat" const val REVOLT_APP = "https://app.revolt.chat" const val REVOLT_INVITES = "https://rvlt.gg" val REVOLT_WEBSOCKET = - if (BuildConfig.USE_ALPHA_API) "wss://alpha.revolt.chat/ws" else "wss://ws.revolt.chat" + if (USE_ALPHA_API) "wss://alpha.revolt.chat/ws" else "wss://ws.revolt.chat" const val REVOLT_KJBOOK = "https://revoltchat.github.io/android" fun String.api(): String { @@ -72,7 +74,9 @@ fun String.api(): String { } fun buildUserAgent(accessMethod: String = "Ktor"): String { - return "$accessMethod RevoltAndroid/${BuildConfig.VERSION_NAME} ${BuildConfig.APPLICATION_ID} (Android ${android.os.Build.VERSION.SDK_INT}; ${android.os.Build.MANUFACTURER} ${android.os.Build.DEVICE}; (Kotlin ${KotlinVersion.CURRENT})" + return "$accessMethod RevoltAndroid/${BuildConfig.VERSION_NAME} " + + "${BuildConfig.APPLICATION_ID} Android/${android.os.Build.VERSION.SDK_INT} " + + "(${android.os.Build.MANUFACTURER} ${android.os.Build.DEVICE}) Kotlin/${KotlinVersion.CURRENT}" } private const val BACKEND_IS_STABLE = false diff --git a/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt b/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt index 93aa3541..a6d7dbdc 100644 --- a/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt +++ b/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt @@ -44,7 +44,7 @@ data class ReadyFrame( val servers: List, val channels: List, val emojis: List, - @SerialName("voice_states") val voiceStates: List, + @SerialName("voice_states") val voiceStates: List = listOf(), ) typealias MessageFrame = Message diff --git a/app/src/main/java/chat/revolt/composables/screens/chat/drawer/ChannelSideDrawer.kt b/app/src/main/java/chat/revolt/composables/screens/chat/drawer/ChannelSideDrawer.kt index 75442216..4df73658 100644 --- a/app/src/main/java/chat/revolt/composables/screens/chat/drawer/ChannelSideDrawer.kt +++ b/app/src/main/java/chat/revolt/composables/screens/chat/drawer/ChannelSideDrawer.kt @@ -1,7 +1,9 @@ package chat.revolt.composables.screens.chat.drawer +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.VisibilityThreshold import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring @@ -28,6 +30,7 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState @@ -71,6 +74,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController @@ -96,6 +100,7 @@ import chat.revolt.composables.generic.UserAvatar import chat.revolt.composables.generic.presenceFromStatus import chat.revolt.composables.screens.chat.ChannelIcon import chat.revolt.screens.chat.ChatRouterDestination +import chat.revolt.screens.chat.LocalIsConnected import chat.revolt.sheets.ChannelContextSheet import kotlinx.coroutines.launch @@ -154,6 +159,17 @@ fun ChannelSideDrawer( ), label = "Server banner height" ) + val serverInfoOffset by animateDpAsState( + if (LocalIsConnected.current) + WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + else + 0.dp, + animationSpec = spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = Dp.VisibilityThreshold + ) + ) + // - Take the list of servers and filter them by the ones that are in the ordering. // - Sort the servers that are in the ordering using the ordering. // - Add the servers that aren't in the ordering to the end of the list. @@ -202,30 +218,40 @@ fun ChannelSideDrawer( ) ) { stickyHeader(key = "self") { - UserAvatar( - username = RevoltAPI.userCache[RevoltAPI.selfId]?.let { - User.resolveDefaultName( - it + Column(Modifier.background(MaterialTheme.colorScheme.background)) { + AnimatedVisibility(LocalIsConnected.current) { + Spacer( + Modifier + .height( + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() + ) ) } - ?: "", - presence = presenceFromStatus( - RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence, - RevoltAPI.userCache[RevoltAPI.selfId]?.online ?: false - ), - userId = RevoltAPI.selfId ?: "", - avatar = RevoltAPI.userCache[RevoltAPI.selfId]?.avatar, - size = 48.dp, - presenceSize = 16.dp, - onClick = { - onDestinationChanged(ChatRouterDestination.defaultForDMList) - }, - onLongClick = onLongPressAvatar, - modifier = Modifier - .background(MaterialTheme.colorScheme.background) - .padding(8.dp) - .size(48.dp) - ) + UserAvatar( + username = RevoltAPI.userCache[RevoltAPI.selfId]?.let { + User.resolveDefaultName( + it + ) + } + ?: "", + presence = presenceFromStatus( + RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence, + RevoltAPI.userCache[RevoltAPI.selfId]?.online ?: false + ), + userId = RevoltAPI.selfId ?: "", + avatar = RevoltAPI.userCache[RevoltAPI.selfId]?.avatar, + size = 48.dp, + presenceSize = 16.dp, + onClick = { + onDestinationChanged(ChatRouterDestination.defaultForDMList) + }, + onLongClick = onLongPressAvatar, + modifier = Modifier + .padding(8.dp) + .size(48.dp) + ) + } } items( @@ -314,12 +340,12 @@ fun ChannelSideDrawer( ) val leftIndicatorColour = animateColorAsState( targetValue = - if (serverInList.id == currentServer) - MaterialTheme.colorScheme.primary - else if (serverHasUnread) - MaterialTheme.colorScheme.onSurfaceVariant - else - Color.Transparent, + if (serverInList.id == currentServer) + MaterialTheme.colorScheme.primary + else if (serverHasUnread) + MaterialTheme.colorScheme.onSurfaceVariant + else + Color.Transparent, animationSpec = spring( dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow @@ -433,27 +459,30 @@ fun ChannelSideDrawer( } Column( Modifier - .clip( - MaterialTheme.shapes.extraLarge.copy( - bottomEnd = CornerSize(0) - ) - ) .background(MaterialTheme.colorScheme.surfaceContainer) .weight(1f) .fillMaxHeight() ) { - Box(Modifier.height(serverBannerHeight)) { + Box( + Modifier + .clip( + MaterialTheme.shapes.medium.copy( + topStart = CornerSize(0.dp), + topEnd = CornerSize(0.dp) + ) + ) + .height( + serverBannerHeight + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() + ) + //.offset(y = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) + ) { if (server?.banner != null) { RemoteImage( url = "$REVOLT_FILES/banners/${server.banner.id}", description = null, contentScale = ContentScale.Crop, modifier = Modifier - .clip( - MaterialTheme.shapes.medium.copy( - topStart = CornerSize(0), topEnd = CornerSize(0) - ) - ) .fillMaxSize() ) @@ -465,7 +494,7 @@ fun ChannelSideDrawer( drawRect( Brush.linearGradient( listOf( - surfaceContainer.copy(alpha = 0.8f), + Color.Black.copy(alpha = 0.6f), Color.Transparent ), Offset.Zero, @@ -477,63 +506,72 @@ fun ChannelSideDrawer( } Row( - Modifier.padding(16.dp), + Modifier + .padding(16.dp) + .offset(y = serverInfoOffset), verticalAlignment = Alignment.CenterVertically ) { - Row( - Modifier.weight(1f), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) + CompositionLocalProvider( + LocalContentColor provides + if (server?.banner != null) Color.White + else LocalContentColor.current ) { - if (server?.flags has ServerFlags.Official) { - Icon( - painter = painterResource( - id = R.drawable.ic_revolt_decagram_24dp - ), - contentDescription = stringResource( - R.string.server_flag_official - ), - tint = LocalContentColor.current, - modifier = Modifier - .size(24.dp) - ) - } - if (server?.flags has ServerFlags.Verified) { - Icon( - painter = painterResource( - id = R.drawable.ic_check_decagram_24dp - ), - contentDescription = stringResource( - R.string.server_flag_verified - ), - tint = LocalContentColor.current, - modifier = Modifier - .size(24.dp) + Row( + Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + if (server?.flags has ServerFlags.Official) { + Icon( + painter = painterResource( + id = R.drawable.ic_revolt_decagram_24dp + ), + contentDescription = stringResource( + R.string.server_flag_official + ), + tint = LocalContentColor.current, + modifier = Modifier + .size(24.dp) + ) + } + if (server?.flags has ServerFlags.Verified) { + Icon( + painter = painterResource( + id = R.drawable.ic_check_decagram_24dp + ), + contentDescription = stringResource( + R.string.server_flag_verified + ), + tint = LocalContentColor.current, + modifier = Modifier + .size(24.dp) + ) + } + + Text( + text = when (currentServer) { + null -> stringResource(R.string.direct_messages) + else -> server?.name ?: stringResource(R.string.unknown) + }, + style = MaterialTheme.typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } - Text( - text = when (currentServer) { - null -> stringResource(R.string.direct_messages) - else -> server?.name ?: stringResource(R.string.unknown) - }, - style = MaterialTheme.typography.titleMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - - if (currentServer != null) { - IconButton(onClick = { - server?.id?.let { srvId -> onShowServerContextSheet(srvId) } - }) { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource(R.string.menu) - ) + if (currentServer != null) { + IconButton(onClick = { + server?.id?.let { srvId -> onShowServerContextSheet(srvId) } + }) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = stringResource(R.string.menu), + tint = LocalContentColor.current + ) + } + } else { + Spacer(Modifier.height(64.dp)) } - } else { - Spacer(Modifier.height(64.dp)) } } } @@ -762,7 +800,8 @@ fun ColumnScope.ServerChannelListRenderer( items(categorisedChannels?.size ?: 0) { when (val channelOrCat = categorisedChannels?.get(it)) { is CategorisedChannelList.Channel -> { - ChannelItem(channel = channelOrCat.channel, + ChannelItem( + channel = channelOrCat.channel, isCurrent = when (currentDestination) { is ChatRouterDestination.Channel -> { currentDestination.channelId == channelOrCat.channel.id @@ -840,7 +879,8 @@ fun ChannelItem( } } ) { - Row(verticalAlignment = Alignment.CenterVertically, + Row( + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), modifier = Modifier .padding(start = 8.dp, end = 8.dp) 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 d9467596..79ebe7b5 100644 --- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt @@ -10,15 +10,17 @@ import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.AlertDialog @@ -38,15 +40,20 @@ import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource @@ -261,6 +268,8 @@ class ChatRouterViewModel @Inject constructor( } } +val LocalIsConnected = compositionLocalOf(structuralEqualityPolicy()) { false } + @OptIn(ExperimentalMaterial3Api::class) @Composable fun ChatRouterScreen( @@ -276,6 +285,8 @@ fun ChatRouterScreen( val context = LocalContext.current val view = LocalView.current + var drawerWidth by remember { mutableFloatStateOf(0.0f) } + var showPlatformModDMHint by remember { mutableStateOf(false) } var showStatusSheet by remember { mutableStateOf(false) } @@ -814,55 +825,11 @@ fun ChatRouterScreen( ) } - AnimatedVisibility( - visible = RealtimeSocket.disconnectionState == DisconnectionState.Connected + CompositionLocalProvider( + LocalIsConnected provides (RealtimeSocket.disconnectionState == DisconnectionState.Connected) ) { - Spacer(Modifier.windowInsetsPadding(WindowInsets.statusBars)) - } - - if (useTabletAwareUI) { - Row { - DismissibleDrawerSheet( - drawerContainerColor = Color.Transparent, - windowInsets = WindowInsets.zero - ) { - Sidebar( - viewModel = viewModel, - topNav = topNav, - currentServer = currentServer, - onShowStatusSheet = { - showStatusSheet = true - }, - onShowServerContextSheet = { - serverContextSheetTarget = it - showServerContextSheet = true - }, - onShowAddServerSheet = { - showAddServerSheet = true - }, - showSettingsButton = isTouchExplorationEnabled, - onOpenSettings = { - topNav.navigate("settings") - }, - ) - } - ChannelNavigator( - dest = viewModel.currentDestination, - topNav = topNav, - useDrawer = false, - disableBackHandler = disableBackHandler, - toggleDrawer = { - toggleDrawerLambda() - }, - onEnterVoiceUI = onEnterVoiceUI, - ) - } - } else { - var useSidebarGesture by remember { mutableStateOf(true) } - DismissibleNavigationDrawer( - drawerState = drawerState, - gesturesEnabled = useSidebarGesture, - drawerContent = { + if (useTabletAwareUI) { + Row { DismissibleDrawerSheet( drawerContainerColor = Color.Transparent, windowInsets = WindowInsets.zero @@ -885,30 +852,104 @@ fun ChatRouterScreen( onOpenSettings = { topNav.navigate("settings") }, - drawerState = drawerState - ) - } - }, - content = { - Row(Modifier.fillMaxSize()) { - ChannelNavigator( - dest = viewModel.currentDestination, - topNav = topNav, - useDrawer = true, - disableBackHandler = disableBackHandler, - toggleDrawer = { - toggleDrawerLambda() - }, - drawerState = drawerState, - drawerGestureEnabled = useSidebarGesture, - setDrawerGestureEnabled = { - useSidebarGesture = it - }, - onEnterVoiceUI = onEnterVoiceUI, ) } + ChannelNavigator( + dest = viewModel.currentDestination, + topNav = topNav, + useDrawer = false, + disableBackHandler = disableBackHandler, + toggleDrawer = { + toggleDrawerLambda() + }, + onEnterVoiceUI = onEnterVoiceUI, + ) } - ) + } else { + var useSidebarGesture by remember { mutableStateOf(true) } + DismissibleNavigationDrawer( + drawerState = drawerState, + gesturesEnabled = useSidebarGesture, + drawerContent = { + DismissibleDrawerSheet( + drawerContainerColor = Color.Transparent, + windowInsets = WindowInsets.zero, + modifier = Modifier.onSizeChanged { + drawerWidth = it.width.toFloat() + } + ) { + Sidebar( + viewModel = viewModel, + topNav = topNav, + currentServer = currentServer, + onShowStatusSheet = { + showStatusSheet = true + }, + onShowServerContextSheet = { + serverContextSheetTarget = it + showServerContextSheet = true + }, + onShowAddServerSheet = { + showAddServerSheet = true + }, + showSettingsButton = isTouchExplorationEnabled, + onOpenSettings = { + topNav.navigate("settings") + }, + drawerState = drawerState + ) + } + }, + content = { + Box(Modifier.fillMaxSize()) { + ChannelNavigator( + dest = viewModel.currentDestination, + topNav = topNav, + useDrawer = true, + disableBackHandler = disableBackHandler, + toggleDrawer = { + toggleDrawerLambda() + }, + drawerState = drawerState, + drawerGestureEnabled = useSidebarGesture, + setDrawerGestureEnabled = { + useSidebarGesture = it + }, + onEnterVoiceUI = onEnterVoiceUI, + ) + + // This is the overlay on the main content when the drawer is open + val interactionSource = remember { MutableInteractionSource() } + Box( + Modifier + .then( + if (drawerState.isOpen) { + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + enabled = drawerState.isOpen, + onClick = { + scope.launch { + drawerState.close() + } + } + ) + } else Modifier + ) + .fillMaxSize() + .background( + MaterialTheme + .colorScheme + .surfaceContainerLowest + .copy( + alpha = (1.0f + (drawerState.currentOffset / drawerWidth)) * 0.7f + ) + ) + ) + } + } + ) + } } } } diff --git a/app/src/main/java/chat/revolt/screens/chat/views/FriendsScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/FriendsScreen.kt index e8dd5163..e08efae7 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/FriendsScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/FriendsScreen.kt @@ -1,11 +1,16 @@ package chat.revolt.screens.chat.views +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu @@ -37,6 +42,7 @@ import chat.revolt.callbacks.ActionChannel import chat.revolt.composables.chat.MemberListItem import chat.revolt.composables.generic.CountableListHeader import chat.revolt.internals.extensions.zero +import chat.revolt.screens.chat.LocalIsConnected import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -48,69 +54,80 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: () Scaffold( topBar = { - TopAppBar( - title = { - Text( - text = stringResource(R.string.friends), - maxLines = 1, - overflow = TextOverflow.Ellipsis, + Column { + AnimatedVisibility(LocalIsConnected.current) { + Spacer( + Modifier + .height( + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() + ) ) - }, - navigationIcon = { - if (useDrawer) { + } + TopAppBar( + title = { + Text( + text = stringResource(R.string.friends), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + navigationIcon = { + if (useDrawer) { + IconButton(onClick = { + onDrawerClicked() + }) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = stringResource(id = R.string.menu) + ) + } + } + }, + actions = { IconButton(onClick = { - onDrawerClicked() + topNav.navigate("create/group") }) { Icon( - imageVector = Icons.Default.Menu, - contentDescription = stringResource(id = R.string.menu) + painter = painterResource(R.drawable.ic_account_multiple_plus_24dp), + contentDescription = stringResource(R.string.frends_new_group) ) } - } - }, - actions = { - IconButton(onClick = { - topNav.navigate("create/group") - }) { - Icon( - painter = painterResource(R.drawable.ic_account_multiple_plus_24dp), - contentDescription = stringResource(R.string.frends_new_group) - ) - } - IconButton(onClick = { - overflowMenuShown = true - }) { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource(R.string.menu) - ) - } - DropdownMenu( - expanded = overflowMenuShown, - onDismissRequest = { - overflowMenuShown = false + IconButton(onClick = { + overflowMenuShown = true + }) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = stringResource(R.string.menu) + ) } - ) { - DropdownMenuItem( - text = { - Text(stringResource(R.string.friends_deny_all_incoming)) - }, - onClick = { - scope.launch { - overflowMenuShown = false - } - with(Dispatchers.IO) { + DropdownMenu( + expanded = overflowMenuShown, + onDismissRequest = { + overflowMenuShown = false + } + ) { + DropdownMenuItem( + text = { + Text(stringResource(R.string.friends_deny_all_incoming)) + }, + onClick = { scope.launch { - FriendRequests.getIncoming() - .forEach { it.id?.let { id -> unfriendUser(id) } } + overflowMenuShown = false + } + with(Dispatchers.IO) { + scope.launch { + FriendRequests.getIncoming() + .forEach { it.id?.let { id -> unfriendUser(id) } } + } } } - } - ) - } - }, - windowInsets = WindowInsets.zero - ) + ) + } + }, + windowInsets = WindowInsets.zero + ) + } }, ) { pv -> Column( @@ -234,7 +251,7 @@ fun FriendsScreen(topNav: NavController, useDrawer: Boolean, onDrawerClicked: () items(FriendRequests.getBlocked().size) { val item = FriendRequests.getBlocked().getOrNull(it) if (item == null) return@items - + MemberListItem( member = null, user = item, diff --git a/app/src/main/java/chat/revolt/screens/chat/views/NoCurrentChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/NoCurrentChannelScreen.kt index acb45ba4..20e1432f 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/NoCurrentChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/NoCurrentChannelScreen.kt @@ -1,10 +1,15 @@ package chat.revolt.screens.chat.views +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.ExperimentalMaterial3Api @@ -23,28 +28,40 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.revolt.R import chat.revolt.internals.extensions.zero +import chat.revolt.screens.chat.LocalIsConnected @OptIn(ExperimentalMaterial3Api::class) @Composable fun NoCurrentChannelScreen(useDrawer: Boolean, onDrawerClicked: () -> Unit) { Scaffold( topBar = { - TopAppBar( - title = {}, - navigationIcon = { - if (useDrawer) { - IconButton(onClick = { - onDrawerClicked() - }) { - Icon( - imageVector = Icons.Default.Menu, - contentDescription = stringResource(id = R.string.menu) + Column { + AnimatedVisibility(LocalIsConnected.current) { + Spacer( + Modifier + .height( + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() ) + ) + } + TopAppBar( + title = {}, + navigationIcon = { + if (useDrawer) { + IconButton(onClick = { + onDrawerClicked() + }) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = stringResource(id = R.string.menu) + ) + } } - } - }, - windowInsets = WindowInsets.zero - ) + }, + windowInsets = WindowInsets.zero + ) + } }, ) { pv -> Column( diff --git a/app/src/main/java/chat/revolt/screens/chat/views/OverviewScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/OverviewScreen.kt index 14a891e3..2b0769b8 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/OverviewScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/OverviewScreen.kt @@ -13,11 +13,13 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.exclude import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.material.icons.Icons @@ -62,6 +64,7 @@ import chat.revolt.composables.generic.NonIdealState import chat.revolt.composables.screens.settings.UserOverview import chat.revolt.composables.skeletons.UserOverviewSkeleton import chat.revolt.internals.extensions.zero +import chat.revolt.screens.chat.LocalIsConnected import chat.revolt.sheets.UserCardSheet import io.sentry.Sentry @@ -110,22 +113,33 @@ fun OverviewScreen( Scaffold( topBar = { - CenterAlignedTopAppBar( - title = { Text(stringResource(R.string.overview_screen_title)) }, - navigationIcon = { - if (useDrawer) { - IconButton(onClick = { - onDrawerClicked() - }) { - Icon( - imageVector = Icons.Default.Menu, - contentDescription = stringResource(id = R.string.menu) + Column { + AnimatedVisibility(LocalIsConnected.current) { + Spacer( + Modifier + .height( + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() ) + ) + } + CenterAlignedTopAppBar( + title = { Text(stringResource(R.string.overview_screen_title)) }, + navigationIcon = { + if (useDrawer) { + IconButton(onClick = { + onDrawerClicked() + }) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = stringResource(id = R.string.menu) + ) + } } - } - }, - windowInsets = WindowInsets.zero - ) + }, + windowInsets = WindowInsets.zero + ) + } }, contentWindowInsets = if (includePadding) ScaffoldDefaults.contentWindowInsets else ScaffoldDefaults.contentWindowInsets.exclude( NavigationBarDefaults.windowInsets diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt index c7261beb..ba163362 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -145,6 +146,7 @@ import chat.revolt.composables.skeletons.MessageSkeleton import chat.revolt.composables.skeletons.MessageSkeletonVariant import chat.revolt.internals.extensions.rememberChannelPermissions import chat.revolt.internals.extensions.zero +import chat.revolt.screens.chat.LocalIsConnected import chat.revolt.sheets.ChannelInfoSheet import chat.revolt.sheets.MessageContextSheet import chat.revolt.sheets.ReactSheet @@ -513,126 +515,137 @@ fun ChannelScreen( Scaffold( contentWindowInsets = WindowInsets.zero, topBar = { - TopAppBar( - modifier = Modifier.clickable { - channelInfoSheetShown = true - }, - title = { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - viewModel.channel?.let { - when (it.channelType) { - ChannelType.DirectMessage -> { + Column { + AnimatedVisibility(LocalIsConnected.current) { + Spacer( + Modifier + .height( + WindowInsets.statusBars.asPaddingValues() + .calculateTopPadding() + ) + ) + } + TopAppBar( + modifier = Modifier.clickable { + channelInfoSheetShown = true + }, + title = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + viewModel.channel?.let { + when (it.channelType) { + ChannelType.DirectMessage -> { + val partner = + RevoltAPI.userCache[ChannelUtils.resolveDMPartner(it)] + UserAvatar( + username = it.name ?: stringResource(R.string.unknown), + userId = ChannelUtils.resolveDMPartner(it) ?: "", + size = 24.dp, + presenceSize = 12.dp, + avatar = partner?.avatar + ) + } + + ChannelType.Group -> { + GroupIcon( + name = it.name ?: stringResource(R.string.unknown), + size = 24.dp, + icon = it.icon + ) + } + + else -> { + ChannelIcon( + channelType = it.channelType ?: ChannelType.TextChannel, + modifier = Modifier + .size(24.dp) + .alpha(0.8f) + ) + } + } + + CompositionLocalProvider( + LocalTextStyle provides LocalTextStyle.current.copy( + fontSize = 20.sp, + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Bottom, + trim = LineHeightStyle.Trim.LastLineBottom + ) + ) + ) { + when (it.channelType) { + ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> Text( + it.name ?: stringResource(R.string.unknown), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + ChannelType.SavedMessages -> Text( + stringResource(R.string.channel_notes), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + ChannelType.DirectMessage -> Text( + ChannelUtils.resolveName(it) + ?: stringResource(R.string.unknown), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + else -> Text( + stringResource(R.string.unknown), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + if (it.channelType == ChannelType.DirectMessage) { val partner = RevoltAPI.userCache[ChannelUtils.resolveDMPartner(it)] - UserAvatar( - username = it.name ?: stringResource(R.string.unknown), - userId = ChannelUtils.resolveDMPartner(it) ?: "", - size = 24.dp, - presenceSize = 12.dp, - avatar = partner?.avatar + PresenceBadge( + presence = presenceFromStatus( + partner?.status?.presence, + online = partner?.online == true + ), + size = 12.dp ) } - ChannelType.Group -> { - GroupIcon( - name = it.name ?: stringResource(R.string.unknown), - size = 24.dp, - icon = it.icon - ) - } - - else -> { - ChannelIcon( - channelType = it.channelType ?: ChannelType.TextChannel, - modifier = Modifier - .size(24.dp) - .alpha(0.8f) - ) - } - } - - CompositionLocalProvider( - LocalTextStyle provides LocalTextStyle.current.copy( - fontSize = 20.sp, - lineHeightStyle = LineHeightStyle( - alignment = LineHeightStyle.Alignment.Bottom, - trim = LineHeightStyle.Trim.LastLineBottom - ) - ) - ) { - when (it.channelType) { - ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> Text( - it.name ?: stringResource(R.string.unknown), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - ChannelType.SavedMessages -> Text( - stringResource(R.string.channel_notes), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - ChannelType.DirectMessage -> Text( - ChannelUtils.resolveName(it) - ?: stringResource(R.string.unknown), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - else -> Text( - stringResource(R.string.unknown), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - } - - if (it.channelType == ChannelType.DirectMessage) { - val partner = - RevoltAPI.userCache[ChannelUtils.resolveDMPartner(it)] - PresenceBadge( - presence = presenceFromStatus( - partner?.status?.presence, - online = partner?.online == true - ), - size = 12.dp + Icon( + imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, + contentDescription = null, + modifier = Modifier + .size(16.dp) + .alpha(0.5f) + ) + } + } + }, + windowInsets = if (useChatUI) WindowInsets.statusBars else WindowInsets.zero, + navigationIcon = { + if (useDrawer) { + IconButton(onClick = onToggleDrawer) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = stringResource(id = R.string.menu) + ) + } + } + if (useBackButton) { + IconButton(onClick = backButtonAction ?: {}) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = stringResource(id = R.string.back) ) } - - Icon( - imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, - contentDescription = null, - modifier = Modifier - .size(16.dp) - .alpha(0.5f) - ) } } - }, - windowInsets = if (useChatUI) WindowInsets.statusBars else WindowInsets.zero, - navigationIcon = { - if (useDrawer) { - IconButton(onClick = onToggleDrawer) { - Icon( - imageVector = Icons.Default.Menu, - contentDescription = stringResource(id = R.string.menu) - ) - } - } - if (useBackButton) { - IconButton(onClick = backButtonAction ?: {}) { - Icon( - imageVector = Icons.AutoMirrored.Default.ArrowBack, - contentDescription = stringResource(id = R.string.back) - ) - } - } - } - ) + ) + } } ) { pv -> Crossfade(