Collapsed deleted admin log message groups.

This commit is contained in:
23rd
2026-04-28 09:57:37 +03:00
committed by John Preston
parent c8f316302b
commit ffacf3a582
5 changed files with 488 additions and 38 deletions
+6
View File
@@ -6501,6 +6501,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_edited_message" = "{from} edited message:";
"lng_admin_log_previous_message" = "Original message";
"lng_admin_log_deleted_message" = "{from} deleted message:";
"lng_admin_log_deleted_messages_collapsed#one" = "{from} deleted {count} message from {names} ({link}).";
"lng_admin_log_deleted_messages_collapsed#other" = "{from} deleted {count} messages from {names} ({link}).";
"lng_admin_log_show_all" = "Show all";
"lng_admin_log_hide_all" = "Hide all";
"lng_admin_log_expand_more#one" = "Show {count} More Message";
"lng_admin_log_expand_more#other" = "Show {count} More Messages";
"lng_admin_log_sent_message" = "{from} sent this message:";
"lng_admin_log_participant_joined" = "{from} joined the group";
"lng_admin_log_participant_joined_channel" = "{from} joined the channel";
@@ -11,7 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media.h"
#include "history/view/media/history_view_web_page.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "history/history_item_components.h"
#include "history/history_item_reply_markup.h"
#include "history/history_item_text.h"
#include "history/admin_log/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_filter.h"
@@ -92,14 +94,14 @@ void InnerWidget::enumerateItems(Method method) {
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
// No displayed messages in this history.
if (_items.empty()) {
if (_displayItems.empty()) {
return;
}
if (_visibleBottom <= _itemsTop || _itemsTop + _itemsHeight <= _visibleTop) {
return;
}
auto begin = std::rbegin(_items), end = std::rend(_items);
auto begin = std::rbegin(_displayItems), end = std::rend(_displayItems);
auto from = TopToBottom ? std::lower_bound(begin, end, _visibleTop, [this](auto &elem, int top) {
return this->itemTop(elem) + elem->height() <= top;
}) : std::upper_bound(begin, end, _visibleBottom, [this](int bottom, auto &elem) {
@@ -110,13 +112,13 @@ void InnerWidget::enumerateItems(Method method) {
--from;
}
if (TopToBottom) {
Assert(itemTop(from->get()) + from->get()->height() > _visibleTop);
Assert(itemTop(*from) + (*from)->height() > _visibleTop);
} else {
Assert(itemTop(from->get()) < _visibleBottom);
Assert(itemTop(*from) < _visibleBottom);
}
while (true) {
auto item = from->get();
auto item = *from;
auto itemtop = itemTop(item);
auto itembottom = itemtop + item->height();
@@ -386,7 +388,7 @@ void InnerWidget::updateVisibleTopItem() {
if (_visibleBottom == height()) {
_visibleTopItem = nullptr;
} else {
auto begin = std::rbegin(_items), end = std::rend(_items);
auto begin = std::rbegin(_displayItems), end = std::rend(_displayItems);
auto from = std::lower_bound(begin, end, _visibleTop, [this](auto &&elem, int top) {
return this->itemTop(elem) + elem->height() <= top;
});
@@ -798,7 +800,11 @@ void InnerWidget::saveState(not_null<SectionMemento*> memento) {
memento->setAdmins(std::move(_admins));
memento->setAdminsCanEdit(std::move(_adminsCanEdit));
memento->setSearchQuery(std::move(_searchQuery));
memento->setExpandedGroups(std::move(_expandedGroups));
if (!_filterChanged) {
clearExpandButtons();
_displayItems.clear();
_summaryItems.clear();
for (auto &item : _items) {
item.clearView();
}
@@ -807,6 +813,9 @@ void InnerWidget::saveState(not_null<SectionMemento*> memento) {
base::take(_eventIds),
_upLoaded,
_downLoaded);
memento->setDeleteEventMeta(
base::take(_itemEventIds),
base::take(_eventAdminIds));
base::take(_itemsByData);
}
_upLoaded = _downLoaded = true; // Don't load or handle anything anymore.
@@ -819,7 +828,6 @@ void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
auto items = memento->takeItems();
for (auto &item : items) {
item.refreshView(this);
_itemsByData.emplace(item->data(), item.get());
}
_items = std::move(items);
@@ -828,11 +836,15 @@ void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
_adminsCanEdit = memento->takeAdminsCanEdit();
_filter = memento->takeFilter();
_searchQuery = memento->takeSearchQuery();
_expandedGroups = memento->takeExpandedGroups();
_itemEventIds = memento->takeItemEventIds();
_eventAdminIds = memento->takeEventAdminIds();
_upLoaded = memento->upLoaded();
_downLoaded = memento->downLoaded();
_filterChanged = false;
updateMinMaxIds();
updateSize();
computeDeleteGroups();
rebuildDisplayItems();
}
void InnerWidget::preloadMore(Direction direction) {
@@ -943,6 +955,13 @@ void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLo
}
const auto rememberRealMsgId = (antiSpamUserId
== peerToUser(peerFromUser(data.vuser_id())));
const auto isDeleteAction = (data.vaction().type()
== mtpc_channelAdminLogEventActionDeleteMessage);
if (isDeleteAction) {
_eventAdminIds.emplace(
id,
peerToUser(peerFromUser(data.vuser_id())));
}
auto count = 0;
const auto addOne = [&](
@@ -954,6 +973,7 @@ void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLo
}
_eventIds.emplace(id);
_itemsByData.emplace(item->data(), item.get());
_itemEventIds.emplace(item->data(), id);
if (realId) {
if (rememberRealMsgId) {
_antiSpamValidator.addEventMsgId(
@@ -991,7 +1011,8 @@ void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLo
_items = std::move(newItemsForDownDirection);
}
updateMinMaxIds();
itemsAdded(direction, newItemsCount - oldItemsCount);
computeDeleteGroups();
rebuildDisplayItems();
}
update();
}
@@ -1008,26 +1029,371 @@ void InnerWidget::updateMinMaxIds() {
}
}
void InnerWidget::itemsAdded(Direction direction, int addedCount) {
Expects(addedCount >= 0);
auto checkFrom = (direction == Direction::Up)
? (_items.size() - addedCount)
: 1; // Should be ": 0", but zero is skipped anyway.
auto checkTo = (direction == Direction::Up) ? (_items.size() + 1) : (addedCount + 1);
for (auto i = checkFrom; i != checkTo; ++i) {
if (i > 0) {
const auto view = _items[i - 1].get();
if (i < _items.size()) {
const auto previous = _items[i].get();
view->setDisplayDate(view->dateTime().date() != previous->dateTime().date());
const auto attach = view->computeIsAttachToPrevious(previous);
view->setAttachToPrevious(attach, previous);
previous->setAttachToNext(attach, view);
} else {
view->setDisplayDate(true);
void InnerWidget::computeDeleteGroups() {
_deleteGroups.clear();
if (_items.empty()) {
return;
}
// Walk _items and find consecutive runs of delete-action items
// from the same admin. Each delete event produces exactly 2 items
// (content + service). Delete events are identified via _eventAdminIds.
auto groupStart = -1;
auto groupAdmin = UserId();
auto groupEventId = uint64(0);
auto groupEventCount = 0;
auto currentEventId = uint64(0);
const auto finalizeGroup = [&](int endIndex) {
if (groupEventCount > 0) {
Assert(endIndex - groupStart >= groupEventCount * 2);
_deleteGroups.push_back({
.eventId = groupEventId,
.adminId = groupAdmin,
.startIndex = groupStart,
.endIndex = endIndex,
.eventCount = groupEventCount,
});
}
groupStart = -1;
groupAdmin = UserId();
groupEventId = 0;
groupEventCount = 0;
};
for (auto i = 0, count = int(_items.size()); i < count; ++i) {
const auto item = _items[i]->data();
const auto eit = _itemEventIds.find(item);
if (eit == _itemEventIds.end()) {
finalizeGroup(i);
continue;
}
const auto eventId = eit->second;
const auto adminIt = _eventAdminIds.find(eventId);
if (adminIt == _eventAdminIds.end()) {
finalizeGroup(i);
continue;
}
const auto adminId = adminIt->second;
if (eventId != currentEventId) {
// New event encountered.
if (groupStart < 0 || adminId != groupAdmin) {
finalizeGroup(i);
groupStart = i;
groupAdmin = adminId;
}
currentEventId = eventId;
groupEventId = eventId;
++groupEventCount;
}
// Items within the same event just extend the range.
}
finalizeGroup(int(_items.size()));
}
OwnedItem InnerWidget::createGroupSummaryItem(
const DeleteGroup &group,
bool expanded) {
const auto admin = _history->owner().user(group.adminId);
const auto fromLink = admin->createOpenLink();
const auto fromLinkText = tr::link(admin->name(), QString());
// Collect unique author names from content messages in the group.
constexpr auto kMaxNames = 4;
auto authorNames = QStringList();
auto seenAuthors = base::flat_set<PeerId>();
auto totalAuthors = 0;
for (auto i = group.startIndex; i < group.endIndex; ++i) {
const auto item = _items[i]->data();
if (!item->isService()) {
const auto authorId = item->from()->id;
if (!seenAuthors.contains(authorId)) {
seenAuthors.emplace(authorId);
++totalAuthors;
if (authorNames.size() < kMaxNames) {
authorNames.push_back(item->from()->name());
}
}
}
}
auto names = authorNames.join(u", "_q);
if (totalAuthors > kMaxNames) {
names += u", \u2026"_q;
}
const auto toggleText = expanded
? tr::lng_admin_log_hide_all(tr::now)
: tr::lng_admin_log_show_all(tr::now);
const auto groupEventId = group.eventId;
const auto toggleLink = std::make_shared<LambdaClickHandler>(
[weak = QPointer<InnerWidget>(this), groupEventId] {
if (const auto strong = weak.data()) {
strong->toggleDeleteGroup(groupEventId);
}
});
auto text = tr::lng_admin_log_deleted_messages_collapsed(
tr::now,
lt_count,
group.eventCount,
lt_from,
fromLinkText,
lt_names,
{ names },
lt_link,
tr::link(toggleText, QString()),
tr::marked);
auto message = PreparedServiceText{ text };
message.links.push_back(fromLink);
message.links.push_back(toggleLink);
const auto date = (group.endIndex > group.startIndex)
? _items[group.endIndex - 1]->dateTime().toSecsSinceEpoch()
: 0;
return OwnedItem(this, _history->makeMessage({
.id = _history->nextNonHistoryEntryId(),
.flags = MessageFlag::AdminLogEntry,
.from = peerFromUser(group.adminId),
.date = TimeId(date),
}, std::move(message)));
}
void InnerWidget::setupExpandButton(
not_null<HistoryItem*> item,
int hiddenCount,
uint64 groupEventId) {
const auto text = tr::lng_admin_log_expand_more(
tr::now,
lt_count,
hiddenCount);
auto markup = HistoryMessageMarkupData();
markup.flags = ReplyMarkupFlag::Inline;
markup.rows.push_back({
HistoryMessageMarkupButton(
HistoryMessageMarkupButton::Type::Callback,
text,
{},
QByteArray()),
});
item->updateReplyMarkup(std::move(markup));
_expandMarkupItems.emplace(item);
}
void InnerWidget::clearExpandButtons() {
for (const auto &item : _expandMarkupItems) {
item->updateReplyMarkup({});
}
_expandMarkupItems.clear();
}
void InnerWidget::toggleDeleteGroup(uint64 groupEventId) {
_toggleAnimation.stop();
const auto scrollBefore = _visibleTop;
// Find a stable scroll anchor from _items (not a summary item)
// near the current visible top position.
Element *anchor = nullptr;
auto anchorDelta = 0;
if (!_displayItems.empty()) {
auto begin = std::rbegin(_displayItems);
auto end = std::rend(_displayItems);
auto from = std::lower_bound(begin, end, _visibleTop,
[this](auto &elem, int top) {
return this->itemTop(elem) + elem->height() <= top;
});
for (auto it = from; it != end; ++it) {
const auto view = *it;
if (_itemEventIds.contains(view->data())) {
anchor = view;
anchorDelta = _visibleTop - itemTop(view);
break;
}
}
}
// Prepare a fallback anchor: the last item of the toggled group
// (always visible in both collapsed and expanded states).
Element *fallback = nullptr;
for (const auto &group : _deleteGroups) {
if (group.eventId == groupEventId && group.endIndex > 0) {
fallback = _items[group.endIndex - 1].get();
break;
}
}
if (_expandedGroups.contains(groupEventId)) {
_expandedGroups.erase(groupEventId);
} else {
_expandedGroups.insert(groupEventId);
}
// Clear pointers that may reference summary items
// which will be destroyed by rebuildDisplayItems().
_visibleTopItem = nullptr;
_scrollDateLastItem = nullptr;
_mouseActionItem = nullptr;
_selectedItem = nullptr;
Element::Hovered(nullptr);
Element::Pressed(nullptr);
Element::HoveredLink(nullptr);
Element::PressedLink(nullptr);
Element::Moused(nullptr);
// Rebuild without triggering scroll restore inside updateSize().
_skipScrollRestore = true;
rebuildDisplayItems();
_skipScrollRestore = false;
// Compute target scroll position.
auto scrollTarget = scrollBefore;
if (anchor && _itemsByData.contains(anchor->data())) {
scrollTarget = itemTop(anchor) + anchorDelta;
} else if (fallback && _itemsByData.contains(fallback->data())) {
scrollTarget = itemTop(fallback);
}
// Snap to old position and animate to target.
_scrollToSignal.fire_copy(scrollBefore);
if (scrollBefore != scrollTarget) {
const auto from = scrollBefore;
const auto to = scrollTarget;
_toggleAnimation.start(
[=] { _scrollToSignal.fire_copy(anim::interpolate(
from, to, _toggleAnimation.value(1.))); },
0.,
1.,
st::slideDuration,
anim::easeOutCubic);
}
}
void InnerWidget::clearTransientDisplayPointers() {
const auto transient = [this](const Element *view) {
return view
&& view->delegate().get() == this
&& !_itemEventIds.contains(view->data());
};
if (transient(_visibleTopItem)) {
_visibleTopItem = nullptr;
_visibleTopFromItem = 0;
}
if (transient(_scrollDateLastItem)) {
_scrollDateLastItem = nullptr;
_scrollDateLastItemTop = 0;
}
if (transient(_mouseActionItem)) {
_mouseActionItem = nullptr;
_mouseAction = MouseAction::None;
}
if (transient(_selectedItem)) {
_selectedItem = nullptr;
_selectedText = TextSelection();
}
if (transient(Element::Hovered())) {
Element::Hovered(nullptr);
ClickHandler::clearActive();
}
if (transient(Element::Pressed())) {
Element::Pressed(nullptr);
ClickHandler::unpressed();
}
if (transient(Element::HoveredLink())) {
Element::HoveredLink(nullptr);
ClickHandler::clearActive();
}
if (transient(Element::PressedLink())) {
Element::PressedLink(nullptr);
ClickHandler::unpressed();
}
if (transient(Element::Moused())) {
Element::Moused(nullptr);
}
}
void InnerWidget::rebuildDisplayItems() {
clearTransientDisplayPointers();
clearExpandButtons();
_summaryItems.clear();
_displayItems.clear();
_itemsByData.clear();
const auto groupDisplayEnabled = _searchQuery.isEmpty();
// Build a set of group start indices for quick lookup.
auto groupByStart = base::flat_map<int, int>(); // startIndex -> group index
for (auto g = 0, gc = int(_deleteGroups.size()); g < gc; ++g) {
groupByStart.emplace(_deleteGroups[g].startIndex, g);
}
auto i = 0;
const auto count = int(_items.size());
while (i < count) {
const auto git = groupByStart.find(i);
if (groupDisplayEnabled && git != groupByStart.end()) {
const auto &group = _deleteGroups[git->second];
if (group.eventCount > 3) {
const auto expanded = _expandedGroups.contains(group.eventId);
if (expanded) {
for (auto j = group.startIndex; j < group.endIndex; ++j) {
const auto view = _items[j].get();
_displayItems.push_back(view);
_itemsByData.emplace(view->data(), view);
}
} else if (group.endIndex >= group.startIndex + 2) {
// Collapsed: show only the content message
// (skip service header at endIndex-1).
const auto contentItem = _items[group.endIndex - 2]->data();
setupExpandButton(
contentItem,
group.eventCount - 1,
group.eventId);
const auto contentView = _items[group.endIndex - 2].get();
_displayItems.push_back(contentView);
_itemsByData.emplace(contentView->data(), contentView);
}
// Add summary item.
auto summary = createGroupSummaryItem(group, expanded);
const auto summaryView = summary.get();
_displayItems.push_back(summaryView);
_itemsByData.emplace(summaryView->data(), summaryView);
_summaryItems.push_back(std::move(summary));
i = group.endIndex;
continue;
}
}
const auto view = _items[i].get();
_displayItems.push_back(view);
_itemsByData.emplace(view->data(), view);
++i;
}
for (const auto view : _displayItems) {
view->setAttachToPrevious(false);
view->setAttachToNext(false);
}
for (auto d = 0, dc = int(_displayItems.size()); d < dc; ++d) {
const auto view = _displayItems[d];
if (d + 1 < dc) {
const auto previous = _displayItems[d + 1];
view->setDisplayDate(
view->dateTime().date() != previous->dateTime().date());
const auto attach = view->computeIsAttachToPrevious(previous);
view->setAttachToPrevious(attach, previous);
previous->setAttachToNext(attach, view);
} else {
view->setDisplayDate(true);
}
}
updateSize();
}
@@ -1043,12 +1409,13 @@ int InnerWidget::resizeGetHeight(int newWidth) {
const auto resizeAllItems = (_itemsWidth != newWidth);
auto newHeight = 0;
for (const auto &item : ranges::views::reverse(_items)) {
item->setY(newHeight);
if (item->pendingResize() || resizeAllItems) {
newHeight += item->resizeGetHeight(newWidth);
for (auto it = _displayItems.rbegin(); it != _displayItems.rend(); ++it) {
const auto view = *it;
view->setY(newHeight);
if (view->pendingResize() || resizeAllItems) {
newHeight += view->resizeGetHeight(newWidth);
} else {
newHeight += item->height();
newHeight += view->height();
}
}
_itemsWidth = newWidth;
@@ -1058,6 +1425,9 @@ int InnerWidget::resizeGetHeight(int newWidth) {
}
void InnerWidget::restoreScrollPosition() {
if (_skipScrollRestore) {
return;
}
const auto newVisibleTop = _visibleTopItem
? (itemTop(_visibleTopItem) + _visibleTopFromItem)
: ScrollMax;
@@ -1096,7 +1466,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
width(),
std::min(st::msgMaxWidth / 2, width() / 2));
auto begin = std::rbegin(_items), end = std::rend(_items);
auto begin = std::rbegin(_displayItems), end = std::rend(_displayItems);
auto from = std::lower_bound(begin, end, clip.top(), [this](auto &elem, int top) {
return this->itemTop(elem) + elem->height() <= top;
});
@@ -1104,11 +1474,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
return this->itemTop(elem) < bottom;
});
if (from != end) {
auto top = itemTop(from->get());
auto top = itemTop(*from);
context.translate(0, -top);
p.translate(0, top);
for (auto i = from; i != to; ++i) {
const auto view = i->get();
const auto view = *i;
context.outbg = view->hasOutLayout();
context.selection = (view == _selectedItem)
? _selectedText
@@ -1199,6 +1569,13 @@ void InnerWidget::clearAfterFilterChange() {
_selectedItem = nullptr;
_selectedText = TextSelection();
_filterChanged = false;
clearExpandButtons();
_displayItems.clear();
_summaryItems.clear();
_deleteGroups.clear();
_expandedGroups.clear();
_itemEventIds.clear();
_eventAdminIds.clear();
_items.clear();
_eventIds.clear();
_itemsByData.clear();
@@ -1882,6 +2259,20 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but
_wasSelectedText = false;
if (activated) {
// Intercept inline keyboard button clicks on items
// with our expand button markup.
if (_mouseActionItem) {
const auto item = _mouseActionItem->data();
if (dynamic_cast<ReplyMarkupClickHandler*>(activated.get())
&& _expandMarkupItems.contains(item)) {
const auto it = _itemEventIds.find(item);
if (it != _itemEventIds.end()) {
mouseActionCancel();
toggleDeleteGroup(it->second);
return;
}
}
}
mouseActionCancel();
ActivateClickHandler(window(), activated, {
button,
@@ -1925,13 +2316,13 @@ void InnerWidget::updateSelected() {
std::clamp(mousePosition.y(), _visibleTop, _visibleBottom));
auto itemPoint = QPoint();
auto begin = std::rbegin(_items), end = std::rend(_items);
auto begin = std::rbegin(_displayItems), end = std::rend(_displayItems);
auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight)
? std::lower_bound(begin, end, point.y(), [this](auto &elem, int top) {
return this->itemTop(elem) + elem->height() <= top;
})
: end;
const auto view = (from != end) ? from->get() : nullptr;
const auto view = (from != end) ? *from : nullptr;
const auto item = view ? view->data().get() : nullptr;
if (item) {
Element::Moused(view);
@@ -48,6 +48,14 @@ namespace AdminLog {
class SectionMemento;
struct DeleteGroup {
uint64 eventId = 0;
UserId adminId;
int startIndex = -1;
int endIndex = -1;
int eventCount = 0;
};
class InnerWidget final
: public Ui::RpWidget
, public Ui::AbstractTooltipShower
@@ -238,7 +246,6 @@ private:
void checkPreloadMore();
void updateVisibleTopItem();
void preloadMore(Direction direction);
void itemsAdded(Direction direction, int addedCount);
void updateSize();
void updateMinMaxIds();
void updateEmptyText();
@@ -248,6 +255,18 @@ private:
void addEvents(
Direction direction,
const QVector<MTPChannelAdminLogEvent> &events);
void computeDeleteGroups();
void rebuildDisplayItems();
void clearTransientDisplayPointers();
void toggleDeleteGroup(uint64 groupEventId);
OwnedItem createGroupSummaryItem(
const DeleteGroup &group,
bool expanded);
void setupExpandButton(
not_null<HistoryItem*> item,
int hiddenCount,
uint64 groupEventId);
void clearExpandButtons();
[[nodiscard]] Element *viewForItem(const HistoryItem *item);
[[nodiscard]] bool myView(
not_null<const HistoryView::Element*> view) const;
@@ -307,6 +326,18 @@ private:
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
base::flat_map<FullMsgId, MsgId> _realIdsForReport;
// Delete event grouping.
std::vector<Element*> _displayItems;
std::vector<DeleteGroup> _deleteGroups;
std::set<uint64> _expandedGroups;
std::vector<OwnedItem> _summaryItems;
base::flat_map<not_null<const HistoryItem*>, uint64> _itemEventIds;
base::flat_map<uint64, UserId> _eventAdminIds;
base::flat_set<not_null<HistoryItem*>> _expandMarkupItems;
Ui::Animations::Simple _toggleAnimation;
bool _skipScrollRestore = false;
int _itemsTop = 0;
int _itemsWidth = 0;
int _itemsHeight = 0;
@@ -159,6 +159,24 @@ public:
QString takeSearchQuery() {
return std::move(_searchQuery);
}
void setExpandedGroups(std::set<uint64> &&groups) {
_expandedGroups = std::move(groups);
}
std::set<uint64> takeExpandedGroups() {
return std::move(_expandedGroups);
}
void setDeleteEventMeta(
base::flat_map<not_null<const HistoryItem*>, uint64> &&itemEventIds,
base::flat_map<uint64, UserId> &&eventAdminIds) {
_itemEventIds = std::move(itemEventIds);
_eventAdminIds = std::move(eventAdminIds);
}
auto takeItemEventIds() {
return std::move(_itemEventIds);
}
auto takeEventAdminIds() {
return std::move(_eventAdminIds);
}
private:
not_null<ChannelData*> _channel;
@@ -171,6 +189,9 @@ private:
bool _downLoaded = true;
FilterValue _filter;
QString _searchQuery;
std::set<uint64> _expandedGroups;
base::flat_map<not_null<const HistoryItem*>, uint64> _itemEventIds;
base::flat_map<uint64, UserId> _eventAdminIds;
};
@@ -1060,7 +1060,8 @@ ClickHandlerPtr ReplyKeyboard::getLink(QPoint point) const {
if (rect.contains(point)) {
if (_item->isAdminLogEntry()
&& button.type != HistoryMessageMarkupButton::Type::Url) {
&& button.type != HistoryMessageMarkupButton::Type::Url
&& button.type != HistoryMessageMarkupButton::Type::Callback) {
return ClickHandlerPtr();
}
_savedCoords = point;