feat: placeholder media channel overlay

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-04-06 00:07:34 +02:00
parent 4784bb3f3f
commit 67881fefb7
8 changed files with 132 additions and 10 deletions

View File

@ -17,6 +17,19 @@ sealed class LabsAccessControlVariates {
data class Restricted(val predicate: () -> Boolean) : LabsAccessControlVariates() data class Restricted(val predicate: () -> Boolean) : LabsAccessControlVariates()
} }
@FeatureFlag("MediaConversations")
sealed class MediaConversationsVariates {
@Treatment(
"Enable voice, video and screen conversations for all users"
)
object Enabled : MediaConversationsVariates()
@Treatment(
"Enable voice, video and screen conversations for users that meet certain or all criteria (implementation-specific)"
)
data class Restricted(val predicate: () -> Boolean) : MediaConversationsVariates()
}
object FeatureFlags { object FeatureFlags {
@FeatureFlag("LabsAccessControl") @FeatureFlag("LabsAccessControl")
var labsAccessControl by mutableStateOf<LabsAccessControlVariates>( var labsAccessControl by mutableStateOf<LabsAccessControlVariates>(
@ -24,4 +37,22 @@ object FeatureFlags {
RevoltAPI.selfId == SpecialUsers.JENNIFER RevoltAPI.selfId == SpecialUsers.JENNIFER
} }
) )
val labsAccessControlGranted: Boolean
get() = when (labsAccessControl) {
is LabsAccessControlVariates.Restricted -> (labsAccessControl as LabsAccessControlVariates.Restricted).predicate()
}
@FeatureFlag("MediaConversations")
var mediaConversations by mutableStateOf<MediaConversationsVariates>(
MediaConversationsVariates.Restricted {
RevoltAPI.selfId == SpecialUsers.JENNIFER
}
)
val mediaConversationsGranted: Boolean
get() = when (mediaConversations) {
is MediaConversationsVariates.Enabled -> true
is MediaConversationsVariates.Restricted -> (mediaConversations as MediaConversationsVariates.Restricted).predicate()
}
} }

View File

@ -10,6 +10,7 @@ sealed class Action {
data class MessageReactionInfo(val messageId: String, val emoji: String) : Action() data class MessageReactionInfo(val messageId: String, val emoji: String) : Action()
data class TopNavigate(val route: String) : Action() data class TopNavigate(val route: String) : Action()
data class ChatNavigate(val route: String) : Action() data class ChatNavigate(val route: String) : Action()
data class OpenVoiceChannelOverlay(val channelId: String) : Action()
} }
val ActionChannel = Channel<Action>( val ActionChannel = Channel<Action>(

View File

@ -0,0 +1,64 @@
package chat.revolt.components.screens.voice
import android.content.Context
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@Composable
fun VoiceChannelOverlay(channelId: String, onCollapse: () -> Unit) {
val context = LocalContext.current
val windowManager =
remember { context.getSystemService(Context.WINDOW_SERVICE) as WindowManager }
val metrics = DisplayMetrics().apply {
@Suppress("DEPRECATION") // We *need* the real metrics here, not the window metrics
windowManager.defaultDisplay.getRealMetrics(this)
}
val (width, height) = with(LocalDensity.current) {
Pair(metrics.widthPixels.toDp(), metrics.heightPixels.toDp())
}
Dialog(
onDismissRequest = {
onCollapse()
},
properties = DialogProperties(
usePlatformDefaultWidth = false,
decorFitsSystemWindows = true,
dismissOnClickOutside = false,
dismissOnBackPress = true
)
) {
Column(
Modifier
.requiredSize(width, height)
.background(MaterialTheme.colorScheme.background),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically)
) {
Text(text = "Voice channel overlay for $channelId", textAlign = TextAlign.Center)
Button(onClick = {
onCollapse()
}) {
Text("Close")
}
}
}
}

View File

@ -87,6 +87,7 @@ import chat.revolt.components.screens.chat.drawer.channel.ChannelList
import chat.revolt.components.screens.chat.drawer.server.DrawerServer 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.DrawerServerlikeIcon
import chat.revolt.components.screens.chat.drawer.server.ServerDrawerSeparator import chat.revolt.components.screens.chat.drawer.server.ServerDrawerSeparator
import chat.revolt.components.screens.voice.VoiceChannelOverlay
import chat.revolt.internals.Changelogs import chat.revolt.internals.Changelogs
import chat.revolt.persistence.KVStorage import chat.revolt.persistence.KVStorage
import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog
@ -290,6 +291,9 @@ fun ChatRouterScreen(
var useTabletAwareUI by remember { mutableStateOf(false) } var useTabletAwareUI by remember { mutableStateOf(false) }
var voiceChannelOverlay by remember { mutableStateOf(false) }
var voiceChannelOverlayChannelId by remember { mutableStateOf("") }
val toggleDrawerLambda = remember { val toggleDrawerLambda = remember {
{ {
scope.launch { scope.launch {
@ -410,6 +414,11 @@ fun ChatRouterScreen(
is Action.ChatNavigate -> { is Action.ChatNavigate -> {
navController.navigate(action.route) navController.navigate(action.route)
} }
is Action.OpenVoiceChannelOverlay -> {
voiceChannelOverlayChannelId = action.channelId
voiceChannelOverlay = true
}
} }
} }
} }
@ -671,6 +680,12 @@ fun ChatRouterScreen(
} }
} }
if (voiceChannelOverlay) {
VoiceChannelOverlay(voiceChannelOverlayChannelId) {
voiceChannelOverlay = false
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -72,6 +73,9 @@ import chat.revolt.api.routes.channel.react
import chat.revolt.api.routes.microservices.autumn.FileArgs import chat.revolt.api.routes.microservices.autumn.FileArgs
import chat.revolt.api.schemas.Channel import chat.revolt.api.schemas.Channel
import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.settings.FeatureFlags
import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel
import chat.revolt.components.chat.Message import chat.revolt.components.chat.Message
import chat.revolt.components.chat.NativeMessageField import chat.revolt.components.chat.NativeMessageField
import chat.revolt.components.chat.SystemMessage import chat.revolt.components.chat.SystemMessage
@ -267,6 +271,20 @@ fun ChannelScreen(
useDrawer = useDrawer useDrawer = useDrawer
) )
if (FeatureFlags.mediaConversationsGranted && channel?.channelType == ChannelType.VoiceChannel) {
Button(onClick = {
coroutineScope.launch {
ActionChannel.send(
Action.OpenVoiceChannelOverlay(
channelId
)
)
}
}) {
Text("Open voice channel overlay")
}
}
val isScrolledToBottom = remember(lazyListState) { val isScrolledToBottom = remember(lazyListState) {
derivedStateOf { derivedStateOf {
lazyListState.firstVisibleItemIndex <= 6 lazyListState.firstVisibleItemIndex <= 6

View File

@ -12,16 +12,13 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import chat.revolt.api.settings.FeatureFlags import chat.revolt.api.settings.FeatureFlags
import chat.revolt.api.settings.LabsAccessControlVariates
import chat.revolt.screens.labs.ui.mockups.CallScreenMockup import chat.revolt.screens.labs.ui.mockups.CallScreenMockup
annotation class LabsFeature annotation class LabsFeature
@Composable @Composable
fun LabsGuard(onTurnBack: () -> Unit = {}, content: @Composable () -> Unit) { fun LabsGuard(onTurnBack: () -> Unit = {}, content: @Composable () -> Unit) {
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted && if (!FeatureFlags.labsAccessControlGranted) {
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate().not()
) {
AlertDialog( AlertDialog(
onDismissRequest = { onTurnBack() }, onDismissRequest = { onTurnBack() },
confirmButton = { confirmButton = {

View File

@ -45,7 +45,6 @@ import chat.revolt.activities.InviteActivity
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.settings.FeatureFlags import chat.revolt.api.settings.FeatureFlags
import chat.revolt.api.settings.GlobalState import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LabsAccessControlVariates
import chat.revolt.components.generic.ListHeader import chat.revolt.components.generic.ListHeader
import chat.revolt.components.screens.settings.SelfUserOverview import chat.revolt.components.screens.settings.SelfUserOverview
import chat.revolt.persistence.KVStorage import chat.revolt.persistence.KVStorage
@ -241,9 +240,7 @@ fun SettingsScreen(
) )
} }
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted && if (FeatureFlags.labsAccessControlGranted) {
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate()
) {
ListItem( ListItem(
headlineContent = { headlineContent = {
Text( Text(

View File

@ -47,7 +47,6 @@ import chat.revolt.api.internals.hasPermission
import chat.revolt.api.routes.channel.leaveDeleteOrCloseChannel import chat.revolt.api.routes.channel.leaveDeleteOrCloseChannel
import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.settings.FeatureFlags import chat.revolt.api.settings.FeatureFlags
import chat.revolt.api.settings.LabsAccessControlVariates
import chat.revolt.internals.extensions.rememberChannelPermissions import chat.revolt.internals.extensions.rememberChannelPermissions
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -151,7 +150,7 @@ fun ChannelSettingsHome(navController: NavController, channelId: String) {
} }
// TODO Implement permissions UI and remove the predicate check // TODO Implement permissions UI and remove the predicate check
if (permissions.hasPermission(PermissionBit.ManageRole) && (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted && (FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate())) { if (permissions.hasPermission(PermissionBit.ManageRole) && FeatureFlags.labsAccessControlGranted) {
ListItem( ListItem(
headlineContent = { headlineContent = {
Text( Text(