mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Add peer-list-like view for message reactions list.
This commit is contained in:
parent
b5d80a3c15
commit
b151d210bf
25 changed files with 516 additions and 39 deletions
|
@ -621,6 +621,10 @@ PRIVATE
|
|||
history/view/media/history_view_theme_document.cpp
|
||||
history/view/media/history_view_web_page.h
|
||||
history/view/media/history_view_web_page.cpp
|
||||
history/view/reactions/message_reactions_list.cpp
|
||||
history/view/reactions/message_reactions_list.h
|
||||
history/view/reactions/message_reactions_selector.cpp
|
||||
history/view/reactions/message_reactions_selector.h
|
||||
history/view/history_view_bottom_info.cpp
|
||||
history/view/history_view_bottom_info.h
|
||||
history/view/history_view_contact_status.cpp
|
||||
|
|
|
@ -97,8 +97,13 @@ void PeerListBox::createMultiSelect() {
|
|||
_select->moveToLeft(0, 0);
|
||||
}
|
||||
|
||||
void PeerListBox::setAddedTopScrollSkip(int skip) {
|
||||
_addedTopScrollSkip = skip;
|
||||
updateScrollSkips();
|
||||
}
|
||||
|
||||
int PeerListBox::getTopScrollSkip() const {
|
||||
auto result = 0;
|
||||
auto result = _addedTopScrollSkip;
|
||||
if (_select && !_select->isHidden()) {
|
||||
result += _select->height();
|
||||
}
|
||||
|
@ -109,7 +114,7 @@ void PeerListBox::updateScrollSkips() {
|
|||
// If we show / hide the search field scroll top is fixed.
|
||||
// If we resize search field by bubbles scroll bottom is fixed.
|
||||
setInnerTopSkip(getTopScrollSkip(), _scrollBottomFixed);
|
||||
if (!_select->animating()) {
|
||||
if (_select && !_select->animating()) {
|
||||
_scrollBottomFixed = true;
|
||||
}
|
||||
}
|
||||
|
@ -186,8 +191,15 @@ void PeerListBox::paintEvent(QPaintEvent *e) {
|
|||
const auto &bg = (_controller->listSt()
|
||||
? *_controller->listSt()
|
||||
: st::peerListBox).bg;
|
||||
const auto fill = QRect(
|
||||
0,
|
||||
_addedTopScrollSkip,
|
||||
width(),
|
||||
height() - _addedTopScrollSkip);
|
||||
for (const auto &rect : e->region()) {
|
||||
p.fillRect(rect, bg);
|
||||
if (const auto part = rect.intersected(fill); !part.isEmpty()) {
|
||||
p.fillRect(part, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1008,6 +1008,8 @@ public:
|
|||
int peerListSelectedRowsCount() override;
|
||||
void peerListScrollToTop() override;
|
||||
|
||||
void setAddedTopScrollSkip(int skip);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
|
@ -1047,5 +1049,6 @@ private:
|
|||
std::unique_ptr<PeerListController> _controller;
|
||||
Fn<void(PeerListBox*)> _init;
|
||||
bool _scrollBottomFixed = false;
|
||||
int _addedTopScrollSkip = 0;
|
||||
|
||||
};
|
||||
|
|
|
@ -669,6 +669,9 @@ void InnerWidget::elementReplyTo(const FullMsgId &to) {
|
|||
void InnerWidget::elementStartInteraction(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void InnerWidget::elementShowReactions(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
|
||||
memento->setFilter(std::move(_filter));
|
||||
memento->setAdmins(std::move(_admins));
|
||||
|
|
|
@ -139,6 +139,8 @@ public:
|
|||
void elementReplyTo(const FullMsgId &to) override;
|
||||
void elementStartInteraction(
|
||||
not_null<const HistoryView::Element*> view) override;
|
||||
void elementShowReactions(
|
||||
not_null<const HistoryView::Element*> view) override;
|
||||
|
||||
~InnerWidget();
|
||||
|
||||
|
|
|
@ -15,13 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_web_page.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_text.h"
|
||||
#include "history/view/reactions/message_reactions_list.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_context_menu.h"
|
||||
#include "history/view/history_view_emoji_interactions.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_text.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/image/image.h"
|
||||
|
@ -37,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/inactive_press.h"
|
||||
#include "window/window_adaptive.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/notifications_manager.h"
|
||||
|
@ -2867,6 +2869,12 @@ void HistoryInner::elementStartInteraction(not_null<const Element*> view) {
|
|||
_controller->emojiInteractions().startOutgoing(view);
|
||||
}
|
||||
|
||||
void HistoryInner::elementShowReactions(not_null<const Element*> view) {
|
||||
_controller->window().show(HistoryView::ReactionsListBox(
|
||||
_controller,
|
||||
view->data()));
|
||||
}
|
||||
|
||||
auto HistoryInner::getSelectionState() const
|
||||
-> HistoryView::TopBarWidget::SelectedState {
|
||||
auto result = HistoryView::TopBarWidget::SelectedState {};
|
||||
|
@ -3776,6 +3784,12 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
|
|||
Instance->elementStartInteraction(view);
|
||||
}
|
||||
}
|
||||
void elementShowReactions(not_null<const Element*> view) override {
|
||||
if (Instance) {
|
||||
Instance->elementShowReactions(view);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static Result result;
|
||||
|
|
|
@ -116,6 +116,7 @@ public:
|
|||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
|
||||
void elementReplyTo(const FullMsgId &to);
|
||||
void elementStartInteraction(not_null<const Element*> view);
|
||||
void elementShowReactions(not_null<const Element*> view);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
|
|
|
@ -14,20 +14,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item_components.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
BottomInfo::BottomInfo(Data &&data)
|
||||
BottomInfo::BottomInfo(Data &&data, Context &&context)
|
||||
: _data(std::move(data))
|
||||
, _context(std::move(context))
|
||||
, _reactions(st::msgMinWidth / 2) {
|
||||
layout();
|
||||
}
|
||||
|
||||
void BottomInfo::update(Data &&data, int availableWidth) {
|
||||
void BottomInfo::update(Data &&data, Context &&context, int availableWidth) {
|
||||
_data = std::move(data);
|
||||
_context = std::move(context);
|
||||
layout();
|
||||
if (!_size.isEmpty()) {
|
||||
resizeToWidth(std::min(optimalSize().width(), availableWidth));
|
||||
|
@ -53,13 +56,38 @@ int BottomInfo::firstLineWidth() const {
|
|||
return noReactionsWidth;
|
||||
}
|
||||
|
||||
bool BottomInfo::pointInTime(QPoint position) const {
|
||||
return QRect(
|
||||
TextState BottomInfo::textState(
|
||||
not_null<const HistoryItem*> item,
|
||||
QPoint position) const {
|
||||
auto result = TextState(item);
|
||||
if (!_reactions.isEmpty()) {
|
||||
const auto reactionsPosition = [&] {
|
||||
if (_size.height() == _optimalSize.height()) {
|
||||
return QPoint(0, 0);
|
||||
}
|
||||
const auto available = _size.width();
|
||||
const auto use = std::min(available, _reactions.maxWidth());
|
||||
return QPoint(_size.width() - use, st::msgDateFont->height);
|
||||
}();
|
||||
const auto state = _reactions.getStateLeft(
|
||||
position - reactionsPosition,
|
||||
std::min(_size.width(), _reactions.maxWidth()),
|
||||
_size.width());
|
||||
if (state.uponSymbol) {
|
||||
result.link = _context.reactions;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const auto inTime = QRect(
|
||||
_size.width() - _dateWidth,
|
||||
0,
|
||||
_dateWidth,
|
||||
st::msgDateFont->height
|
||||
).contains(position);
|
||||
if (inTime) {
|
||||
result.cursor = CursorState::Date;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BottomInfo::isSignedAuthorElided() const {
|
||||
|
@ -336,4 +364,13 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
|||
return result;
|
||||
}
|
||||
|
||||
BottomInfo::Context BottomInfoContextFromMessage(
|
||||
not_null<Message*> message) {
|
||||
auto result = BottomInfo::Context();
|
||||
result.reactions = std::make_shared<LambdaClickHandler>([=] {
|
||||
message->delegate()->elementShowReactions(message);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace HistoryView {
|
|||
using PaintContext = Ui::ChatPaintContext;
|
||||
|
||||
class Message;
|
||||
struct TextState;
|
||||
|
||||
class BottomInfo {
|
||||
public:
|
||||
|
@ -40,14 +41,19 @@ public:
|
|||
std::optional<int> replies;
|
||||
Flags flags;
|
||||
};
|
||||
explicit BottomInfo(Data &&data);
|
||||
struct Context {
|
||||
ClickHandlerPtr reactions;
|
||||
};
|
||||
BottomInfo(Data &&data, Context &&context);
|
||||
|
||||
void update(Data &&data, int availableWidth);
|
||||
void update(Data &&data, Context &&context, int availableWidth);
|
||||
|
||||
[[nodiscard]] QSize optimalSize() const;
|
||||
[[nodiscard]] QSize size() const;
|
||||
[[nodiscard]] int firstLineWidth() const;
|
||||
[[nodiscard]] bool pointInTime(QPoint position) const;
|
||||
[[nodiscard]] TextState textState(
|
||||
not_null<const HistoryItem*> item,
|
||||
QPoint position) const;
|
||||
[[nodiscard]] bool isSignedAuthorElided() const;
|
||||
|
||||
void paint(
|
||||
|
@ -69,6 +75,7 @@ private:
|
|||
void countOptimalSize();
|
||||
|
||||
Data _data;
|
||||
Context _context;
|
||||
QSize _optimalSize;
|
||||
QSize _size;
|
||||
Ui::Text::String _authorEditedDate;
|
||||
|
@ -83,4 +90,7 @@ private:
|
|||
[[nodiscard]] BottomInfo::Data BottomInfoDataFromMessage(
|
||||
not_null<Message*> message);
|
||||
|
||||
[[nodiscard]] BottomInfo::Context BottomInfoContextFromMessage(
|
||||
not_null<Message*> message);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_media_grouped.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_large_emoji.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/history.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
|
@ -179,11 +180,14 @@ auto SimpleElementDelegate::elementPathShiftGradient()
|
|||
void SimpleElementDelegate::elementReplyTo(const FullMsgId &to) {
|
||||
}
|
||||
|
||||
|
||||
void SimpleElementDelegate::elementStartInteraction(
|
||||
not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void SimpleElementDelegate::elementShowReactions(
|
||||
not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
TextSelection UnshiftItemSelection(
|
||||
TextSelection selection,
|
||||
uint16 byLength) {
|
||||
|
@ -956,12 +960,12 @@ void Element::drawInfo(
|
|||
InfoDisplayType type) const {
|
||||
}
|
||||
|
||||
bool Element::pointInTime(
|
||||
TextState Element::bottomInfoTextState(
|
||||
int right,
|
||||
int bottom,
|
||||
QPoint point,
|
||||
InfoDisplayType type) const {
|
||||
return false;
|
||||
return TextState();
|
||||
}
|
||||
|
||||
TextSelection Element::adjustSelection(
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0;
|
||||
virtual void elementReplyTo(const FullMsgId &to) = 0;
|
||||
virtual void elementStartInteraction(not_null<const Element*> view) = 0;
|
||||
virtual void elementShowReactions(not_null<const Element*> view) = 0;
|
||||
|
||||
virtual ~ElementDelegate() {
|
||||
}
|
||||
|
@ -148,6 +149,7 @@ public:
|
|||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
void elementReplyTo(const FullMsgId &to) override;
|
||||
void elementStartInteraction(not_null<const Element*> view) override;
|
||||
void elementShowReactions(not_null<const Element*> view) override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] not_null<Window::SessionController*> controller() const {
|
||||
|
@ -298,7 +300,7 @@ public:
|
|||
int bottom,
|
||||
int width,
|
||||
InfoDisplayType type) const;
|
||||
virtual bool pointInTime(
|
||||
virtual TextState bottomInfoTextState(
|
||||
int right,
|
||||
int bottom,
|
||||
QPoint point,
|
||||
|
|
|
@ -1458,6 +1458,9 @@ void ListWidget::elementReplyTo(const FullMsgId &to) {
|
|||
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void ListWidget::elementShowReactions(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void ListWidget::saveState(not_null<ListMemento*> memento) {
|
||||
memento->setAroundPosition(_aroundPosition);
|
||||
auto state = countScrollState();
|
||||
|
|
|
@ -278,6 +278,7 @@ public:
|
|||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
void elementReplyTo(const FullMsgId &to) override;
|
||||
void elementStartInteraction(not_null<const Element*> view) override;
|
||||
void elementShowReactions(not_null<const Element*> view) override;
|
||||
|
||||
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);
|
||||
|
||||
|
|
|
@ -242,7 +242,9 @@ Message::Message(
|
|||
not_null<HistoryMessage*> data,
|
||||
Element *replacing)
|
||||
: Element(delegate, data, replacing)
|
||||
, _bottomInfo(BottomInfoDataFromMessage(this)) {
|
||||
, _bottomInfo(
|
||||
BottomInfoDataFromMessage(this),
|
||||
BottomInfoContextFromMessage(this)) {
|
||||
initLogEntryOriginal();
|
||||
initPsa();
|
||||
}
|
||||
|
@ -1260,17 +1262,18 @@ TextState Message::textState(
|
|||
}
|
||||
}
|
||||
|
||||
auto checkForPointInTime = [&] {
|
||||
auto checkBottomInfoState = [&] {
|
||||
if (mediaOnBottom && (entry || media->customInfoLayout())) {
|
||||
return;
|
||||
}
|
||||
const auto inDate = pointInTime(
|
||||
const auto bottomInfoResult = bottomInfoTextState(
|
||||
bubble.left() + bubble.width(),
|
||||
bubble.top() + bubble.height(),
|
||||
point,
|
||||
InfoDisplayType::Default);
|
||||
if (inDate) {
|
||||
result.cursor = CursorState::Date;
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
result = bottomInfoResult;
|
||||
}
|
||||
};
|
||||
if (inBubble) {
|
||||
|
@ -1283,19 +1286,19 @@ TextState Message::textState(
|
|||
result = media->textState(point - QPoint(mediaLeft, mediaTop), request);
|
||||
result.symbol += item->_text.length();
|
||||
} else if (getStateText(point, trect, &result, request)) {
|
||||
checkForPointInTime();
|
||||
checkBottomInfoState();
|
||||
return result;
|
||||
} else if (point.y() >= trect.y() + trect.height()) {
|
||||
result.symbol = item->_text.length();
|
||||
}
|
||||
} else if (getStateText(point, trect, &result, request)) {
|
||||
checkForPointInTime();
|
||||
checkBottomInfoState();
|
||||
return result;
|
||||
} else if (point.y() >= trect.y() + trect.height()) {
|
||||
result.symbol = item->_text.length();
|
||||
}
|
||||
}
|
||||
checkForPointInTime();
|
||||
checkBottomInfoState();
|
||||
if (const auto size = rightActionSize()) {
|
||||
const auto fastShareSkip = std::clamp(
|
||||
(g.height() - size->height()) / 2,
|
||||
|
@ -1770,7 +1773,7 @@ void Message::drawInfo(
|
|||
context);
|
||||
}
|
||||
|
||||
bool Message::pointInTime(
|
||||
TextState Message::bottomInfoTextState(
|
||||
int right,
|
||||
int bottom,
|
||||
QPoint point,
|
||||
|
@ -1794,7 +1797,9 @@ bool Message::pointInTime(
|
|||
const auto size = _bottomInfo.size();
|
||||
const auto infoLeft = infoRight - size.width();
|
||||
const auto infoTop = infoBottom - size.height();
|
||||
return _bottomInfo.pointInTime({ infoLeft, infoTop });
|
||||
return _bottomInfo.textState(
|
||||
data(),
|
||||
point - QPoint{ infoLeft, infoTop });
|
||||
}
|
||||
|
||||
int Message::infoWidth() const {
|
||||
|
@ -1811,7 +1816,10 @@ bool Message::isSignedAuthorElided() const {
|
|||
|
||||
void Message::itemDataChanged() {
|
||||
const auto was = _bottomInfo.size();
|
||||
_bottomInfo.update(BottomInfoDataFromMessage(this), width());
|
||||
_bottomInfo.update(
|
||||
BottomInfoDataFromMessage(this),
|
||||
BottomInfoContextFromMessage(this),
|
||||
width());
|
||||
if (was != _bottomInfo.size()) {
|
||||
history()->owner().requestViewResize(this);
|
||||
} else {
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
int bottom,
|
||||
int width,
|
||||
InfoDisplayType type) const override;
|
||||
bool pointInTime(
|
||||
TextState bottomInfoTextState(
|
||||
int right,
|
||||
int bottom,
|
||||
QPoint point,
|
||||
|
|
|
@ -868,8 +868,16 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
|
|||
}
|
||||
}
|
||||
if (!inWebPage) {
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, isRound ? InfoDisplayType::Background : InfoDisplayType::Image)) {
|
||||
result.cursor = CursorState::Date;
|
||||
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||
fullRight,
|
||||
fullBottom,
|
||||
point,
|
||||
(isRound
|
||||
? InfoDisplayType::Background
|
||||
: InfoDisplayType::Image));
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
return bottomInfoResult;
|
||||
}
|
||||
}
|
||||
if (const auto size = bubble ? std::nullopt : _parent->rightActionSize()) {
|
||||
|
|
|
@ -287,8 +287,14 @@ TextState Location::textState(QPoint point, StateRequest request) const {
|
|||
if (_parent->media() == this) {
|
||||
auto fullRight = paintx + paintw;
|
||||
auto fullBottom = height();
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
|
||||
result.cursor = CursorState::Date;
|
||||
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||
fullRight,
|
||||
fullBottom,
|
||||
point,
|
||||
InfoDisplayType::Image);
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
return bottomInfoResult;
|
||||
}
|
||||
if (const auto size = bubble ? std::nullopt : _parent->rightActionSize()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
|
|
|
@ -421,8 +421,14 @@ TextState GroupedMedia::textState(QPoint point, StateRequest request) const {
|
|||
} else if (_parent->media() == this) {
|
||||
auto fullRight = width();
|
||||
auto fullBottom = height();
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
|
||||
result.cursor = CursorState::Date;
|
||||
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||
fullRight,
|
||||
fullBottom,
|
||||
point,
|
||||
InfoDisplayType::Image);
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
return bottomInfoResult;
|
||||
}
|
||||
if (const auto size = _parent->hasBubble() ? std::nullopt : _parent->rightActionSize()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
|
|
|
@ -367,8 +367,14 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const {
|
|||
const auto fullRight = calculateFullRight(inner);
|
||||
const auto rightActionSize = _parent->rightActionSize();
|
||||
auto fullBottom = height();
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Background)) {
|
||||
result.cursor = CursorState::Date;
|
||||
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||
fullRight,
|
||||
fullBottom,
|
||||
point,
|
||||
InfoDisplayType::Background);
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
return bottomInfoResult;
|
||||
}
|
||||
if (rightActionSize) {
|
||||
const auto position = calculateFastActionPosition(
|
||||
|
|
|
@ -464,8 +464,14 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
|||
if (_caption.isEmpty() && _parent->media() == this) {
|
||||
auto fullRight = paintx + paintw;
|
||||
auto fullBottom = painty + painth;
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
|
||||
result.cursor = CursorState::Date;
|
||||
const auto bottomInfoResult = _parent->bottomInfoTextState(
|
||||
fullRight,
|
||||
fullBottom,
|
||||
point,
|
||||
InfoDisplayType::Image);
|
||||
if (bottomInfoResult.link
|
||||
|| bottomInfoResult.cursor != CursorState::None) {
|
||||
return bottomInfoResult;
|
||||
}
|
||||
if (const auto size = bubble ? std::nullopt : _parent->rightActionSize()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
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/view/reactions/message_reactions_list.h"
|
||||
|
||||
#include "history/view/reactions/message_reactions_selector.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPerPageFirst = 20;
|
||||
constexpr auto kPerPage = 200;
|
||||
|
||||
class Controller final : public PeerListController {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
rpl::producer<QString> switches);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
void loadMoreRows() override;
|
||||
|
||||
private:
|
||||
using AllEntry = std::pair<not_null<UserData*>, QString>;
|
||||
|
||||
void loadMore(const QString &offset);
|
||||
bool appendRow(not_null<UserData*> user, QString reaction = QString());
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user,
|
||||
QString reaction) const;
|
||||
void showReaction(const QString &reaction);
|
||||
|
||||
const not_null<Window::SessionController*> _window;
|
||||
const not_null<HistoryItem*> _item;
|
||||
MTP::Sender _api;
|
||||
|
||||
QString _shownReaction;
|
||||
|
||||
std::vector<AllEntry> _all;
|
||||
QString _allOffset;
|
||||
|
||||
std::vector<not_null<UserData*>> _filtered;
|
||||
QString _filteredOffset;
|
||||
|
||||
mtpRequestId _loadRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
Controller::Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
rpl::producer<QString> switches)
|
||||
: _window(window)
|
||||
, _item(item)
|
||||
, _api(&window->session().mtp()) {
|
||||
std::move(
|
||||
switches
|
||||
) | rpl::filter([=](const QString &reaction) {
|
||||
return (_shownReaction != reaction);
|
||||
}) | rpl::start_with_next([=](const QString &reaction) {
|
||||
showReaction(reaction);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
Main::Session &Controller::session() const {
|
||||
return _window->session();
|
||||
}
|
||||
|
||||
void Controller::prepare() {
|
||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
delegate()->peerListRefreshRows();
|
||||
|
||||
loadMore(QString());
|
||||
}
|
||||
|
||||
void Controller::showReaction(const QString &reaction) {
|
||||
if (_shownReaction == reaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
_api.request(base::take(_loadRequestId)).cancel();
|
||||
while (const auto count = delegate()->peerListFullRowsCount()) {
|
||||
delegate()->peerListRemoveRow(delegate()->peerListRowAt(count - 1));
|
||||
}
|
||||
|
||||
_shownReaction = reaction;
|
||||
if (_shownReaction.isEmpty()) {
|
||||
_filtered.clear();
|
||||
for (const auto &[user, reaction] : _all) {
|
||||
appendRow(user, reaction);
|
||||
}
|
||||
} else {
|
||||
_filtered = _all | ranges::view::filter([&](const AllEntry &entry) {
|
||||
return (entry.second == reaction);
|
||||
}) | ranges::view::transform(
|
||||
&AllEntry::first
|
||||
) | ranges::to_vector;
|
||||
for (const auto user : _filtered) {
|
||||
appendRow(user);
|
||||
}
|
||||
loadMore(QString());
|
||||
}
|
||||
setDescriptionText(delegate()->peerListFullRowsCount()
|
||||
? QString()
|
||||
: tr::lng_contacts_loading(tr::now));
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
void Controller::loadMoreRows() {
|
||||
const auto &offset = _shownReaction.isEmpty()
|
||||
? _allOffset
|
||||
: _filteredOffset;
|
||||
if (_loadRequestId || offset.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
loadMore(offset);
|
||||
}
|
||||
|
||||
void Controller::loadMore(const QString &offset) {
|
||||
_api.request(_loadRequestId).cancel();
|
||||
|
||||
using Flag = MTPmessages_GetMessageReactionsList::Flag;
|
||||
const auto flags = Flag(0)
|
||||
| (offset.isEmpty() ? Flag(0) : Flag::f_offset)
|
||||
| (_shownReaction.isEmpty() ? Flag(0) : Flag::f_reaction);
|
||||
_loadRequestId = _api.request(MTPmessages_GetMessageReactionsList(
|
||||
MTP_flags(flags),
|
||||
_item->history()->peer->input,
|
||||
MTP_int(_item->id),
|
||||
MTP_string(_shownReaction),
|
||||
MTP_string(offset),
|
||||
MTP_int(kPerPageFirst)
|
||||
)).done([=](const MTPmessages_MessageReactionsList &result) {
|
||||
_loadRequestId = 0;
|
||||
const auto filtered = !_shownReaction.isEmpty();
|
||||
result.match([&](const MTPDmessages_messageReactionsList &data) {
|
||||
const auto sessionData = &session().data();
|
||||
sessionData->processUsers(data.vusers());
|
||||
(filtered ? _filteredOffset : _allOffset)
|
||||
= data.vnext_offset().value_or_empty();
|
||||
for (const auto &reaction : data.vreactions().v) {
|
||||
reaction.match([&](const MTPDmessageUserReaction &data) {
|
||||
const auto user = sessionData->userLoaded(
|
||||
data.vuser_id().v);
|
||||
const auto reaction = filtered ? QString() : qs(data.vreaction());
|
||||
if (user && appendRow(user, reaction)) {
|
||||
if (filtered) {
|
||||
_filtered.emplace_back(user);
|
||||
} else {
|
||||
_all.emplace_back(user, reaction);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
setDescriptionText(QString());
|
||||
delegate()->peerListRefreshRows();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peerId = row->peer()->id;
|
||||
const auto window = _window;
|
||||
crl::on_main(&session(), [=] {
|
||||
_window->showPeerHistory(peerId);
|
||||
});
|
||||
}
|
||||
|
||||
bool Controller::appendRow(not_null<UserData*> user, QString reaction) {
|
||||
if (delegate()->peerListFindRow(user->id.value)) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListAppendRow(createRow(user, reaction));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> Controller::createRow(
|
||||
not_null<UserData*> user,
|
||||
QString reaction) const {
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (!reaction.isEmpty()) {
|
||||
result->setCustomStatus(reaction);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item) {
|
||||
Expects(IsServerMsgId(item->id));
|
||||
|
||||
const auto tabRequests = std::make_shared<rpl::event_stream<QString>>();
|
||||
const auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto selector = CreateReactionSelector(box, item->reactions());
|
||||
box->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
selector->resizeToWidth(width);
|
||||
selector->move(0, 0);
|
||||
}, box->lifetime());
|
||||
selector->changes(
|
||||
) | rpl::start_to_stream(*tabRequests, box->lifetime());
|
||||
box->setAddedTopScrollSkip(selector->height());
|
||||
box->addButton(tr::lng_close(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
};
|
||||
return Box<PeerListBox>(
|
||||
std::make_unique<Controller>(window, item, tabRequests->events()),
|
||||
initBox);
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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 "base/object_ptr.h"
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
class BoxContent;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item);
|
||||
|
||||
} // namespace HistoryView
|
|
@ -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
|
||||
*/
|
||||
#include "history/view/reactions/message_reactions_selector.h"
|
||||
|
||||
#include "ui/widgets/discrete_sliders.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items) {
|
||||
const auto sectionsCount = int(items.size() + 1);
|
||||
const auto result = Ui::CreateChild<Selector>(parent.get());
|
||||
using Entry = std::pair<int, QString>;
|
||||
auto sorted = std::vector<Entry>();
|
||||
for (const auto &[reaction, count] : items) {
|
||||
sorted.emplace_back(count, reaction);
|
||||
}
|
||||
ranges::sort(sorted, std::greater<>(), &Entry::first);
|
||||
const auto count = ranges::accumulate(
|
||||
sorted,
|
||||
0,
|
||||
std::plus<>(),
|
||||
&Entry::first);
|
||||
auto labels = QStringList() << ("ALL (" + QString::number(count) + ")");
|
||||
for (const auto &[count, reaction] : sorted) {
|
||||
labels.append(reaction + " (" + QString::number(count) + ")");
|
||||
}
|
||||
auto tabs = Ui::CreateChild<Ui::SettingsSlider>(
|
||||
parent.get(),
|
||||
st::defaultTabsSlider);
|
||||
tabs->setSections(labels | ranges::to_vector);
|
||||
tabs->setRippleTopRoundRadius(st::boxRadius);
|
||||
result->move = [=](int x, int y) {
|
||||
tabs->moveToLeft(x, y);
|
||||
};
|
||||
result->resizeToWidth = [=](int width) {
|
||||
tabs->resizeToWidth(std::min(
|
||||
width,
|
||||
sectionsCount * st::defaultTabsSlider.height * 2));
|
||||
};
|
||||
result->height = [=] {
|
||||
return tabs->height() - st::lineWidth;
|
||||
};
|
||||
result->changes = [=] {
|
||||
return tabs->sectionActivated() | rpl::map([=](int section) {
|
||||
return (section > 0) ? sorted[section - 1].second : QString();
|
||||
});
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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 HistoryView {
|
||||
|
||||
struct Selector {
|
||||
Fn<void(int, int)> move;
|
||||
Fn<void(int)> resizeToWidth;
|
||||
Fn<rpl::producer<QString>()> changes;
|
||||
Fn<int()> height;
|
||||
};
|
||||
|
||||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items);
|
||||
|
||||
} // namespace HistoryView
|
|
@ -164,7 +164,7 @@ DiscreteSlider::Section::Section(
|
|||
|
||||
SettingsSlider::SettingsSlider(
|
||||
QWidget *parent,
|
||||
const style::SettingsSlider &st)
|
||||
const style::SettingsSlider &st)
|
||||
: DiscreteSlider(parent)
|
||||
, _st(st) {
|
||||
setSelectOnPress(_st.ripple.showDuration == 0);
|
||||
|
|
Loading…
Add table
Reference in a new issue