mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 07:33:52 +02:00
Implement replies list request.
This commit is contained in:
parent
437c9320cd
commit
00cdae0369
24 changed files with 1991 additions and 20 deletions
|
@ -467,6 +467,8 @@ PRIVATE
|
||||||
data/data_poll.h
|
data/data_poll.h
|
||||||
data/data_pts_waiter.cpp
|
data/data_pts_waiter.cpp
|
||||||
data/data_pts_waiter.h
|
data/data_pts_waiter.h
|
||||||
|
data/data_replies_list.cpp
|
||||||
|
data/data_replies_list.h
|
||||||
data/data_reply_preview.cpp
|
data/data_reply_preview.cpp
|
||||||
data/data_reply_preview.h
|
data/data_reply_preview.h
|
||||||
data/data_search_controller.cpp
|
data/data_search_controller.cpp
|
||||||
|
@ -588,6 +590,8 @@ PRIVATE
|
||||||
history/view/history_view_message.cpp
|
history/view/history_view_message.cpp
|
||||||
history/view/history_view_message.h
|
history/view/history_view_message.h
|
||||||
history/view/history_view_object.h
|
history/view/history_view_object.h
|
||||||
|
history/view/history_view_replies_section.cpp
|
||||||
|
history/view/history_view_replies_section.h
|
||||||
history/view/history_view_schedule_box.cpp
|
history/view/history_view_schedule_box.cpp
|
||||||
history/view/history_view_schedule_box.h
|
history/view/history_view_schedule_box.h
|
||||||
history/view/history_view_scheduled_section.cpp
|
history/view/history_view_scheduled_section.cpp
|
||||||
|
|
BIN
Telegram/Resources/icons/history_replies.png
Normal file
BIN
Telegram/Resources/icons/history_replies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 315 B |
BIN
Telegram/Resources/icons/history_replies@2x.png
Normal file
BIN
Telegram/Resources/icons/history_replies@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 553 B |
BIN
Telegram/Resources/icons/history_replies@3x.png
Normal file
BIN
Telegram/Resources/icons/history_replies@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 573 B |
|
@ -1344,6 +1344,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
|
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
|
||||||
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
|
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
|
||||||
|
|
||||||
|
"lng_replies_view#one" = "View {count} Reply";
|
||||||
|
"lng_replies_view#other" = "View {count} Replies";
|
||||||
|
"lng_replies_view_thread" = "View Thread";
|
||||||
|
"lng_replies_header#one" = "{count} reply";
|
||||||
|
"lng_replies_header#other" = "{count} replies";
|
||||||
|
"lng_replies_header_none" = "No replies";
|
||||||
|
"lng_replies_send_placeholder" = "Reply";
|
||||||
|
|
||||||
"lng_archived_name" = "Archived chats";
|
"lng_archived_name" = "Archived chats";
|
||||||
"lng_archived_add" = "Archive";
|
"lng_archived_add" = "Archive";
|
||||||
"lng_archived_remove" = "Unarchive";
|
"lng_archived_remove" = "Unarchive";
|
||||||
|
|
|
@ -272,8 +272,10 @@ void BoxController::prepare() {
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
session().changes().messageUpdates(
|
session().changes().messageUpdates(
|
||||||
Data::MessageUpdate::Flag::CallAdded
|
Data::MessageUpdate::Flag::NewAdded
|
||||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
) | rpl::filter([=](const Data::MessageUpdate &update) {
|
||||||
|
return (update.item->media()->call() != nullptr);
|
||||||
|
}) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||||
insertRow(update.item, InsertWay::Prepend);
|
insertRow(update.item, InsertWay::Prepend);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
|
|
@ -133,11 +133,12 @@ struct MessageUpdate {
|
||||||
Destroyed = (1 << 1),
|
Destroyed = (1 << 1),
|
||||||
DialogRowRepaint = (1 << 2),
|
DialogRowRepaint = (1 << 2),
|
||||||
DialogRowRefresh = (1 << 3),
|
DialogRowRefresh = (1 << 3),
|
||||||
CallAdded = (1 << 4),
|
NewAdded = (1 << 4),
|
||||||
ReplyMarkup = (1 << 5),
|
ReplyMarkup = (1 << 5),
|
||||||
BotCallbackSent = (1 << 6),
|
BotCallbackSent = (1 << 6),
|
||||||
|
NewMaybeAdded = (1 << 7),
|
||||||
|
|
||||||
LastUsedBit = (1 << 6),
|
LastUsedBit = (1 << 7),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
|
@ -676,6 +676,9 @@ void Histories::checkPostponed(not_null<History*> history, int id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histories::cancelRequest(int id) {
|
void Histories::cancelRequest(int id) {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto history = _historyByRequest.take(id);
|
const auto history = _historyByRequest.take(id);
|
||||||
if (!history) {
|
if (!history) {
|
||||||
return;
|
return;
|
||||||
|
|
371
Telegram/SourceFiles/data/data_replies_list.cpp
Normal file
371
Telegram/SourceFiles/data/data_replies_list.cpp
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
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 "data/data_replies_list.h"
|
||||||
|
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "data/data_histories.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kMessagesPerPage = 4; // #TODO replies
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct RepliesList::Viewer {
|
||||||
|
MessagesSlice slice;
|
||||||
|
MsgId around = 0;
|
||||||
|
int limitBefore = 0;
|
||||||
|
int limitAfter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
|
||||||
|
: _history(history)
|
||||||
|
, _rootId(rootId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
RepliesList::~RepliesList() {
|
||||||
|
histories().cancelRequest(base::take(_beforeId));
|
||||||
|
histories().cancelRequest(base::take(_afterId));
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<MessagesSlice> RepliesList::source(
|
||||||
|
MessagePosition aroundId,
|
||||||
|
int limitBefore,
|
||||||
|
int limitAfter) {
|
||||||
|
const auto around = aroundId.fullId.msg;
|
||||||
|
return [=](auto consumer) {
|
||||||
|
auto lifetime = rpl::lifetime();
|
||||||
|
const auto viewer = lifetime.make_state<Viewer>();
|
||||||
|
const auto push = [=] {
|
||||||
|
if (buildFromData(viewer)) {
|
||||||
|
consumer.put_next_copy(viewer->slice);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
viewer->around = around;
|
||||||
|
viewer->limitBefore = limitBefore;
|
||||||
|
viewer->limitAfter = limitAfter;
|
||||||
|
|
||||||
|
_history->session().changes().messageUpdates(
|
||||||
|
MessageUpdate::Flag::NewAdded
|
||||||
|
| MessageUpdate::Flag::NewMaybeAdded
|
||||||
|
| MessageUpdate::Flag::Destroyed
|
||||||
|
) | rpl::filter([=](const MessageUpdate &update) {
|
||||||
|
return applyUpdate(viewer, update);
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
crl::on_main(this, push);
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
_partLoaded.events(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
crl::on_main(this, push);
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
push();
|
||||||
|
return lifetime;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> RepliesList::fullCount() const {
|
||||||
|
return _fullCount.value() | rpl::filter_optional();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
|
||||||
|
if (_list.empty() && _skippedBefore == 0 && _skippedAfter == 0) {
|
||||||
|
viewer->slice.ids.clear();
|
||||||
|
viewer->slice.fullCount
|
||||||
|
= viewer->slice.skippedBefore
|
||||||
|
= viewer->slice.skippedAfter
|
||||||
|
= 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto around = viewer->around;
|
||||||
|
if (_list.empty()
|
||||||
|
|| (!around && _skippedAfter != 0)
|
||||||
|
|| (around > 0 && around < _list.back())
|
||||||
|
|| (around > _list.front())) {
|
||||||
|
loadAround(around);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto i = around
|
||||||
|
? ranges::lower_bound(_list, around, std::greater<>())
|
||||||
|
: end(_list);
|
||||||
|
const auto availableBefore = (i - begin(_list));
|
||||||
|
const auto availableAfter = (end(_list) - i);
|
||||||
|
const auto useBefore = std::min(availableBefore, viewer->limitBefore);
|
||||||
|
const auto useAfter = std::min(availableAfter, viewer->limitAfter + 1);
|
||||||
|
const auto slice = &viewer->slice;
|
||||||
|
if (_skippedBefore.has_value()) {
|
||||||
|
slice->skippedBefore
|
||||||
|
= (*_skippedBefore + (availableBefore - useBefore));
|
||||||
|
}
|
||||||
|
if (_skippedAfter.has_value()) {
|
||||||
|
slice->skippedAfter
|
||||||
|
= (*_skippedAfter + (availableAfter - useAfter));
|
||||||
|
}
|
||||||
|
const auto channelId = _history->channelId();
|
||||||
|
slice->ids.clear();
|
||||||
|
slice->ids.reserve(useBefore + useAfter);
|
||||||
|
for (auto j = i - useBefore, e = i + useAfter; j != e; ++j) {
|
||||||
|
slice->ids.emplace_back(channelId, *j);
|
||||||
|
}
|
||||||
|
ranges::reverse(slice->ids);
|
||||||
|
slice->fullCount = _fullCount.current();
|
||||||
|
if (_skippedBefore != 0 && useBefore < viewer->limitBefore) {
|
||||||
|
loadBefore();
|
||||||
|
}
|
||||||
|
if (_skippedAfter != 0 && useAfter < viewer->limitAfter + 1) {
|
||||||
|
loadAfter();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RepliesList::applyUpdate(
|
||||||
|
not_null<Viewer*> viewer,
|
||||||
|
const MessageUpdate &update) {
|
||||||
|
if (update.item->history() != _history
|
||||||
|
|| update.item->replyToTop() != _rootId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto id = update.item->id;
|
||||||
|
const auto i = ranges::lower_bound(_list, id, std::greater<>());
|
||||||
|
if (update.flags & MessageUpdate::Flag::Destroyed) {
|
||||||
|
if (i == end(_list) || *i != id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_list.erase(i);
|
||||||
|
if (_skippedBefore && _skippedAfter) {
|
||||||
|
_fullCount = *_skippedBefore + _list.size() + *_skippedAfter;
|
||||||
|
} else if (const auto known = _fullCount.current()) {
|
||||||
|
if (*known > 0) {
|
||||||
|
_fullCount = (*known - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (_skippedAfter != 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (i != end(_list) && *i == id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_list.insert(i, id);
|
||||||
|
if (_skippedBefore && _skippedAfter) {
|
||||||
|
_fullCount = *_skippedBefore + _list.size() + *_skippedAfter;
|
||||||
|
} else if (const auto known = _fullCount.current()) {
|
||||||
|
_fullCount = *known + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Histories &RepliesList::histories() {
|
||||||
|
return _history->owner().histories();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::loadAround(MsgId id) {
|
||||||
|
if (_loadingAround && *_loadingAround == id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
histories().cancelRequest(base::take(_beforeId));
|
||||||
|
histories().cancelRequest(base::take(_afterId));
|
||||||
|
|
||||||
|
const auto send = [=](Fn<void()> finish) {
|
||||||
|
return _history->session().api().request(MTPmessages_GetReplies(
|
||||||
|
_history->peer->input,
|
||||||
|
MTP_int(_rootId),
|
||||||
|
MTP_int(id),
|
||||||
|
MTP_int(id ? (-kMessagesPerPage / 2) : 0),
|
||||||
|
MTP_int(kMessagesPerPage),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0)
|
||||||
|
)).done([=](const MTPmessages_Messages &result) {
|
||||||
|
_beforeId = 0;
|
||||||
|
_loadingAround = std::nullopt;
|
||||||
|
finish();
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
_skippedAfter = 0;
|
||||||
|
} else {
|
||||||
|
_skippedAfter = std::nullopt;
|
||||||
|
}
|
||||||
|
_skippedBefore = std::nullopt;
|
||||||
|
_list.clear();
|
||||||
|
if (processMessagesIsEmpty(result)) {
|
||||||
|
_fullCount = _skippedBefore = _skippedAfter = 0;
|
||||||
|
}
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_beforeId = 0;
|
||||||
|
_loadingAround = std::nullopt;
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
_loadingAround = id;
|
||||||
|
_beforeId = histories().sendRequest(
|
||||||
|
_history,
|
||||||
|
Histories::RequestType::History,
|
||||||
|
send);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::loadBefore() {
|
||||||
|
Expects(!_list.empty());
|
||||||
|
|
||||||
|
if (_loadingAround) {
|
||||||
|
histories().cancelRequest(base::take(_beforeId));
|
||||||
|
} else if (_beforeId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto last = _list.back();
|
||||||
|
const auto send = [=](Fn<void()> finish) {
|
||||||
|
return _history->session().api().request(MTPmessages_GetReplies(
|
||||||
|
_history->peer->input,
|
||||||
|
MTP_int(_rootId),
|
||||||
|
MTP_int(last),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(kMessagesPerPage),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0)
|
||||||
|
)).done([=](const MTPmessages_Messages &result) {
|
||||||
|
_beforeId = 0;
|
||||||
|
finish();
|
||||||
|
|
||||||
|
if (_list.empty()) {
|
||||||
|
return;
|
||||||
|
} else if (_list.back() != last) {
|
||||||
|
loadBefore();
|
||||||
|
} else if (processMessagesIsEmpty(result)) {
|
||||||
|
_skippedBefore = 0;
|
||||||
|
if (_skippedAfter == 0) {
|
||||||
|
_fullCount = _list.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_beforeId = 0;
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
_beforeId = histories().sendRequest(
|
||||||
|
_history,
|
||||||
|
Histories::RequestType::History,
|
||||||
|
send);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::loadAfter() {
|
||||||
|
Expects(!_list.empty());
|
||||||
|
|
||||||
|
if (_afterId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto first = _list.front();
|
||||||
|
const auto send = [=](Fn<void()> finish) {
|
||||||
|
return _history->session().api().request(MTPmessages_GetReplies(
|
||||||
|
_history->peer->input,
|
||||||
|
MTP_int(_rootId),
|
||||||
|
MTP_int(first + 1),
|
||||||
|
MTP_int(-kMessagesPerPage),
|
||||||
|
MTP_int(kMessagesPerPage),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_int(0)
|
||||||
|
)).done([=](const MTPmessages_Messages &result) {
|
||||||
|
_afterId = 0;
|
||||||
|
finish();
|
||||||
|
|
||||||
|
if (_list.empty()) {
|
||||||
|
return;
|
||||||
|
} else if (_list.front() != first) {
|
||||||
|
loadAfter();
|
||||||
|
} else if (processMessagesIsEmpty(result)) {
|
||||||
|
_skippedAfter = 0;
|
||||||
|
if (_skippedBefore == 0) {
|
||||||
|
_fullCount = _list.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_afterId = 0;
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
_afterId = histories().sendRequest(
|
||||||
|
_history,
|
||||||
|
Histories::RequestType::History,
|
||||||
|
send);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
|
||||||
|
const auto guard = gsl::finally([&] { _partLoaded.fire({}); });
|
||||||
|
|
||||||
|
_fullCount = result.match([&](const MTPDmessages_messagesNotModified &) {
|
||||||
|
LOG(("API Error: received messages.messagesNotModified! "
|
||||||
|
"(HistoryWidget::messagesReceived)"));
|
||||||
|
return 0;
|
||||||
|
}, [&](const MTPDmessages_messages &data) {
|
||||||
|
return int(data.vmessages().v.size());
|
||||||
|
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||||
|
return data.vcount().v;
|
||||||
|
}, [&](const MTPDmessages_channelMessages &data) {
|
||||||
|
if (_history->peer->isChannel()) {
|
||||||
|
_history->peer->asChannel()->ptsReceived(data.vpts().v);
|
||||||
|
} else {
|
||||||
|
LOG(("API Error: received messages.channelMessages when "
|
||||||
|
"no channel was passed! (HistoryWidget::messagesReceived)"));
|
||||||
|
}
|
||||||
|
return data.vcount().v;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto &owner = _history->owner();
|
||||||
|
const auto list = result.match([&](
|
||||||
|
const MTPDmessages_messagesNotModified &) {
|
||||||
|
LOG(("API Error: received messages.messagesNotModified! "
|
||||||
|
"(HistoryWidget::messagesReceived)"));
|
||||||
|
return QVector<MTPMessage>();
|
||||||
|
}, [&](const auto &data) {
|
||||||
|
owner.processUsers(data.vusers());
|
||||||
|
owner.processChats(data.vchats());
|
||||||
|
return data.vmessages().v;
|
||||||
|
});
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = IdFromMessage(list.front());
|
||||||
|
const auto toFront = !_list.empty() && (id > _list.front());
|
||||||
|
const auto clientFlags = MTPDmessage_ClientFlags();
|
||||||
|
const auto type = NewMessageType::Existing;
|
||||||
|
auto refreshed = std::vector<MsgId>();
|
||||||
|
if (toFront) {
|
||||||
|
refreshed.reserve(_list.size() + list.size());
|
||||||
|
}
|
||||||
|
for (const auto &message : list) {
|
||||||
|
if (const auto item = owner.addNewMessage(message, clientFlags, type)) {
|
||||||
|
if (item->replyToTop() == _rootId) {
|
||||||
|
if (toFront) {
|
||||||
|
refreshed.push_back(item->id);
|
||||||
|
} else {
|
||||||
|
_list.push_back(item->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toFront) {
|
||||||
|
refreshed.insert(refreshed.end(), _list.begin(), _list.end());
|
||||||
|
_list = std::move(refreshed);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
60
Telegram/SourceFiles/data/data_replies_list.h
Normal file
60
Telegram/SourceFiles/data/data_replies_list.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
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/weak_ptr.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class Histories;
|
||||||
|
struct MessagePosition;
|
||||||
|
struct MessagesSlice;
|
||||||
|
struct MessageUpdate;
|
||||||
|
|
||||||
|
class RepliesList final : public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
RepliesList(not_null<History*> history, MsgId rootId);
|
||||||
|
~RepliesList();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<MessagesSlice> source(
|
||||||
|
MessagePosition aroundId,
|
||||||
|
int limitBefore,
|
||||||
|
int limitAfter);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Viewer;
|
||||||
|
|
||||||
|
[[nodiscard]] Histories &histories();
|
||||||
|
|
||||||
|
[[nodiscard]] bool buildFromData(not_null<Viewer*> viewer);
|
||||||
|
[[nodiscard]] bool applyUpdate(
|
||||||
|
not_null<Viewer*> viewer,
|
||||||
|
const MessageUpdate &update);
|
||||||
|
bool processMessagesIsEmpty(const MTPmessages_Messages &result);
|
||||||
|
void loadAround(MsgId id);
|
||||||
|
void loadBefore();
|
||||||
|
void loadAfter();
|
||||||
|
|
||||||
|
const not_null<History*> _history;
|
||||||
|
const MsgId _rootId = 0;
|
||||||
|
std::vector<MsgId> _list;
|
||||||
|
std::optional<int> _skippedBefore;
|
||||||
|
std::optional<int> _skippedAfter;
|
||||||
|
rpl::variable<std::optional<int>> _fullCount;
|
||||||
|
rpl::event_stream<> _partLoaded;
|
||||||
|
std::optional<MsgId> _loadingAround;
|
||||||
|
int _beforeId = 0;
|
||||||
|
int _afterId = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -1685,6 +1685,9 @@ bool Session::checkEntitiesAndViewsUpdate(const MTPDmessage &data) {
|
||||||
if (result) {
|
if (result) {
|
||||||
stickers().checkSavedGif(existing);
|
stickers().checkSavedGif(existing);
|
||||||
}
|
}
|
||||||
|
session().changes().messageUpdated(
|
||||||
|
existing,
|
||||||
|
Data::MessageUpdate::Flag::NewMaybeAdded);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1060,6 +1060,9 @@ void History::applyMessageChanges(
|
||||||
applyServiceChanges(item, data.c_messageService());
|
applyServiceChanges(item, data.c_messageService());
|
||||||
}
|
}
|
||||||
owner().stickers().checkSavedGif(item);
|
owner().stickers().checkSavedGif(item);
|
||||||
|
session().changes().messageUpdated(
|
||||||
|
item,
|
||||||
|
Data::MessageUpdate::Flag::NewAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::applyServiceChanges(
|
void History::applyServiceChanges(
|
||||||
|
@ -1247,12 +1250,6 @@ void History::applyServiceChanges(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case mtpc_messageActionPhoneCall: {
|
|
||||||
item->history()->session().changes().messageUpdated(
|
|
||||||
item,
|
|
||||||
Data::MessageUpdate::Flag::CallAdded);
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,11 @@ historyViewsInSelectedIcon: icon {{ "history_views", msgInDateFgSelected }};
|
||||||
historyViewsOutIcon: icon {{ "history_views", historyOutIconFg }};
|
historyViewsOutIcon: icon {{ "history_views", historyOutIconFg }};
|
||||||
historyViewsOutSelectedIcon: icon {{ "history_views", historyOutIconFgSelected }};
|
historyViewsOutSelectedIcon: icon {{ "history_views", historyOutIconFgSelected }};
|
||||||
historyViewsInvertedIcon: icon {{ "history_views", historySendingInvertedIconFg }};
|
historyViewsInvertedIcon: icon {{ "history_views", historySendingInvertedIconFg }};
|
||||||
|
historyRepliesInIcon: icon {{ "history_replies", msgInDateFg }};
|
||||||
|
historyRepliesInSelectedIcon: icon {{ "history_replies", msgInDateFgSelected }};
|
||||||
|
historyRepliesOutIcon: icon {{ "history_replies", historyOutIconFg }};
|
||||||
|
historyRepliesOutSelectedIcon: icon {{ "history_replies", historyOutIconFgSelected }};
|
||||||
|
historyRepliesInvertedIcon: icon {{ "history_replies", historySendingInvertedIconFg }};
|
||||||
|
|
||||||
historyComposeField: InputField(defaultInputField) {
|
historyComposeField: InputField(defaultInputField) {
|
||||||
font: msgFont;
|
font: msgFont;
|
||||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
|
#include "history/view/history_view_replies_section.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
|
@ -1540,6 +1541,17 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
_widget->replyToMessage(itemId);
|
_widget->replyToMessage(itemId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (IsServerMsgId(item->id) && item->repliesCount() > 0) {
|
||||||
|
_menu->addAction(tr::lng_replies_view(tr::now, lt_count, item->repliesCount()), [=] {
|
||||||
|
controller->showSection(
|
||||||
|
HistoryView::RepliesMemento(_history, itemId.msg));
|
||||||
|
});
|
||||||
|
} else if (const auto replyToTop = item->replyToTop()) {
|
||||||
|
_menu->addAction(tr::lng_replies_view_thread(tr::now), [=] {
|
||||||
|
controller->showSection(
|
||||||
|
HistoryView::RepliesMemento(_history, replyToTop));
|
||||||
|
});
|
||||||
|
}
|
||||||
if (item->allowsEdit(base::unixtime::now())) {
|
if (item->allowsEdit(base::unixtime::now())) {
|
||||||
_menu->addAction(tr::lng_context_edit_msg(tr::now), [=] {
|
_menu->addAction(tr::lng_context_edit_msg(tr::now), [=] {
|
||||||
_widget->editMessage(itemId);
|
_widget->editMessage(itemId);
|
||||||
|
|
|
@ -1343,14 +1343,25 @@ void Message::drawInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto views = item->Get<HistoryMessageViews>()) {
|
if (auto views = item->Get<HistoryMessageViews>()) {
|
||||||
|
const auto showReplies = (views->views < 0) && (views->replies > 0);
|
||||||
auto icon = [&] {
|
auto icon = [&] {
|
||||||
if (item->id > 0) {
|
if (item->id > 0) {
|
||||||
if (outbg) {
|
if (outbg) {
|
||||||
return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsOutSelectedIcon : st::historyViewsOutIcon));
|
return &(invertedsprites
|
||||||
|
? (showReplies ? st::historyRepliesInvertedIcon : st::historyViewsInvertedIcon)
|
||||||
|
: selected
|
||||||
|
? (showReplies ? st::historyRepliesOutSelectedIcon : st::historyViewsOutSelectedIcon)
|
||||||
|
: (showReplies ? st::historyRepliesOutIcon : st::historyViewsOutIcon));
|
||||||
}
|
}
|
||||||
return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsInSelectedIcon : st::historyViewsInIcon));
|
return &(invertedsprites
|
||||||
|
? (showReplies ? st::historyRepliesInvertedIcon : st::historyViewsInvertedIcon)
|
||||||
|
: selected
|
||||||
|
? (showReplies ? st::historyRepliesInSelectedIcon : st::historyViewsInSelectedIcon)
|
||||||
|
: (showReplies ? st::historyRepliesInIcon : st::historyViewsInIcon));
|
||||||
}
|
}
|
||||||
return &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon);
|
return &(invertedsprites
|
||||||
|
? st::historyViewsSendingInvertedIcon
|
||||||
|
: st::historyViewsSendingIcon);
|
||||||
}();
|
}();
|
||||||
if (item->id > 0) {
|
if (item->id > 0) {
|
||||||
icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width);
|
icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width);
|
||||||
|
|
1218
Telegram/SourceFiles/history/view/history_view_replies_section.cpp
Normal file
1218
Telegram/SourceFiles/history/view/history_view_replies_section.cpp
Normal file
File diff suppressed because it is too large
Load diff
263
Telegram/SourceFiles/history/view/history_view_replies_section.h
Normal file
263
Telegram/SourceFiles/history/view/history_view_replies_section.h
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
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/view/history_view_list_widget.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
|
enum class CompressConfirm;
|
||||||
|
enum class SendMediaType;
|
||||||
|
struct SendingAlbum;
|
||||||
|
|
||||||
|
namespace SendMenu {
|
||||||
|
enum class Type;
|
||||||
|
} // namespace SendMenu
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
struct SendOptions;
|
||||||
|
} // namespace Api
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
struct PreparedList;
|
||||||
|
} // namespace Storage
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ScrollArea;
|
||||||
|
class PlainShadow;
|
||||||
|
class FlatButton;
|
||||||
|
class HistoryDownButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
class BackButton;
|
||||||
|
} // namespace Profile
|
||||||
|
|
||||||
|
namespace InlineBots {
|
||||||
|
class Result;
|
||||||
|
} // namespace InlineBots
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class RepliesList;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
class TopBarWidget;
|
||||||
|
class RepliesMemento;
|
||||||
|
class ComposeControls;
|
||||||
|
|
||||||
|
class RepliesWidget final
|
||||||
|
: public Window::SectionWidget
|
||||||
|
, private ListDelegate {
|
||||||
|
public:
|
||||||
|
RepliesWidget(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<History*> history,
|
||||||
|
MsgId rootId);
|
||||||
|
~RepliesWidget();
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<History*> history() const;
|
||||||
|
Dialogs::RowDescriptor activeChat() const override;
|
||||||
|
|
||||||
|
bool hasTopBarShadow() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap grabForShowAnimation(
|
||||||
|
const Window::SectionSlideParams ¶ms) override;
|
||||||
|
|
||||||
|
bool showInternal(
|
||||||
|
not_null<Window::SectionMemento*> memento,
|
||||||
|
const Window::SectionShow ¶ms) override;
|
||||||
|
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||||
|
|
||||||
|
void setInternalState(
|
||||||
|
const QRect &geometry,
|
||||||
|
not_null<RepliesMemento*> memento);
|
||||||
|
|
||||||
|
// Tabbed selector management.
|
||||||
|
bool pushTabbedSelectorToThirdSection(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
const Window::SectionShow ¶ms) override;
|
||||||
|
bool returnTabbedSelector() override;
|
||||||
|
|
||||||
|
// Float player interface.
|
||||||
|
bool floatPlayerHandleWheelEvent(QEvent *e) override;
|
||||||
|
QRect floatPlayerAvailableRect() override;
|
||||||
|
|
||||||
|
// ListDelegate interface.
|
||||||
|
Context listContext() override;
|
||||||
|
void listScrollTo(int top) override;
|
||||||
|
void listCancelRequest() override;
|
||||||
|
void listDeleteRequest() override;
|
||||||
|
rpl::producer<Data::MessagesSlice> listSource(
|
||||||
|
Data::MessagePosition aroundId,
|
||||||
|
int limitBefore,
|
||||||
|
int limitAfter) override;
|
||||||
|
bool listAllowsMultiSelect() override;
|
||||||
|
bool listIsItemGoodForSelection(not_null<HistoryItem*> item) override;
|
||||||
|
bool listIsLessInOrder(
|
||||||
|
not_null<HistoryItem*> first,
|
||||||
|
not_null<HistoryItem*> second) override;
|
||||||
|
void listSelectionChanged(SelectedItems &&items) override;
|
||||||
|
void listVisibleItemsChanged(HistoryItemsList &&items) override;
|
||||||
|
std::optional<int> listUnreadBarView(
|
||||||
|
const std::vector<not_null<Element*>> &elements) override;
|
||||||
|
void listContentRefreshed() override;
|
||||||
|
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
void showAnimatedHook(
|
||||||
|
const Window::SectionSlideParams ¶ms) override;
|
||||||
|
void showFinishedHook() override;
|
||||||
|
void doSetInnerFocus() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onScroll();
|
||||||
|
void updateInnerVisibleArea();
|
||||||
|
void updateControlsGeometry();
|
||||||
|
void updateAdaptiveLayout();
|
||||||
|
void saveState(not_null<RepliesMemento*> memento);
|
||||||
|
void restoreState(not_null<RepliesMemento*> memento);
|
||||||
|
void showAtPosition(Data::MessagePosition position);
|
||||||
|
bool showAtPositionNow(Data::MessagePosition position);
|
||||||
|
|
||||||
|
void setupComposeControls();
|
||||||
|
|
||||||
|
void setupDragArea();
|
||||||
|
|
||||||
|
void setupScrollDownButton();
|
||||||
|
void scrollDownClicked();
|
||||||
|
void scrollDownAnimationFinish();
|
||||||
|
void updateScrollDownVisibility();
|
||||||
|
void updateScrollDownPosition();
|
||||||
|
|
||||||
|
void confirmSendNowSelected();
|
||||||
|
void confirmDeleteSelected();
|
||||||
|
void clearSelected();
|
||||||
|
|
||||||
|
void send();
|
||||||
|
void send(Api::SendOptions options);
|
||||||
|
void edit(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
Api::SendOptions options,
|
||||||
|
mtpRequestId *const saveEditMsgRequestId);
|
||||||
|
void highlightSingleNewMessage(const Data::MessagesSlice &slice);
|
||||||
|
void chooseAttach();
|
||||||
|
[[nodiscard]] SendMenu::Type sendMenuType() const;
|
||||||
|
|
||||||
|
void uploadFile(const QByteArray &fileContent, SendMediaType type);
|
||||||
|
bool confirmSendingFiles(
|
||||||
|
QImage &&image,
|
||||||
|
QByteArray &&content,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel = QString());
|
||||||
|
bool confirmSendingFiles(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel = QString());
|
||||||
|
bool confirmSendingFiles(
|
||||||
|
not_null<const QMimeData*> data,
|
||||||
|
CompressConfirm compressed,
|
||||||
|
const QString &insertTextOnCancel = QString());
|
||||||
|
bool showSendingFilesError(const Storage::PreparedList &list) const;
|
||||||
|
void uploadFilesAfterConfirmation(
|
||||||
|
Storage::PreparedList &&list,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
MsgId replyTo,
|
||||||
|
Api::SendOptions options,
|
||||||
|
std::shared_ptr<SendingAlbum> album);
|
||||||
|
|
||||||
|
void sendExistingDocument(not_null<DocumentData*> document);
|
||||||
|
bool sendExistingDocument(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Api::SendOptions options);
|
||||||
|
void sendExistingPhoto(not_null<PhotoData*> photo);
|
||||||
|
bool sendExistingPhoto(
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
Api::SendOptions options);
|
||||||
|
void sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot);
|
||||||
|
void sendInlineResult(
|
||||||
|
not_null<InlineBots::Result*> result,
|
||||||
|
not_null<UserData*> bot,
|
||||||
|
Api::SendOptions options);
|
||||||
|
|
||||||
|
const not_null<History*> _history;
|
||||||
|
const MsgId _rootId = 0;
|
||||||
|
std::shared_ptr<Data::RepliesList> _replies;
|
||||||
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
|
QPointer<ListWidget> _inner;
|
||||||
|
object_ptr<TopBarWidget> _topBar;
|
||||||
|
object_ptr<Ui::PlainShadow> _topBarShadow;
|
||||||
|
std::unique_ptr<ComposeControls> _composeControls;
|
||||||
|
bool _skipScrollEvent = false;
|
||||||
|
|
||||||
|
FullMsgId _highlightMessageId;
|
||||||
|
std::optional<Data::MessagePosition> _nextAnimatedScrollPosition;
|
||||||
|
int _nextAnimatedScrollDelta = 0;
|
||||||
|
|
||||||
|
Ui::Animations::Simple _scrollDownShown;
|
||||||
|
bool _scrollDownIsShown = false;
|
||||||
|
object_ptr<Ui::HistoryDownButton> _scrollDown;
|
||||||
|
|
||||||
|
Data::MessagesSlice _lastSlice;
|
||||||
|
bool _choosingAttach = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class RepliesMemento : public Window::SectionMemento {
|
||||||
|
public:
|
||||||
|
RepliesMemento(not_null<History*> history, MsgId rootId)
|
||||||
|
: _history(history)
|
||||||
|
, _rootId(rootId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Window::SectionWidget> createWidget(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Window::Column column,
|
||||||
|
const QRect &geometry) override;
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<History*> getHistory() const {
|
||||||
|
return _history;
|
||||||
|
}
|
||||||
|
[[nodiscard]] MsgId getRootId() const {
|
||||||
|
return _rootId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReplies(std::shared_ptr<Data::RepliesList> replies) {
|
||||||
|
_replies = std::move(replies);
|
||||||
|
}
|
||||||
|
[[nodiscard]] std::shared_ptr<Data::RepliesList> getReplies() const {
|
||||||
|
return _replies;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<ListMemento*> list() {
|
||||||
|
return &_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<History*> _history;
|
||||||
|
const MsgId _rootId = 0;
|
||||||
|
ListMemento _list;
|
||||||
|
std::shared_ptr<Data::RepliesList> _replies;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -240,4 +240,4 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HistoryScheduled
|
} // namespace HistoryView
|
||||||
|
|
|
@ -330,9 +330,12 @@ void TopBarWidget::paintTopBar(Painter &p) {
|
||||||
const auto folder = _activeChat.folder();
|
const auto folder = _activeChat.folder();
|
||||||
if (folder
|
if (folder
|
||||||
|| history->peer->isSelf()
|
|| history->peer->isSelf()
|
||||||
|| (_section == Section::Scheduled)) {
|
|| (_section == Section::Scheduled)
|
||||||
|
|| !_customTitleText.isEmpty()) {
|
||||||
// #TODO feed name emoji.
|
// #TODO feed name emoji.
|
||||||
auto text = (_section == Section::Scheduled)
|
auto text = !_customTitleText.isEmpty()
|
||||||
|
? _customTitleText
|
||||||
|
: (_section == Section::Scheduled)
|
||||||
? ((history && history->peer->isSelf())
|
? ((history && history->peer->isSelf())
|
||||||
? tr::lng_reminder_messages(tr::now)
|
? tr::lng_reminder_messages(tr::now)
|
||||||
: tr::lng_scheduled_messages(tr::now))
|
: tr::lng_scheduled_messages(tr::now))
|
||||||
|
@ -497,6 +500,13 @@ void TopBarWidget::setActiveChat(Dialogs::Key chat, Section section) {
|
||||||
refreshUnreadBadge();
|
refreshUnreadBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TopBarWidget::setCustomTitle(const QString &title) {
|
||||||
|
if (_customTitleText != title) {
|
||||||
|
_customTitleText = title;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TopBarWidget::refreshInfoButton() {
|
void TopBarWidget::refreshInfoButton() {
|
||||||
if (const auto peer = _activeChat.peer()) {
|
if (const auto peer = _activeChat.peer()) {
|
||||||
auto info = object_ptr<Ui::UserpicButton>(
|
auto info = object_ptr<Ui::UserpicButton>(
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
enum class Section {
|
enum class Section {
|
||||||
History,
|
History,
|
||||||
Scheduled,
|
Scheduled,
|
||||||
|
Replies,
|
||||||
};
|
};
|
||||||
|
|
||||||
TopBarWidget(
|
TopBarWidget(
|
||||||
|
@ -62,6 +63,7 @@ public:
|
||||||
void setAnimatingMode(bool enabled);
|
void setAnimatingMode(bool enabled);
|
||||||
|
|
||||||
void setActiveChat(Dialogs::Key chat, Section section);
|
void setActiveChat(Dialogs::Key chat, Section section);
|
||||||
|
void setCustomTitle(const QString &title);
|
||||||
|
|
||||||
rpl::producer<> forwardSelectionRequest() const {
|
rpl::producer<> forwardSelectionRequest() const {
|
||||||
return _forwardSelection.events();
|
return _forwardSelection.events();
|
||||||
|
@ -125,6 +127,7 @@ private:
|
||||||
const not_null<Window::SessionController*> _controller;
|
const not_null<Window::SessionController*> _controller;
|
||||||
Dialogs::Key _activeChat;
|
Dialogs::Key _activeChat;
|
||||||
Section _section = Section::History;
|
Section _section = Section::History;
|
||||||
|
QString _customTitleText;
|
||||||
|
|
||||||
int _selectedCount = 0;
|
int _selectedCount = 0;
|
||||||
bool _canDelete = false;
|
bool _canDelete = false;
|
||||||
|
|
|
@ -1137,7 +1137,7 @@ bool Gif::needsBubble() const {
|
||||||
}
|
}
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
return item->viaBot()
|
return item->viaBot()
|
||||||
|| item->Has<HistoryMessageReply>()
|
|| _parent->displayedReply()
|
||||||
|| _parent->displayForwardedFrom()
|
|| _parent->displayForwardedFrom()
|
||||||
|| _parent->displayFromName();
|
|| _parent->displayFromName();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -320,7 +320,7 @@ bool Location::needsBubble() const {
|
||||||
}
|
}
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
return item->viaBot()
|
return item->viaBot()
|
||||||
|| item->Has<HistoryMessageReply>()
|
|| _parent->displayedReply()
|
||||||
|| _parent->displayForwardedFrom()
|
|| _parent->displayForwardedFrom()
|
||||||
|| _parent->displayFromName();
|
|| _parent->displayFromName();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -454,7 +454,7 @@ bool GroupedMedia::computeNeedBubble() const {
|
||||||
}
|
}
|
||||||
if (const auto item = _parent->data()) {
|
if (const auto item = _parent->data()) {
|
||||||
if (item->viaBot()
|
if (item->viaBot()
|
||||||
|| item->Has<HistoryMessageReply>()
|
|| _parent->displayedReply()
|
||||||
|| _parent->displayForwardedFrom()
|
|| _parent->displayForwardedFrom()
|
||||||
|| _parent->displayFromName()
|
|| _parent->displayFromName()
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -802,7 +802,7 @@ bool Photo::needsBubble() const {
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
if (item->toHistoryMessage()) {
|
if (item->toHistoryMessage()) {
|
||||||
return item->viaBot()
|
return item->viaBot()
|
||||||
|| item->Has<HistoryMessageReply>()
|
|| _parent->displayedReply()
|
||||||
|| _parent->displayForwardedFrom()
|
|| _parent->displayForwardedFrom()
|
||||||
|| _parent->displayFromName();
|
|| _parent->displayFromName();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue