init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/admin_log/history_admin_log_filter.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "history/admin_log/history_admin_log_filter_value.h"
#include "lang/lang_keys.h"
#include "ui/wrap/vertical_layout.h"
namespace AdminLog {
EditFlagsDescriptor<FilterValue::Flags> FilterValueLabels(bool isChannel) {
using Label = EditFlagsLabel<FilterValue::Flags>;
using Flag = FilterValue::Flag;
const auto adminRights = Flag::Promote | Flag::Demote;
const auto restrictions = Flag::Ban
| Flag::Unban
| Flag::Kick
| Flag::Unkick;
const auto membersNew = Flag::Join | Flag::Invite;
const auto membersRemoved = Flag::Leave;
auto membersNewText = (isChannel
? tr::lng_admin_log_filter_subscribers_new
: tr::lng_admin_log_filter_members_new)(tr::now);
auto membersRemovedText = (isChannel
? tr::lng_admin_log_filter_subscribers_removed
: tr::lng_admin_log_filter_members_removed)(tr::now);
auto members = std::vector<Label>{
{ adminRights, tr::lng_admin_log_filter_admins_new(tr::now) },
{ restrictions, tr::lng_admin_log_filter_restrictions(tr::now) },
{ membersNew, std::move(membersNewText) },
{ membersRemoved, std::move(membersRemovedText) },
};
const auto info = Flag::Info | Flag::Settings;
const auto invites = Flag::Invites;
const auto calls = Flag::GroupCall;
auto settings = std::vector<Label>{
{
info,
((!isChannel)
? tr::lng_admin_log_filter_info_group
: tr::lng_admin_log_filter_info_channel)(tr::now),
},
{ invites, tr::lng_admin_log_filter_invite_links(tr::now) },
{
calls,
((!isChannel)
? tr::lng_admin_log_filter_voice_chats
: tr::lng_admin_log_filter_voice_chats_channel)(tr::now),
},
{
Flag::SubExtend,
tr::lng_admin_log_filter_sub_extend(tr::now),
},
};
if (!isChannel) {
settings.push_back({
Flag::Topics,
tr::lng_admin_log_filter_topics(tr::now),
});
}
const auto deleted = Flag::Delete;
const auto edited = Flag::Edit;
const auto pinned = Flag::Pinned;
auto messages = std::vector<Label>{
{ deleted, tr::lng_admin_log_filter_messages_deleted(tr::now) },
{ edited, tr::lng_admin_log_filter_messages_edited(tr::now) },
};
if (!isChannel) {
messages.push_back({
pinned,
tr::lng_admin_log_filter_messages_pinned(tr::now),
});
}
return { .labels = {
{
!isChannel
? tr::lng_admin_log_filter_actions_member_section()
: tr::lng_admin_log_filter_actions_subscriber_section(),
std::move(members),
},
{
!isChannel
? tr::lng_admin_log_filter_actions_settings_section()
: tr::lng_admin_log_filter_actions_channel_settings_section(),
std::move(settings),
},
{
tr::lng_admin_log_filter_actions_messages_section(),
std::move(messages),
},
}, .st = nullptr };
}
Fn<FilterValue::Flags()> FillFilterValueList(
not_null<Ui::VerticalLayout*> container,
bool isChannel,
const FilterValue &filter) {
auto [checkboxes, getResult, changes] = CreateEditAdminLogFilter(
container,
filter.flags ? (*filter.flags) : ~FilterValue::Flags(0),
isChannel);
container->add(std::move(checkboxes));
return getResult;
}
} // namespace AdminLog

View File

@@ -0,0 +1,31 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/admin_log/history_admin_log_filter_value.h"
#include "ui/layers/box_content.h"
template <typename Flags>
struct EditFlagsDescriptor;
namespace Ui {
class VerticalLayout;
} // namespace Ui
namespace AdminLog {
struct FilterValue;
[[nodiscard]] Fn<FilterValue::Flags()> FillFilterValueList(
not_null<Ui::VerticalLayout*> container,
bool isChannel,
const FilterValue &filter);
EditFlagsDescriptor<FilterValue::Flags> FilterValueLabels(bool isChannel);
} // namespace AdminLog

View File

@@ -0,0 +1,53 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace AdminLog {
struct FilterValue final {
enum class Flag : uint32 {
Join = (1U << 0),
Leave = (1U << 1),
Invite = (1U << 2),
Ban = (1U << 3),
Unban = (1U << 4),
Kick = (1U << 5),
Unkick = (1U << 6),
Promote = (1U << 7),
Demote = (1U << 8),
Info = (1U << 9),
Settings = (1U << 10),
Pinned = (1U << 11),
Edit = (1U << 12),
Delete = (1U << 13),
GroupCall = (1U << 14),
Invites = (1U << 15),
Topics = (1U << 16),
SubExtend = (1U << 17),
MAX_FIELD = (1U << 17),
};
using Flags = base::flags<Flag>;
friend inline constexpr bool is_flag_type(Flag) { return true; };
// Std::nullopt "flags" means all events.
std::optional<Flags> flags = std::nullopt;
// Std::nullopt admins means all users.
std::optional<std::vector<not_null<UserData*>>> admins = std::nullopt;
};
inline bool operator==(const FilterValue &a, const FilterValue &b) {
return (a.flags == b.flags) && (a.admins == b.admins);
}
inline bool operator!=(const FilterValue &a, const FilterValue &b) {
return !(a == b);
}
} // namespace AdminLog

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/view/history_view_element.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_filter_value.h"
#include "menu/menu_antispam_validator.h"
#include "ui/rp_widget.h"
#include "ui/effects/animations.h"
#include "ui/widgets/tooltip.h"
#include "mtproto/sender.h"
#include "base/timer.h"
struct ChatRestrictionsInfo;
namespace Main {
class Session;
} // namespace Main
namespace HistoryView {
class Element;
struct TextState;
struct StateRequest;
enum class CursorState : char;
enum class PointState : char;
} // namespace HistoryView
namespace Ui {
class PopupMenu;
class ChatStyle;
class ChatTheme;
struct PeerUserpicView;
struct ChatPaintContext;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
namespace AdminLog {
class SectionMemento;
class InnerWidget final
: public Ui::RpWidget
, public Ui::AbstractTooltipShower
, public HistoryView::ElementDelegate {
public:
InnerWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<ChannelData*> channel);
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] not_null<Ui::ChatTheme*> theme() const {
return _theme.get();
}
[[nodiscard]] rpl::producer<> showSearchSignal() const;
[[nodiscard]] rpl::producer<int> scrollToSignal() const;
[[nodiscard]] rpl::producer<> cancelSignal() const;
[[nodiscard]] not_null<ChannelData*> channel() const {
return _channel;
}
Ui::ChatPaintContext preparePaintContext(QRect clip) const;
// Set the correct scroll position after being resized.
void restoreScrollPosition();
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return RpWidget::resizeToWidth(newWidth);
}
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
// Empty "flags" means all events.
void applyFilter(FilterValue &&value);
void applySearch(const QString &query);
void showFilter(Fn<void(FilterValue &&filter)> callback);
// Ui::AbstractTooltipShower interface.
QString tooltipText() const override;
QPoint tooltipPos() const override;
bool tooltipWindowActive() const override;
// HistoryView::ElementDelegate interface.
HistoryView::Context elementContext() override;
bool elementUnderCursor(
not_null<const HistoryView::Element*> view) override;
HistoryView::SelectionModeResult elementInSelectionMode(
const HistoryView::Element *view) override;
bool elementIntersectsRange(
not_null<const HistoryView::Element*> view,
int from,
int till) override;
void elementStartStickerLoop(
not_null<const HistoryView::Element*> view) override;
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override;
void elementOpenPhoto(
not_null<PhotoData*> photo,
FullMsgId context) override;
void elementOpenDocument(
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false) override;
void elementCancelUpload(const FullMsgId &context) override;
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override;
bool elementAnimationsPaused() override;
bool elementHideReply(
not_null<const HistoryView::Element*> view) override;
bool elementShownUnread(
not_null<const HistoryView::Element*> view) override;
void elementSendBotCommand(
const QString &command,
const FullMsgId &context) override;
void elementSearchInList(
const QString &query,
const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData*> bot) override;
HistoryView::ElementChatMode elementChatMode() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction(
not_null<const HistoryView::Element*> view) override;
void elementStartPremium(
not_null<const HistoryView::Element*> view,
HistoryView::Element *replacing) override;
void elementCancelPremium(
not_null<const HistoryView::Element*> view) override;
void elementStartEffect(
not_null<const HistoryView::Element*> view,
HistoryView::Element *replacing) override;
QString elementAuthorRank(
not_null<const HistoryView::Element*> view) override;
bool elementHideTopicButton(
not_null<const HistoryView::Element*> view) override;
~InnerWidget();
protected:
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
void enterEventHook(QEnterEvent *e) override;
void leaveEventHook(QEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private:
using Element = HistoryView::Element;
enum class Direction {
Up,
Down,
};
enum class MouseAction {
None,
PrepareDrag,
Dragging,
Selecting,
};
enum class EnumItemsDirection {
TopToBottom,
BottomToTop,
};
using TextState = HistoryView::TextState;
using CursorState = HistoryView::CursorState;
using PointState = HistoryView::PointState;
using StateRequest = HistoryView::StateRequest;
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionUpdate(const QPoint &screenPos);
void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button);
void mouseActionCancel();
void updateSelected();
void performDrag();
int itemTop(not_null<const Element*> view) const;
void repaintItem(const Element *view);
void refreshItem(not_null<const Element*> view);
void resizeItem(not_null<Element*> view);
QPoint mapPointToItem(QPoint point, const Element *view) const;
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
void savePhotoToFile(not_null<PhotoData*> photo);
void saveDocumentToFile(not_null<DocumentData*> document);
void copyContextImage(not_null<PhotoData*> photo);
void showStickerPackInfo(not_null<DocumentData*> document);
void cancelContextDownload(not_null<DocumentData*> document);
void showContextInFolder(not_null<DocumentData*> document);
void openContextGif(FullMsgId itemId);
void copyContextText(FullMsgId itemId);
void copySelectedText();
TextForMimeData getSelectedText() const;
void suggestRestrictParticipant(not_null<PeerData*> participant);
void restrictParticipant(
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights);
void restrictParticipantDone(
not_null<PeerData*> participant,
ChatRestrictionsInfo rights);
void requestAdmins();
void checkPreloadMore();
void updateVisibleTopItem();
void preloadMore(Direction direction);
void itemsAdded(Direction direction, int addedCount);
void updateSize();
void updateMinMaxIds();
void updateEmptyText();
void paintEmpty(Painter &p, not_null<const Ui::ChatStyle*> st);
void clearAfterFilterChange();
void clearAndRequestLog();
void addEvents(
Direction direction,
const QVector<MTPChannelAdminLogEvent> &events);
[[nodiscard]] Element *viewForItem(const HistoryItem *item);
[[nodiscard]] bool myView(
not_null<const HistoryView::Element*> view) const;
void toggleScrollDateShown();
void repaintScrollDateCallback();
bool displayScrollDate() const;
void scrollDateHide();
void scrollDateCheck();
void scrollDateHideByTimer();
// This function finds all history items that are displayed and calls template method
// for each found message (in given direction) in the passed history with passed top offset.
//
// Method has "bool (*Method)(not_null<Element*> view, int itemtop, int itembottom)" signature
// if it returns false the enumeration stops immediately.
template <EnumItemsDirection direction, typename Method>
void enumerateItems(Method method);
// This function finds all userpics on the left that are displayed and calls template method
// for each found userpic (from the top to the bottom) using enumerateItems() method.
//
// Method has "bool (*Method)(not_null<Element*> view, int userpicTop)" signature
// if it returns false the enumeration stops immediately.
template <typename Method>
void enumerateUserpics(Method method);
// This function finds all date elements that are displayed and calls template method
// for each found date element (from the bottom to the top) using enumerateItems() method.
//
// Method has "bool (*Method)(not_null<HistoryItem*> item, int itemtop, int dateTop)" signature
// if it returns false the enumeration stops immediately.
template <typename Method>
void enumerateDates(Method method);
const not_null<Window::SessionController*> _controller;
const not_null<ChannelData*> _channel;
const not_null<History*> _history;
MTP::Sender _api;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
std::shared_ptr<Ui::ChatTheme> _theme;
std::vector<OwnedItem> _items;
std::set<uint64> _eventIds;
std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData;
base::flat_map<not_null<const HistoryItem*>, TimeId> _itemDates;
base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
int _itemsTop = 0;
int _itemsWidth = 0;
int _itemsHeight = 0;
int _minHeight = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
Element *_visibleTopItem = nullptr;
int _visibleTopFromItem = 0;
bool _isChatWide = false;
bool _scrollDateShown = false;
Ui::Animations::Simple _scrollDateOpacity;
SingleQueuedInvokation _scrollDateCheck;
base::Timer _scrollDateHideTimer;
Element *_scrollDateLastItem = nullptr;
int _scrollDateLastItemTop = 0;
// Up - max, Down - min.
uint64 _maxId = 0;
uint64 _minId = 0;
mtpRequestId _preloadUpRequestId = 0;
mtpRequestId _preloadDownRequestId = 0;
// Don't load anything until the memento was read.
bool _upLoaded = true;
bool _downLoaded = true;
bool _filterChanged = false;
Ui::Text::String _emptyText;
MouseAction _mouseAction = MouseAction::None;
TextSelectType _mouseSelectType = TextSelectType::Letters;
QPoint _dragStartPosition;
QPoint _mousePosition;
Element *_mouseActionItem = nullptr;
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;
Element *_selectedItem = nullptr;
TextSelection _selectedText;
bool _wasSelectedText = false; // was some text selected in current drag action
Qt::CursorShape _cursor = style::cur_default;
base::unique_qptr<Ui::PopupMenu> _menu;
AntiSpamMenu::AntiSpamValidator _antiSpamValidator;
QPoint _trippleClickPoint;
base::Timer _trippleClickTimer;
FilterValue _filter;
QString _searchQuery;
std::vector<not_null<UserData*>> _admins;
std::vector<not_null<UserData*>> _adminsCanEdit;
Fn<void(FilterValue &&filter)> _showFilterCallback;
rpl::event_stream<> _showSearchSignal;
rpl::event_stream<int> _scrollToSignal;
rpl::event_stream<> _cancelSignal;
};
} // namespace AdminLog

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
namespace HistoryView {
class ElementDelegate;
class Element;
} // namespace HistoryView
namespace AdminLog {
class OwnedItem;
void GenerateItems(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
const MTPDchannelAdminLogEvent &event,
Fn<void(OwnedItem item, TimeId sentDate, MsgId)> callback);
// Smart pointer wrapper for HistoryItem* that destroys the owned item.
class OwnedItem {
public:
OwnedItem(std::nullptr_t = nullptr);
OwnedItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<HistoryItem*> data);
OwnedItem(const OwnedItem &other) = delete;
OwnedItem &operator=(const OwnedItem &other) = delete;
OwnedItem(OwnedItem &&other);
OwnedItem &operator=(OwnedItem &&other);
~OwnedItem();
[[nodiscard]] HistoryView::Element *get() const {
return _view.get();
}
[[nodiscard]] HistoryView::Element *operator->() const {
return get();
}
[[nodiscard]] operator HistoryView::Element*() const {
return get();
}
void refreshView(not_null<HistoryView::ElementDelegate*> delegate);
void clearView();
private:
HistoryItem *_data = nullptr;
std::unique_ptr<HistoryView::Element> _view;
};
} // namespace AdminLog

View File

@@ -0,0 +1,557 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/admin_log/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_inner.h"
#include "history/admin_log/history_admin_log_filter.h"
#include "profile/profile_back_button.h"
#include "core/shortcuts.h"
#include "ui/chat/chat_style.h"
#include "ui/controls/swipe_handler.h"
#include "ui/effects/animations.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/ui_utility.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "window/themes/window_theme.h"
#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "base/timer.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
#include "styles/style_info.h"
namespace AdminLog {
class FixedBar final : public Ui::RpWidget {
public:
FixedBar(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<ChannelData*> channel);
[[nodiscard]] rpl::producer<> showFilterRequests() const;
[[nodiscard]] rpl::producer<> searchCancelRequests() const;
[[nodiscard]] rpl::producer<QString> searchRequests() const;
// When animating mode is enabled the content is hidden and the
// whole fixed bar acts like a back button.
void setAnimatingMode(bool enabled);
void applyFilter(const FilterValue &value);
void goBack();
void showSearch();
bool setSearchFocus() {
if (_searchShown) {
_field->setFocus();
return true;
}
return false;
}
protected:
void mousePressEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
void toggleSearch();
void cancelSearch();
void searchUpdated();
void applySearch();
void searchAnimationCallback();
not_null<Window::SessionController*> _controller;
not_null<ChannelData*> _channel;
object_ptr<Ui::InputField> _field;
object_ptr<Profile::BackButton> _backButton;
object_ptr<Ui::IconButton> _search;
object_ptr<Ui::CrossButton> _cancel;
object_ptr<Ui::RoundButton> _filter;
Ui::Animations::Simple _searchShownAnimation;
bool _searchShown = false;
bool _animatingMode = false;
base::Timer _searchTimer;
rpl::event_stream<> _searchCancelRequests;
rpl::event_stream<QString> _searchRequests;
};
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
Window::Column column,
const QRect &geometry) {
if (column == Window::Column::Third) {
return nullptr;
}
auto result = object_ptr<Widget>(parent, controller, _channel);
result->setInternalState(geometry, this);
return result;
}
FixedBar::FixedBar(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<ChannelData*> channel)
: RpWidget(parent)
, _controller(controller)
, _channel(channel)
, _field(this, st::defaultMultiSelectSearchField, tr::lng_dlg_filter())
, _backButton(
this,
&controller->session(),
tr::lng_admin_log_title_all(tr::now),
controller->adaptive().oneColumnValue())
, _search(this, st::topBarSearch)
, _cancel(this, st::historyAdminLogCancelSearch)
, _filter(this, tr::lng_admin_log_filter(), st::topBarButton) {
_backButton->moveToLeft(0, 0);
_backButton->setClickedCallback([=] { goBack(); });
_search->setClickedCallback([=] { showSearch(); });
_cancel->setClickedCallback([=] { cancelSearch(); });
_field->hide();
_filter->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
_field->cancelled(
) | rpl::on_next([=] {
cancelSearch();
}, _field->lifetime());
_field->changes(
) | rpl::on_next([=] {
searchUpdated();
}, _field->lifetime());
_field->submits(
) | rpl::on_next([=] { applySearch(); }, _field->lifetime());
_searchTimer.setCallback([=] { applySearch(); });
_cancel->hide(anim::type::instant);
}
void FixedBar::applyFilter(const FilterValue &value) {
auto hasFilter = value.flags || value.admins;
_backButton->setText(hasFilter
? tr::lng_admin_log_title_selected(tr::now)
: tr::lng_admin_log_title_all(tr::now));
}
void FixedBar::goBack() {
_controller->showBackFromStack();
}
void FixedBar::showSearch() {
if (!_searchShown) {
toggleSearch();
}
}
void FixedBar::toggleSearch() {
_searchShown = !_searchShown;
_cancel->toggle(_searchShown, anim::type::normal);
_searchShownAnimation.start(
[=] { searchAnimationCallback(); },
_searchShown ? 0. : 1.,
_searchShown ? 1. : 0.,
st::historyAdminLogSearchSlideDuration);
_search->setDisabled(_searchShown);
if (_searchShown) {
_field->show();
_field->setFocus();
} else {
_searchCancelRequests.fire({});
}
}
void FixedBar::searchAnimationCallback() {
if (!_searchShownAnimation.animating()) {
_field->setVisible(_searchShown);
_search->setIconOverride(
_searchShown ? &st::topBarSearch.icon : nullptr,
_searchShown ? &st::topBarSearch.icon : nullptr);
_search->setRippleColorOverride(
_searchShown ? &st::topBarBg : nullptr);
_search->setCursor(
_searchShown ? style::cur_default : style::cur_pointer);
}
resizeToWidth(width());
}
void FixedBar::cancelSearch() {
if (_searchShown) {
if (!_field->getLastText().isEmpty()) {
_field->clear();
_field->setFocus();
applySearch();
} else {
toggleSearch();
}
}
}
void FixedBar::searchUpdated() {
if (_field->getLastText().isEmpty()) {
applySearch();
} else {
_searchTimer.callOnce(AutoSearchTimeout);
}
}
void FixedBar::applySearch() {
_searchRequests.fire_copy(_field->getLastText());
}
int FixedBar::resizeGetHeight(int newWidth) {
auto filterLeft = newWidth - _filter->width();
_filter->moveToLeft(filterLeft, 0);
auto cancelLeft = filterLeft - _cancel->width();
_cancel->moveToLeft(cancelLeft, 0);
auto searchShownLeft = st::topBarArrowPadding.left();
auto searchHiddenLeft = filterLeft - _search->width();
auto searchShown = _searchShownAnimation.value(_searchShown ? 1. : 0.);
auto searchCurrentLeft = anim::interpolate(searchHiddenLeft, searchShownLeft, searchShown);
_search->moveToLeft(searchCurrentLeft, 0);
_backButton->resizeToWidth(searchCurrentLeft);
_backButton->moveToLeft(0, 0);
auto newHeight = _backButton->height();
auto fieldLeft = searchShownLeft + _search->width();
_field->setGeometryToLeft(fieldLeft, st::historyAdminLogSearchTop, cancelLeft - fieldLeft, _field->height());
return newHeight;
}
rpl::producer<> FixedBar::showFilterRequests() const {
return _filter->clicks() | rpl::to_empty;
}
rpl::producer<> FixedBar::searchCancelRequests() const {
return _searchCancelRequests.events();
}
rpl::producer<QString> FixedBar::searchRequests() const {
return _searchRequests.events();
}
void FixedBar::setAnimatingMode(bool enabled) {
if (_animatingMode != enabled) {
_animatingMode = enabled;
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
if (_animatingMode) {
setAttribute(Qt::WA_OpaquePaintEvent, false);
hideChildren();
} else {
setAttribute(Qt::WA_OpaquePaintEvent);
showChildren();
_field->hide();
_cancel->setVisible(false);
}
show();
}
}
void FixedBar::paintEvent(QPaintEvent *e) {
if (!_animatingMode) {
auto p = QPainter(this);
p.fillRect(e->rect(), st::topBarBg);
}
}
void FixedBar::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
goBack();
} else {
RpWidget::mousePressEvent(e);
}
}
Widget::Widget(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<ChannelData*> channel)
: Window::SectionWidget(parent, controller, rpl::single<PeerData*>(channel))
, _scroll(this, st::historyScroll, false)
, _fixedBar(this, controller, channel)
, _fixedBarShadow(this)
, _whatIsThis(
this,
tr::lng_admin_log_about(tr::now),
st::historyComposeButton) {
_fixedBar->move(0, 0);
_fixedBar->resizeToWidth(width());
_fixedBar->showFilterRequests(
) | rpl::on_next([=] {
showFilter();
}, lifetime());
_fixedBar->searchCancelRequests(
) | rpl::on_next([=] {
setInnerFocus();
}, lifetime());
_fixedBar->searchRequests(
) | rpl::on_next([=](const QString &query) {
_inner->applySearch(query);
}, lifetime());
_fixedBar->show();
_fixedBarShadow->raise();
controller->adaptive().value(
) | rpl::on_next([=] {
updateAdaptiveLayout();
}, lifetime());
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller, channel));
_inner->showSearchSignal(
) | rpl::on_next([=] {
_fixedBar->showSearch();
}, lifetime());
_inner->cancelSignal(
) | rpl::on_next([=] {
_fixedBar->goBack();
}, lifetime());
_inner->scrollToSignal(
) | rpl::on_next([=](int top) {
_scroll->scrollToY(top);
}, lifetime());
_scroll->move(0, _fixedBar->height());
_scroll->show();
_scroll->scrolls(
) | rpl::on_next([=] {
onScroll();
}, lifetime());
_whatIsThis->setClickedCallback([=] {
controller->show(Ui::MakeInformBox(channel->isMegagroup()
? tr::lng_admin_log_about_text()
: tr::lng_admin_log_about_text_channel()));
});
setupShortcuts();
setupSwipeReply();
}
void Widget::showFilter() {
_inner->showFilter([this](FilterValue &&filter) {
applyFilter(std::move(filter));
controller()->hideLayer();
});
}
void Widget::updateAdaptiveLayout() {
_fixedBarShadow->moveToLeft(
controller()->adaptive().isOneColumn()
? 0
: st::lineWidth,
_fixedBar->height());
}
not_null<ChannelData*> Widget::channel() const {
return _inner->channel();
}
Dialogs::RowDescriptor Widget::activeChat() const {
return {
channel()->owner().history(channel()),
FullMsgId(channel()->id, ShowAtUnreadMsgId)
};
}
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams &params) {
if (params.withTopBarShadow) _fixedBarShadow->hide();
auto result = Ui::GrabWidget(this);
if (params.withTopBarShadow) _fixedBarShadow->show();
return result;
}
void Widget::doSetInnerFocus() {
if (!_fixedBar->setSearchFocus()) {
_inner->setFocus();
}
}
bool Widget::showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) {
if (auto logMemento = dynamic_cast<SectionMemento*>(memento.get())) {
if (logMemento->getChannel() == channel()) {
restoreState(logMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> memento) {
setGeometry(geometry);
Ui::SendPendingMoveResizeEvents(this);
restoreState(memento);
}
void Widget::setupShortcuts() {
Shortcuts::Requests(
) | rpl::filter([=] {
return Ui::AppInFocus()
&& Ui::InFocusChain(this)
&& !controller()->isLayerShown()
&& isActiveWindow();
}) | rpl::on_next([=](not_null<Shortcuts::Request*> request) {
using Command = Shortcuts::Command;
request->check(Command::Search, 2) && request->handle([=] {
_fixedBar->showSearch();
return true;
});
}, lifetime());
}
void Widget::setupSwipeReply() {
auto update = [=](Ui::Controls::SwipeContextData data) {
if (data.translation > 0) {
if (!_swipeBackData.callback) {
_swipeBackData = Ui::Controls::SetupSwipeBack(
this,
[=]() -> std::pair<QColor, QColor> {
auto context = _inner->preparePaintContext({});
return {
context.st->msgServiceBg()->c,
context.st->msgServiceFg()->c,
};
});
}
_swipeBackData.callback(data);
return;
} else if (_swipeBackData.lifetime) {
_swipeBackData = {};
}
};
auto init = [=](int, Qt::LayoutDirection direction) {
if (direction == Qt::RightToLeft) {
return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
controller()->showBackFromStack();
});
}
return Ui::Controls::SwipeHandlerFinishData();
};
Ui::Controls::SetupSwipeHandler({
.widget = _inner.data(),
.scroll = _scroll.data(),
.update = std::move(update),
.init = std::move(init),
});
}
std::shared_ptr<Window::SectionMemento> Widget::createMemento() {
auto result = std::make_shared<SectionMemento>(channel());
saveState(result.get());
return result;
}
void Widget::saveState(not_null<SectionMemento*> memento) {
memento->setScrollTop(_scroll->scrollTop());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<SectionMemento*> memento) {
_inner->restoreState(memento);
auto scrollTop = memento->getScrollTop();
_scroll->scrollToY(scrollTop);
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::resizeEvent(QResizeEvent *e) {
if (!width() || !height()) {
return;
}
auto contentWidth = width();
auto newScrollTop = _scroll->scrollTop() + topDelta();
_fixedBar->resizeToWidth(contentWidth);
_fixedBarShadow->resize(contentWidth, st::lineWidth);
auto bottom = height();
auto scrollHeight = bottom - _fixedBar->height() - _whatIsThis->height();
auto scrollSize = QSize(contentWidth, scrollHeight);
if (_scroll->size() != scrollSize) {
_scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
_inner->restoreScrollPosition();
}
if (!_scroll->isHidden()) {
if (topDelta()) {
_scroll->scrollToY(newScrollTop);
}
auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
auto fullWidthButtonRect = myrtlrect(0, bottom - _whatIsThis->height(), contentWidth, _whatIsThis->height());
_whatIsThis->setGeometry(fullWidthButtonRect);
}
void Widget::paintEvent(QPaintEvent *e) {
if (animatingShow()) {
SectionWidget::paintEvent(e);
return;
} else if (controller()->contentOverlapped(this, e)) {
return;
}
//if (hasPendingResizedItems()) {
// updateListSize();
//}
//auto ms = crl::now();
//_historyDownShown.step(ms);
const auto clip = e->rect();
SectionWidget::PaintBackground(controller(), _inner->theme(), this, clip);
}
void Widget::onScroll() {
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::showAnimatedHook(
const Window::SectionSlideParams &params) {
_fixedBar->setAnimatingMode(true);
if (params.withTopBarShadow) _fixedBarShadow->show();
}
void Widget::showFinishedHook() {
_fixedBar->setAnimatingMode(false);
}
bool Widget::floatPlayerHandleWheelEvent(QEvent *e) {
return _scroll->viewportEvent(e);
}
QRect Widget::floatPlayerAvailableRect() {
return mapToGlobal(_scroll->geometry());
}
void Widget::applyFilter(FilterValue &&value) {
_fixedBar->applyFilter(value);
_inner->applyFilter(std::move(value));
}
} // namespace AdminLog

View File

@@ -0,0 +1,175 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "window/section_widget.h"
#include "window/section_memento.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_filter_value.h"
#include "ui/controls/swipe_handler_data.h"
#include "mtproto/sender.h"
namespace Ui {
class ScrollArea;
class PlainShadow;
class FlatButton;
} // namespace Ui
namespace Profile {
class BackButton;
} // namespace Profile
namespace AdminLog {
class FixedBar;
class InnerWidget;
class SectionMemento;
class Widget final : public Window::SectionWidget {
public:
Widget(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<ChannelData*> channel);
not_null<ChannelData*> channel() const;
Dialogs::RowDescriptor activeChat() const override;
bool hasTopBarShadow() const override {
return true;
}
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) override;
std::shared_ptr<Window::SectionMemento> createMemento() override;
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
// Float player interface.
bool floatPlayerHandleWheelEvent(QEvent *e) override;
QRect floatPlayerAvailableRect() override;
void applyFilter(FilterValue &&value);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void showAnimatedHook(
const Window::SectionSlideParams &params) override;
void showFinishedHook() override;
void doSetInnerFocus() override;
private:
void showFilter();
void onScroll();
void updateAdaptiveLayout();
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
void setupShortcuts();
void setupSwipeReply();
object_ptr<Ui::ScrollArea> _scroll;
QPointer<InnerWidget> _inner;
object_ptr<FixedBar> _fixedBar;
object_ptr<Ui::PlainShadow> _fixedBarShadow;
object_ptr<Ui::FlatButton> _whatIsThis;
Ui::Controls::SwipeBackResult _swipeBackData;
};
class SectionMemento : public Window::SectionMemento {
public:
using Element = HistoryView::Element;
SectionMemento(not_null<ChannelData*> channel) : _channel(channel) {
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
Window::Column column,
const QRect &geometry) override;
not_null<ChannelData*> getChannel() const {
return _channel;
}
void setScrollTop(int scrollTop) {
_scrollTop = scrollTop;
}
int getScrollTop() const {
return _scrollTop;
}
void setAdmins(std::vector<not_null<UserData*>> admins) {
_admins = std::move(admins);
}
void setAdminsCanEdit(std::vector<not_null<UserData*>> admins) {
_adminsCanEdit = std::move(admins);
}
std::vector<not_null<UserData*>> takeAdmins() {
return std::move(_admins);
}
std::vector<not_null<UserData*>> takeAdminsCanEdit() {
return std::move(_adminsCanEdit);
}
void setItems(
std::vector<OwnedItem> &&items,
std::set<uint64> &&eventIds,
bool upLoaded,
bool downLoaded) {
_items = std::move(items);
_eventIds = std::move(eventIds);
_upLoaded = upLoaded;
_downLoaded = downLoaded;
}
void setFilter(FilterValue &&filter) {
_filter = std::move(filter);
}
void setSearchQuery(QString &&query) {
_searchQuery = std::move(query);
}
std::vector<OwnedItem> takeItems() {
return std::move(_items);
}
std::set<uint64> takeEventIds() {
return std::move(_eventIds);
}
bool upLoaded() const {
return _upLoaded;
}
bool downLoaded() const {
return _downLoaded;
}
FilterValue takeFilter() {
return std::move(_filter);
}
QString takeSearchQuery() {
return std::move(_searchQuery);
}
private:
not_null<ChannelData*> _channel;
int _scrollTop = 0;
std::vector<not_null<UserData*>> _admins;
std::vector<not_null<UserData*>> _adminsCanEdit;
std::vector<OwnedItem> _items;
std::set<uint64> _eventIds;
bool _upLoaded = false;
bool _downLoaded = true;
FilterValue _filter;
QString _searchQuery;
};
} // namespace AdminLog