From ea2ee1711c89efd3d8d2668ffa4bd79d3026a6feac21b5ea752eaac63c4e401b Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 16 Jul 2026 19:48:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=AD=D0=BC=D0=BE=D0=B4=D0=B7=D0=B8/=D0=BC?= =?UTF-8?q?=D0=BE=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=87=D0=B0?= =?UTF-8?q?=D1=82:=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D1=83=20=D1=81=20GitHub,=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D0=B2=D0=B8=D0=B0=D1=82=D1=83=D1=80=D0=B0,?= =?UTF-8?q?=20=D1=82=D0=B8=D1=85=D0=B8=D0=B9=20Script=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Пикер: анимированные webp грузились с raw.githubusercontent.com (в репо их нет) — у пользователей с VPN/операторскими блокировками тап по эмодзи давал сетевую ошибку и «моргание»/подмену картинки. Теперь всегда локальные статичные превью, без внешних запросов и pointer capture; мёртвая константа GitHub-URL удалена. Возврат анимаций — только локальным паком. - Чат (тач): открытие эмодзи-пикера прячет клавиатуру (blur), вставка эмодзи не фокусирует textarea на сенсорных устройствах — поле больше не перекрывается клавиатурой (на десктопе фокус как раньше). - Глобальный обработчик ошибок: кросс-ориджин «Script error.» без файла/стека больше не показывается алертом (лог и captureClientError остаются) — устраняет пугающую плашку на Xiaomi/Safari. - VERSION: client 1.2.327. Проверено в превью: тап по эмодзи — 0 запросов к githubusercontent, 😈 вставляется ровно как 😈, картинка кнопки стабильна; фильтр алерта: Script error. — тихо, реальная ошибка — алерт показывается. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION.properties | 2 +- shine-UI/js/app.js | 6 ++++++ shine-UI/js/components/emoji-picker.js | 13 +++++++++++++ .../js/components/telegram-emoji-catalog.js | 9 +++++---- shine-UI/js/pages/chat-view.js | 19 ++++++++++++++----- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/VERSION.properties b/VERSION.properties index e7984ec6..992e8191 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.326 +client.version=1.2.327 server.version=1.2.297 diff --git a/shine-UI/js/app.js b/shine-UI/js/app.js index ccf89d43..5a19fe3d 100644 --- a/shine-UI/js/app.js +++ b/shine-UI/js/app.js @@ -777,6 +777,12 @@ window.addEventListener('error', (event) => { }, }); + // Кросс-ориджин ошибки приходят как «Script error.» без файла/строки/стека — + // алертом такие не показываем (в лог и captureClientError выше уже записаны). + const isOpaqueCrossOriginError = String(event.message || '') === 'Script error.' + && !event.filename && !(event.error?.stack); + if (isOpaqueCrossOriginError) return; + showGlobalErrorAlert('Поймана глобальная ошибка UI', { message: event.message || 'Global JS error', stack: event.error?.stack || '', diff --git a/shine-UI/js/components/emoji-picker.js b/shine-UI/js/components/emoji-picker.js index 0eadeb07..f91f1c2c 100644 --- a/shine-UI/js/components/emoji-picker.js +++ b/shine-UI/js/components/emoji-picker.js @@ -40,12 +40,25 @@ function saveRecent(items) { function appendAnimatedAsset(root, item) { const previewUrl = item.previewUrl || getTelegramEmojiPreviewUrl(item.file); const animatedUrl = item.animatedUrl || getTelegramEmojiAssetUrl(item.file); + const hasAnimation = animatedUrl && animatedUrl !== previewUrl; const image = document.createElement('img'); image.className = 'telegram-emoji-image'; image.alt = ''; image.decoding = 'async'; image.setAttribute('aria-hidden', 'true'); + // Без отдельного анимационного ассета — просто статичная картинка, + // без hover/tap-подмены src и pointer capture (лишние запросы и «моргание»). + if (!hasAnimation) { + image.src = previewUrl; + image.onerror = () => { + root.classList.add('is-error'); + image.removeAttribute('src'); + }; + root.append(image); + return; + } + let isActive = false; let sourceVersion = 0; diff --git a/shine-UI/js/components/telegram-emoji-catalog.js b/shine-UI/js/components/telegram-emoji-catalog.js index 88232171..69e6bc64 100644 --- a/shine-UI/js/components/telegram-emoji-catalog.js +++ b/shine-UI/js/components/telegram-emoji-catalog.js @@ -8,7 +8,6 @@ import { TELEGRAM_EMOJI_GROUP_SMILEYS } from './telegram-emoji-smileys.js?v=2026 import { TELEGRAM_EMOJI_GROUP_SYMBOLS } from './telegram-emoji-symbols.js?v=202607151545'; import { TELEGRAM_EMOJI_GROUP_TRAVEL_PLACES } from './telegram-emoji-travel_places.js?v=202607151545'; -const TELEGRAM_EMOJI_ASSET_BASE_URL = 'https://raw.githubusercontent.com/Tarikul-Islam-Anik/Telegram-Animated-Emojis/main/'; const TELEGRAM_EMOJI_PREVIEW_BASE_URL = '/assets/emoji/telegram-previews/'; function getTelegramEmojiName(file) { @@ -40,11 +39,13 @@ export const TELEGRAM_EMOJI_GROUPS = [ ].map(withPreviewUrls); export function getTelegramEmojiAssetUrl(file) { - return `${TELEGRAM_EMOJI_ASSET_BASE_URL}${file.split('/').map((part) => encodeURIComponent(part)).join('/')}`; + // Анимированные webp с raw.githubusercontent.com отключены: внешний хост + // блокируется у части пользователей (VPN/операторы) — тап по эмодзи вызывал + // сетевую ошибку и «моргание» картинки. Пока отдаём локальное статичное превью; + // возврат анимаций — только локальным паком в assets. + return getTelegramEmojiPreviewUrl(file); } export function getTelegramEmojiPreviewUrl(file) { return `${TELEGRAM_EMOJI_PREVIEW_BASE_URL}${String(file || '').replace(/\.webp$/i, '.png').split('/').map((part) => encodeURIComponent(part)).join('/')}`; } - -export { TELEGRAM_EMOJI_ASSET_BASE_URL }; diff --git a/shine-UI/js/pages/chat-view.js b/shine-UI/js/pages/chat-view.js index bdbd3417..25f90ad0 100644 --- a/shine-UI/js/pages/chat-view.js +++ b/shine-UI/js/pages/chat-view.js @@ -914,6 +914,10 @@ export function render({ navigate, route }) { emojiToggle?.setAttribute('aria-expanded', 'false'); }; + // На сенсорных устройствах фокус в textarea поднимает клавиатуру поверх пикера — + // поэтому при вставке эмодзи фокусируем поле только там, где есть «точный» указатель. + const isFinePointer = window.matchMedia?.('(hover: hover) and (pointer: fine)')?.matches ?? true; + const insertEmoji = (emoji) => { if (!input) return; const value = String(input.value || ''); @@ -922,11 +926,13 @@ export function render({ navigate, route }) { input.value = `${value.slice(0, start)}${emoji}${value.slice(end)}`; const nextPosition = start + emoji.length; emojiSelection = { start: nextPosition, end: nextPosition }; - input.focus(); - try { - input.setSelectionRange(nextPosition, nextPosition); - } catch { - // Ignore browsers that do not expose textarea selection during focus changes. + if (isFinePointer) { + input.focus(); + try { + input.setSelectionRange(nextPosition, nextPosition); + } catch { + // Ignore browsers that do not expose textarea selection during focus changes. + } } autoResizeComposer(input); }; @@ -1247,6 +1253,9 @@ export function render({ navigate, route }) { emojiPickerOpen = !emojiPickerOpen; emojiSlot.hidden = !emojiPickerOpen; emojiToggle.setAttribute('aria-expanded', String(emojiPickerOpen)); + // Как в Telegram: либо клавиатура, либо пикер. На тач-устройствах при + // открытии пикера прячем клавиатуру, чтобы поле ввода оставалось видно. + if (emojiPickerOpen && !isFinePointer) input?.blur(); }); screen.addEventListener('pointerdown', (event) => { if (!emojiPickerOpen || form.contains(event.target)) return;