diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 55555727c7..cd42bc0994 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4455,6 +4455,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_inline_bot_no_results" = "No results."; "lng_inline_bot_via" = "via {inline_bot}"; +"lng_guest_chat_for" = "for {user}"; "lng_box_remove" = "Remove"; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 89d145c0bd..c5313fb154 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -591,6 +591,7 @@ not_null Session::processUser(const MTPUser &data) { info->hasMainApp = data.is_bot_has_main_app(); info->userCreatesTopics = data.is_bot_forum_can_manage_topics(); info->canManageBots = data.is_bot_can_manage_bots(); + info->supportsGuestChat = data.is_bot_guestchat(); } } diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 54381b6330..43d1f4faff 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -367,6 +367,8 @@ enum class MessageFlag : uint64 { TextAppearing = (1ULL << 60), TextAppearingStarted = (1ULL << 61), + + GuestChatViaFrom = (1ULL << 62), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index e1894fcd26..8eb1076614 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -108,6 +108,7 @@ struct BotInfo { bool userCreatesTopics : 1 = false; bool setBotPhotoHidden : 1 = false; bool canManageBots : 1 = false; + bool supportsGuestChat : 1 = false; private: std::unique_ptr _forum; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 88c7bba933..fd01503198 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -168,6 +168,7 @@ struct HistoryItem::CreateConfig { UserId viaBotId = 0; UserId viaBusinessBotId = 0; + PeerId guestChatViaFrom = 0; int viewsCount = -1; int forwardsCount = -1; int boostsApplied = 0; @@ -1917,6 +1918,10 @@ UserData *HistoryItem::viaBot() const { return nullptr; } +bool HistoryItem::isGuestChatBotMessage() const { + return (_flags & MessageFlag::GuestChatViaFrom); +} + UserData *HistoryItem::getMessageBot() const { if (const auto bot = viaBot()) { return bot; @@ -4203,7 +4208,7 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { const auto sender = [&]() -> std::optional { if (options.hideSender || isPostHidingAuthor() || isEmpty()) { return {}; - } else if (!_history->peer->isUser()) { + } else if (!_history->peer->isUser() || isGuestChatBotMessage()) { if (const auto from = displayFrom()) { return fromSender(from); } @@ -4260,6 +4265,9 @@ void HistoryItem::createComponents(CreateConfig &&config) { if (config.viaBotId) { mask |= HistoryMessageVia::Bit(); } + if (config.guestChatViaFrom) { + mask |= HistoryMessageGuestChat::Bit(); + } if (config.viewsCount >= 0 || !config.replies.isNull) { mask |= HistoryMessageViews::Bit(); } @@ -4354,6 +4362,9 @@ void HistoryItem::createComponents(CreateConfig &&config) { if (const auto via = Get()) { via->create(&_history->owner(), config.viaBotId); } + if (const auto guestChat = Get()) { + guestChat->create(&_history->owner(), config.guestChatViaFrom); + } if (Has()) { changeViewsCount(config.viewsCount); if (config.replies.isNull @@ -4737,6 +4748,9 @@ void HistoryItem::createComponents(const MTPDmessage &data) { } config.viaBotId = data.vvia_bot_id().value_or_empty(); config.viaBusinessBotId = data.vvia_business_bot_id().value_or_empty(); + config.guestChatViaFrom = data.vguestchat_via_from() + ? peerFromMTP(*data.vguestchat_via_from()) + : PeerId(); config.viewsCount = data.vviews().value_or(-1); config.forwardsCount = data.vforwards().value_or(-1); config.replies = isScheduled() diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index dd189bccef..1da3599ef8 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -193,6 +193,7 @@ public: void updateStoryMentionText(); [[nodiscard]] UserData *viaBot() const; + [[nodiscard]] bool isGuestChatBotMessage() const; [[nodiscard]] UserData *getMessageBot() const; [[nodiscard]] bool hideLinks() const; [[nodiscard]] bool isHistoryEntry() const; diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 47007d9c39..b77cf37ecb 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -163,6 +163,42 @@ void HistoryMessageVia::resize(int32 availw) const { } } +void HistoryMessageGuestChat::create( + not_null owner, + PeerId visitorId) { + visitor = owner->peer(visitorId); + const auto firstName = visitor->isUser() + ? visitor->asUser()->firstName + : visitor->name(); + maxWidth = st::msgServiceNameFont->width( + tr::lng_guest_chat_for(tr::now, lt_user, firstName)); + link = std::make_shared([peer = this->visitor]( + ClickContext context) { + const auto my = context.other.value(); + if (const auto controller = my.sessionWindow.get()) { + controller->showPeerInfo(peer); + } + }); +} + +void HistoryMessageGuestChat::resize(int32 availw) const { + if (availw < 0) { + text = QString(); + width = 0; + } else { + const auto firstName = visitor->isUser() + ? visitor->asUser()->firstName + : visitor->name(); + text = tr::lng_guest_chat_for(tr::now, lt_user, firstName); + if (availw < maxWidth) { + text = st::msgServiceNameFont->elided(text, availw); + width = st::msgServiceNameFont->width(text); + } else if (width < maxWidth) { + width = maxWidth; + } + } +} + HiddenSenderInfo::HiddenSenderInfo( const QString &name, bool external, diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 39fd5058e1..9dcd817b26 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -78,6 +78,18 @@ struct HistoryMessageVia : RuntimeComponent { ClickHandlerPtr link; }; +struct HistoryMessageGuestChat +: RuntimeComponent { + void create(not_null owner, PeerId visitorId); + void resize(int32 availw) const; + + PeerData *visitor = nullptr; + mutable QString text; + mutable int width = 0; + mutable int maxWidth = 0; + ClickHandlerPtr link; +}; + struct HistoryMessageViews : RuntimeComponent { static constexpr auto kMaxRecentRepliers = 3; diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 5068d3aaac..11dd7b40dd 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -859,6 +859,9 @@ MessageFlags FlagsFromMTP( : Flag()) | ((flags & MTP::f_summary_from_language) ? Flag::CanBeSummarized + : Flag()) + | ((flags & MTP::f_guestchat_via_from) + ? Flag::GuestChatViaFrom : Flag()); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index b94296a470..d1a6a18d43 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -1973,6 +1973,7 @@ bool Element::computeIsAttachToPrevious(not_null previous) { return !item->isService() && !item->isEmpty() && !item->isPostHidingAuthor() + && !item->isGuestChatBotMessage() && (!item->history()->peer->isMegagroup() || !view->hasOutLayout() || !item->from()->isChannel()); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 7027e88378..c6415941c6 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -891,6 +891,9 @@ QSize Message::performCountOptimalSize() { namew += st::msgServiceFont->spacew + via->maxWidth + (_fromNameStatus ? st::msgServiceFont->spacew : 0); } + if (const auto guestChat = item->Get()) { + namew += st::msgServiceFont->spacew + guestChat->maxWidth; + } if (Has()) { namew += st::msgPadding.right() + rightBadgeWidth(); } @@ -1904,6 +1907,20 @@ void Message::paintFromName( availableLeft += skipWidth; availableWidth -= skipWidth; } + if (const auto guestChat = item->Get()) { + if (availableWidth > 0) { + p.setPen(stm->msgServiceFg); + paintLinkRipple( + p, + guestChat->link, + QRect(availableLeft, trect.top(), guestChat->width, st::msgServiceFont->height), + trect.topLeft()); + p.drawText(availableLeft, trect.top() + st::msgServiceFont->ascent, guestChat->text); + auto skipWidth = guestChat->width + st::msgServiceFont->spacew; + availableLeft += skipWidth; + availableWidth -= skipWidth; + } + } if (badgeWidth) { p.setPen(stm->msgDateFg); if (const auto badge = Get()) { @@ -2927,6 +2944,9 @@ bool Message::hasFromPhoto() const { return !hasOutLayout(); } } + if (item->isGuestChatBotMessage()) { + return true; + } return !item->out() && !item->history()->peer->isUser(); } break; case Context::ContactPreview: @@ -3325,6 +3345,24 @@ bool Message::getStateFromName( recordLinkRipplePoint(point, trect.topLeft()); return true; } + if (const auto guestChat = item->Get()) { + auto guestChatLeft = availableLeft + nameText->maxWidth() + + st::msgServiceFont->spacew; + if (via && !displayForwardedFrom()) { + guestChatLeft += via->width + st::msgServiceFont->spacew; + } + if (_fromNameStatus) { + guestChatLeft += st::dialogsPremiumIcon.icon.width() + + st::msgServiceFont->spacew; + } + if (point.x() >= guestChatLeft + && point.x() < availableLeft + availableWidth + && point.x() < guestChatLeft + guestChat->width) { + outResult->link = guestChat->link; + recordLinkRipplePoint(point, trect.topLeft()); + return true; + } + } if (badgeWidth) { const auto badge = Get(); const auto badgeLeft = trect.left() @@ -4338,7 +4376,7 @@ bool Message::hasFromName() const { } } return false; - } else if (!peer->isUser()) { + } else if (!peer->isUser() || item->isGuestChatBotMessage()) { if (const auto media = this->media()) { return !media->hideFromName(); } @@ -4416,6 +4454,9 @@ bool Message::hasOutLayout() const { } } } + if (item->isGuestChatBotMessage()) { + return false; + } return item->out() && !item->isPost(); } @@ -4922,6 +4963,33 @@ void Message::fromNameUpdated(int width) const { - st::msgServiceFont->spacew); } } + if (const auto guestChat = item->Get()) { + const auto nameText = [&]() -> const Ui::Text::String * { + if (from) { + return &_fromName; + } else if (const auto info = item->originalHiddenSenderInfo()) { + return &info->nameText(); + } else { + Unexpected("Corrupted forwarded information in message."); + } + }(); + auto viaWidth = 0; + if (const auto via = item->Get()) { + if (!displayForwardedFrom()) { + viaWidth = st::msgServiceFont->spacew + via->width; + } + } + guestChat->resize(width + - st::msgPadding.left() + - st::msgPadding.right() + - nameText->maxWidth() + - (_fromNameStatus + ? (st::dialogsPremiumIcon.icon.width() + + st::msgServiceFont->spacew) + : 0) + - st::msgServiceFont->spacew + - viaWidth); + } } TextSelection Message::skipTextSelection(TextSelection selection) const {