feat: channel settings and attachment rendering
- also general design improvements
This commit is contained in:
parent
d93b9f1bcb
commit
4a5365c3c1
|
|
@ -61,6 +61,7 @@ dependencies {
|
|||
|
||||
// Jetpack Compose
|
||||
implementation "androidx.compose.ui:ui:$compose_libraries_version"
|
||||
implementation "androidx.compose.ui:ui-util:$compose_libraries_version"
|
||||
implementation 'androidx.compose.material3:material3:1.0.1'
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_libraries_version"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||
|
|
|
|||
|
|
@ -156,6 +156,16 @@ object RealtimeSocket {
|
|||
RevoltAPI.userCache[userUpdateFrame.id] =
|
||||
existing.mergeWithPartial(userUpdateFrame.data)
|
||||
}
|
||||
"ChannelUpdate" -> {
|
||||
val channelUpdateFrame =
|
||||
RevoltJson.decodeFromString(ChannelUpdateFrame.serializer(), rawFrame)
|
||||
|
||||
val existing = RevoltAPI.channelCache[channelUpdateFrame.id]
|
||||
?: return // if we don't have the channel no point in updating it
|
||||
|
||||
RevoltAPI.channelCache[channelUpdateFrame.id] =
|
||||
existing.mergeWithPartial(channelUpdateFrame.data)
|
||||
}
|
||||
else -> {
|
||||
Log.i("RealtimeSocket", "Unknown frame: $rawFrame")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,12 +168,6 @@ data class ServerDeleteFrame(
|
|||
val id: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ServerUserChoice(
|
||||
val server: String,
|
||||
val user: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ServerMemberUpdateFrame(
|
||||
val type: String = "ServerMemberUpdate",
|
||||
|
|
|
|||
|
|
@ -12,10 +12,16 @@ data class MessagesInChannel(
|
|||
val members: List<Member>? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ServerUserChoice(
|
||||
val server: String,
|
||||
val user: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Member(
|
||||
@SerialName("_id")
|
||||
val id: String? = null,
|
||||
val id: ServerUserChoice? = null,
|
||||
|
||||
@SerialName("joined_at")
|
||||
val joinedAt: String? = null,
|
||||
|
|
|
|||
|
|
@ -1,28 +1,44 @@
|
|||
package chat.revolt.components.chat
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.REVOLT_BASE
|
||||
import chat.revolt.api.REVOLT_FILES
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.schemas.AutumnResource
|
||||
import chat.revolt.components.generic.RemoteImage
|
||||
import chat.revolt.api.schemas.Message as MessageSchema
|
||||
|
||||
fun viewAttachmentInBrowser(ctx: android.content.Context, attachment: AutumnResource) {
|
||||
val customTab = CustomTabsIntent
|
||||
.Builder()
|
||||
.build()
|
||||
customTab.launchUrl(
|
||||
ctx,
|
||||
Uri.parse("$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}")
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Message(
|
||||
message: MessageSchema
|
||||
) {
|
||||
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(modifier = Modifier.padding(8.dp)) {
|
||||
if (author.avatar != null) {
|
||||
|
|
@ -55,6 +71,39 @@ fun Message(
|
|||
text = it
|
||||
)
|
||||
}
|
||||
|
||||
message.attachments?.let {
|
||||
if (message.attachments.isNotEmpty()) {
|
||||
message.attachments.forEach { attachment ->
|
||||
if (attachment.metadata?.type == "Image") {
|
||||
RemoteImage(
|
||||
url = "$REVOLT_FILES/attachments/${attachment.id}/image.png",
|
||||
modifier = Modifier
|
||||
.padding(top = 5.dp)
|
||||
.clickable {
|
||||
viewAttachmentInBrowser(context, attachment)
|
||||
},
|
||||
width = attachment.metadata.width?.toInt() ?: 0,
|
||||
height = attachment.metadata.height?.toInt() ?: 0,
|
||||
contentScale = ContentScale.Fit,
|
||||
description = "Attached image ${attachment.filename}"
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = attachment.filename ?: "Attachment",
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable {
|
||||
viewAttachmentInBrowser(context, attachment)
|
||||
}
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,9 +28,9 @@ fun CollapsibleCard(
|
|||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { expanded = !expanded }
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable { expanded = !expanded },
|
||||
.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package chat.revolt.components.generic
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun PageHeader(
|
||||
text: String,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Left,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PageHeaderPreview() {
|
||||
PageHeader(text = "Page Header")
|
||||
}
|
||||
|
|
@ -19,15 +19,26 @@ fun RemoteImage(
|
|||
url: String,
|
||||
description: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentScale: ContentScale = ContentScale.Crop
|
||||
contentScale: ContentScale = ContentScale.Crop,
|
||||
width: Int = 0,
|
||||
height: Int = 0,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
fun imageRequest() = run {
|
||||
val builder = ImageRequest.Builder(context)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
.data(url)
|
||||
|
||||
if (width != 0 && height != 0) {
|
||||
builder.size(width, height)
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
AsyncImage(
|
||||
model = imageRequest(),
|
||||
imageLoader = ImageLoader.Builder(context)
|
||||
.components {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
|
|
@ -45,7 +56,7 @@ fun RemoteImage(
|
|||
.build(),
|
||||
contentDescription = description,
|
||||
contentScale = contentScale,
|
||||
modifier = modifier
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.R
|
||||
|
||||
@Composable
|
||||
fun ChannelIcon(
|
||||
channelType: ChannelType,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (channelType) {
|
||||
ChannelType.TextChannel -> {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_pound_24dp),
|
||||
contentDescription = stringResource(R.string.channel_text),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ChannelType.VoiceChannel -> {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_volume_up_24dp),
|
||||
contentDescription = stringResource(R.string.channel_voice),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ChannelType.SavedMessages -> {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_note_24dp),
|
||||
contentDescription = stringResource(R.string.channel_notes),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ChannelType.DirectMessage -> {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AccountCircle,
|
||||
contentDescription = stringResource(R.string.channel_dm),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ChannelType.Group -> {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AccountBox,
|
||||
contentDescription = stringResource(R.string.channel_group),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,20 +5,14 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.R
|
||||
|
||||
@Composable
|
||||
fun DrawerChannel(
|
||||
|
|
@ -36,36 +30,7 @@ fun DrawerChannel(
|
|||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||
) {
|
||||
when (channelType) {
|
||||
ChannelType.TextChannel -> {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
painter = painterResource(R.drawable.ic_pound_24dp),
|
||||
contentDescription = stringResource(R.string.channel_text)
|
||||
)
|
||||
}
|
||||
ChannelType.VoiceChannel -> {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
painter = painterResource(R.drawable.ic_volume_up_24dp),
|
||||
contentDescription = stringResource(R.string.channel_voice)
|
||||
)
|
||||
}
|
||||
ChannelType.SavedMessages -> {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
painter = painterResource(R.drawable.ic_note_24dp),
|
||||
contentDescription = stringResource(R.string.channel_notes)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
imageVector = Icons.Default.List,
|
||||
contentDescription = "Channel"
|
||||
)
|
||||
}
|
||||
}
|
||||
ChannelIcon(channelType = channelType, modifier = Modifier.padding(end = 8.dp))
|
||||
Text(
|
||||
text = name,
|
||||
fontWeight = FontWeight.Medium,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ fun DisconnectedScreen(
|
|||
textAlign = TextAlign.Center
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ fun DisconnectedScreen(
|
|||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||
.padding(vertical = 10.dp, horizontal = 20.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ class SplashScreenViewModel @Inject constructor(
|
|||
val connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
val networkCapabilities = connectivityManager.activeNetwork ?: return false
|
||||
val actNw =
|
||||
connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
|
||||
val network = connectivityManager.activeNetwork ?: return false
|
||||
val capabilities =
|
||||
connectivityManager.getNetworkCapabilities(network) ?: return false
|
||||
|
||||
return when {
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,24 @@ class ChatRouterViewModel : ViewModel() {
|
|||
_currentServer.value = serverId
|
||||
}
|
||||
|
||||
fun goToHome() {
|
||||
_currentServer.value = "home"
|
||||
fun navigateToServer(serverId: String, navController: NavController) {
|
||||
setCurrentServer(serverId)
|
||||
|
||||
if (serverId == "home") {
|
||||
navController.navigate("home") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val channelId = RevoltAPI.serverCache[serverId]?.channels?.firstOrNull()
|
||||
navController.navigate("channel/$channelId") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +101,9 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
.background(MaterialTheme.colorScheme.surface)
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { viewModel.goToHome() },
|
||||
onClick = {
|
||||
viewModel.navigateToServer("home", navController)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.size(48.dp)
|
||||
|
|
@ -107,7 +125,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
.padding(8.dp)
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable { viewModel.setCurrentServer(server.id!!) },
|
||||
.clickable {
|
||||
viewModel.navigateToServer(
|
||||
server.id!!,
|
||||
navController
|
||||
)
|
||||
},
|
||||
description = "${server.name}"
|
||||
)
|
||||
} else {
|
||||
|
|
@ -119,7 +142,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
.clickable { viewModel.setCurrentServer(server.id!!) }
|
||||
.clickable {
|
||||
viewModel.navigateToServer(
|
||||
server.id!!,
|
||||
navController
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = server.name.first().toString(),
|
||||
|
|
@ -143,11 +171,11 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.DirectMessage }
|
||||
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.Group }
|
||||
.forEach { channel ->
|
||||
DrawerChannel(
|
||||
name = "DM #${channel.id}", // TODO get user or group name
|
||||
channelType = ChannelType.DirectMessage,
|
||||
name = channel.name ?: "GDM #${channel.id}",
|
||||
channelType = ChannelType.Group,
|
||||
selected = channel.id == (navBackStackEntry?.arguments?.getString(
|
||||
"channelId"
|
||||
) ?: false),
|
||||
|
|
@ -185,7 +213,11 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
) == ch.id,
|
||||
onClick = {
|
||||
scope.launch { channelDrawerState.close() }
|
||||
navController.navigate("channel/${ch.id}")
|
||||
navController.navigate("channel/${ch.id}") {
|
||||
popUpTo("home") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,29 @@
|
|||
package chat.revolt.screens.chat.views
|
||||
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
|
@ -33,6 +44,9 @@ import chat.revolt.RevoltTweenFloat
|
|||
import chat.revolt.RevoltTweenInt
|
||||
import chat.revolt.api.routes.channel.fetchMessagesFromChannel
|
||||
import chat.revolt.components.chat.MessageField
|
||||
import chat.revolt.components.generic.CollapsibleCard
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.chat.ChannelIcon
|
||||
|
||||
class ChannelScreenViewModel : ViewModel() {
|
||||
private var _channel by mutableStateOf<Channel?>(null)
|
||||
|
|
@ -109,6 +123,8 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
return
|
||||
}
|
||||
|
||||
_renderableMessages.clear()
|
||||
|
||||
viewModelScope.launch {
|
||||
fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let {
|
||||
it.messages!!.reversed().forEach { message ->
|
||||
|
|
@ -122,6 +138,29 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun fetchOlderMessages() {
|
||||
if (channel == null) {
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
fetchMessagesFromChannel(
|
||||
channel!!.id!!,
|
||||
limit = 20,
|
||||
true,
|
||||
before = renderableMessages.first().id
|
||||
).let {
|
||||
it.messages!!.forEach { message ->
|
||||
addUserIfUnknown(message.author!!)
|
||||
if (!RevoltAPI.messageCache.containsKey(message.id)) {
|
||||
RevoltAPI.messageCache[message.id!!] = message
|
||||
}
|
||||
_renderableMessages.add(0, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchChannel(id: String) {
|
||||
if (id in RevoltAPI.channelCache) {
|
||||
_channel = RevoltAPI.channelCache[id]
|
||||
|
|
@ -158,6 +197,79 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChannelInfoScreen(
|
||||
channel: Channel,
|
||||
viewModel: ChannelScreenViewModel,
|
||||
onClosed: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val clipboardManager: ClipboardManager =
|
||||
LocalClipboardManager.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ChannelIcon(
|
||||
channelType = channel.channelType!!,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
PageHeader(text = channel.name ?: channel.id!!)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
CollapsibleCard(title = "Advanced") {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text("Channel ID: ${channel.id}")
|
||||
|
||||
Button(onClick = {
|
||||
clipboardManager.setText(AnnotatedString(channel.id!!))
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Copied",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}) {
|
||||
Text("Copy ID")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
viewModel.fetchMessages()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text("Refetch messages")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onClosed,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun ChannelScreen(
|
||||
navController: NavController,
|
||||
|
|
@ -166,6 +278,8 @@ fun ChannelScreen(
|
|||
) {
|
||||
val channel = viewModel.channel
|
||||
val scrollState = rememberScrollState()
|
||||
val channelInfoOpen = remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(channelId) {
|
||||
viewModel.fetchChannel(channelId)
|
||||
|
|
@ -184,20 +298,61 @@ fun ChannelScreen(
|
|||
return
|
||||
}
|
||||
|
||||
if (channelInfoOpen.value) {
|
||||
Dialog(
|
||||
onDismissRequest = {
|
||||
channelInfoOpen.value = false
|
||||
},
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false,
|
||||
)
|
||||
) {
|
||||
ChannelInfoScreen(channel, viewModel) {
|
||||
channelInfoOpen.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
coroutineScope.launch {
|
||||
channelInfoOpen.value = true
|
||||
}
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.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,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
||||
LazyColumn(Modifier.weight(1f)) {
|
||||
item {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
viewModel.fetchOlderMessages()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text("Load older")
|
||||
}
|
||||
}
|
||||
items(viewModel.renderableMessages) { message ->
|
||||
Message(message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,8 @@ package chat.revolt.screens.chat.views
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.navigation.NavController
|
||||
|
|
@ -19,6 +13,7 @@ import chat.revolt.components.screens.home.LinkOnHome
|
|||
import chat.revolt.persistence.KVStorage
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -36,19 +31,8 @@ class HomeScreenViewModel @Inject constructor(
|
|||
|
||||
@Composable
|
||||
fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hiltViewModel()) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(id = R.string.home),
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Left,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Column {
|
||||
PageHeader(text = stringResource(id = R.string.home))
|
||||
LinkOnHome(
|
||||
heading = stringResource(id = R.string.logout),
|
||||
icon = Icons.Default.Close,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.ViewCompat
|
||||
|
||||
const val FORCE_ANDROID_DEFAULTS = false
|
||||
|
||||
const val FOREGROUND = 0xffffffff
|
||||
|
||||
val DarkColorScheme = darkColorScheme(
|
||||
|
|
@ -60,9 +62,15 @@ fun RevoltTheme(
|
|||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = RevoltTypography,
|
||||
content = content
|
||||
)
|
||||
if (FORCE_ANDROID_DEFAULTS) {
|
||||
MaterialTheme(
|
||||
content = content
|
||||
)
|
||||
} else {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = RevoltTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue