Show "Join" button for pinned call links.

This commit is contained in:
John Preston 2025-05-07 18:03:59 +04:00
parent fb25d90b48
commit ee7a2b564b
4 changed files with 160 additions and 99 deletions

View file

@ -7651,12 +7651,12 @@ void HistoryWidget::checkPinnedBarState() {
}
return (count > 1);
}) | rpl::distinct_until_changed();
auto markupRefreshed = HistoryView::PinnedBarItemWithReplyMarkup(
auto customButtonItem = HistoryView::PinnedBarItemWithCustomButton(
&session(),
_pinnedTracker->shownMessageId());
rpl::combine(
rpl::duplicate(pinnedRefreshed),
rpl::duplicate(markupRefreshed)
rpl::duplicate(customButtonItem)
) | rpl::start_with_next([=](bool many, HistoryItem *item) {
refreshPinnedBarButton(many, item);
}, _pinnedBar->lifetime());
@ -7667,7 +7667,7 @@ void HistoryWidget::checkPinnedBarState() {
_pinnedTracker->shownMessageId(),
[bar = _pinnedBar.get()] { bar->customEmojiRepaint(); }),
std::move(pinnedRefreshed),
std::move(markupRefreshed)
std::move(customButtonItem)
) | rpl::map([=](Ui::MessageBarContent &&content, bool, HistoryItem*) {
const auto id = (!content.title.isEmpty() || !content.text.empty())
? _pinnedTracker->currentMessageId().message
@ -7805,58 +7805,31 @@ void HistoryWidget::refreshPinnedBarButton(bool many, HistoryItem *item) {
? id.message.msg
: (id.message.msg - ServerMaxMsgId))));
};
if (const auto replyMarkup = item ? item->inlineReplyMarkup() : nullptr) {
const auto &rows = replyMarkup->data.rows;
if ((rows.size() == 1) && (rows.front().size() == 1)) {
const auto text = rows.front().front().text;
if (!text.isEmpty()) {
const auto &st = st::historyPinnedBotButton;
auto button = object_ptr<Ui::RoundButton>(
this,
rpl::never<QString>(),
st);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button.data(),
text,
st::historyPinnedBotLabel);
if (label->width() > st::historyPinnedBotButtonMaxWidth) {
label->resizeToWidth(st::historyPinnedBotButtonMaxWidth);
}
button->setFullWidth(label->width()
+ st.padding.left()
+ st.padding.right()
+ st.height);
label->moveToLeft(
st.padding.left() + st.height / 2,
(button->height() - label->height()) / 2);
label->setTextColorOverride(st.textFg->c);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
button->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
button->setFullRadius(true);
button->setClickedCallback([=] {
Api::ActivateBotCommand(
_list->prepareClickHandlerContext(item->fullId()),
0,
0);
});
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto state = button->lifetime().make_state<State>();
_pinnedBar->contextMenuRequested(
) | rpl::start_with_next([=, raw = button.data()] {
state->menu = base::make_unique_q<Ui::PopupMenu>(raw);
state->menu->addAction(
tr::lng_settings_events_pinned(tr::now),
openSection);
state->menu->popup(QCursor::pos());
}, button->lifetime());
_pinnedBar->setRightButton(std::move(button));
return;
}
const auto context = [copy = _list](FullMsgId itemId) {
if (const auto raw = copy.data()) {
return raw->prepareClickHandlerContext(itemId);
}
return ClickHandlerContext();
};
auto customButton = CreatePinnedBarCustomButton(this, item, context);
if (customButton) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto buttonRaw = customButton.data();
const auto state = buttonRaw->lifetime().make_state<State>();
_pinnedBar->contextMenuRequested(
) | rpl::start_with_next([=] {
state->menu = base::make_unique_q<Ui::PopupMenu>(buttonRaw);
state->menu->addAction(
tr::lng_settings_events_pinned(tr::now),
openSection);
state->menu->popup(QCursor::pos());
}, buttonRaw->lifetime());
_pinnedBar->setRightButton(std::move(customButton));
return;
}
const auto close = !many;
auto button = object_ptr<Ui::IconButton>(
this,

View file

@ -7,17 +7,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_pinned_bar.h"
#include "api/api_bot.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_changes.h"
#include "data/data_poll.h"
#include "data/data_web_page.h"
#include "history/view/history_view_pinned_tracker.h"
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "history/history.h"
#include "core/click_handler_types.h"
#include "core/ui_integration.h"
#include "base/weak_ptr.h"
#include "apiwrap.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/basic_click_handlers.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
@ -153,6 +160,45 @@ auto WithPinnedTitle(not_null<Main::Session*> session, PinnedId id) {
};
}
[[nodiscard]] object_ptr<Ui::RoundButton> MakePinnedBarCustomButton(
not_null<QWidget*> parent,
const QString &buttonText,
Fn<void()> clickCallback) {
const auto &stButton = st::historyPinnedBotButton;
const auto &stLabel = st::historyPinnedBotLabel;
auto button = object_ptr<Ui::RoundButton>(
parent,
rpl::never<QString>(), // Text is handled by the inner label.
stButton);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button.data(),
buttonText,
stLabel);
if (label->width() > st::historyPinnedBotButtonMaxWidth) {
label->resizeToWidth(st::historyPinnedBotButtonMaxWidth);
}
button->setFullWidth(label->width()
+ stButton.padding.left()
+ stButton.padding.right()
+ stButton.height); // stButton.height is likely for icon spacing.
label->moveToLeft(
stButton.padding.left() + stButton.height / 2,
(button->height() - label->height()) / 2);
label->setTextColorOverride(stButton.textFg->c); // Use button's text color for label.
label->setAttribute(Qt::WA_TransparentForMouseEvents);
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
button->setFullRadius(true);
button->setClickedCallback(std::move(clickCallback));
return button;
}
} // namespace
rpl::producer<Ui::MessageBarContent> MessageBarContentByItemId(
@ -178,7 +224,7 @@ rpl::producer<Ui::MessageBarContent> PinnedBarContent(
}) | rpl::flatten_latest();
}
rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
rpl::producer<HistoryItem*> PinnedBarItemWithCustomButton(
not_null<Main::Session*> session,
rpl::producer<PinnedId> id) {
return rpl::make_producer<HistoryItem*>([=,
@ -187,7 +233,7 @@ rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
consumer.put_next(nullptr);
struct State {
bool hasReplyMarkup = false;
bool hasCustomButton = false;
base::has_weak_ptr guard;
rpl::lifetime lifetime;
FullMsgId resolvedId;
@ -196,10 +242,17 @@ rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
const auto pushUnique = [=](not_null<HistoryItem*> item) {
const auto replyMarkup = item->inlineReplyMarkup();
if (!state->hasReplyMarkup && !replyMarkup) {
const auto media = item->media();
const auto page = media ? media->webpage() : nullptr;
const auto possiblyHasCustomButton = replyMarkup
|| (page
&& (page->type == WebPageType::VoiceChat
|| page->type == WebPageType::Livestream
|| page->type == WebPageType::ConferenceCall));
if (!state->hasCustomButton && !possiblyHasCustomButton) {
return;
}
state->hasReplyMarkup = (replyMarkup != nullptr);
state->hasCustomButton = possiblyHasCustomButton;
consumer.put_next(item.get());
};
@ -217,12 +270,14 @@ rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
using Update = Data::MessageUpdate;
session->changes().messageUpdates(
item,
Update::Flag::ReplyMarkup | Update::Flag::Destroyed
(Update::Flag::ReplyMarkup
| Update::Flag::Edited
| Update::Flag::Destroyed)
) | rpl::start_with_next([=](const Update &update) {
if (update.flags & Update::Flag::Destroyed) {
state->lifetime.destroy();
invalidate_weak_ptrs(&state->guard);
state->hasReplyMarkup = false;
state->hasCustomButton = false;
consumer.put_next(nullptr);
} else {
pushUnique(update.item);
@ -248,4 +303,42 @@ rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
});
}
[[nodiscard]] object_ptr<Ui::RoundButton> CreatePinnedBarCustomButton(
not_null<QWidget*> parent,
HistoryItem *item,
Fn<ClickHandlerContext(FullMsgId)> context) {
if (!item) {
return nullptr;
} else if (const auto replyMarkup = item->inlineReplyMarkup()) {
const auto &rows = replyMarkup->data.rows;
if ((rows.size() == 1) && (rows.front().size() == 1)) {
const auto text = rows.front().front().text;
if (!text.isEmpty()) {
const auto contextId = item->fullId();
const auto callback = [=] {
Api::ActivateBotCommand(context(contextId), 0, 0);
};
return MakePinnedBarCustomButton(parent, text, callback);
}
}
} else if (const auto media = item->media()) {
if (const auto page = media->webpage()) {
if (page->type == WebPageType::VoiceChat
|| page->type == WebPageType::Livestream
|| page->type == WebPageType::ConferenceCall) {
const auto url = page->url;
const auto contextId = item->fullId();
const auto callback = [=] {
UrlClickHandler::Open(
url,
QVariant::fromValue(context(contextId)));
};
const auto text = tr::lng_group_call_join(tr::now);
return MakePinnedBarCustomButton(parent, text, callback);
}
}
}
return nullptr;
}
} // namespace HistoryView

View file

@ -7,10 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/object_ptr.h"
#include "ui/chat/message_bar.h"
#include <tuple>
struct ClickHandlerContext;
namespace Main {
class Session;
} // namespace Main
@ -18,6 +21,7 @@ class Session;
namespace Ui {
class IconButton;
class PlainShadow;
class RoundButton;
struct MessageBarContent;
} // namespace Ui
@ -51,8 +55,13 @@ struct PinnedId {
rpl::producer<PinnedId> id,
Fn<void()> repaint);
[[nodiscard]] rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
[[nodiscard]] rpl::producer<HistoryItem*> PinnedBarItemWithCustomButton(
not_null<Main::Session*> session,
rpl::producer<PinnedId> id);
[[nodiscard]] object_ptr<Ui::RoundButton> CreatePinnedBarCustomButton(
not_null<QWidget*> parent,
HistoryItem *item,
Fn<ClickHandlerContext(FullMsgId)> context);
} // namespace HistoryView

View file

@ -1838,12 +1838,12 @@ void RepliesWidget::checkPinnedBarState() {
}
return (count > 1);
}) | rpl::distinct_until_changed();
auto markupRefreshed = HistoryView::PinnedBarItemWithReplyMarkup(
auto customButtonItem = HistoryView::PinnedBarItemWithCustomButton(
&session(),
_pinnedTracker->shownMessageId());
rpl::combine(
rpl::duplicate(pinnedRefreshed),
rpl::duplicate(markupRefreshed)
rpl::duplicate(customButtonItem)
) | rpl::start_with_next([=](bool many, HistoryItem *item) {
refreshPinnedBarButton(many, item);
}, _pinnedBar->lifetime());
@ -1854,7 +1854,7 @@ void RepliesWidget::checkPinnedBarState() {
_pinnedTracker->shownMessageId(),
[bar = _pinnedBar.get()] { bar->customEmojiRepaint(); }),
std::move(pinnedRefreshed),
std::move(markupRefreshed),
std::move(customButtonItem),
_rootVisible.value()
) | rpl::map([=](Ui::MessageBarContent &&content, auto, auto, bool show) {
const auto shown = !content.title.isEmpty() && !content.text.empty();
@ -1935,43 +1935,29 @@ void RepliesWidget::refreshPinnedBarButton(bool many, HistoryItem *item) {
controller()->showSection(
std::make_shared<PinnedMemento>(_topic, id.message.msg));
};
if (const auto replyMarkup = item ? item->inlineReplyMarkup() : nullptr) {
const auto &rows = replyMarkup->data.rows;
if ((rows.size() == 1) && (rows.front().size() == 1)) {
const auto text = rows.front().front().text;
if (!text.isEmpty()) {
auto button = object_ptr<Ui::RoundButton>(
this,
rpl::single(text),
st::historyPinnedBotButton);
button->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
button->setFullRadius(true);
button->setClickedCallback([=] {
Api::ActivateBotCommand(
_inner->prepareClickHandlerContext(item->fullId()),
0,
0);
});
if (button->width() > st::historyPinnedBotButtonMaxWidth) {
button->setFullWidth(st::historyPinnedBotButtonMaxWidth);
}
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto state = button->lifetime().make_state<State>();
_pinnedBar->contextMenuRequested(
) | rpl::start_with_next([=, raw = button.data()] {
state->menu = base::make_unique_q<Ui::PopupMenu>(raw);
state->menu->addAction(
tr::lng_settings_events_pinned(tr::now),
openSection);
state->menu->popup(QCursor::pos());
}, button->lifetime());
_pinnedBar->setRightButton(std::move(button));
return;
}
const auto context = [copy = _inner](FullMsgId itemId) {
if (const auto raw = copy.data()) {
return raw->prepareClickHandlerContext(itemId);
}
return ClickHandlerContext();
};
auto customButton = CreatePinnedBarCustomButton(this, item, context);
if (customButton) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto buttonRaw = customButton.data();
const auto state = buttonRaw->lifetime().make_state<State>();
_pinnedBar->contextMenuRequested(
) | rpl::start_with_next([=] {
state->menu = base::make_unique_q<Ui::PopupMenu>(buttonRaw);
state->menu->addAction(
tr::lng_settings_events_pinned(tr::now),
openSection);
state->menu->popup(QCursor::pos());
}, buttonRaw->lifetime());
_pinnedBar->setRightButton(std::move(customButton));
return;
}
const auto close = !many;
auto button = object_ptr<Ui::IconButton>(