Keep track of scheduled messages.

This commit is contained in:
John Preston 2019-08-08 15:13:26 +01:00
parent 549789bfb7
commit 3814b0833d
23 changed files with 477 additions and 104 deletions

View File

@ -0,0 +1,37 @@
/*
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 Api {
uint32 HashInit() {
return 0;
}
template <
typename Int,
typename = std::enable_if_t<
std::is_same_v<Int, int32> || std::is_same_v<Int, uint32>>>
void HashUpdate(uint32 &already, Int value) {
already += (already * 20261) + uint32(value);
}
int32 HashFinalize(uint32 already) {
return int32(already & 0x7FFFFFFF);
}
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
auto result = HashInit();
for (const auto value : range) {
HashUpdate(result, value);
}
return HashFinalize(result);
}
} // namespace Api

View File

@ -65,15 +65,6 @@ inline QString ToString(uint64 value) {
} // namespace details
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;
for (auto value : range) {
acc += (acc * 20261) + uint32(value);
}
return int32(acc & 0x7FFFFFFF);
}
template <
typename ...Types,
typename = std::enable_if_t<(sizeof...(Types) > 0)>>

View File

@ -31,7 +31,7 @@ QImage Prepare(
QByteArray data,
FileType type) {
if (type == FileType::Video) {
return Media::Clip::PrepareForSending(path, data).thumbnail;
return ::Media::Clip::PrepareForSending(path, data).thumbnail;
} else if (type == FileType::AnimatedSticker) {
return Lottie::ReadThumbnail(Lottie::ReadContent(data, path));
}

View File

@ -0,0 +1,278 @@
/*
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_scheduled_messages.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "api/api_hash.h"
#include "main/main_session.h"
#include "history/history.h"
#include "history/history_item_components.h"
#include "apiwrap.h"
namespace Data {
namespace {
MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) {
return message.match([&](const MTPDmessageEmpty &) {
return MTP_messageEmpty(MTP_int(id));
}, [&](const MTPDmessageService &data) {
return MTP_messageService(
MTP_flags(data.vflags().v
| MTPDmessageService::Flag(
MTPDmessage::Flag::f_from_scheduled)),
MTP_int(id),
MTP_int(data.vfrom_id().value_or_empty()),
data.vto_id(),
MTP_int(data.vreply_to_msg_id().value_or_empty()),
data.vdate(),
data.vaction());
}, [&](const MTPDmessage &data) {
const auto fwdFrom = data.vfwd_from();
const auto media = data.vmedia();
const auto markup = data.vreply_markup();
const auto entities = data.ventities();
return MTP_message(
MTP_flags(data.vflags().v | MTPDmessage::Flag::f_from_scheduled),
MTP_int(id),
MTP_int(data.vfrom_id().value_or_empty()),
data.vto_id(),
fwdFrom ? *fwdFrom : MTPMessageFwdHeader(),
MTP_int(data.vvia_bot_id().value_or_empty()),
MTP_int(data.vreply_to_msg_id().value_or_empty()),
data.vdate(),
data.vmessage(),
media ? *media : MTPMessageMedia(),
markup ? *markup : MTPReplyMarkup(),
entities ? *entities : MTPVector<MTPMessageEntity>(),
MTP_int(data.vviews().value_or_empty()),
MTP_int(data.vedit_date().value_or_empty()),
MTP_bytes(data.vpost_author().value_or_empty()),
MTP_long(data.vgrouped_id().value_or_empty()),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>());
});
}
} // namespace
ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
: _session(&owner->session()) {
owner->itemRemoved(
) | rpl::filter([](not_null<const HistoryItem*> item) {
return item->isScheduled();
}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
remove(item);
}, _lifetime);
}
ScheduledMessages::~ScheduledMessages() {
for (const auto &request : _requests) {
_session->api().request(request.second.requestId).cancel();
}
}
MsgId ScheduledMessages::scheduledId(not_null<HistoryItem*> item) {
if (const auto i = _data.find(item->history()); i != end(_data)) {
const auto &list = i->second.idByItem;
if (const auto j = list.find(item); j != end(list)) {
return j->second;
}
}
return MsgId(0);
}
HistoryItem *ScheduledMessages::scheduledMessage(
not_null<History*> history,
MsgId id) {
if (const auto i = _data.find(history); i != end(_data)) {
const auto &list = i->second.itemById;
if (const auto j = list.find(id); j != end(list)) {
return j->second;
}
}
return nullptr;
}
void ScheduledMessages::apply(const MTPDupdateNewScheduledMessage &update) {
const auto &message = update.vmessage();
const auto peer = PeerFromMessage(message);
if (!peer) {
return;
}
const auto history = _session->data().historyLoaded(peer);
if (!history) {
return;
}
append(history, _data[history], message);
_updates.fire_copy(history);
}
void ScheduledMessages::apply(
const MTPDupdateDeleteScheduledMessages &update) {
const auto peer = peerFromMTP(update.vpeer());
if (!peer) {
return;
}
const auto history = _session->data().historyLoaded(peer);
if (!history) {
return;
}
auto i = _data.find(history);
if (i == end(_data)) {
return;
}
for (const auto &id : update.vmessages().v) {
const auto &list = i->second;
const auto j = list.itemById.find(id.v);
if (j != end(list.itemById)) {
j->second->destroy();
i = _data.find(history);
if (i == end(_data)) {
break;
}
}
}
_updates.fire_copy(history);
}
rpl::producer<> ScheduledMessages::updates(not_null<History*> history) {
request(history);
return _updates.events(
) | rpl::filter([=](not_null<History*> value) {
return (value == history);
}) | rpl::map([] {
return rpl::empty_value();
});
}
void ScheduledMessages::request(not_null<History*> history) {
auto &request = _requests[history];
if (request.requestId) {
return;
}
request.requestId = _session->api().request(
MTPmessages_GetScheduledHistory(
history->peer->input,
MTP_int(request.hash))
).done([=](const MTPmessages_Messages &result) {
parse(history, result);
}).fail([=](const RPCError &error) {
_requests.remove(history);
}).send();
}
void ScheduledMessages::parse(
not_null<History*> history,
const MTPmessages_Messages &list) {
auto &request = _requests[history];
request.requestId = 0;
auto element = _data.find(history);
list.match([&](const MTPDmessages_messagesNotModified &data) {
}, [&](const auto &data) {
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
const auto &messages = data.vmessages().v;
if (messages.isEmpty()) {
return;
}
element = _data.emplace(history, List()).first;
auto &list = element->second;
for (const auto &message : messages) {
append(history, list, message);
}
if (list.items.empty()) {
_data.erase(element);
element = end(_data);
}
_updates.fire_copy(history);
});
request.hash = (element != end(_data))
? countListHash(element->second)
: 0;
if (!request.requestId && !request.hash) {
_requests.remove(history);
}
}
void ScheduledMessages::append(
not_null<History*> history,
List &list,
const MTPMessage &message) {
const auto id = message.match([&](const auto &data) {
return data.vid().v;
});
const auto i = list.itemById.find(id);
if (i != end(list.itemById)) {
const auto j = ranges::find(
list.items,
i->second.get(),
&OwnedItem::get);
const auto e = end(list.items);
Assert(j != e);
if (j + 1 != e) {
std::rotate(j, j + 1, e);
}
return;
}
const auto item = _session->data().addNewMessage(
PrepareMessage(message, history->nextNonHistoryEntryId()),
MTPDmessage_ClientFlags(),
NewMessageType::Existing);
if (!item || item->history() != history) {
LOG(("API Error: Bad data received in scheduled messages."));
return;
}
list.items.emplace_back(item);
list.itemById.emplace(id, item);
list.idByItem.emplace(item, id);
}
void ScheduledMessages::remove(not_null<const HistoryItem*> item) {
const auto history = item->history();
const auto i = _data.find(history);
Assert(i != end(_data));
auto &list = i->second;
const auto j = list.idByItem.find(item);
Assert(j != end(list.idByItem));
list.itemById.remove(j->second);
list.idByItem.erase(j);
const auto k = ranges::find(list.items, item, &OwnedItem::get);
Assert(k != list.items.end());
k->release();
list.items.erase(k);
if (list.items.empty()) {
_data.erase(i);
}
_updates.fire_copy(history);
}
int32 ScheduledMessages::countListHash(const List &list) const {
using namespace Api;
auto hash = HashInit();
for (const auto &item : list.items) {
const auto j = list.idByItem.find(item.get());
HashUpdate(hash, j->second);
if (const auto edited = item->Get<HistoryMessageEdited>()) {
HashUpdate(hash, std::max(item->date(), edited->date));
} else {
HashUpdate(hash, item->date());
}
}
return HashFinalize(hash);
}
} // namespace Data

View File

@ -0,0 +1,72 @@
/*
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/history_item.h"
class History;
namespace Main {
class Session;
} // namespace Main
namespace Data {
class Session;
class ScheduledMessages final {
public:
explicit ScheduledMessages(not_null<Session*> owner);
ScheduledMessages(const ScheduledMessages &other) = delete;
ScheduledMessages &operator=(const ScheduledMessages &other) = delete;
~ScheduledMessages();
[[nodiscard]] MsgId scheduledId(not_null<HistoryItem*> item);
[[nodiscard]] HistoryItem *scheduledMessage(
not_null<History*> history,
MsgId id);
void apply(const MTPDupdateNewScheduledMessage &update);
void apply(const MTPDupdateDeleteScheduledMessages &update);
[[nodiscard]] rpl::producer<> updates(not_null<History*> history);
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
struct List {
std::vector<OwnedItem> items;
base::flat_map<MsgId, not_null<HistoryItem*>> itemById;
base::flat_map<not_null<HistoryItem*>, MsgId> idByItem;
};
struct Request {
int32 hash = 0;
mtpRequestId requestId = 0;
};
void request(not_null<History*> history);
void parse(
not_null<History*> history,
const MTPmessages_Messages &list);
void append(
not_null<History*> history,
List &list,
const MTPMessage &message);
void remove(not_null<const HistoryItem*> item);
[[nodiscard]] int32 countListHash(const List &list) const;
const not_null<Main::Session*> _session;
base::flat_map<not_null<History*>, List> _data;
base::flat_map<not_null<History*>, Request> _requests;
rpl::event_stream<not_null<History*>> _updates;
rpl::lifetime _lifetime;
};
} // namespace Data

View File

@ -205,8 +205,9 @@ Session::Session(not_null<Main::Session*> session)
, _sendActionsAnimation([=](crl::time now) {
return sendActionsAnimationCallback(now);
})
, _unmuteByFinishedTimer([=] { unmuteByFinished(); })
, _groups(this)
, _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
, _scheduledMessages(this) {
_cache->open(Local::cacheKey());
_bigFileCache->open(Local::cacheBigFileKey());
@ -1814,10 +1815,10 @@ void Session::removeDependencyMessage(not_null<HistoryItem*> item) {
}
void Session::destroyMessage(not_null<HistoryItem*> item) {
Expects(!item->isLogEntry() || !item->mainView());
Expects(item->isHistoryEntry() || !item->mainView());
const auto peerId = item->history()->peer->id;
if (!item->isLogEntry()) {
if (item->isHistoryEntry()) {
// All this must be done for all items manually in History::clear()!
item->eraseFromUnreadMentions();
if (IsServerMsgId(item->id)) {

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_main_list.h"
#include "data/data_groups.h"
#include "data/data_notify_settings.h"
#include "data/data_scheduled_messages.h"
#include "history/history_location_manager.h"
#include "base/timer.h"
#include "base/flags.h"
@ -63,6 +64,7 @@ namespace Data {
class Folder;
class LocationPoint;
class WallPaper;
class ScheduledMessages;
class Session final {
public:
@ -80,6 +82,20 @@ public:
return *_session;
}
[[nodiscard]] Groups &groups() {
return _groups;
}
[[nodiscard]] const Groups &groups() const {
return _groups;
}
[[nodiscard]] ScheduledMessages &scheduledMessages() {
return _scheduledMessages;
}
[[nodiscard]] const ScheduledMessages &scheduledMessages() const {
return _scheduledMessages;
}
void clear();
void startExport(PeerData *peer = nullptr);
@ -670,13 +686,6 @@ public:
void setProxyPromoted(PeerData *promoted);
PeerData *proxyPromoted() const;
Groups &groups() {
return _groups;
}
const Groups &groups() const {
return _groups;
}
bool updateWallpapers(const MTPaccount_WallPapers &data);
void removeWallpaper(const WallPaper &paper);
const std::vector<WallPaper> &wallpapers() const;
@ -945,7 +954,6 @@ private:
base::flat_map<FolderId, std::unique_ptr<Folder>> _folders;
//rpl::variable<FeedId> _defaultFeedId = FeedId(); // #feed
Groups _groups;
std::unordered_map<
not_null<const HistoryItem*>,
std::vector<not_null<ViewElement*>>> _views;
@ -980,6 +988,9 @@ private:
std::vector<WallPaper> _wallpapers;
int32 _wallpapersHash = 0;
Groups _groups;
ScheduledMessages _scheduledMessages;
rpl::lifetime _lifetime;
};

View File

@ -219,34 +219,33 @@ InnerWidget::InnerWidget(
, _controller(controller)
, _channel(channel)
, _history(channel->owner().history(channel))
, _scrollDateCheck([this] { scrollDateCheck(); })
, _scrollDateCheck([=] { scrollDateCheck(); })
, _emptyText(
st::historyAdminLogEmptyWidth
- st::historyAdminLogEmptyPadding.left()
- st::historyAdminLogEmptyPadding.left())
, _idManager(_history->adminLogIdManager()) {
st::historyAdminLogEmptyWidth
- st::historyAdminLogEmptyPadding.left()
- st::historyAdminLogEmptyPadding.left()) {
setMouseTracking(true);
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
_scrollDateHideTimer.setCallback([=] { scrollDateHideByTimer(); });
session().data().viewRepaintRequest(
) | rpl::start_with_next([this](auto view) {
) | rpl::start_with_next([=](auto view) {
if (view->delegate() == this) {
repaintItem(view);
}
}, lifetime());
session().data().viewResizeRequest(
) | rpl::start_with_next([this](auto view) {
) | rpl::start_with_next([=](auto view) {
if (view->delegate() == this) {
resizeItem(view);
}
}, lifetime());
session().data().itemViewRefreshRequest(
) | rpl::start_with_next([this](auto item) {
) | rpl::start_with_next([=](auto item) {
if (const auto view = viewForItem(item)) {
refreshItem(view);
}
}, lifetime());
session().data().viewLayoutChanged(
) | rpl::start_with_next([this](auto view) {
) | rpl::start_with_next([=](auto view) {
if (view->delegate() == this) {
if (view->isUnderCursor()) {
updateSelected();
@ -254,15 +253,18 @@ InnerWidget::InnerWidget(
}
}, lifetime());
session().data().animationPlayInlineRequest(
) | rpl::start_with_next([this](auto item) {
) | rpl::start_with_next([=](auto item) {
if (const auto view = viewForItem(item)) {
if (const auto media = view->media()) {
media->playAnimation();
}
}
}, lifetime());
subscribe(session().data().queryItemVisibility(), [this](const Data::Session::ItemVisibilityQuery &query) {
if (_history != query.item->history() || !query.item->isLogEntry() || !isVisible()) {
subscribe(session().data().queryItemVisibility(), [=](
const Data::Session::ItemVisibilityQuery &query) {
if (_history != query.item->history()
|| query.item->isHistoryEntry()
|| !isVisible()) {
return;
}
if (const auto view = viewForItem(query.item)) {
@ -562,7 +564,6 @@ void InnerWidget::saveState(not_null<SectionMemento*> memento) {
base::take(_eventIds),
_upLoaded,
_downLoaded);
memento->setIdManager(base::take(_idManager));
base::take(_itemsByData);
}
_upLoaded = _downLoaded = true; // Don't load or handle anything anymore.
@ -575,9 +576,6 @@ void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
_itemsByData.emplace(item->data(), item.get());
}
_eventIds = memento->takeEventIds();
if (auto manager = memento->takeIdManager()) {
_idManager = std::move(manager);
}
_admins = memento->takeAdmins();
_adminsCanEdit = memento->takeAdminsCanEdit();
_filter = memento->takeFilter();
@ -678,7 +676,6 @@ void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLo
GenerateItems(
this,
_history,
_idManager.get(),
data,
addOne);
if (count > 1) {
@ -883,8 +880,6 @@ void InnerWidget::clearAfterFilterChange() {
_items.clear();
_eventIds.clear();
_itemsByData.clear();
_idManager = nullptr;
_idManager = _history->adminLogIdManager();
updateEmptyText();
updateSize();
}

View File

@ -276,8 +276,6 @@ private:
std::vector<not_null<UserData*>> _adminsCanEdit;
Fn<void(FilterValue &&filter)> _showFilterCallback;
std::shared_ptr<LocalIdManager> _idManager;
};
} // namespace AdminLog

View File

@ -369,7 +369,6 @@ void OwnedItem::refreshView(
void GenerateItems(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
not_null<LocalIdManager*> idManager,
const MTPDchannelAdminLogEvent &event,
Fn<void(OwnedItem item)> callback) {
Expects(history->peer->isChannel());
@ -394,7 +393,7 @@ void GenerateItems(
addPart(history->owner().makeServiceMessage(
history,
MTPDmessage_ClientFlags(),
idManager->next(),
history->nextNonHistoryEntryId(),
date,
message,
MTPDmessage::Flags(0),
@ -434,7 +433,7 @@ void GenerateItems(
auto newDescription = PrepareText(newValue, QString());
auto body = history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -470,7 +469,7 @@ void GenerateItems(
auto newLink = newValue.isEmpty() ? TextWithEntities() : PrepareText(Core::App().createInternalLinkFull(newValue), QString());
auto body = history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -535,7 +534,7 @@ void GenerateItems(
addPart(history->createItem(
PrepareLogMessage(
action.vmessage(),
idManager->next(),
history->nextNonHistoryEntryId(),
date),
MTPDmessage_ClientFlags(),
detachExistingItem));
@ -560,7 +559,7 @@ void GenerateItems(
auto body = history->createItem(
PrepareLogMessage(
action.vnew_message(),
idManager->next(),
history->nextNonHistoryEntryId(),
date),
MTPDmessage_ClientFlags(),
detachExistingItem);
@ -583,7 +582,7 @@ void GenerateItems(
auto detachExistingItem = false;
addPart(history->createItem(
PrepareLogMessage(action.vmessage(), idManager->next(), date),
PrepareLogMessage(action.vmessage(), history->nextNonHistoryEntryId(), date),
MTPDmessage_ClientFlags(),
detachExistingItem));
};
@ -610,7 +609,7 @@ void GenerateItems(
auto bodyText = GenerateParticipantChangeText(channel, action.vparticipant());
addPart(history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -629,7 +628,7 @@ void GenerateItems(
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant());
addPart(history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -654,7 +653,7 @@ void GenerateItems(
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant());
addPart(history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -689,7 +688,7 @@ void GenerateItems(
addPart(history->owner().makeServiceMessage(
history,
MTPDmessage_ClientFlags(),
idManager->next(),
history->nextNonHistoryEntryId(),
date,
message,
MTPDmessage::Flags(0),
@ -713,7 +712,7 @@ void GenerateItems(
auto bodyText = GenerateDefaultBannedRightsChangeText(channel, action.vnew_banned_rights(), action.vprev_banned_rights());
addPart(history->owner().makeMessage(
history,
idManager->next(),
history->nextNonHistoryEntryId(),
bodyFlags,
bodyClientFlags,
bodyReplyTo,
@ -730,7 +729,7 @@ void GenerateItems(
auto detachExistingItem = false;
addPart(history->createItem(
PrepareLogMessage(action.vmessage(), idManager->next(), date),
PrepareLogMessage(action.vmessage(), history->nextNonHistoryEntryId(), date),
MTPDmessage_ClientFlags(),
detachExistingItem));
};
@ -765,7 +764,7 @@ void GenerateItems(
addPart(history->owner().makeServiceMessage(
history,
MTPDmessage_ClientFlags(),
idManager->next(),
history->nextNonHistoryEntryId(),
date,
message,
MTPDmessage::Flags(0),

View File

@ -15,12 +15,10 @@ class Element;
namespace AdminLog {
class OwnedItem;
class LocalIdManager;
void GenerateItems(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
not_null<LocalIdManager*> idManager,
const MTPDchannelAdminLogEvent &event,
Fn<void(OwnedItem item)> callback);

View File

@ -47,20 +47,6 @@ inline bool operator!=(const FilterValue &a, const FilterValue &b) {
return !(a == b);
}
class LocalIdManager {
public:
LocalIdManager() = default;
LocalIdManager(const LocalIdManager &other) = delete;
LocalIdManager &operator=(const LocalIdManager &other) = delete;
MsgId next() {
return ++_counter;
}
private:
MsgId _counter = ServerMaxMsgId;
};
class Widget final : public Window::SectionWidget {
public:
Widget(QWidget *parent, not_null<Window::SessionController*> controller, not_null<ChannelData*> channel);
@ -164,18 +150,12 @@ public:
void setSearchQuery(QString &&query) {
_searchQuery = std::move(query);
}
void setIdManager(std::shared_ptr<LocalIdManager> &&manager) {
_idManager = std::move(manager);
}
std::vector<OwnedItem> takeItems() {
return std::move(_items);
}
std::set<uint64> takeEventIds() {
return std::move(_eventIds);
}
std::shared_ptr<LocalIdManager> takeIdManager() {
return std::move(_idManager);
}
bool upLoaded() const {
return _upLoaded;
}
@ -198,7 +178,6 @@ private:
std::set<uint64> _eventIds;
bool _upLoaded = false;
bool _downLoaded = true;
std::shared_ptr<LocalIdManager> _idManager;
FilterValue _filter;
QString _searchQuery;

View File

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/view/history_view_element.h"
#include "history/admin_log/history_admin_log_section.h"
#include "history/history_message.h"
#include "history/history_service.h"
#include "history/history_item_components.h"
@ -1823,13 +1822,8 @@ void History::getNextFirstUnreadMessage() {
_firstUnreadView = nullptr;
}
std::shared_ptr<AdminLog::LocalIdManager> History::adminLogIdManager() {
if (const auto strong = _adminLogIdManager.lock()) {
return strong;
}
auto result = std::make_shared<AdminLog::LocalIdManager>();
_adminLogIdManager = result;
return result;
MsgId History::nextNonHistoryEntryId() {
return ++_nonHistoryEntryId;
}
bool History::folderKnown() const {

View File

@ -42,10 +42,6 @@ namespace HistoryView {
class Element;
} // namespace HistoryView
namespace AdminLog {
class LocalIdManager;
} // namespace AdminLog
enum class NewMessageType {
Unread,
Last,
@ -337,7 +333,7 @@ public:
// of the displayed window relative to the history start coordinate
void countScrollState(int top);
std::shared_ptr<AdminLog::LocalIdManager> adminLogIdManager();
MsgId nextNonHistoryEntryId();
bool folderKnown() const override;
Data::Folder *folder() const override;
@ -538,7 +534,7 @@ private:
std::deque<not_null<HistoryItem*>> _notifications;
std::weak_ptr<AdminLog::LocalIdManager> _adminLogIdManager;
MsgId _nonHistoryEntryId = ServerMaxMsgId;
};

View File

@ -352,7 +352,8 @@ void HistoryItem::addLogEntryOriginal(
WebPageId localId,
const QString &label,
const TextWithEntities &content) {
Expects(isLogEntry());
Expects(!isHistoryEntry());
Expects(!isScheduled());
AddComponents(HistoryMessageLogEntryOriginal::Bit());
Get<HistoryMessageLogEntryOriginal>()->page = _history->owner().webpage(
@ -505,7 +506,9 @@ bool HistoryItem::canStopPoll() const {
}
bool HistoryItem::canDelete() const {
if (isLogEntry() || (!IsServerMsgId(id) && serviceMsg())) {
if (!IsServerMsgId(id) && serviceMsg()) {
return false;
} else if (!isHistoryEntry() && !isScheduled()) {
return false;
}
auto channel = _history->peer->asChannel();

View File

@ -85,8 +85,16 @@ public:
UserData *viaBot() const;
UserData *getMessageBot() const;
bool isLogEntry() const {
return (id > ServerMaxMsgId);
[[nodiscard]] bool isHistoryEntry() const {
return (id < ServerMaxMsgId);
}
[[nodiscard]] bool isFromScheduled() const {
return isHistoryEntry()
&& (_flags & MTPDmessage::Flag::f_from_scheduled);
}
[[nodiscard]] bool isScheduled() const {
return !isHistoryEntry()
&& (_flags & MTPDmessage::Flag::f_from_scheduled);
}
void addLogEntryOriginal(
WebPageId localId,

View File

@ -736,7 +736,7 @@ void HistoryMessage::applyGroupAdminChanges(
}
bool HistoryMessage::allowsForward() const {
if (id < 0 || isLogEntry()) {
if (id < 0 || !isHistoryEntry()) {
return false;
}
return !_media || _media->allowsForward();

View File

@ -608,7 +608,7 @@ const HistoryBlock *Element::block() const {
}
void Element::attachToBlock(not_null<HistoryBlock*> block, int index) {
Expects(!_data->isLogEntry());
Expects(_data->isHistoryEntry());
Expects(_block == nullptr);
Expects(_indexInBlock < 0);
Expects(index >= 0);

View File

@ -846,7 +846,7 @@ TextState Message::textState(
result.symbol += item->_text.length();
}
if (keyboard && !item->isLogEntry()) {
if (keyboard && item->isHistoryEntry()) {
auto keyboardTop = g.top() + g.height() + st::msgBotKbButton.margin;
if (QRect(g.left(), keyboardTop, g.width(), keyboardHeight).contains(point)) {
result.link = keyboard->getLink(point - QPoint(g.left(), keyboardTop));

View File

@ -313,7 +313,7 @@ TextState Game::textState(QPoint point, StateRequest request) const {
tshift += _descriptionLines * lineHeight;
}
if (inThumb) {
if (!_parent->data()->isLogEntry()) {
if (_parent->data()->isHistoryEntry()) {
result.link = _openl;
}
} else if (_attach) {
@ -326,7 +326,7 @@ TextState Game::textState(QPoint point, StateRequest request) const {
if (QRect(attachLeft, tshift, _attach->width(), height() - tshift - bshift).contains(point)) {
if (_attach->isReadyForOpen()) {
if (!_parent->data()->isLogEntry()) {
if (_parent->data()->isHistoryEntry()) {
result.link = _openl;
}
} else {

View File

@ -702,7 +702,7 @@ QMargins WebPage::inBubblePadding() const {
}
bool WebPage::isLogEntryOriginal() const {
return _parent->data()->isLogEntry() && _parent->media() != this;
return !_parent->data()->isHistoryEntry() && _parent->media() != this;
}
int WebPage::bottomInfoPadding() const {

View File

@ -4073,6 +4073,16 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
}
} break;
case mtpc_updateNewScheduledMessage: {
const auto &d = update.c_updateNewScheduledMessage();
session().data().scheduledMessages().apply(d);
} break;
case mtpc_updateDeleteScheduledMessages: {
const auto &d = update.c_updateDeleteScheduledMessages();
session().data().scheduledMessages().apply(d);
} break;
case mtpc_updateWebPage: {
auto &d = update.c_updateWebPage();

View File

@ -1,3 +1,4 @@
<(src_loc)/api/api_hash.h
<(src_loc)/api/api_sending.cpp
<(src_loc)/api/api_sending.h
<(src_loc)/api/api_single_message_search.cpp
@ -220,6 +221,8 @@
<(src_loc)/data/data_search_controller.h
<(src_loc)/data/data_session.cpp
<(src_loc)/data/data_session.h
<(src_loc)/data/data_scheduled_messages.cpp
<(src_loc)/data/data_scheduled_messages.h
<(src_loc)/data/data_shared_media.cpp
<(src_loc)/data/data_shared_media.h
<(src_loc)/data/data_sparse_ids.cpp