Реалізація пересилання повідомлень у мобільному застосунку
Forward простіше, ніж reply, — ніякої рекурсії, нема привʼязки до позиції в стрічці. Але деталі їдять час: вибір кількох чатів одразу, превью медіа перед надіслання, атрибуція («переслано від Івана»), та обов'язкове питання — копіюємо вкладення або пересилаємо посилання на оригінал.
Модель даних
На сервері переслане повідомлення — новий об'єкт з полем forwarded_from: { message_id, sender_name, sender_id }. Вкладення або копіюються (новий об'єкт у сховищі), або вказують на той же S3-ключ. Другий варіант дешевше по сховищу, але створює залежність: видалення оригінального повідомлення ламає вкладення у всіх пересланих копій. Копіювання — надійніше.
Якщо пересилаємо медіа між чатами різних типів (особистий → груповий) — бекенд повинен перевірити права доступу до оригінального вкладення. Інакше приватні фото з закритого чату витечуть у публічний.
UI: вибір чатів та надіслання
Паттерн один: лонг-тап на повідомленні відкриває контекстне меню, пункт «Переслати» → bottom sheet або модальний екран зі списком чатів. Інтерфейс аналогічний share-листу iOS (UIActivityViewController), але зі своїм списком всередину застосунку.
На iOS поверх стандартного UITableView зі списком чатів додаємо мультиселект через tableView(_:didSelectRowAt:) з зберіганням обраних IndexPath у Set<IndexPath>. Кнопка «Надіслати (N)» в navbar оновлюється через navigationItem.rightBarButtonItem.title. При підтвердженні — послідовні POST-запити або один batch-endpoint.
На Compose — LazyColumn з selectedChats: Set<String> у ViewModel. Кожен елемент ChatItem перевіряє item.id in selectedChats та малює Checkbox або кольоровий overlay. Кнопка «Надіслати» у FloatingActionButton активна при selectedChats.isNotEmpty().
Атрибуція в стрічці
У bubble переслано повідомлення відображаємо підпис «Переслано від [ім'я]». Якщо оригінальний відправник заборонив пересилання (налаштування приватності) — приховуємо ім'я, показуємо просто «Переслане повідомлення». Перевірку робимо на сервері при створенні forward: якщо у оригінального користувача allow_forwarding = false, в forwarded_from повертаємо null.
Flutter
ForwardCubit тримає список чатів та selectedIds. UI — showModalBottomSheet з StatefulBuilder або окремий route. Після вибору — cubit.forwardMessage(messageId, toChats: selectedIds). Анімація закриття шторки та повернення у чат — стандартна через Navigator.pop.
Строки
1-3 робочих дня. Якщо потрібна пересилка між різними типами чатів з розмежуванням прав — ближче до 3. Вартість розраховується індивідуально.







