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:
parent
e5f771b836
commit
a5b3f03a95
5 changed files with 77 additions and 4 deletions
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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">It’s 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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue