From 388d279ea9ce6309ce12d5fe4e58a356267df8da Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Thu, 26 Feb 2026 16:02:01 +0500 Subject: [PATCH] feat: Bump version to 1.0.9 and update release notes; remove debug logs functionality --- app/build.gradle.kts | 4 +- .../com/rosetta/messenger/MainActivity.kt | 20 +-- .../rosetta/messenger/data/ReleaseNotes.kt | 31 +++-- .../messenger/database/MessageEntities.kt | 16 +++ .../messenger/network/ProtocolManager.kt | 116 +++++++++++++++--- .../messenger/ui/chats/ChatDetailScreen.kt | 24 +--- .../messenger/ui/chats/ChatViewModel.kt | 16 +-- .../messenger/ui/chats/ChatsListScreen.kt | 19 +-- .../AttachmentDownloadDebugLogger.kt | 17 +-- .../chats/components/ChatDetailComponents.kt | 9 -- 10 files changed, 147 insertions(+), 125 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 25d6094..0d55d44 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -23,8 +23,8 @@ val gitShortSha = safeGitOutput("rev-parse", "--short", "HEAD") ?: "unknown" // ═══════════════════════════════════════════════════════════ // Rosetta versioning — bump here on each release // ═══════════════════════════════════════════════════════════ -val rosettaVersionName = "1.0.8" -val rosettaVersionCode = 8 // Increment on each release +val rosettaVersionName = "1.0.9" +val rosettaVersionCode = 9 // Increment on each release android { namespace = "com.rosetta.messenger" diff --git a/app/src/main/java/com/rosetta/messenger/MainActivity.kt b/app/src/main/java/com/rosetta/messenger/MainActivity.kt index 983bfcd..1037fc4 100644 --- a/app/src/main/java/com/rosetta/messenger/MainActivity.kt +++ b/app/src/main/java/com/rosetta/messenger/MainActivity.kt @@ -41,7 +41,6 @@ import com.rosetta.messenger.ui.auth.AuthFlow import com.rosetta.messenger.ui.auth.DeviceConfirmScreen import com.rosetta.messenger.ui.chats.ChatDetailScreen import com.rosetta.messenger.ui.chats.ChatsListScreen -import com.rosetta.messenger.ui.chats.ConnectionLogsScreen import com.rosetta.messenger.ui.chats.RequestsListScreen import com.rosetta.messenger.ui.chats.SearchScreen import com.rosetta.messenger.ui.components.OptimizedEmojiCache @@ -507,7 +506,6 @@ sealed class Screen { data object CrashLogs : Screen() data object Biometric : Screen() data object Appearance : Screen() - data object DebugLogs : Screen() } @Composable @@ -616,9 +614,6 @@ fun MainScreen( val isAppearanceVisible by remember { derivedStateOf { navStack.any { it is Screen.Appearance } } } - val isDebugLogsVisible by remember { - derivedStateOf { navStack.any { it is Screen.DebugLogs } } - } // Navigation helpers fun pushScreen(screen: Screen) { @@ -640,8 +635,7 @@ fun MainScreen( it is Screen.Logs || it is Screen.CrashLogs || it is Screen.Biometric || - it is Screen.Appearance || - it is Screen.DebugLogs + it is Screen.Appearance } } fun popChatAndChildren() { @@ -718,7 +712,6 @@ fun MainScreen( ) }, onSettingsClick = { pushScreen(Screen.Profile) }, - onLogsClick = { pushScreen(Screen.DebugLogs) }, onInviteFriendsClick = { // TODO: Share invite link }, @@ -1010,17 +1003,6 @@ fun MainScreen( ) } - SwipeBackContainer( - isVisible = isDebugLogsVisible, - onBack = { navStack = navStack.filterNot { it is Screen.DebugLogs } }, - isDarkTheme = isDarkTheme - ) { - ConnectionLogsScreen( - isDarkTheme = isDarkTheme, - onBack = { navStack = navStack.filterNot { it is Screen.DebugLogs } } - ) - } - var isOtherProfileSwipeEnabled by remember { mutableStateOf(true) } LaunchedEffect(selectedOtherUser?.publicKey) { isOtherProfileSwipeEnabled = true diff --git a/app/src/main/java/com/rosetta/messenger/data/ReleaseNotes.kt b/app/src/main/java/com/rosetta/messenger/data/ReleaseNotes.kt index d6323ce..ec5a0cf 100644 --- a/app/src/main/java/com/rosetta/messenger/data/ReleaseNotes.kt +++ b/app/src/main/java/com/rosetta/messenger/data/ReleaseNotes.kt @@ -17,22 +17,27 @@ object ReleaseNotes { val RELEASE_NOTICE = """ Update v$VERSION_PLACEHOLDER - Загрузка файлов и фото - - Исправлена ошибка загрузки больших файлов (>2 МБ) — стриминг вместо загрузки в память - - Автоматический повторный запрос при сбое скачивания фото - - Проверка целостности загруженных данных - - Исправлены OOM-крэши при расшифровке тяжёлых вложений + Стабильность и производительность + - Фильтрация неподдерживаемых пакетов (групповые чаты) — исключение крэшей при обработке + - Таймаут очереди входящих сообщений (20 сек) — защита от зависания синхронизации + - Повторный запрос синхронизации при таймауте без потери сообщений + - Отключено накопление отладочных логов в памяти — снижение расхода RAM - Фото и медиа - - Исправлено открытие неправильного фото по нажатию на реплай - - Корректный показ фото из пересланных сообщений + Индикаторы прочтения + - Исправлена логика отображения статуса «прочитано» в чате + - Добавлена повторная отправка read receipt при сбое + - Автоматическая отправка read receipt при обновлении сообщений из БД + + Верификация + - Бейдж верификации корректно сохраняется при обновлении имени собеседника + - Статус верификации передаётся при открытии диалога + + FCM Push-уведомления + - Дедупликация подписки FCM-токена — устранены повторные регистрации + - Автоматическая отписка старого токена перед регистрацией нового Интерфейс - - Бейдж непрочитанных больше не перекрывает стрелку назад - - Индикатор печати адаптирован под светлую тему - - Белый значок верификации в профиле собеседника - - Белые галочки на пузырьках с файлами - - Убран отладочный интерфейс из боковой панели + - Убран отладочный интерфейс (Debug Logs) из бокового меню и экрана чата """.trimIndent() fun getNotice(version: String): String = diff --git a/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt b/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt index 5f9edd0..ae472b6 100644 --- a/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt +++ b/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt @@ -643,6 +643,22 @@ interface DialogDao { verified: Int ) + /** Обновить только имя/username собеседника, не трогая verified */ + @Query( + """ + UPDATE dialogs SET + opponent_title = :title, + opponent_username = :username + WHERE account = :account AND opponent_key = :opponentKey + """ + ) + suspend fun updateOpponentDisplayName( + account: String, + opponentKey: String, + title: String, + username: String + ) + /** * Получить общее количество непрочитанных сообщений, исключая указанный диалог Используется для * отображения badge на кнопке "назад" в экране чата diff --git a/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt b/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt index 1ba0f9b..74e97c8 100644 --- a/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt +++ b/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import java.security.SecureRandom -import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.resume @@ -22,7 +21,7 @@ import kotlin.coroutines.resume */ object ProtocolManager { private const val TAG = "ProtocolManager" - private const val MAX_DEBUG_LOGS = 2000 + private const val INBOUND_QUEUE_WAIT_TIMEOUT_MS = 20_000L // Server address - same as React Native version private const val SERVER_ADDRESS = "ws://46.28.71.12:3000" @@ -65,8 +64,6 @@ object ProtocolManager { // Pending resolves: publicKey → list of continuations waiting for the result private val pendingResolves = ConcurrentHashMap>>() - private val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) - // 🚀 Флаг для включения UI логов (по умолчанию ВЫКЛЮЧЕНО - это вызывало ANR!) private var uiLogsEnabled = false private var lastProtocolState: ProtocolState? = null @@ -91,12 +88,9 @@ object ProtocolManager { } } - fun addLog(message: String) { - val timestamp = dateFormat.format(Date()) - val logLine = "[$timestamp] $message" - - // Always keep logs in memory for the Logs screen (opened via `...` in chat) - _debugLogs.value = (_debugLogs.value + logLine).takeLast(MAX_DEBUG_LOGS) + fun addLog(@Suppress("UNUSED_PARAMETER") message: String) { + // Disabled by request: keep debug log buffer empty. + return } fun enableUILogs(enabled: Boolean) { @@ -169,6 +163,18 @@ object ProtocolManager { requireResyncAfterAccountInit("⏳ Incoming message before account init, scheduling re-sync") return@launchInboundPacketTask } + val ownKey = getProtocol().getPublicKey().orEmpty() + if (ownKey.isBlank()) { + requireResyncAfterAccountInit("⏳ Incoming message before protocol account init, scheduling re-sync") + return@launchInboundPacketTask + } + if (!isSupportedDirectMessagePacket(messagePacket, ownKey)) { + android.util.Log.w( + TAG, + "Skipping unsupported message packet (likely conversation): from=${messagePacket.fromPublicKey.take(16)}, to=${messagePacket.toPublicKey.take(16)}" + ) + return@launchInboundPacketTask + } try { repository.handleIncomingMessage(messagePacket) if (!syncBatchInProgress) { @@ -176,7 +182,6 @@ object ProtocolManager { } // ✅ Send delivery ACK only AFTER message is safely stored in DB. // Skip for own sync messages (no need to ACK yourself). - val ownKey = getProtocol().getPublicKey() if (messagePacket.fromPublicKey != ownKey) { val deliveryPacket = PacketDelivery().apply { messageId = messagePacket.messageId @@ -219,6 +224,18 @@ object ProtocolManager { requireResyncAfterAccountInit("⏳ Read status before account init, scheduling re-sync") return@launchInboundPacketTask } + val ownKey = getProtocol().getPublicKey().orEmpty() + if (ownKey.isBlank()) { + requireResyncAfterAccountInit("⏳ Read status before protocol account init, scheduling re-sync") + return@launchInboundPacketTask + } + if (!isSupportedDirectReadPacket(readPacket, ownKey)) { + android.util.Log.w( + TAG, + "Skipping unsupported read packet (likely conversation): from=${readPacket.fromPublicKey.take(16)}, to=${readPacket.toPublicKey.take(16)}" + ) + return@launchInboundPacketTask + } repository.handleRead(readPacket) if (!syncBatchInProgress) { repository.updateLastSyncTimestamp(System.currentTimeMillis()) @@ -365,9 +382,14 @@ object ProtocolManager { } } - private fun launchInboundPacketTask(block: suspend () -> Unit) { + private fun launchInboundPacketTask(block: suspend () -> Unit): Boolean { ensureInboundQueueDrainRunning() - inboundTaskChannel.trySend(block) + val result = inboundTaskChannel.trySend(block) + if (result.isFailure) { + android.util.Log.e(TAG, "Failed to enqueue inbound task", result.exceptionOrNull()) + return false + } + return true } private fun requireResyncAfterAccountInit(reason: String) { @@ -383,10 +405,53 @@ object ProtocolManager { * Since the queue is strictly FIFO, when the sentinel runs, all previously * submitted tasks are guaranteed to have completed. */ - private suspend fun whenInboundTasksFinish() { + private suspend fun whenInboundTasksFinish(timeoutMs: Long = INBOUND_QUEUE_WAIT_TIMEOUT_MS): Boolean { val done = CompletableDeferred() - launchInboundPacketTask { done.complete(Unit) } - done.await() + if (!launchInboundPacketTask { done.complete(Unit) }) { + return false + } + return try { + withTimeout(timeoutMs) { done.await() } + true + } catch (_: TimeoutCancellationException) { + false + } + } + + private fun isLikelyUserPublicKey(value: String): Boolean { + val normalized = value.removePrefix("0x") + if (normalized.length != 64 && normalized.length != 66 && normalized.length != 128 && normalized.length != 130) { + return false + } + return normalized.all { it.isDigit() || it in 'a'..'f' || it in 'A'..'F' } + } + + private fun isSupportedDirectPeerKey(peerKey: String, ownKey: String): Boolean { + return peerKey == ownKey || + MessageRepository.isSystemAccount(peerKey) || + isLikelyUserPublicKey(peerKey) + } + + private fun isSupportedDirectMessagePacket(packet: PacketMessage, ownKey: String): Boolean { + val from = packet.fromPublicKey.trim() + val to = packet.toPublicKey.trim() + if (from.isBlank() || to.isBlank()) return false + return when { + from == ownKey -> isSupportedDirectPeerKey(to, ownKey) + to == ownKey -> isSupportedDirectPeerKey(from, ownKey) + else -> false + } + } + + private fun isSupportedDirectReadPacket(packet: PacketRead, ownKey: String): Boolean { + val from = packet.fromPublicKey.trim() + val to = packet.toPublicKey.trim() + if (from.isBlank() || to.isBlank()) return false + return when { + from == ownKey -> isSupportedDirectPeerKey(to, ownKey) + to == ownKey -> isSupportedDirectPeerKey(from, ownKey) + else -> false + } } private fun onAuthenticated() { @@ -499,10 +564,21 @@ object ProtocolManager { // syncBatchInProgress stays true until NOT_NEEDED arrives. scope.launch { setSyncInProgress(true) - // Desktop: await whenFinish() — wait for ALL queued tasks without timeout. - // Old code used 15s polling timeout which could advance the sync timestamp - // before all messages were processed, causing message loss on app crash. - whenInboundTasksFinish() + val tasksFinished = whenInboundTasksFinish() + if (!tasksFinished) { + android.util.Log.w( + TAG, + "SYNC BATCH_END: inbound queue did not drain in time, requesting re-sync without advancing cursor" + ) + val fallbackTimestamp = try { + messageRepository?.getLastSyncTimestamp() ?: packet.timestamp + } catch (e: Exception) { + android.util.Log.e(TAG, "Failed to read last sync timestamp for fallback", e) + packet.timestamp + } + sendSynchronize(fallbackTimestamp) + return@launch + } addLog("🔄 SYNC tasks done — saving timestamp ${packet.timestamp}, requesting next batch") messageRepository?.updateLastSyncTimestamp(packet.timestamp) sendSynchronize(packet.timestamp) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index e7f686f..6cd025d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -77,7 +77,6 @@ import com.rosetta.messenger.data.ForwardManager import com.rosetta.messenger.data.MessageRepository import com.rosetta.messenger.database.RosettaDatabase import com.rosetta.messenger.network.AttachmentType -import com.rosetta.messenger.network.ProtocolManager import com.rosetta.messenger.network.SearchUser import com.rosetta.messenger.repository.AvatarRepository import com.rosetta.messenger.ui.chats.attach.ChatAttachAlert @@ -402,18 +401,11 @@ fun ChatDetailScreen( } } - DisposableEffect(Unit) { - ProtocolManager.enableUILogs(true) - onDispose { ProtocolManager.enableUILogs(false) } - } - // Состояние выпадающего меню var showMenu by remember { mutableStateOf(false) } - var showDebugLogs by remember { mutableStateOf(false) } var showDeleteConfirm by remember { mutableStateOf(false) } var showBlockConfirm by remember { mutableStateOf(false) } var showUnblockConfirm by remember { mutableStateOf(false) } - val debugLogs by ProtocolManager.debugLogs.collectAsState() // Наблюдаем за статусом блокировки в реальном времени через Flow val isBlocked by database.blacklistDao() @@ -606,7 +598,7 @@ fun ChatDetailScreen( // forwardTrigger добавлен чтобы перезагрузить диалог при forward в тот же чат LaunchedEffect(user.publicKey, forwardTrigger) { viewModel.setUserKeys(currentUserPublicKey, currentUserPrivateKey) - viewModel.openDialog(user.publicKey, user.title, user.username) + viewModel.openDialog(user.publicKey, user.title, user.username, user.verified) viewModel.markVisibleMessagesAsRead() // 🔥 Убираем уведомление этого чата из шторки при заходе com.rosetta.messenger.push.RosettaFirebaseMessagingService @@ -1221,10 +1213,6 @@ fun ChatDetailScreen( isSystemAccount, isBlocked = isBlocked, - onLogsClick = { - showMenu = false - showDebugLogs = true - }, onBlockClick = { showMenu = false @@ -2745,15 +2733,5 @@ fun ChatDetailScreen( ) } - if (showDebugLogs) { - DebugLogsBottomSheet( - logs = debugLogs, - isDarkTheme = isDarkTheme, - onDismiss = { showDebugLogs = false }, - onClearLogs = { ProtocolManager.clearLogs() } - ) - } - } } - diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt index 906b12d..b84ea5a 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt @@ -110,6 +110,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { // Информация о собеседнике private var opponentTitle: String = "" private var opponentUsername: String = "" + private var opponentVerified: Int = 0 // Текущий диалог private var opponentKey: String? = null @@ -534,7 +535,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } /** Открыть диалог */ - fun openDialog(publicKey: String, title: String = "", username: String = "") { + fun openDialog(publicKey: String, title: String = "", username: String = "", verified: Int = 0) { // 🔥 ВСЕГДА перезагружаем данные - не кешируем, т.к. диалог мог быть удалён // if (opponentKey == publicKey) { @@ -547,6 +548,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { opponentKey = publicKey opponentTitle = title opponentUsername = username + opponentVerified = verified.coerceAtLeast(0) // 📨 СНАЧАЛА проверяем ForwardManager - ДО сброса состояния! // Это важно для правильного отображения forward сообщений сразу @@ -1774,7 +1776,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { title = opponentTitle, username = opponentUsername, publicKey = publicKey, - verified = 0, + verified = opponentVerified, online = 0 ) } @@ -1791,7 +1793,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { title = dialog.opponentTitle, username = dialog.opponentUsername, publicKey = publicKey, - verified = 0, + verified = dialog.verified, online = 0 ) } @@ -3569,8 +3571,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { // 🔥 FIX: updateDialogFromMessages создаёт новый диалог с пустым title/username // когда диалога ещё не было. Обновляем метаданные из уже известных данных. if (opponent != account && opponentTitle.isNotEmpty()) { - dialogDao.updateOpponentInfo( - account, opponent, opponentTitle, opponentUsername, 0 + dialogDao.updateOpponentDisplayName( + account, opponent, opponentTitle, opponentUsername ) } } catch (e: Exception) {} @@ -3602,8 +3604,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { // 🔥 FIX: Сохраняем title/username после пересчёта счётчиков if (opponentKey != account && opponentTitle.isNotEmpty()) { - dialogDao.updateOpponentInfo( - account, opponentKey, opponentTitle, opponentUsername, 0 + dialogDao.updateOpponentDisplayName( + account, opponentKey, opponentTitle, opponentUsername ) } } catch (e: Exception) {} diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 157fefe..d6ea31b 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -235,8 +235,7 @@ fun ChatsListScreen( avatarRepository: com.rosetta.messenger.repository.AvatarRepository? = null, onAddAccount: () -> Unit = {}, onSwitchAccount: (String) -> Unit = {}, - onDeleteAccountFromSidebar: (String) -> Unit = {}, - onLogsClick: () -> Unit = {} + onDeleteAccountFromSidebar: (String) -> Unit = {} ) { // Theme transition state var hasInitialized by remember { mutableStateOf(false) } @@ -1149,22 +1148,6 @@ fun ChatsListScreen( } ) - // 📋 Logs - DrawerMenuItemEnhanced( - icon = TablerIcons.Bug, - text = "Logs", - iconColor = menuIconColor, - textColor = menuTextColor, - onClick = { - scope.launch { - drawerState.close() - kotlinx.coroutines - .delay(100) - onLogsClick() - } - } - ) - } // ═══════════════════════════════════════════════════════════ diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentDownloadDebugLogger.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentDownloadDebugLogger.kt index 0d79d66..728b2c2 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentDownloadDebugLogger.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentDownloadDebugLogger.kt @@ -3,25 +3,14 @@ package com.rosetta.messenger.ui.chats.components import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale object AttachmentDownloadDebugLogger { - private const val MAX_LOGS = 1000 - private val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) - private val _logs = MutableStateFlow>(emptyList()) val logs: StateFlow> = _logs.asStateFlow() - fun log(message: String) { - val timestamp = dateFormat.format(Date()) - val line = "[$timestamp] 🖼️ $message" - _logs.update { current -> (current + line).takeLast(MAX_LOGS) } - // Всегда дублируем в debug logs чата напрямую через ProtocolManager - // (не через MessageLogger, чтобы обойти isEnabled гейт) - com.rosetta.messenger.network.ProtocolManager.addLog("🖼️ $message") + fun log(@Suppress("UNUSED_PARAMETER") message: String) { + // Disabled by request: no runtime accumulation of photo debug logs. + return } fun clear() { diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt index a75f205..331c2e2 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt @@ -2370,7 +2370,6 @@ fun KebabMenu( isSavedMessages: Boolean, isSystemAccount: Boolean = false, isBlocked: Boolean, - onLogsClick: () -> Unit, onBlockClick: () -> Unit, onUnblockClick: () -> Unit, onDeleteClick: () -> Unit @@ -2399,14 +2398,6 @@ fun KebabMenu( dismissOnClickOutside = true ) ) { - KebabMenuItem( - icon = TelegramIcons.Info, - text = "Debug Logs", - onClick = onLogsClick, - tintColor = iconColor, - textColor = textColor - ) - if (!isSavedMessages && !isSystemAccount) { KebabMenuItem( icon = if (isBlocked) TelegramIcons.Done else TelegramIcons.Block,