feat: channel settings and attachment rendering

- also general design improvements
This commit is contained in:
Infi 2023-01-04 03:39:27 +01:00
parent d93b9f1bcb
commit 4a5365c3c1
16 changed files with 403 additions and 97 deletions

View File

@ -61,6 +61,7 @@ dependencies {
// Jetpack Compose // Jetpack Compose
implementation "androidx.compose.ui:ui:$compose_libraries_version" 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.material3:material3:1.0.1'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_libraries_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_libraries_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'

View File

@ -156,6 +156,16 @@ object RealtimeSocket {
RevoltAPI.userCache[userUpdateFrame.id] = RevoltAPI.userCache[userUpdateFrame.id] =
existing.mergeWithPartial(userUpdateFrame.data) 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 -> { else -> {
Log.i("RealtimeSocket", "Unknown frame: $rawFrame") Log.i("RealtimeSocket", "Unknown frame: $rawFrame")
} }

View File

@ -168,12 +168,6 @@ data class ServerDeleteFrame(
val id: String val id: String
) )
@Serializable
data class ServerUserChoice(
val server: String,
val user: String,
)
@Serializable @Serializable
data class ServerMemberUpdateFrame( data class ServerMemberUpdateFrame(
val type: String = "ServerMemberUpdate", val type: String = "ServerMemberUpdate",

View File

@ -12,10 +12,16 @@ data class MessagesInChannel(
val members: List<Member>? = null val members: List<Member>? = null
) )
@Serializable
data class ServerUserChoice(
val server: String,
val user: String,
)
@Serializable @Serializable
data class Member( data class Member(
@SerialName("_id") @SerialName("_id")
val id: String? = null, val id: ServerUserChoice? = null,
@SerialName("joined_at") @SerialName("joined_at")
val joinedAt: String? = null, val joinedAt: String? = null,

View File

@ -1,28 +1,44 @@
package chat.revolt.components.chat package chat.revolt.components.chat
import androidx.compose.foundation.layout.Column import android.net.Uri
import androidx.compose.foundation.layout.Row import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.background
import androidx.compose.foundation.layout.size import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
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.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import chat.revolt.api.REVOLT_BASE import chat.revolt.api.REVOLT_BASE
import chat.revolt.api.REVOLT_FILES import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.schemas.AutumnResource
import chat.revolt.components.generic.RemoteImage import chat.revolt.components.generic.RemoteImage
import chat.revolt.api.schemas.Message as MessageSchema 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 @Composable
fun Message( fun Message(
message: MessageSchema message: MessageSchema
) { ) {
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator() val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
val context = LocalContext.current
Row(modifier = Modifier.padding(8.dp)) { Row(modifier = Modifier.padding(8.dp)) {
if (author.avatar != null) { if (author.avatar != null) {
@ -55,6 +71,39 @@ fun Message(
text = it 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)
)
}
}
}
}
} }
} }
} }

View File

@ -28,9 +28,9 @@ fun CollapsibleCard(
Column { Column {
Row( Row(
modifier = Modifier modifier = Modifier
.clickable { expanded = !expanded }
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp),
.clickable { expanded = !expanded },
) { ) {
Text( Text(
text = title, text = title,

View File

@ -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")
}

View File

@ -19,15 +19,26 @@ fun RemoteImage(
url: String, url: String,
description: String, description: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Crop contentScale: ContentScale = ContentScale.Crop,
width: Int = 0,
height: Int = 0,
) { ) {
val context = LocalContext.current val context = LocalContext.current
AsyncImage( fun imageRequest() = run {
model = ImageRequest.Builder(context) val builder = ImageRequest.Builder(context)
.data(url)
.crossfade(true) .crossfade(true)
.build(), .data(url)
if (width != 0 && height != 0) {
builder.size(width, height)
}
builder.build()
}
AsyncImage(
model = imageRequest(),
imageLoader = ImageLoader.Builder(context) imageLoader = ImageLoader.Builder(context)
.components { .components {
if (Build.VERSION.SDK_INT >= 28) { if (Build.VERSION.SDK_INT >= 28) {
@ -45,7 +56,7 @@ fun RemoteImage(
.build(), .build(),
contentDescription = description, contentDescription = description,
contentScale = contentScale, contentScale = contentScale,
modifier = modifier modifier = modifier,
) )
} }

View File

@ -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,
)
}
}
}

View File

@ -5,20 +5,14 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding 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.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.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelType
import chat.revolt.R
@Composable @Composable
fun DrawerChannel( fun DrawerChannel(
@ -36,36 +30,7 @@ fun DrawerChannel(
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(vertical = 8.dp, horizontal = 16.dp) .padding(vertical = 8.dp, horizontal = 16.dp)
) { ) {
when (channelType) { ChannelIcon(channelType = channelType, modifier = Modifier.padding(end = 8.dp))
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"
)
}
}
Text( Text(
text = name, text = name,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,

View File

@ -34,7 +34,6 @@ fun DisconnectedScreen(
textAlign = TextAlign.Center textAlign = TextAlign.Center
), ),
modifier = Modifier modifier = Modifier
.padding(horizontal = 20.dp, vertical = 10.dp)
.fillMaxWidth(), .fillMaxWidth(),
) )
@ -46,7 +45,7 @@ fun DisconnectedScreen(
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
), ),
modifier = Modifier modifier = Modifier
.padding(horizontal = 20.dp, vertical = 10.dp) .padding(vertical = 10.dp, horizontal = 20.dp)
.fillMaxWidth() .fillMaxWidth()
) )

View File

@ -55,14 +55,14 @@ class SplashScreenViewModel @Inject constructor(
val connectivityManager = val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkCapabilities = connectivityManager.activeNetwork ?: return false val network = connectivityManager.activeNetwork ?: return false
val actNw = val capabilities =
connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false connectivityManager.getNetworkCapabilities(network) ?: return false
return when { return when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false else -> false
} }
} }

View File

@ -51,8 +51,24 @@ class ChatRouterViewModel : ViewModel() {
_currentServer.value = serverId _currentServer.value = serverId
} }
fun goToHome() { fun navigateToServer(serverId: String, navController: NavController) {
_currentServer.value = "home" 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) .background(MaterialTheme.colorScheme.surface)
) { ) {
IconButton( IconButton(
onClick = { viewModel.goToHome() }, onClick = {
viewModel.navigateToServer("home", navController)
},
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(8.dp)
.size(48.dp) .size(48.dp)
@ -107,7 +125,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
.padding(8.dp) .padding(8.dp)
.size(48.dp) .size(48.dp)
.clip(CircleShape) .clip(CircleShape)
.clickable { viewModel.setCurrentServer(server.id!!) }, .clickable {
viewModel.navigateToServer(
server.id!!,
navController
)
},
description = "${server.name}" description = "${server.name}"
) )
} else { } else {
@ -119,7 +142,12 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
.size(48.dp) .size(48.dp)
.clip(CircleShape) .clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceVariant) .background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { viewModel.setCurrentServer(server.id!!) } .clickable {
viewModel.navigateToServer(
server.id!!,
navController
)
}
) { ) {
Text( Text(
text = server.name.first().toString(), text = server.name.first().toString(),
@ -143,11 +171,11 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
.weight(1f) .weight(1f)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.DirectMessage } RevoltAPI.channelCache.values.filter { it.channelType == ChannelType.Group }
.forEach { channel -> .forEach { channel ->
DrawerChannel( DrawerChannel(
name = "DM #${channel.id}", // TODO get user or group name name = channel.name ?: "GDM #${channel.id}",
channelType = ChannelType.DirectMessage, channelType = ChannelType.Group,
selected = channel.id == (navBackStackEntry?.arguments?.getString( selected = channel.id == (navBackStackEntry?.arguments?.getString(
"channelId" "channelId"
) ?: false), ) ?: false),
@ -185,7 +213,11 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
) == ch.id, ) == ch.id,
onClick = { onClick = {
scope.launch { channelDrawerState.close() } scope.launch { channelDrawerState.close() }
navController.navigate("channel/${ch.id}") navController.navigate("channel/${ch.id}") {
popUpTo("home") {
inclusive = true
}
}
}) })
} }
} }

View File

@ -1,18 +1,29 @@
package chat.revolt.screens.chat.views package chat.revolt.screens.chat.views
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.foundation.background 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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier 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.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.text.style.TextOverflow
import androidx.compose.ui.unit.dp 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.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -33,6 +44,9 @@ import chat.revolt.RevoltTweenFloat
import chat.revolt.RevoltTweenInt import chat.revolt.RevoltTweenInt
import chat.revolt.api.routes.channel.fetchMessagesFromChannel import chat.revolt.api.routes.channel.fetchMessagesFromChannel
import chat.revolt.components.chat.MessageField 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() { class ChannelScreenViewModel : ViewModel() {
private var _channel by mutableStateOf<Channel?>(null) private var _channel by mutableStateOf<Channel?>(null)
@ -109,6 +123,8 @@ class ChannelScreenViewModel : ViewModel() {
return return
} }
_renderableMessages.clear()
viewModelScope.launch { viewModelScope.launch {
fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let { fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let {
it.messages!!.reversed().forEach { message -> 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) { fun fetchChannel(id: String) {
if (id in RevoltAPI.channelCache) { if (id in RevoltAPI.channelCache) {
_channel = RevoltAPI.channelCache[id] _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 @Composable
fun ChannelScreen( fun ChannelScreen(
navController: NavController, navController: NavController,
@ -166,6 +278,8 @@ fun ChannelScreen(
) { ) {
val channel = viewModel.channel val channel = viewModel.channel
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val channelInfoOpen = remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(channelId) { LaunchedEffect(channelId) {
viewModel.fetchChannel(channelId) viewModel.fetchChannel(channelId)
@ -184,20 +298,61 @@ fun ChannelScreen(
return return
} }
if (channelInfoOpen.value) {
Dialog(
onDismissRequest = {
channelInfoOpen.value = false
},
properties = DialogProperties(
usePlatformDefaultWidth = false,
)
) {
ChannelInfoScreen(channel, viewModel) {
channelInfoOpen.value = false
}
}
}
Column { Column {
Row( Row(
modifier = Modifier modifier = Modifier
.clickable {
coroutineScope.launch {
channelInfoOpen.value = true
}
}
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surface)
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) { ) {
ChannelIcon(
channelType = channel.channelType!!,
modifier = Modifier.padding(end = 8.dp)
)
Text( Text(
text = channel.name ?: channel.id!!, text = channel.name ?: channel.id!!,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(16.dp) fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f),
) )
} }
LazyColumn(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 -> items(viewModel.renderableMessages) { message ->
Message(message) Message(message)
} }

View File

@ -3,14 +3,8 @@ package chat.revolt.screens.chat.views
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
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.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.navigation.NavController import androidx.navigation.NavController
@ -19,6 +13,7 @@ import chat.revolt.components.screens.home.LinkOnHome
import chat.revolt.persistence.KVStorage import chat.revolt.persistence.KVStorage
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import chat.revolt.R import chat.revolt.R
import chat.revolt.components.generic.PageHeader
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import javax.inject.Inject import javax.inject.Inject
@ -36,19 +31,8 @@ class HomeScreenViewModel @Inject constructor(
@Composable @Composable
fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hiltViewModel()) { fun HomeScreen(navController: NavController, viewModel: HomeScreenViewModel = hiltViewModel()) {
Column() { Column {
Text( PageHeader(text = stringResource(id = R.string.home))
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(),
)
LinkOnHome( LinkOnHome(
heading = stringResource(id = R.string.logout), heading = stringResource(id = R.string.logout),
icon = Icons.Default.Close, icon = Icons.Default.Close,

View File

@ -15,6 +15,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
const val FORCE_ANDROID_DEFAULTS = false
const val FOREGROUND = 0xffffffff const val FOREGROUND = 0xffffffff
val DarkColorScheme = darkColorScheme( val DarkColorScheme = darkColorScheme(
@ -60,9 +62,15 @@ fun RevoltTheme(
} }
} }
MaterialTheme( if (FORCE_ANDROID_DEFAULTS) {
colorScheme = colorScheme, MaterialTheme(
typography = RevoltTypography, content = content
content = content )
) } else {
MaterialTheme(
colorScheme = colorScheme,
typography = RevoltTypography,
content = content
)
}
} }