Розробка системи репостів/шеринга в мобільній програмі
Репост всередину програми та шеринг назовні — технічно різні задачі, хоча кнопка одна. Внутрішній репост — публікація чужого контенту у своїй стрічці з атрибуцією. Зовнішній шеринг — передача контенту в іншу програму через системний share sheet. Зазвичай потрібні обидва варіанти.
Внутрішній репост: модель даних
Два підходи:
1. Копіювання контенту — новий пост з полем reposted_from_id. Простота відображення, але при редагуванні оригіналу копія застаріває.
2. Посилання на оригінал — пост з repost_of_id, тіло не копіюється, береться при запиті. При видаленні оригіналу репост показує «Оригінал видалений». Telegram та Twitter/X використовують саме цей підхід.
Другий підхід правильніший. У стрічці користувача репост рендериться як embedded-карточка оригіналу всередину bubble репостера.
Відображення у стрічці
// iOS — ячейка поста з вложеною карточкою
if let repostOf = post.repostOf {
// Рисуємо RepostCardView всередину PostCell
let repostCard = RepostCardView(post: repostOf)
contentStack.addArrangedSubview(repostCard)
}
Вложена карточка — UIView з закругленими кутами, обводкою CALayer.borderColor, аватаром та ім'ям оригінального автора. Важливо: бесконечної вложеності немає — репост репоста показує тільки один рівень вложеності (карточку вихідного оригіналу).
На Compose: if (post.repostOf != null) EmbeddedPostCard(post = post.repostOf).
Лічильник та унікальність
Таблиця reposts (user_id, original_post_id, UNIQUE) — користувач може репостити оригінал один раз. Кнопка репоста після натискання стає активною (підсвічується), повторне натискання — скасовує репост. Лічильник reposts_count у таблиці оригінального поста.
Зовнішній шеринг
iOS — UIActivityViewController:
let items: [Any] = [postText, URL(string: deeplink)!]
let vc = UIActivityViewController(activityItems: items, applicationActivities: nil)
// На iPad потрібен popoverPresentationController
vc.popoverPresentationController?.sourceView = shareButton
present(vc, animated: true)
Для шеринга зображення поста — рендеріть UIView у UIImage через UIGraphicsImageRenderer та додайте в activityItems.
Android — Intent.ACTION_SEND:
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "$postText\n$deeplinkUrl")
}
startActivity(Intent.createChooser(intent, "Поділитися"))
Для зображень — Intent.ACTION_SEND з type = "image/*" та URI через FileProvider. Прямий file:// URI не працює з Android 7+, тільки content:// через FileProvider.
Flutter — пакет share_plus:
await Share.shareXFiles([XFile(imagePath)], text: '$postText\n$deeplinkUrl');
Deeplink для шеринга
Зовнішній шеринг без deeplink втрачає смисл. Посилання повинно відкривати конкретний пост у програмі. На iOS — Universal Links (apple-app-site-association на сервері + NSUserActivityTypes у Info.plist). На Android — App Links (assetlinks.json на сервері + intent-filter у manifest). При відсутності програми — fallback на веб-версію поста.
Етапи роботи
Проектування моделі репоста → реалізація API (create/delete repost, лічильники) → UI embedded-карточки у стрічці → зовнішній шеринг з deeplink → тестування un-repost та відображення видалених оригіналів.
Часові рамки
Внутрішній репост з UI — 1-2 дні. Зовнішній шеринг з deeplink — ще 1-2 дні. Повна система з обома режимами — 2-3 дні при паралельній розробці платформ. Вартість розраховується індивідуально.







