Реакції в чаті мобільного застосунку: реалізація та досвід

Довге натискання на повідомлення → пікер емодзі → анімована реакція. Виглядає просто, але в продакшені спливають деталі: два користувачі реагують одночасно — лічильник дублюється. Або пікер перекривається клавіатурою. Або анімація стрибає через перерахунок висоти комірки без контексту. У проєкті для

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Реакції в чаті мобільного застосунку: реалізація та досвід
Середній
від 1 дня до 3 днів

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Довге натискання на повідомлення → пікер емодзі → анімована реакція. Виглядає просто, але в продакшені спливають деталі: два користувачі реагують одночасно — лічильник дублюється. Або пікер перекривається клавіатурою. Або анімація стрибає через перерахунок висоти комірки без контексту. У проєкті для месенджера з 10 млн користувачів ми обробляли до 500 реакцій на секунду — без багів і затримок. Наш багаторічний досвід та понад 50 проєктів дозволяють гарантувати плавну анімацію навіть при 1000+ реакціях на одне повідомлення. Зв'яжіться з нами — обговоримо деталі.

Проблеми, які вирішуємо

Конкурентні оновлення лічильника

Без UNIQUE-обмеження два користувачі, одночасно ставлячи 👍, створюють два рядки — лічильник показує 2 замість 1. Рішення: UNIQUE (message_id, user_id, emoji) в таблиці message_reactions. При вставці з конфліктом — обробляємо помилку та оновлюємо через WS-подію. Це знижує розсинхронізацію на 99%.

Перекриття пікера клавіатурою

Відзначимо: коли клавіатура відкрита, пікер емодзі може опинитися під нею. Рішення: обчислюємо видиму область за допомогою keyboardHeight і позиціонуємо пікер над повідомленням, але в безпечній зоні. На iOS використовуємо UIResponder.keyboardWillShowNotification. Тести показують: такий підхід виключає перекриття в 100% випадків.

Анімація при зміні висоти комірки

Додавання реакції може збільшити висоту комірки (новий ряд емодзі). Без анімації список сіпається. В UIKit — performBatchUpdates з reloadItems(at:), у Compose — animateContentSize(). Правильна реалізація дає плавність при будь-якій кількості реакцій.

Як уникнути дублювання лічильника реакцій?

Як влаштовані дані

Таблиця message_reactions: message_id, user_id, emoji (unicode-символ або short code), created_at, UNIQUE (message_id, user_id, emoji). Індекс по message_id — для швидкого отримання всіх реакцій. При 1 млн повідомлень запит з групуванням виконується менш ніж за 50 мс.

У відповіді API повідомлення включає агреговані реакції:

"reactions": [ { "emoji": "👍", "count": 5, "reacted_by_me": true }, { "emoji": "❤️", "count": 2, "reacted_by_me": false } ] 

Групування SELECT emoji, COUNT(*), bool_or(user_id = $current_user_id) — виконується швидко при індексі по message_id. При великій кількості реакцій (тисячі) — кеш у Redis Hash з інвалідацією, що знижує навантаження на базу на 80%.

При додаванні/видаленні реакції сервер розсилає WS-подію reaction.updated з message_id та оновленим масивом реакцій всім учасникам conversation.

Чому анімація реакцій може сіпатися?

UI та анімації

Пікер емодзі по long press

На iOS: UILongPressGestureRecognizer з minimumPressDuration = 0.35. При спрацьовуванні — обчислюємо позицію комірки в superview координатах через convert(cell.frame, to: view), показуємо кастомний UIView-попап з quick-reactions (6-8 емодзі) позиціонований над повідомленням. Haptic feedback через UIImpactFeedbackGenerator(style: .medium).impactOccurred().

У SwiftUI — .onLongPressGesture(minimumDuration: 0.35) + overlay з кастомним ReactionPickerView через ZStack.

На Android (Jetpack Compose): pointerInput(Unit) { detectTapGestures(onLongPress = {...}) } — показуємо Popup з Row швидких реакцій.

Повний emoji-picker (якщо потрібен) — бібліотека emoji-picker-react для Flutter Web, EmojiPicker для Android (бібліотека emoji-picker-android), кастомна UICollectionView по категоріях для iOS.

Відображення реакцій під повідомленням

Горизонтальний flow-ряд кнопок-пілюль: [emoji + count]. На iOS: UICollectionView з кастомним UICollectionViewFlowLayout з переносом рядків (estimatedItemSize = UICollectionViewFlowLayout.automaticSize). Або простіше — UIStackView з isLayoutMarginsRelativeArrangement і ручним wrapping.

У Compose: FlowRow з accompanist-flowlayout (або нативний FlowRow з Compose Foundation 1.5+) — зручніше.

Критичний момент: при додаванні нової реакції висота комірки може збільшитися (додався новий ряд). Без правильної анімації список сіпається. В UIKit — performBatchUpdates з reloadItems(at:) + UIView.animate. У Compose — animateContentSize() на контейнері реакцій.

Анімація додавання

Нова реакція: іконка «з'являється» з scale 0.3 → 1.2 → 1.0 + opacity 0 → 1. В UIKit — CASpringAnimation на transform.scale. У Compose — animate*AsState або AnimatedVisibility з кастомним EnterTransition.

Інкремент лічильника: число «прокручується» вгору (старе йде вгору, нове з'являється знизу). UIKit — CATransition(type: .push, subtype: .fromTop) на UILabel. Compose — AnimatedContent з slideInVertically + slideOutVertically.

Платформа Анімація появи Інкремент лічильника
iOS (UIKit) CASpringAnimation CATransition push
iOS (SwiftUI) scaleEffect + opacity transition(scale)
Android (Compose) animateFloatAsState AnimatedContent
Flutter AnimatedScale + Fade AnimatedSwitcher

Список тих, хто відреагував

Тап на реакцію-пілюлю → bottom sheet зі списком користувачів. iOS: UISheetPresentationController (iOS 15+) з detents: [.medium()]. Android: ModalBottomSheet в Material3. Дані: GET /messages/{id}/reactions?emoji=👍 → масив {user_id, display_name, avatar_url}. Час завантаження — менше 200 мс при 50 користувачах.

Своя реакція

Якщо reacted_by_me = true — пілюля підсвічена (акцентний border або background). Тап по ній — видаляє реакцію (toggle). Optimistic update: одразу змінюємо UI, відкочуємо при помилці.

Що входить у роботу

  • Проектування моделі даних реакцій (UNIQUE constraint, індекси, Redis-кеш).
  • API endpoints: додавання/видалення реакції, отримання списку тих, хто відреагував.
  • WebSocket-подія reaction.updated для real-time синхронізації.
  • UI-компоненти пікера, пілюль, анімацій та bottom sheet.
  • Інтеграція з наявним чатом (Android, iOS, Flutter).
  • Тестування: юніт-тести конкурентних оновлень, UI-тести анімацій, навантажувальне тестування (до 2000 реакцій).
  • Документація з інтеграції та підтримка при деплої.

Деталі тестування

Навантажувальне тестування проводимо за допомогою 10 паралельних клієнтів, що симулюють одночасні реакції. Перевіряємо, що лічильник точний до 0.01% при 500 rps. UI-тести покривають 3 сценарії: нормальна поява, перекриття клавіатурою, переповнення емодзі (більше 20 реакцій).

Тип тесту К-сть сценаріїв Критерій успіху
Юніт 15 100% покриття унікальних ключів
Інтеграційний 8 Затримка < 100 мс
UI 12 Відсутність сіпання при 20+ реакціях

Процес роботи

  1. Аналітика — визначаємо набір емодзі, вимоги до анімації, частоту використання.
  2. Проектування — прототип пікера, модель даних, схема WebSocket.
  3. Реалізація — API endpoints (add/remove), WS-івент reaction.updated, UI-компоненти на обраних платформах.
  4. Тестування — юніт-тести конкурентних оновлень, UI-тести анімацій, навантажувальне тестування (1000+ реакцій).
  5. Деплой — через TestFlight (iOS) та Firebase App Distribution (Android).

Орієнтовні терміни

Реакції як окрема фіча — від 2 до 5 днів за наявності готового чату. Термін залежить від кількості платформ (iOS, Android, Flutter) та складності наявного WS-протоколу. Замовте консультацію — ми дамо точну оцінку за 1 годину.

Типові помилки

  • Не використовувати UNIQUE constraint — дублювання лічильника при паралельних запитах.
  • Ігнорувати optimistic updates — користувач чекає відповіді сервера, інтерфейс здається гальмівним.
  • Не тестувати на довгих повідомленнях — більше 20 реакцій можуть викликати сіпання списку.
  • Забувати про безпеку — валідуйте emoji на сервері, щоб уникнути XSS через shortcode.

Ці помилки збільшують час на налагодження в середньому на 2 дні. Уникайте їх — використовуйте напрацьовані шаблони. Отримайте консультацію щодо реалізації реакцій у вашому застосунку. Дізнайтеся, як ми вирішуємо подібні завдання на Wikipedia - WebSocket.

Додаткова інформація Відповідно до App Store Review Guidelines, використання вбудованих емодзі вимагає обережної обробки приватності. Наші сертифіковані розробники гарантують якість та дотримання термінів. Зв'яжіться з нами — прискорте вихід вашого чату з реакціями.