feat: message context sheets and message replying
This commit is contained in:
parent
9c26a7ab93
commit
2db44900ae
|
|
@ -24,6 +24,7 @@ const val REVOLT_SUPPORT = "https://support.revolt.chat"
|
|||
const val REVOLT_MARKETING = "https://revolt.chat"
|
||||
const val REVOLT_FILES = "https://autumn.revolt.chat"
|
||||
const val REVOLT_JANUARY = "https://jan.revolt.chat"
|
||||
const val REVOLT_APP = "https://app.revolt.chat"
|
||||
const val REVOLT_WEBSOCKET = "wss://ws.revolt.chat"
|
||||
|
||||
fun asJanuaryProxyUrl(url: String): String {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package chat.revolt.callbacks
|
||||
|
||||
/**
|
||||
* Callbacks for UI events, such as when a user selects "reply" on a message, so that the
|
||||
* channel screen can add a reply to the message, for example.
|
||||
*
|
||||
* We do this by having a singleton object that contains all the receivers, and then
|
||||
* the UI can set the callbacks to whatever it wants.
|
||||
*/
|
||||
object UiCallbacks {
|
||||
interface CallbackReceiver {
|
||||
fun onQueueMessageForReply(messageId: String)
|
||||
}
|
||||
|
||||
var receivers = mutableListOf<CallbackReceiver>()
|
||||
|
||||
fun registerReceiver(receiver: CallbackReceiver) {
|
||||
receivers.add(receiver)
|
||||
}
|
||||
|
||||
fun unregisterReceiver(receiver: CallbackReceiver) {
|
||||
receivers.remove(receiver)
|
||||
}
|
||||
|
||||
fun emitQueueMessageForReply(messageId: String) {
|
||||
receivers.forEach { it.onQueueMessageForReply(messageId) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package chat.revolt.components.chat
|
||||
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -11,14 +10,11 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.unit.sp
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.REVOLT_FILES
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.asJanuaryProxyUrl
|
||||
|
|
@ -53,11 +49,12 @@ fun formatLongAsTime(time: Long): String {
|
|||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Message(
|
||||
message: MessageSchema
|
||||
message: MessageSchema,
|
||||
truncate: Boolean = false,
|
||||
onMessageContextMenu: () -> Unit = {},
|
||||
) {
|
||||
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
|
||||
Column {
|
||||
if (message.tail == false) {
|
||||
|
|
@ -80,17 +77,7 @@ fun Message(
|
|||
.combinedClickable(
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
if (message.content != null && message.content.isNotEmpty()) {
|
||||
clipboardManager.setText(AnnotatedString(message.content))
|
||||
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
context.getString(R.string.copied),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
onMessageContextMenu()
|
||||
}
|
||||
)
|
||||
.padding(horizontal = 10.dp)
|
||||
|
|
@ -145,6 +132,8 @@ fun Message(
|
|||
message.content?.let {
|
||||
Text(
|
||||
text = Renderer.annotateMarkdown(it),
|
||||
maxLines = if (truncate) 1 else Int.MAX_VALUE,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package chat.revolt.components.screens.settings
|
||||
package chat.revolt.components.generic
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -21,7 +21,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SettingsCategory(
|
||||
fun SheetClickable(
|
||||
icon: @Composable (Modifier) -> Unit,
|
||||
label: @Composable (TextStyle) -> Unit,
|
||||
onClick: () -> Unit,
|
||||
|
|
@ -30,7 +30,7 @@ fun SettingsCategory(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(all = 4.dp)
|
||||
.fillMaxWidth()
|
||||
|
|
@ -40,7 +40,7 @@ fun SettingsCategory(
|
|||
icon(Modifier.padding(end = 16.dp))
|
||||
label(
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
)
|
||||
|
|
@ -51,7 +51,7 @@ fun SettingsCategory(
|
|||
@Preview
|
||||
@Composable
|
||||
fun SettingsCategoryPreview() {
|
||||
SettingsCategory(
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package chat.revolt.components.screens.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.routes.channel.SendMessageReply
|
||||
import chat.revolt.components.chat.InReplyTo
|
||||
|
||||
@Composable
|
||||
fun ManageableReply(
|
||||
reply: SendMessageReply,
|
||||
onToggleMention: () -> Unit,
|
||||
onRemove: () -> Unit,
|
||||
) {
|
||||
// TODO Revamp this. Placeholder design ("functional" but extremely ugly)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = "Remove reply",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onRemove()
|
||||
}
|
||||
)
|
||||
InReplyTo(
|
||||
messageId = reply.id,
|
||||
withMention = reply.mention,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
onToggleMention()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReplyManager(
|
||||
replies: List<SendMessageReply>,
|
||||
onToggleMention: (SendMessageReply) -> Unit,
|
||||
onRemove: (SendMessageReply) -> Unit,
|
||||
) {
|
||||
Column {
|
||||
replies.forEach { reply ->
|
||||
ManageableReply(
|
||||
reply = reply,
|
||||
onToggleMention = { onToggleMention(reply) },
|
||||
onRemove = { onRemove(reply) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import chat.revolt.api.schemas.ChannelType
|
|||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
import chat.revolt.components.screens.chat.*
|
||||
import chat.revolt.screens.chat.sheets.ChannelInfoSheet
|
||||
import chat.revolt.screens.chat.sheets.MessageContextSheet
|
||||
import chat.revolt.screens.chat.views.ChannelScreen
|
||||
import chat.revolt.screens.chat.views.HomeScreen
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
|
|
@ -228,6 +229,7 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
bottomSheet("channel/{channelId}/info") { backStackEntry ->
|
||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||
if (channelId != null) {
|
||||
|
|
@ -237,6 +239,15 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
|
|||
)
|
||||
}
|
||||
}
|
||||
bottomSheet("message/{messageId}/menu") { backStackEntry ->
|
||||
val messageId = backStackEntry.arguments?.getString("messageId")
|
||||
if (messageId != null) {
|
||||
MessageContextSheet(
|
||||
navController = navController,
|
||||
messageId = messageId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,276 @@
|
|||
package chat.revolt.screens.chat.sheets
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.REVOLT_APP
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.callbacks.UiCallbacks
|
||||
import chat.revolt.components.chat.Message
|
||||
import chat.revolt.components.generic.SheetClickable
|
||||
import chat.revolt.api.schemas.Message as MessageSchema
|
||||
|
||||
@Composable
|
||||
fun MessageContextSheet(
|
||||
navController: NavController,
|
||||
messageId: String,
|
||||
) {
|
||||
val message = RevoltAPI.messageCache[messageId]
|
||||
if (message == null) {
|
||||
navController.popBackStack()
|
||||
return
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
|
||||
Surface {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
.padding(bottom = 8.dp)
|
||||
) {
|
||||
Message(
|
||||
message = message.mergeWithPartial(MessageSchema(tail = false)),
|
||||
truncate = true
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_reply_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_reply),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
UiCallbacks.emitQueueMessageForReply(messageId)
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_hamburger_plus_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_react),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_copy),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (message.content == null || message.content.isEmpty()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_context_sheet_actions_copy_failed_empty),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
return@SheetClickable
|
||||
}
|
||||
|
||||
clipboardManager.setText(AnnotatedString(message.content))
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.copied),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_link_variant_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_copy_link),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (message.content == null || message.content.isEmpty()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_context_sheet_actions_copy_failed_empty),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
return@SheetClickable
|
||||
}
|
||||
|
||||
val server = RevoltAPI.serverCache.values.find { server ->
|
||||
server.channels?.contains(message.channel) ?: false
|
||||
}
|
||||
val messageLink =
|
||||
"$REVOLT_APP/server/${server?.id}/channel/${message.channel}/${message.id}"
|
||||
|
||||
clipboardManager.setText(AnnotatedString(messageLink))
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_context_sheet_actions_copy_link_copied),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_copy_id),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (message.id == null) return@SheetClickable
|
||||
|
||||
clipboardManager.setText(AnnotatedString(message.id))
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_context_sheet_actions_copy_id_copied),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_edit),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_delete),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_flag_24dp),
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { style ->
|
||||
Text(
|
||||
text = stringResource(id = R.string.message_context_sheet_actions_report),
|
||||
style = style
|
||||
)
|
||||
},
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ import chat.revolt.api.realtime.RealtimeSocket
|
|||
import chat.revolt.api.realtime.frames.receivable.ChannelStartTypingFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ChannelStopTypingFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.MessageFrame
|
||||
import chat.revolt.api.routes.channel.SendMessageReply
|
||||
import chat.revolt.api.routes.channel.fetchMessagesFromChannel
|
||||
import chat.revolt.api.routes.channel.sendMessage
|
||||
import chat.revolt.api.routes.microservices.autumn.FileArgs
|
||||
|
|
@ -43,10 +44,12 @@ import chat.revolt.api.routes.microservices.autumn.MAX_ATTACHMENTS_PER_MESSAGE
|
|||
import chat.revolt.api.routes.microservices.autumn.uploadToAutumn
|
||||
import chat.revolt.api.routes.user.addUserIfUnknown
|
||||
import chat.revolt.api.schemas.Channel
|
||||
import chat.revolt.callbacks.UiCallbacks
|
||||
import chat.revolt.components.chat.Message
|
||||
import chat.revolt.components.chat.MessageField
|
||||
import chat.revolt.components.screens.chat.AttachmentManager
|
||||
import chat.revolt.components.screens.chat.ChannelIcon
|
||||
import chat.revolt.components.screens.chat.ReplyManager
|
||||
import chat.revolt.components.screens.chat.TypingIndicator
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
|
@ -60,9 +63,9 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
val channel: Channel?
|
||||
get() = _channel
|
||||
|
||||
private var _callback = mutableStateOf<RealtimeSocket.ChannelCallback?>(null)
|
||||
private val callback: RealtimeSocket.ChannelCallback?
|
||||
get() = _callback.value
|
||||
private var _channelCallback = mutableStateOf<RealtimeSocket.ChannelCallback?>(null)
|
||||
private val channelCallback: RealtimeSocket.ChannelCallback?
|
||||
get() = _channelCallback.value
|
||||
|
||||
private var _renderableMessages = mutableStateListOf<MessageSchema>()
|
||||
val renderableMessages: List<MessageSchema>
|
||||
|
|
@ -114,6 +117,36 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
_sendingMessage = sending
|
||||
}
|
||||
|
||||
private var _replies = mutableStateListOf<SendMessageReply>()
|
||||
val replies: List<SendMessageReply>
|
||||
get() = _replies
|
||||
|
||||
fun addInReplyTo(reply: SendMessageReply) {
|
||||
_replies.add(reply)
|
||||
}
|
||||
|
||||
fun removeReply(reply: SendMessageReply) {
|
||||
_replies.remove(reply)
|
||||
}
|
||||
|
||||
fun toggleReplyMentionFor(reply: SendMessageReply) {
|
||||
val index = _replies.indexOf(reply)
|
||||
val newReply = SendMessageReply(
|
||||
reply.id,
|
||||
!reply.mention
|
||||
)
|
||||
|
||||
_replies[index] = newReply
|
||||
}
|
||||
|
||||
private fun clearInReplyTo() {
|
||||
_replies.clear()
|
||||
}
|
||||
|
||||
private var _uiCallbackReceiver = mutableStateOf<UiCallbacks.CallbackReceiver?>(null)
|
||||
val uiCallbackReceiver: UiCallbacks.CallbackReceiver?
|
||||
get() = _uiCallbackReceiver.value
|
||||
|
||||
inner class ChannelScreenCallback : RealtimeSocket.ChannelCallback {
|
||||
override fun onMessage(message: MessageFrame) {
|
||||
viewModelScope.launch {
|
||||
|
|
@ -145,9 +178,20 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun registerCallback() {
|
||||
_callback.value = ChannelScreenCallback()
|
||||
RealtimeSocket.registerChannelCallback(channel!!.id!!, callback!!)
|
||||
inner class UiCallbackReceiver : UiCallbacks.CallbackReceiver {
|
||||
override fun onQueueMessageForReply(messageId: String) {
|
||||
viewModelScope.launch {
|
||||
addInReplyTo(SendMessageReply(messageId, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerCallbacks() {
|
||||
_channelCallback.value = ChannelScreenCallback()
|
||||
RealtimeSocket.registerChannelCallback(channel!!.id!!, channelCallback!!)
|
||||
|
||||
_uiCallbackReceiver.value = UiCallbackReceiver()
|
||||
UiCallbacks.registerReceiver(uiCallbackReceiver!!)
|
||||
}
|
||||
|
||||
fun fetchMessages() {
|
||||
|
|
@ -218,7 +262,7 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
Log.e("ChannelScreen", "Channel $id not in cache, for now this is fatal!") // FIXME
|
||||
}
|
||||
|
||||
registerCallback()
|
||||
registerCallbacks()
|
||||
}
|
||||
|
||||
fun sendPendingMessage() {
|
||||
|
|
@ -246,11 +290,13 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
sendMessage(
|
||||
channel!!.id!!,
|
||||
messageContent,
|
||||
attachments = if (attachmentIds.isEmpty()) null else attachmentIds
|
||||
attachments = if (attachmentIds.isEmpty()) null else attachmentIds,
|
||||
replies = replies
|
||||
)
|
||||
|
||||
_messageContent = ""
|
||||
popAttachmentBatch()
|
||||
clearInReplyTo()
|
||||
setSendingMessage(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -340,6 +386,7 @@ fun ChannelScreen(
|
|||
DisposableEffect(channelId) {
|
||||
onDispose {
|
||||
RealtimeSocket.unregisterChannelCallback(channelId)
|
||||
viewModel.uiCallbackReceiver?.let { UiCallbacks.unregisterReceiver(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +458,9 @@ fun ChannelScreen(
|
|||
}
|
||||
|
||||
items(viewModel.renderableMessages) { message ->
|
||||
Message(message)
|
||||
Message(message) {
|
||||
navController.navigate("message/${message.id}/menu")
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
|
|
@ -466,6 +515,14 @@ fun ChannelScreen(
|
|||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = viewModel.replies.isNotEmpty()) {
|
||||
ReplyManager(
|
||||
replies = viewModel.replies,
|
||||
onRemove = viewModel::removeReply,
|
||||
onToggleMention = viewModel::toggleReplyMentionFor
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = viewModel.attachments.isNotEmpty()) {
|
||||
AttachmentManager(
|
||||
attachments = viewModel.attachments,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.SettingsCategory
|
||||
import chat.revolt.components.generic.SheetClickable
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
|
|
@ -37,7 +37,7 @@ fun SettingsScreen(
|
|||
.fillMaxSize()
|
||||
.padding(10.dp)
|
||||
) {
|
||||
SettingsCategory(
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_palette_24dp),
|
||||
|
|
@ -56,7 +56,7 @@ fun SettingsScreen(
|
|||
navController.navigate("settings/appearance")
|
||||
}
|
||||
|
||||
SettingsCategory(
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M4,1A2,2 0,0 0,2 3L2,17L4,17L4,3L16,3L16,1L4,1zM8,5A2,2 0,0 0,6 7L6,21A2,2 0,0 0,8 23L12,23L12,21L8,21L8,7L19,7L19,13.693L21,13.693L21,7A2,2 0,0 0,19 5L8,5z"/>
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="m16.297,15.593v1.176h-0.588v3.527h0.588v1.176h-2.351v-1.176h0.588v-3.527h-0.588v-1.176h2.351m3.527,0C20.477,15.593 21,16.122 21,16.769v3.527c0,0.653 -0.523,1.176 -1.176,1.176h-2.351v-5.878m2.351,1.176h-1.176v3.527h1.176z"
|
||||
android:strokeWidth="0.587848"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M14.4,6L14,4H5V21H7V14H12.6L13,16H20V6H14.4Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M21 9H3C3 9 3 3 12 3S21 9 21 9M13.35 17H3V18C3 19.66 4.34 21 6 21H13.35C13.13 20.37 13 19.7 13 19C13 18.3 13.13 17.63 13.35 17M21.86 13.73C21.95 13.5 22 13.26 22 13C22 11.9 21.11 11 20 11H11L8.5 13L6 11H4C2.9 11 2 11.9 2 13S2.9 15 4 15H14.54C15.64 13.78 17.23 13 19 13C20.04 13 21 13.26 21.86 13.73M20 18V15H18V18H15V20H18V23H20V20H23V18H20Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M10,9V5L3,12L10,19V14.9C15,14.9 18.5,16.5 21,20C20,15 17,10 10,9Z" />
|
||||
</vector>
|
||||
|
|
@ -61,6 +61,7 @@
|
|||
|
||||
<string name="comingsoon_heading">Gah, you found me!</string>
|
||||
<string name="comingsoon_body">The feature you are trying to access is not ready yet, but we are steadily working on polishing it to perfection..</string>
|
||||
<string name="comingsoon_toast">Sorry, this feature is not ready yet.</string>
|
||||
|
||||
<string name="typing_blank"><!-- this is a hack to prevent the typing indicator from showing typing_several when it's animating away --></string>
|
||||
<string name="typing_one">%1$s is typing…</string>
|
||||
|
|
@ -116,6 +117,21 @@
|
|||
<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="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_reply">Reply</string>
|
||||
<string name="message_context_sheet_actions_edit">Edit</string>
|
||||
<string name="message_context_sheet_actions_delete">Delete</string>
|
||||
<string name="message_context_sheet_actions_delete_confirm">Are you sure you want to delete this message?</string>
|
||||
<string name="message_context_sheet_actions_delete_confirm_yes">Yes</string>
|
||||
<string name="message_context_sheet_actions_delete_confirm_no">No</string>
|
||||
<string name="message_context_sheet_actions_copy_id">Copy ID</string>
|
||||
<string name="message_context_sheet_actions_copy_id_copied">Copied message ID to clipboard</string>
|
||||
<string name="message_context_sheet_actions_copy_link">Copy link</string>
|
||||
<string name="message_context_sheet_actions_copy_link_copied">Copied message link to clipboard</string>
|
||||
<string name="message_context_sheet_actions_react">React</string>
|
||||
<string name="message_context_sheet_actions_report">Report</string>
|
||||
|
||||
<string name="settings_appearance">Appearance</string>
|
||||
<string name="settings_appearance_theme">Theme</string>
|
||||
<string name="settings_appearance_theme_none">System</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue