1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-07 21:37:02 +09:00

DROID-2893 Chats | Fix | Chat limit fixes (#2491)

This commit is contained in:
Evgenii Kozlov 2025-06-02 18:43:30 +02:00 committed by GitHub
parent e5f771b836
commit a5b3f03a95
Signed by: github
GPG key ID: B5690EEEBB952194
5 changed files with 77 additions and 4 deletions

View file

@ -23,11 +23,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.membership.Membership.Status
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
@ -36,7 +37,6 @@ import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.ButtonWarningLoading
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_models.membership.MembershipStatus
@Composable
fun Toolbar(
@ -344,7 +344,7 @@ fun Announcement(
}
}
@Preview
@DefaultPreviews
@Composable
fun WarningPreview() {
Warning(
@ -357,7 +357,7 @@ fun WarningPreview() {
)
}
@Preview
@DefaultPreviews
@Composable
fun AnnouncementPreview() {
Announcement(

View file

@ -107,6 +107,7 @@ class ChatViewModel @Inject constructor(
val showNotificationPermissionDialog = MutableStateFlow(false)
private val dateFormatter = SimpleDateFormat("d MMMM YYYY")
private val messageRateLimiter = MessageRateLimiter()
private var account: Id = ""
@ -266,6 +267,8 @@ class ChatViewModel @Inject constructor(
isUserAuthor = msg.creator == account,
isEdited = msg.modifiedAt > msg.createdAt,
reactions = msg.reactions
.toList()
.sortedByDescending { (emoji, ids) -> ids.size }
.map { (emoji, ids) ->
ChatView.Message.Reaction(
emoji = emoji,
@ -625,6 +628,10 @@ class ChatViewModel @Inject constructor(
when (val mode = chatBoxMode.value) {
is ChatBoxMode.Default -> {
// TODO consider moving this use-case inside chat container
if (messageRateLimiter.shouldShowRateLimitWarning()) {
uXCommands.emit(UXCommand.ShowRateLimitWarning)
}
addChatMessage.async(
params = Command.ChatCommand.AddMessage(
chat = vmParams.ctx,
@ -1179,6 +1186,7 @@ class ChatViewModel @Inject constructor(
data object JumpToBottom : UXCommand()
data class SetChatBoxInput(val input: String) : UXCommand()
data class OpenFullScreenImage(val url: String) : UXCommand()
data object ShowRateLimitWarning: UXCommand()
}
sealed class ChatBoxMode {

View file

@ -0,0 +1,27 @@
package com.anytypeio.anytype.feature_chats.presentation
class MessageRateLimiter(
private val maxMessages: Int = DEFAULT_MAX_MESSAGES,
private val timeWindowSeconds: Int = DEFAULT_TIME_WINDOW_SECONDS
) {
private val messageTimestamps = mutableListOf<Long>()
fun shouldShowRateLimitWarning(): Boolean {
val currentTime = System.currentTimeMillis()
val windowStart = currentTime - (timeWindowSeconds * 1000)
// Remove timestamps outside the current window
messageTimestamps.removeAll { it < windowStart }
// Add current message timestamp
messageTimestamps.add(currentTime)
// Return true if we've exceeded the rate limit
return messageTimestamps.size > maxMessages
}
companion object {
private const val DEFAULT_MAX_MESSAGES = 5
private const val DEFAULT_TIME_WINDOW_SECONDS = 5
}
}

View file

@ -69,8 +69,11 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
import com.anytypeio.anytype.core_ui.foundation.AlertIcon
import com.anytypeio.anytype.core_ui.foundation.BUTTON_SECONDARY
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_BLUE
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_RED
import com.anytypeio.anytype.core_ui.foundation.GenericAlert
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_ui.views.Caption1Regular
@ -108,6 +111,7 @@ fun ChatScreenWrapper(
) {
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
var showReactionSheet by remember { mutableStateOf(false) }
var showSendRateLimitWarning by remember { mutableStateOf(false) }
val context = LocalContext.current
Box(
modifier = modifier.fillMaxSize()
@ -245,6 +249,9 @@ fun ChatScreenWrapper(
is UXCommand.OpenFullScreenImage -> {
onRequestOpenFullScreenImage(command.url)
}
is UXCommand.ShowRateLimitWarning -> {
showSendRateLimitWarning = true
}
}
}
}
@ -264,6 +271,34 @@ fun ChatScreenWrapper(
)
}
}
if (showSendRateLimitWarning) {
ModalBottomSheet(
onDismissRequest = {
showSendRateLimitWarning = false
},
sheetState = sheetState,
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
dragHandle = null
) {
GenericAlert(
config = AlertConfig.WithOneButton(
title = stringResource(R.string.chat_send_message_rate_limit_title),
firstButtonText = stringResource(id = R.string.button_okay),
firstButtonType = BUTTON_SECONDARY,
description = stringResource(R.string.chat_send_message_rate_limit_desc),
icon = AlertConfig.Icon(
gradient = GRADIENT_TYPE_RED,
icon = R.drawable.ic_alert_message
)
),
onFirstButtonClicked = {
showSendRateLimitWarning = false
}
)
}
}
}
@Composable

View file

@ -1877,6 +1877,9 @@ Please provide specific details of your needs here.</string>
<string name="chats_reply">Reply</string>
<string name="chats_add_reaction">Add Reaction</string>
<string name="chat_send_message_rate_limit_title">Hold up! Turbo typing detected!</string>
<string name="chat_send_message_rate_limit_desc">Looks like you\'re sending messages at lightning speed. Give it a sec before your next one.</string>
<string name="object_type_empty_items_title">Its empty here.</string>
<string name="object_type_empty_items_subtitle">Create your first objects to get started.</string>
<string name="object_type_menu_delete">Delete completely</string>