Implement action messages in messages history

This commit is contained in:
2024-07-16 04:52:47 +03:00
committed by GitHub
parent fa5f707e52
commit 9df35bf6bf
16 changed files with 709 additions and 95 deletions
@@ -4,10 +4,12 @@ import android.content.res.Resources
interface ResourceProvider {
val resources: Resources
fun getString(resId: Int): String
}
class ResourceProviderImpl(private val resources: Resources) : ResourceProvider {
class ResourceProviderImpl(override val resources: Resources) : ResourceProvider {
override fun getString(resId: Int): String {
return resources.getString(resId)
@@ -180,6 +180,13 @@ object AppSettings {
)
set(value) = put(SettingsKeys.KEY_SHOW_EMOJI_BUTTON, value)
var showTimeInActionMessages: Boolean
get() = get(
SettingsKeys.KEY_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES
)
set(value) = put(SettingsKeys.KEY_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES, value)
var showDebugCategory: Boolean
get() = get(
SettingsKeys.KEY_SHOW_DEBUG_CATEGORY,
@@ -29,6 +29,9 @@ object SettingsKeys {
const val DEFAULT_APPEARANCE_LANGUAGE = ""
const val KEY_APPEARANCE_USE_BLUR = "appearance_use_blur"
const val DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR = false
const val KEY_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES =
"appearance_show_time_in_action_messages"
const val DEFAULT_VALUE_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES = false
const val KEY_FEATURES_FAST_TEXT = "features_fast_text"
const val DEFAULT_VALUE_FEATURES_FAST_TEXT = "¯\\_(ツ)_/¯"
@@ -23,6 +23,7 @@ interface UserSettings {
val longPollInBackground: StateFlow<Boolean>
val useBlur: StateFlow<Boolean>
val showEmojiButton: StateFlow<Boolean>
val showTimeInActionMessages: StateFlow<Boolean>
val showDebugCategory: StateFlow<Boolean>
fun onUseContactNamesChanged(use: Boolean)
@@ -42,6 +43,7 @@ interface UserSettings {
fun onLongPollInBackgroundChanged(inBackground: Boolean)
fun onUseBlurChanged(use: Boolean)
fun onShowEmojiButtonChanged(show: Boolean)
fun onShowTimeInActionMessagesChanged(show: Boolean)
fun onShowDebugCategoryChanged(show: Boolean)
}
@@ -64,6 +66,8 @@ class UserSettingsImpl : UserSettings {
override val longPollInBackground = MutableStateFlow(AppSettings.Debug.longPollInBackground)
override val useBlur = MutableStateFlow(AppSettings.Debug.useBlur)
override val showEmojiButton = MutableStateFlow(AppSettings.Debug.showEmojiButton)
override val showTimeInActionMessages =
MutableStateFlow(AppSettings.Debug.showTimeInActionMessages)
override val showDebugCategory = MutableStateFlow(AppSettings.Debug.showDebugCategory)
override fun onUseContactNamesChanged(use: Boolean) {
@@ -118,6 +122,10 @@ class UserSettingsImpl : UserSettings {
showEmojiButton.value = show
}
override fun onShowTimeInActionMessagesChanged(show: Boolean) {
showTimeInActionMessages.value = show
}
override fun onShowDebugCategoryChanged(show: Boolean) {
showDebugCategory.value = show
}
@@ -1,7 +1,6 @@
package com.meloda.app.fast.messageshistory
import android.content.SharedPreferences
import android.content.res.Resources
import android.util.Log
import androidx.core.content.edit
import androidx.lifecycle.SavedStateHandle
@@ -14,14 +13,18 @@ import com.meloda.app.fast.common.UserConfig
import com.meloda.app.fast.common.extensions.listenValue
import com.meloda.app.fast.common.extensions.setValue
import com.meloda.app.fast.common.extensions.updateValue
import com.meloda.app.fast.common.provider.ResourceProvider
import com.meloda.app.fast.data.LongPollUpdatesParser
import com.meloda.app.fast.data.VkMemoryCache
import com.meloda.app.fast.data.api.conversations.ConversationsUseCase
import com.meloda.app.fast.data.api.messages.MessagesUseCase
import com.meloda.app.fast.data.processState
import com.meloda.app.fast.datastore.AppSettings
import com.meloda.app.fast.datastore.SettingsKeys
import com.meloda.app.fast.datastore.UserSettings
import com.meloda.app.fast.messageshistory.model.ActionMode
import com.meloda.app.fast.messageshistory.model.MessagesHistoryScreenState
import com.meloda.app.fast.messageshistory.model.UiItem
import com.meloda.app.fast.messageshistory.navigation.MessagesHistory
import com.meloda.app.fast.messageshistory.util.asPresentation
import com.meloda.app.fast.messageshistory.util.extractAvatar
@@ -62,7 +65,8 @@ class MessagesHistoryViewModelImpl(
private val messagesUseCase: MessagesUseCase,
private val conversationsUseCase: ConversationsUseCase,
private val preferences: SharedPreferences,
private val resources: Resources,
private val resourceProvider: ResourceProvider,
private val userSettings: UserSettings,
updatesParser: LongPollUpdatesParser,
savedStateHandle: SavedStateHandle
) : MessagesHistoryViewModel, ViewModel() {
@@ -92,6 +96,8 @@ class MessagesHistoryViewModelImpl(
updatesParser.onMessageEdited(::handleEditedMessage)
updatesParser.onMessageIncomingRead(::handleReadIncomingEvent)
updatesParser.onMessageOutgoingRead(::handleReadOutgoingEvent)
userSettings.showTimeInActionMessages.listenValue(::toggleShowTimeInActionMessages)
}
override fun onRefresh() {
@@ -169,19 +175,23 @@ class MessagesHistoryViewModelImpl(
}
val newMessage = message.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = prevMessage,
nextMessage = null
nextMessage = null,
showTimeInActionMessages = userSettings.showTimeInActionMessages.value
)
newMessages.add(0, newMessage)
prevMessage?.let { prev ->
newMessages[1] = prev.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = prevMessage,
nextMessage = messages.value.first()
nextMessage = messages.value.first(),
showTimeInActionMessages = userSettings.showTimeInActionMessages.value
)
}
@@ -196,10 +206,12 @@ class MessagesHistoryViewModelImpl(
.indexOfFirstOrNull { it.id == message.id }
?.let { index ->
val newMessage = message.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = messages.value.getOrNull(index + 1),
nextMessage = messages.value.getOrNull(index - 1)
nextMessage = messages.value.getOrNull(index - 1),
showTimeInActionMessages = userSettings.showTimeInActionMessages.value
)
val newMessages = screenState.value.messages.toMutableList()
@@ -248,10 +260,12 @@ class MessagesHistoryViewModelImpl(
val loadedMessages = fullMessages.mapIndexed { index, message ->
message.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = messages.getOrNull(index + 1),
nextMessage = messages.getOrNull(index - 1),
showTimeInActionMessages = userSettings.showTimeInActionMessages.value
)
}
@@ -268,11 +282,8 @@ class MessagesHistoryViewModelImpl(
?.let { conversation ->
newState = newState.copy(
title = conversation.extractTitle(
useContactName = preferences.getBoolean(
SettingsKeys.KEY_USE_CONTACT_NAMES,
SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES
),
resources = resources
useContactName = AppSettings.General.useContactNames,
resources = resourceProvider.resources
),
avatar = conversation.extractAvatar()
)
@@ -337,10 +348,12 @@ class MessagesHistoryViewModelImpl(
val newMessages = screenState.value.messages.toMutableList()
val newUiMessage = newMessage.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = messages.value.firstOrNull(),
nextMessage = null
nextMessage = null,
showTimeInActionMessages = userSettings.showTimeInActionMessages.value
)
newMessages.add(0, newUiMessage)
@@ -373,7 +386,9 @@ class MessagesHistoryViewModelImpl(
val messages = screenState.value.messages.toMutableList()
messages.indexOfOrNull(newUiMessage)?.let { index ->
messages[index] = messages[index].copy(id = messageId)
(messages[index] as? UiItem.Message)?.let { message ->
messages[index] = message.copy(id = messageId)
}
}
screenState.setValue { old -> old.copy(messages = messages) }
@@ -490,6 +505,24 @@ class MessagesHistoryViewModelImpl(
}
}
private fun toggleShowTimeInActionMessages(show: Boolean) {
val messages = messages.value
val uiMessages = messages.mapIndexed { index, item ->
item.asPresentation(
resourceProvider = resourceProvider,
showDate = false,
showName = false,
prevMessage = messages.getOrNull(index + 1),
nextMessage = messages.getOrNull(index - 1),
showTimeInActionMessages = show
)
}
screenState.setValue { old ->
old.copy(messages = uiMessages)
}
}
companion object {
const val MESSAGES_LOAD_COUNT = 30
}
@@ -10,7 +10,7 @@ data class MessagesHistoryScreenState(
val title: String,
val status: String?,
val avatar: UiImage,
val messages: List<UiMessage>,
val messages: List<UiItem>,
val message: String,
val attachments: List<VkAttachment>,
val isLoading: Boolean,
@@ -0,0 +1,35 @@
package com.meloda.app.fast.messageshistory.model
import androidx.compose.ui.text.AnnotatedString
import com.meloda.app.fast.common.model.UiImage
sealed class UiItem(
open val id: Int,
val cmId: Int
) {
data class Message(
override val id: Int,
val conversationMessageId: Int,
val text: String?,
val isOut: Boolean,
val fromId: Int,
val date: String,
val randomId: Int,
val isInChat: Boolean,
val name: String,
val showDate: Boolean,
val showAvatar: Boolean,
val showName: Boolean,
val avatar: UiImage,
val isEdited: Boolean
) : UiItem(id, conversationMessageId)
data class ActionMessage(
override val id: Int,
val conversationMessageId: Int,
val text: AnnotatedString,
val actionCmId: Int?
) : UiItem(id, conversationMessageId)
}
@@ -1,20 +0,0 @@
package com.meloda.app.fast.messageshistory.model
import com.meloda.app.fast.common.model.UiImage
data class UiMessage(
val id: Int,
val conversationMessageId: Int,
val text: String?,
val isOut: Boolean,
val fromId: Int,
val date: String,
val randomId: Int,
val isInChat: Boolean,
val name: String,
val showDate: Boolean,
val showAvatar: Boolean,
val showName: Boolean,
val avatar: UiImage,
val isEdited: Boolean
)
@@ -0,0 +1,67 @@
package com.meloda.app.fast.messageshistory.presentation
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.meloda.app.fast.messageshistory.model.UiItem
@Composable
fun ActionMessageItem(
item: UiItem.ActionMessage,
modifier: Modifier = Modifier,
onClick: () -> Unit = {}
) {
Text(
text = item.text,
modifier = modifier
.padding(horizontal = 32.dp)
.clip(RoundedCornerShape(12.dp))
.then(
if (item.actionCmId != null) {
Modifier.clickable(onClick = onClick)
}
else Modifier
)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp))
.fillMaxWidth()
.padding(
horizontal = 32.dp,
vertical = 4.dp
),
textAlign = TextAlign.Center
)
}
@Preview
@Composable
fun ActionMessageItemPreview() {
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) {
ActionMessageItem(
item = UiItem.ActionMessage(
id = 0,
text = buildAnnotatedString {
append("You pinned message \"wow hello there\"")
},
actionCmId = null,
conversationMessageId = 2135
)
)
}
}
@@ -25,12 +25,12 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
import coil.imageLoader
import com.meloda.app.fast.messageshistory.model.UiMessage
import com.meloda.app.fast.messageshistory.model.UiItem
@Composable
fun IncomingMessageBubble(
modifier: Modifier = Modifier,
message: UiMessage,
message: UiItem.Message,
) {
val context = LocalContext.current
@@ -78,6 +78,8 @@ import com.meloda.app.fast.messageshistory.MessagesHistoryViewModel
import com.meloda.app.fast.messageshistory.MessagesHistoryViewModelImpl
import com.meloda.app.fast.messageshistory.model.ActionMode
import com.meloda.app.fast.messageshistory.model.MessagesHistoryScreenState
import com.meloda.app.fast.messageshistory.util.firstMessage
import com.meloda.app.fast.messageshistory.util.indexOfMessageByCmId
import com.meloda.app.fast.model.BaseError
import com.meloda.app.fast.ui.theme.LocalThemeConfig
import com.meloda.app.fast.ui.util.ImmutableList
@@ -142,6 +144,8 @@ fun MessagesHistoryScreen(
) {
val view = LocalView.current
val coroutineScope = rememberCoroutineScope()
val preferences: SharedPreferences = koinInject()
val currentTheme = LocalThemeConfig.current
@@ -250,7 +254,7 @@ fun MessagesHistoryScreen(
onChatMaterialsDropdownItemClicked(
screenState.conversationId,
screenState.messages.first().conversationMessageId
screenState.messages.firstMessage().conversationMessageId
)
},
text = {
@@ -313,7 +317,14 @@ fun MessagesHistoryScreen(
immutableMessages = ImmutableList.copyOf(screenState.messages),
isPaginating = screenState.isPaginating,
enableAnimations = animationsEnabled,
messageBarHeight = messageBarHeight
messageBarHeight = messageBarHeight,
onRequestScrollToCmId = { cmId ->
coroutineScope.launch {
listState.animateScrollToItem(
index = screenState.messages.indexOfMessageByCmId(cmId)
)
}
}
)
Column(
@@ -17,7 +17,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.meloda.app.fast.messageshistory.model.UiMessage
import com.meloda.app.fast.messageshistory.model.UiItem
import com.meloda.app.fast.ui.theme.LocalThemeConfig
import com.meloda.app.fast.ui.util.ImmutableList
import dev.chrisbanes.haze.HazeState
@@ -31,10 +31,11 @@ fun MessagesList(
modifier: Modifier = Modifier,
hazeState: HazeState,
listState: LazyListState,
immutableMessages: ImmutableList<UiMessage>,
immutableMessages: ImmutableList<UiItem>,
isPaginating: Boolean,
enableAnimations: Boolean,
messageBarHeight: Dp
messageBarHeight: Dp,
onRequestScrollToCmId: (cmId: Int) -> Unit = {}
) {
val messages = immutableMessages.toList()
val currentTheme = LocalThemeConfig.current
@@ -65,32 +66,53 @@ fun MessagesList(
items(
items = messages,
key = UiMessage::id,
) { message ->
if (message.isOut) {
OutgoingMessageBubble(
modifier =
Modifier.then(
if (enableAnimations) Modifier.animateItem(
fadeInSpec = null,
fadeOutSpec = null
key = UiItem::id,
contentType = { item ->
when (item) {
is UiItem.ActionMessage -> "action_message"
is UiItem.Message -> "message"
}
}
) { item ->
when (item) {
is UiItem.ActionMessage -> {
ActionMessageItem(
item = item,
onClick = {
if (item.actionCmId != null) {
onRequestScrollToCmId(item.actionCmId)
}
}
)
}
is UiItem.Message -> {
if (item.isOut) {
OutgoingMessageBubble(
modifier =
Modifier.then(
if (enableAnimations) Modifier.animateItem(
fadeInSpec = null,
fadeOutSpec = null
)
else Modifier
),
message = item,
)
else Modifier
),
message = message,
)
} else {
IncomingMessageBubble(
modifier =
Modifier.then(
if (enableAnimations) Modifier.animateItem(
fadeInSpec = null,
fadeOutSpec = null
} else {
IncomingMessageBubble(
modifier =
Modifier.then(
if (enableAnimations) Modifier.animateItem(
fadeInSpec = null,
fadeOutSpec = null
)
else Modifier
),
message = item,
)
else Modifier
),
message = message,
)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
@@ -14,12 +14,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.meloda.app.fast.common.extensions.orDots
import com.meloda.app.fast.messageshistory.model.UiMessage
import com.meloda.app.fast.messageshistory.model.UiItem
@Composable
fun OutgoingMessageBubble(
modifier: Modifier = Modifier,
message: UiMessage,
message: UiItem.Message,
) {
Row(
modifier = modifier.fillMaxWidth(),
@@ -0,0 +1,17 @@
package com.meloda.app.fast.messageshistory.util
import com.meloda.app.fast.messageshistory.model.UiItem
fun List<UiItem>.firstMessage(): UiItem.Message = first() as UiItem.Message
fun List<UiItem>.indexOfMessageById(messageId: Int): Int =
indexOfFirst { it.id == messageId }
fun List<UiItem>.findMessageById(messageId: Int): UiItem.Message =
first { it.id == messageId } as UiItem.Message
fun List<UiItem>.indexOfMessageByCmId(cmId: Int): Int =
indexOfFirst { it.cmId == cmId }
fun List<UiItem>.findMessageByCmId(cmId: Int): UiItem.Message =
first { it.cmId == cmId } as UiItem.Message
@@ -1,17 +1,22 @@
package com.meloda.app.fast.messageshistory.util
import android.content.res.Resources
import com.meloda.app.fast.common.model.UiImage
import com.meloda.app.fast.common.model.UiText
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import com.meloda.app.fast.common.UserConfig
import com.meloda.app.fast.common.extensions.orDots
import com.meloda.app.fast.common.model.UiImage
import com.meloda.app.fast.common.model.UiText
import com.meloda.app.fast.common.model.parseString
import com.meloda.app.fast.common.provider.ResourceProvider
import com.meloda.app.fast.data.VkMemoryCache
import com.meloda.app.fast.ui.R
import com.meloda.app.fast.messageshistory.model.UiMessage
import com.meloda.app.fast.messageshistory.model.UiItem
import com.meloda.app.fast.model.api.PeerType
import com.meloda.app.fast.model.api.domain.VkConversation
import com.meloda.app.fast.model.api.domain.VkMessage
import com.meloda.app.fast.ui.R
import java.text.SimpleDateFormat
import java.util.Locale
import com.meloda.app.fast.ui.R as UiR
@@ -85,26 +90,42 @@ fun VkConversation.extractTitle(
}.parseString(resources).orDots()
fun VkMessage.asPresentation(
resourceProvider: ResourceProvider,
showDate: Boolean,
showName: Boolean,
prevMessage: VkMessage?,
nextMessage: VkMessage?
): UiMessage = UiMessage(
id = id,
conversationMessageId = conversationMessageId,
text = text,
isOut = isOut,
fromId = fromId,
date = extractDate(),
randomId = randomId,
isInChat = isPeerChat(),
name = extractTitle(),
showDate = showDate,
showAvatar = extractShowAvatar(nextMessage),
showName = showName && extractShowName(prevMessage),
avatar = extractAvatar(),
isEdited = updateTime != null
)
nextMessage: VkMessage?,
showTimeInActionMessages: Boolean
): UiItem = when {
action != null -> UiItem.ActionMessage(
id = id,
conversationMessageId = conversationMessageId,
text = extractActionText(
resources = resourceProvider.resources,
youPrefix = resourceProvider.getString(R.string.you_message_prefix),
showTime = showTimeInActionMessages
) ?: buildAnnotatedString { },
actionCmId = actionConversationMessageId
)
else -> UiItem.Message(
id = id,
conversationMessageId = conversationMessageId,
text = text,
isOut = isOut,
fromId = fromId,
date = extractDate(),
randomId = randomId,
isInChat = isPeerChat(),
name = extractTitle(),
showDate = showDate,
showAvatar = extractShowAvatar(nextMessage),
showName = showName && extractShowName(prevMessage),
avatar = extractAvatar(),
isEdited = updateTime != null
)
}
fun VkMessage.extractShowAvatar(nextMessage: VkMessage?): Boolean {
if (isOut) return false
@@ -115,3 +136,399 @@ fun VkMessage.extractShowName(prevMessage: VkMessage?): Boolean {
if (isOut || !isPeerChat()) return false
return prevMessage == null || prevMessage.fromId != fromId
}
fun VkMessage.extractActionText(
resources: Resources,
youPrefix: String,
showTime: Boolean
): AnnotatedString? {
val lastMessage = this
val action = lastMessage.action ?: return null
val formattedMessageDate =
SimpleDateFormat("HH:mm", Locale.getDefault()).format(lastMessage.date * 1000L)
val fromId = lastMessage.fromId
val text = lastMessage.actionText.orDots()
val groupName = lastMessage.group?.name.orDots()
val userName = lastMessage.user?.fullName.orDots()
val actionGroupName = lastMessage.actionGroup?.name.orDots()
val actionUserName = lastMessage.actionUser?.fullName.orDots()
val memberId = lastMessage.actionMemberId
val isMemberUser = (memberId ?: 0) > 0
val isMemberGroup = (memberId ?: 0) < 0
val prefix = when {
lastMessage.fromId == UserConfig.userId -> youPrefix
lastMessage.isGroup() -> groupName
lastMessage.isUser() -> userName
else -> null
}.orDots()
val memberPrefix = when {
memberId == UserConfig.userId -> youPrefix
isMemberUser -> actionUserName
isMemberGroup -> actionGroupName
else -> null
}.orDots()
return buildAnnotatedString {
when (action) {
VkMessage.Action.CHAT_CREATE -> {
val string = UiText.ResourceParams(
UiR.string.message_action_chat_created,
listOf(prefix, text)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
val textStartIndex = string.indexOf(text)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = textStartIndex,
end = textStartIndex + text.length
)
}
VkMessage.Action.CHAT_TITLE_UPDATE -> {
val string = UiText.ResourceParams(
UiR.string.message_action_chat_renamed,
listOf(prefix, text)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
val textStartIndex = string.indexOf(text)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = textStartIndex,
end = textStartIndex + text.length
)
}
VkMessage.Action.CHAT_PHOTO_UPDATE -> {
UiText.ResourceParams(
UiR.string.message_action_chat_photo_update,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_PHOTO_REMOVE -> {
UiText.ResourceParams(
UiR.string.message_action_chat_photo_remove,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_KICK_USER -> {
if (memberId == fromId) {
UiText.ResourceParams(
UiR.string.message_action_chat_user_left,
listOf(memberPrefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = memberPrefix.length
)
} else {
val postfix =
if (memberId == UserConfig.userId) youPrefix.lowercase()
else lastMessage.actionUser.toString()
val string = UiText.ResourceParams(
UiR.string.message_action_chat_user_kicked,
listOf(prefix, postfix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
val postfixStartIndex = string.indexOf(postfix)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = postfixStartIndex,
end = postfixStartIndex + postfix.length
)
}
}
VkMessage.Action.CHAT_INVITE_USER -> {
if (memberId == lastMessage.fromId) {
UiText.ResourceParams(
UiR.string.message_action_chat_user_returned,
listOf(memberPrefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = memberPrefix.length
)
} else {
val postfix =
if (memberId == UserConfig.userId) youPrefix.lowercase()
else lastMessage.actionUser.toString()
val string = UiText.ResourceParams(
UiR.string.message_action_chat_user_invited,
listOf(memberPrefix, postfix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
val postfixStartIndex = string.indexOf(postfix)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = postfixStartIndex,
end = postfixStartIndex + postfix.length
)
}
}
VkMessage.Action.CHAT_INVITE_USER_BY_LINK -> {
UiText.ResourceParams(
UiR.string.message_action_chat_user_joined_by_link,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_INVITE_USER_BY_CALL -> {
UiText.ResourceParams(
UiR.string.message_action_chat_user_joined_by_call,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_INVITE_USER_BY_CALL_LINK -> {
UiText.ResourceParams(
UiR.string.message_action_chat_user_joined_by_call_link,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_PIN_MESSAGE -> {
// TODO: 16/07/2024, Danil Nikolaev: get pinned message by cmid
// val messageText = lastMessage.text.orEmpty().trim()
// val croppedMessage = messageText.take(40)
// val hasMessageText = messageText.isNotEmpty()
UiText.ResourceParams(
UiR.string.message_action_chat_pin_message,
listOf(prefix)
).parseString(resources)
.orEmpty()
// .let { text ->
// if (hasMessageText) {
// text.plus("«%s»".format(croppedMessage))
// .plus(if (messageText.length > 40) "..." else "")
// } else text
// }
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
// if (hasMessageText) {
// val croppedIndex = fullText.indexOf(croppedMessage)
//
// addStyle(
// style = SpanStyle(fontWeight = FontWeight.Medium),
// start = croppedIndex - 1,
// end = croppedIndex - 1 + croppedMessage.length + 1
// )
// }
}
VkMessage.Action.CHAT_UNPIN_MESSAGE -> {
UiText.ResourceParams(
UiR.string.message_action_chat_unpin_message,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_SCREENSHOT -> {
UiText.ResourceParams(
UiR.string.message_action_chat_screenshot,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
VkMessage.Action.CHAT_STYLE_UPDATE -> {
UiText.ResourceParams(
UiR.string.message_action_chat_style_update,
listOf(prefix)
).parseString(resources)
.orEmpty()
.let { text ->
if (showTime) {
text.plus("\n")
.plus(formattedMessageDate)
} else text
}.also(::append)
addStyle(
style = SpanStyle(fontWeight = FontWeight.Medium),
start = 0,
end = prefix.length
)
}
}
}
}
@@ -230,6 +230,12 @@ class SettingsViewModelImpl(
userSettings.onShowEmojiButtonChanged(show)
}
SettingsKeys.KEY_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES -> {
val show = newValue as? Boolean
?: SettingsKeys.DEFAULT_VALUE_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES
userSettings.onShowTimeInActionMessagesChanged(show)
}
SettingsKeys.KEY_SHOW_DEBUG_CATEGORY -> {
val show = newValue as? Boolean ?: false
userSettings.onShowDebugCategoryChanged(show)
@@ -349,12 +355,6 @@ class SettingsViewModelImpl(
)
}
}
val debugLongPollBackground = SettingsItem.Switch(
key = SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
defaultValue = SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND,
title = UiText.Resource(UiR.string.settings_features_long_poll_in_background_title),
text = UiText.Resource(UiR.string.settings_features_long_poll_in_background_summary)
)
val activityTitle = SettingsItem.Title(
key = "activity",
@@ -382,6 +382,12 @@ class SettingsViewModelImpl(
title = UiText.Simple("Show alert after crash"),
text = UiText.Simple("Shows alert dialog with stacktrace after app crashed\n(it will be not shown if you perform crash manually)")
)
val debugLongPollBackground = SettingsItem.Switch(
key = SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
defaultValue = SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND,
title = UiText.Resource(UiR.string.settings_features_long_poll_in_background_title),
text = UiText.Resource(UiR.string.settings_features_long_poll_in_background_summary)
)
val debugUseBlur = SettingsItem.Switch(
key = SettingsKeys.KEY_APPEARANCE_USE_BLUR,
defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR,
@@ -394,6 +400,11 @@ class SettingsViewModelImpl(
text = UiText.Simple("Show emoji button in chat panel"),
defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON
)
val debugShowTimeInActionMessages = SettingsItem.Switch(
key = SettingsKeys.KEY_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES,
defaultValue = SettingsKeys.DEFAULT_VALUE_APPEARANCE_SHOW_TIME_IN_ACTION_MESSAGES,
title = UiText.Simple("Show time in action messages")
)
val debugHideDebugList = SettingsItem.TitleText(
key = SettingsKeys.KEY_DEBUG_HIDE_DEBUG_LIST,
@@ -432,7 +443,8 @@ class SettingsViewModelImpl(
debugShowCrashAlert,
debugLongPollBackground,
debugUseBlur,
debugShowEmojiButton
debugShowEmojiButton,
debugShowTimeInActionMessages
).forEach(debugList::add)
debugList += debugHideDebugList