feat: double drawer is now simple drawer
members will go into bottom sheet Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
a8d70041cd
commit
440bf36ea2
|
|
@ -11,11 +11,24 @@ import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -115,6 +128,8 @@ fun Message(
|
||||||
truncate: Boolean = false,
|
truncate: Boolean = false,
|
||||||
parse: (MessageSchema) -> SpannableStringBuilder = { SpannableStringBuilder(it.content) },
|
parse: (MessageSchema) -> SpannableStringBuilder = { SpannableStringBuilder(it.content) },
|
||||||
onMessageContextMenu: () -> Unit = {},
|
onMessageContextMenu: () -> Unit = {},
|
||||||
|
canReply: Boolean = false,
|
||||||
|
onReply: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package chat.revolt.components.screens.chat
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.api.schemas.Channel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChannelHeader(
|
||||||
|
channel: Channel,
|
||||||
|
onChannelClick: (String) -> Unit,
|
||||||
|
onToggleDrawer: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
channel.id?.let { onChannelClick(it) }
|
||||||
|
}
|
||||||
|
.padding(vertical = 4.dp, horizontal = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
IconButton(onClick = {
|
||||||
|
onToggleDrawer()
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Menu,
|
||||||
|
contentDescription = stringResource(R.string.menu),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
|
||||||
|
channel.channelType?.let {
|
||||||
|
ChannelIcon(
|
||||||
|
channelType = it,
|
||||||
|
modifier = Modifier.alpha(0.6f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = channel.name ?: "Ch #${channel.id}",
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.KeyboardArrowRight,
|
||||||
|
contentDescription = stringResource(R.string.menu),
|
||||||
|
modifier = Modifier
|
||||||
|
.size(18.dp)
|
||||||
|
.alpha(0.4f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
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)
|
|
||||||
.align(Alignment.CenterStart)
|
|
||||||
.offset {
|
|
||||||
IntOffset(
|
|
||||||
x = state.swipeableState.offset.value.roundToInt() - offsetValue.roundToInt(),
|
|
||||||
y = 0
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.clip(RoundedCornerShape(topEnd = 16.dp, bottomEnd = 16.dp))
|
|
||||||
) {
|
|
||||||
startPanel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.align(Alignment.Center)
|
|
||||||
.offset {
|
|
||||||
IntOffset(
|
|
||||||
x = state.swipeableState.offset.value.roundToInt(),
|
|
||||||
y = 0
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.clip(RoundedCornerShape(contentCornerRadius))
|
|
||||||
) {
|
|
||||||
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
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.clip(RoundedCornerShape(topStart = 16.dp, bottomStart = 16.dp))
|
|
||||||
) {
|
|
||||||
endPanel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.DrawerState
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -23,14 +25,14 @@ import androidx.compose.ui.unit.sp
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
import chat.revolt.api.schemas.ChannelType
|
import chat.revolt.api.schemas.ChannelType
|
||||||
import chat.revolt.components.screens.chat.DoubleDrawerState
|
|
||||||
import chat.revolt.components.screens.chat.drawer.server.DrawerChannel
|
import chat.revolt.components.screens.chat.drawer.server.DrawerChannel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RowScope.ChannelList(
|
fun RowScope.ChannelList(
|
||||||
serverId: String,
|
serverId: String,
|
||||||
drawerState: DoubleDrawerState,
|
drawerState: DrawerState,
|
||||||
currentChannel: String?,
|
currentChannel: String?,
|
||||||
onChannelClick: (String) -> Unit,
|
onChannelClick: (String) -> Unit,
|
||||||
onChannelLongClick: (String) -> Unit,
|
onChannelLongClick: (String) -> Unit,
|
||||||
|
|
@ -69,7 +71,7 @@ fun RowScope.ChannelList(
|
||||||
} ?: false,
|
} ?: false,
|
||||||
onClick = {
|
onClick = {
|
||||||
onChannelClick(channel.id ?: return@DrawerChannel)
|
onChannelClick(channel.id ?: return@DrawerChannel)
|
||||||
coroutineScope.launch { drawerState.focusCenter() }
|
coroutineScope.launch { drawerState.close() }
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onChannelLongClick(channel.id ?: return@DrawerChannel)
|
onChannelLongClick(channel.id ?: return@DrawerChannel)
|
||||||
|
|
@ -127,7 +129,7 @@ fun RowScope.ChannelList(
|
||||||
} ?: true,
|
} ?: true,
|
||||||
onClick = {
|
onClick = {
|
||||||
onChannelClick(ch.id ?: return@DrawerChannel)
|
onChannelClick(ch.id ?: return@DrawerChannel)
|
||||||
coroutineScope.launch { drawerState.focusCenter() }
|
coroutineScope.launch { drawerState.close() }
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onChannelLongClick(ch.id ?: return@DrawerChannel)
|
onChannelLongClick(ch.id ?: return@DrawerChannel)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package chat.revolt.screens.chat
|
package chat.revolt.screens.chat
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
|
@ -18,11 +17,15 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.DismissibleDrawerSheet
|
||||||
|
import androidx.compose.material3.DismissibleNavigationDrawer
|
||||||
|
import androidx.compose.material3.DrawerValue
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.rememberDrawerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
@ -33,10 +36,10 @@ import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
|
@ -52,13 +55,10 @@ import chat.revolt.api.realtime.RealtimeSocket
|
||||||
import chat.revolt.components.chat.DisconnectedNotice
|
import chat.revolt.components.chat.DisconnectedNotice
|
||||||
import chat.revolt.components.generic.UserAvatar
|
import chat.revolt.components.generic.UserAvatar
|
||||||
import chat.revolt.components.generic.presenceFromStatus
|
import chat.revolt.components.generic.presenceFromStatus
|
||||||
import chat.revolt.components.screens.chat.DoubleDrawer
|
|
||||||
import chat.revolt.components.screens.chat.DoubleDrawerOpenState
|
|
||||||
import chat.revolt.components.screens.chat.drawer.channel.ChannelList
|
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.chat.rememberDoubleDrawerState
|
|
||||||
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
|
||||||
import chat.revolt.screens.chat.sheets.AddServerSheet
|
import chat.revolt.screens.chat.sheets.AddServerSheet
|
||||||
|
|
@ -168,10 +168,14 @@ class ChatRouterViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialNavigationApi::class, ExperimentalComposeUiApi::class)
|
@OptIn(
|
||||||
|
ExperimentalMaterialNavigationApi::class,
|
||||||
|
ExperimentalComposeUiApi::class,
|
||||||
|
ExperimentalMaterial3Api::class
|
||||||
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hiltViewModel()) {
|
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hiltViewModel()) {
|
||||||
val drawerState = rememberDoubleDrawerState()
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
|
|
@ -184,11 +188,17 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
composition = sidebarSparkComposition,
|
composition = sidebarSparkComposition,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
BackHandler(enabled = drawerState.isClosed) {
|
||||||
|
scope.launch {
|
||||||
|
drawerState.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(drawerState) {
|
LaunchedEffect(drawerState) {
|
||||||
snapshotFlow { drawerState.currentValue }
|
snapshotFlow { drawerState.currentValue }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.collect { state ->
|
.collect { state ->
|
||||||
if (state != DoubleDrawerOpenState.Center) {
|
if (state == DrawerValue.Closed) {
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,165 +284,167 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
DoubleDrawer(
|
DismissibleNavigationDrawer(
|
||||||
state = drawerState,
|
drawerState = drawerState,
|
||||||
startPanel = {
|
drawerContent = {
|
||||||
Column(Modifier.fillMaxWidth()) {
|
DismissibleDrawerSheet(
|
||||||
Row {
|
drawerContainerColor = Color.Transparent,
|
||||||
Column(
|
) {
|
||||||
modifier = Modifier
|
Column(Modifier.fillMaxWidth()) {
|
||||||
.fillMaxHeight()
|
Row {
|
||||||
.verticalScroll(rememberScrollState()),
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
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
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.fillMaxHeight()
|
||||||
.size(48.dp)
|
.verticalScroll(rememberScrollState()),
|
||||||
)
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
|
||||||
ServerDrawerSeparator()
|
|
||||||
|
|
||||||
RevoltAPI.serverCache.values
|
|
||||||
.sortedBy { it.id }
|
|
||||||
.forEach { server ->
|
|
||||||
if (server.id == null || server.name == null) return@forEach
|
|
||||||
|
|
||||||
DrawerServer(
|
|
||||||
iconId = server.icon?.id,
|
|
||||||
serverName = server.name,
|
|
||||||
hasUnreads = RevoltAPI.unreads.serverHasUnread(server.id),
|
|
||||||
) {
|
|
||||||
viewModel.navigateToServer(
|
|
||||||
server.id,
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawerServerlikeIcon(
|
|
||||||
onClick = {
|
|
||||||
navController.navigate("add_server")
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
UserAvatar(
|
||||||
Icons.Default.Add,
|
username = RevoltAPI.userCache[RevoltAPI.selfId]?.username
|
||||||
contentDescription = stringResource(id = R.string.server_plus_alt),
|
?: "",
|
||||||
modifier = Modifier.padding(4.dp)
|
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
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(48.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
ServerDrawerSeparator()
|
||||||
|
|
||||||
|
RevoltAPI.serverCache.values
|
||||||
|
.sortedBy { it.id }
|
||||||
|
.forEach { server ->
|
||||||
|
if (server.id == null || server.name == null) return@forEach
|
||||||
|
|
||||||
|
DrawerServer(
|
||||||
|
iconId = server.icon?.id,
|
||||||
|
serverName = server.name,
|
||||||
|
hasUnreads = RevoltAPI.unreads.serverHasUnread(
|
||||||
|
server.id
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
viewModel.navigateToServer(
|
||||||
|
server.id,
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerServerlikeIcon(
|
||||||
|
onClick = {
|
||||||
|
navController.navigate("add_server")
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Add,
|
||||||
|
contentDescription = stringResource(id = R.string.server_plus_alt),
|
||||||
|
modifier = Modifier.padding(4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Crossfade(
|
||||||
|
targetState = viewModel.currentServer,
|
||||||
|
label = "Channel List"
|
||||||
|
) {
|
||||||
|
ChannelList(
|
||||||
|
serverId = it,
|
||||||
|
drawerState = drawerState,
|
||||||
|
currentChannel = viewModel.currentChannel,
|
||||||
|
onChannelClick = { channelId ->
|
||||||
|
viewModel.navigateToChannel(channelId, navController)
|
||||||
|
},
|
||||||
|
onChannelLongClick = { channelId ->
|
||||||
|
navController.navigate("channel/$channelId/info")
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(
|
|
||||||
targetState = viewModel.currentServer,
|
|
||||||
label = "Channel List"
|
|
||||||
) {
|
|
||||||
ChannelList(
|
|
||||||
serverId = it,
|
|
||||||
drawerState = drawerState,
|
|
||||||
currentChannel = viewModel.currentChannel,
|
|
||||||
onChannelClick = { channelId ->
|
|
||||||
viewModel.navigateToChannel(channelId, navController)
|
|
||||||
},
|
|
||||||
onChannelLongClick = { channelId ->
|
|
||||||
navController.navigate("channel/$channelId/info")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
endPanel = {
|
content = {
|
||||||
Box(
|
Column(Modifier.fillMaxSize()) {
|
||||||
modifier = Modifier
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
composable("home") {
|
||||||
.fillMaxSize(),
|
HomeScreen(navController = topNav)
|
||||||
contentAlignment = Alignment.Center
|
}
|
||||||
) {
|
composable("channel/{channelId}") { backStackEntry ->
|
||||||
Text(text = "👋", fontSize = 64.sp)
|
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||||
}
|
if (channelId != null) {
|
||||||
},
|
ChannelScreen(
|
||||||
) {
|
navController = navController,
|
||||||
Column(Modifier.fillMaxSize()) {
|
channelId = channelId,
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
onToggleDrawer = {
|
||||||
composable("home") {
|
scope.launch {
|
||||||
HomeScreen(navController = topNav)
|
if (drawerState.isOpen) drawerState.close()
|
||||||
}
|
else drawerState.open()
|
||||||
composable("channel/{channelId}") { backStackEntry ->
|
}
|
||||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
}
|
||||||
if (channelId != null) {
|
)
|
||||||
ChannelScreen(
|
}
|
||||||
navController = navController,
|
}
|
||||||
channelId = channelId
|
composable("no_current_channel") {
|
||||||
)
|
NoCurrentChannelScreen()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
composable("no_current_channel") {
|
|
||||||
NoCurrentChannelScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
bottomSheet("channel/{channelId}/info") { backStackEntry ->
|
bottomSheet("channel/{channelId}/info") { backStackEntry ->
|
||||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||||
if (channelId != null) {
|
if (channelId != null) {
|
||||||
ChannelInfoSheet(
|
ChannelInfoSheet(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
channelId = channelId
|
channelId = channelId
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
bottomSheet("channel/{channelId}/menu") { backStackEntry ->
|
||||||
bottomSheet("channel/{channelId}/menu") { backStackEntry ->
|
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
if (channelId != null) {
|
||||||
if (channelId != null) {
|
ChannelContextSheet(
|
||||||
ChannelContextSheet(
|
navController = navController,
|
||||||
navController = navController,
|
channelId = channelId
|
||||||
channelId = channelId
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
bottomSheet("message/{messageId}/menu") { backStackEntry ->
|
||||||
bottomSheet("message/{messageId}/menu") { backStackEntry ->
|
val messageId = backStackEntry.arguments?.getString("messageId")
|
||||||
val messageId = backStackEntry.arguments?.getString("messageId")
|
if (messageId != null) {
|
||||||
if (messageId != null) {
|
MessageContextSheet(
|
||||||
MessageContextSheet(
|
navController = navController,
|
||||||
navController = navController,
|
messageId = messageId
|
||||||
messageId = messageId
|
)
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
bottomSheet("status") {
|
||||||
|
StatusSheet(navController = navController, topNav = topNav)
|
||||||
|
}
|
||||||
|
bottomSheet("add_server") {
|
||||||
|
AddServerSheet()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
bottomSheet("status") {
|
|
||||||
StatusSheet(navController = navController, topNav = topNav)
|
|
||||||
}
|
|
||||||
bottomSheet("add_server") {
|
|
||||||
AddServerSheet()
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog("report/message/{messageId}") { backStackEntry ->
|
dialog("report/message/{messageId}") { backStackEntry ->
|
||||||
val messageId = backStackEntry.arguments?.getString("messageId")
|
val messageId = backStackEntry.arguments?.getString("messageId")
|
||||||
if (messageId != null) {
|
if (messageId != null) {
|
||||||
ReportMessageDialog(
|
ReportMessageDialog(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
messageId = messageId
|
messageId = messageId
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
package chat.revolt.screens.chat.sheets
|
package chat.revolt.screens.chat.sheets
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
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.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.List
|
||||||
|
import androidx.compose.material.icons.filled.Notifications
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
import chat.revolt.api.schemas.ChannelType
|
import chat.revolt.components.generic.SheetClickable
|
||||||
import chat.revolt.components.generic.PageHeader
|
|
||||||
import chat.revolt.components.screens.chat.ChannelIcon
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChannelInfoSheet(
|
fun ChannelInfoSheet(
|
||||||
|
|
@ -33,20 +38,8 @@ fun ChannelInfoSheet(
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
ChannelIcon(
|
|
||||||
channelType = channel.channelType ?: ChannelType.TextChannel,
|
|
||||||
modifier = Modifier.size(32.dp)
|
|
||||||
)
|
|
||||||
PageHeader(
|
|
||||||
text = channel.name ?: channel.id ?: "",
|
|
||||||
modifier = Modifier.offset((-4).dp, 0.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.channel_info_sheet_description),
|
text = stringResource(id = R.string.channel_info_sheet_description),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
|
@ -57,5 +50,69 @@ fun ChannelInfoSheet(
|
||||||
?: stringResource(id = R.string.channel_info_sheet_description_empty),
|
?: stringResource(id = R.string.channel_info_sheet_description_empty),
|
||||||
modifier = Modifier.padding(bottom = 10.dp)
|
modifier = Modifier.padding(bottom = 10.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.channel_info_sheet_options),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.padding(bottom = 4.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
SheetClickable(
|
||||||
|
icon = { modifier ->
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.List,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { style ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.channel_info_sheet_options_members),
|
||||||
|
style = style
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SheetClickable(
|
||||||
|
icon = { modifier ->
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { style ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.channel_info_sheet_options_invite),
|
||||||
|
style = style
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SheetClickable(
|
||||||
|
icon = { modifier ->
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Notifications,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { style ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.channel_info_sheet_options_notifications_manage),
|
||||||
|
style = style
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,13 +4,10 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
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.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
|
@ -21,7 +18,6 @@ import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
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.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
|
@ -36,7 +32,7 @@ import chat.revolt.api.routes.microservices.autumn.FileArgs
|
||||||
import chat.revolt.components.chat.Message
|
import chat.revolt.components.chat.Message
|
||||||
import chat.revolt.components.chat.MessageField
|
import chat.revolt.components.chat.MessageField
|
||||||
import chat.revolt.components.screens.chat.AttachmentManager
|
import chat.revolt.components.screens.chat.AttachmentManager
|
||||||
import chat.revolt.components.screens.chat.ChannelIcon
|
import chat.revolt.components.screens.chat.ChannelHeader
|
||||||
import chat.revolt.components.screens.chat.ReplyManager
|
import chat.revolt.components.screens.chat.ReplyManager
|
||||||
import chat.revolt.components.screens.chat.TypingIndicator
|
import chat.revolt.components.screens.chat.TypingIndicator
|
||||||
import chat.revolt.internals.markdown.ChannelMentionRule
|
import chat.revolt.internals.markdown.ChannelMentionRule
|
||||||
|
|
@ -58,6 +54,7 @@ import java.io.File
|
||||||
fun ChannelScreen(
|
fun ChannelScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
channelId: String,
|
channelId: String,
|
||||||
|
onToggleDrawer: () -> Unit,
|
||||||
viewModel: ChannelScreenViewModel = viewModel()
|
viewModel: ChannelScreenViewModel = viewModel()
|
||||||
) {
|
) {
|
||||||
val channel = viewModel.channel
|
val channel = viewModel.channel
|
||||||
|
|
@ -117,33 +114,13 @@ fun ChannelScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Row(
|
ChannelHeader(
|
||||||
modifier = Modifier
|
channel = channel,
|
||||||
.clickable {
|
onChannelClick = {
|
||||||
navController.navigate("channel/${channel.id}/info")
|
navController.navigate("channel/${channel.id}/info")
|
||||||
}
|
},
|
||||||
.fillMaxWidth()
|
onToggleDrawer = onToggleDrawer
|
||||||
.clip(
|
)
|
||||||
RoundedCornerShape(
|
|
||||||
bottomStart = 16.dp,
|
|
||||||
bottomEnd = 16.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
|
||||||
.padding(16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
ChannelIcon(
|
|
||||||
channelType = channel.channelType,
|
|
||||||
modifier = Modifier.padding(end = 8.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = channel.name ?: channel.id!!,
|
|
||||||
style = MaterialTheme.typography.labelLarge,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isScrolledToBottom = remember(lazyListState) {
|
val isScrolledToBottom = remember(lazyListState) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
@ -189,41 +166,49 @@ fun ChannelScreen(
|
||||||
items = viewModel.renderableMessages,
|
items = viewModel.renderableMessages,
|
||||||
key = { it.id!! }
|
key = { it.id!! }
|
||||||
) { message ->
|
) { message ->
|
||||||
Message(message, parse = {
|
Message(
|
||||||
val parser = MarkdownParser()
|
message,
|
||||||
.addRules(
|
parse = {
|
||||||
SimpleMarkdownRules.createEscapeRule(),
|
val parser = MarkdownParser()
|
||||||
UserMentionRule(),
|
.addRules(
|
||||||
ChannelMentionRule(),
|
SimpleMarkdownRules.createEscapeRule(),
|
||||||
CustomEmoteRule(),
|
UserMentionRule(),
|
||||||
)
|
ChannelMentionRule(),
|
||||||
.addRules(
|
CustomEmoteRule(),
|
||||||
createCodeRule(context, codeBlockColor.toArgb()),
|
)
|
||||||
createInlineCodeRule(context, codeBlockColor.toArgb()),
|
.addRules(
|
||||||
)
|
createCodeRule(context, codeBlockColor.toArgb()),
|
||||||
.addRules(
|
createInlineCodeRule(context, codeBlockColor.toArgb()),
|
||||||
SimpleMarkdownRules.createSimpleMarkdownRules(
|
)
|
||||||
includeEscapeRule = false
|
.addRules(
|
||||||
|
SimpleMarkdownRules.createSimpleMarkdownRules(
|
||||||
|
includeEscapeRule = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
SimpleRenderer.render(
|
||||||
|
source = it.content ?: "",
|
||||||
|
parser = parser,
|
||||||
|
initialState = MarkdownState(0),
|
||||||
|
renderContext = MarkdownContext(
|
||||||
|
memberMap = mapOf(),
|
||||||
|
userMap = RevoltAPI.userCache.toMap(),
|
||||||
|
channelMap = RevoltAPI.channelCache.mapValues { ch ->
|
||||||
|
ch.value.name ?: ch.value.id ?: "#DeletedChannel"
|
||||||
|
},
|
||||||
|
emojiMap = RevoltAPI.emojiCache,
|
||||||
|
serverId = channel.server ?: "",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
},
|
||||||
SimpleRenderer.render(
|
onMessageContextMenu = {
|
||||||
source = it.content ?: "",
|
navController.navigate("message/${message.id}/menu")
|
||||||
parser = parser,
|
},
|
||||||
initialState = MarkdownState(0),
|
canReply = true,
|
||||||
renderContext = MarkdownContext(
|
onReply = {
|
||||||
memberMap = mapOf(),
|
viewModel.replyToMessage(message)
|
||||||
userMap = RevoltAPI.userCache.toMap(),
|
},
|
||||||
channelMap = RevoltAPI.channelCache.mapValues { ch ->
|
)
|
||||||
ch.value.name ?: ch.value.id ?: "#DeletedChannel"
|
|
||||||
},
|
|
||||||
emojiMap = RevoltAPI.emojiCache,
|
|
||||||
serverId = channel.server ?: "",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
navController.navigate("message/${message.id}/menu")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
|
|
|
||||||
|
|
@ -358,4 +358,13 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
Log.d("ChannelScreen", "Acking channel")
|
Log.d("ChannelScreen", "Acking channel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun replyToMessage(message: Message) {
|
||||||
|
addInReplyTo(
|
||||||
|
SendMessageReply(
|
||||||
|
id = message.id!!,
|
||||||
|
mention = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,10 @@
|
||||||
|
|
||||||
<string name="channel_info_sheet_description">Channel description</string>
|
<string name="channel_info_sheet_description">Channel description</string>
|
||||||
<string name="channel_info_sheet_description_empty">There hasn\'t been a description set for this channel yet.</string>
|
<string name="channel_info_sheet_description_empty">There hasn\'t been a description set for this channel yet.</string>
|
||||||
|
<string name="channel_info_sheet_options">Options</string>
|
||||||
|
<string name="channel_info_sheet_options_members">Members</string>
|
||||||
|
<string name="channel_info_sheet_options_invite">Invite</string>
|
||||||
|
<string name="channel_info_sheet_options_notifications_manage">Manage notifications</string>
|
||||||
|
|
||||||
<string name="message_context_sheet_actions_copy">Copy</string>
|
<string name="message_context_sheet_actions_copy">Copy</string>
|
||||||
<string name="message_context_sheet_actions_copy_failed_empty">Message is empty, nothing to copy</string>
|
<string name="message_context_sheet_actions_copy_failed_empty">Message is empty, nothing to copy</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue