Move updates handling MainWidget -> Api::Updates.

This commit is contained in:
John Preston 2020-06-11 13:41:03 +04:00
parent ee43027bea
commit 0b028b959b
30 changed files with 2526 additions and 2218 deletions

View file

@ -167,6 +167,8 @@ PRIVATE
api/api_single_message_search.h api/api_single_message_search.h
api/api_text_entities.cpp api/api_text_entities.cpp
api/api_text_entities.h api/api_text_entities.h
api/api_updates.cpp
api/api_updates.h
boxes/filters/edit_filter_box.cpp boxes/filters/edit_filter_box.cpp
boxes/filters/edit_filter_box.h boxes/filters/edit_filter_box.h
boxes/filters/edit_filter_chats_list.cpp boxes/filters/edit_filter_chats_list.cpp

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,172 @@
/*
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 "mtproto/mtproto_rpc_sender.h"
#include "data/data_pts_waiter.h"
#include "base/timer.h"
class ApiWrap;
class History;
namespace Main {
class Session;
} // namespace Main
namespace Api {
class Updates final {
public:
explicit Updates(not_null<Main::Session*> session);
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] ApiWrap &api() const;
void applyUpdates(
const MTPUpdates &updates,
uint64 sentMessageRandomId = 0);
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
void updateOnline();
[[nodiscard]] bool isIdle() const;
void checkIdleFinish();
bool lastWasOnline() const;
crl::time lastSetOnline() const;
bool isQuitPrevent();
bool updateAndApply(int32 pts, int32 ptsCount, const MTPUpdates &updates);
bool updateAndApply(int32 pts, int32 ptsCount, const MTPUpdate &update);
bool updateAndApply(int32 pts, int32 ptsCount);
void checkLastUpdate(bool afterSleep);
// ms <= 0 - stop timer
void ptsWaiterStartTimerFor(ChannelData *channel, crl::time ms);
void getDifference();
void requestChannelRangeDifference(not_null<History*> history);
void addActiveChat(rpl::producer<PeerData*> chat);
private:
enum class ChannelDifferenceRequest {
Unknown,
PtsGapOrShortPoll,
AfterFail,
};
struct ActiveChatTracker {
PeerData *peer = nullptr;
rpl::lifetime lifetime;
};
void channelRangeDifferenceSend(
not_null<ChannelData*> channel,
MsgRange range,
int32 pts);
void channelRangeDifferenceDone(
not_null<ChannelData*> channel,
MsgRange range,
const MTPupdates_ChannelDifference &result);
void updateOnline(bool gotOtherOffline);
void sendPing();
void getDifferenceByPts();
void getDifferenceAfterFail();
[[nodiscard]] bool requestingDifference() const {
return _ptsWaiter.requesting();
}
void getChannelDifference(
not_null<ChannelData*> channel,
ChannelDifferenceRequest from = ChannelDifferenceRequest::Unknown);
void differenceDone(const MTPupdates_Difference &result);
void differenceFail(const RPCError &error);
void feedDifference(
const MTPVector<MTPUser> &users,
const MTPVector<MTPChat> &chats,
const MTPVector<MTPMessage> &msgs,
const MTPVector<MTPUpdate> &other);
void stateDone(const MTPupdates_State &state);
void setState(int32 pts, int32 date, int32 qts, int32 seq);
void channelDifferenceDone(
not_null<ChannelData*> channel,
const MTPupdates_ChannelDifference &diff);
void channelDifferenceFail(
not_null<ChannelData*> channel,
const RPCError &error);
void failDifferenceStartTimerFor(ChannelData *channel);
void feedChannelDifference(const MTPDupdates_channelDifference &data);
void mtpUpdateReceived(const MTPUpdates &updates);
void mtpNewSessionCreated();
void feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds = false);
// Doesn't call sendHistoryChangeNotifications itself.
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
bool whenGetDiffChanged(
ChannelData *channel,
int32 ms,
base::flat_map<not_null<ChannelData*>, crl::time> &whenMap,
crl::time &curTime);
const not_null<Main::Session*> _session;
int32 _updatesDate = 0;
int32 _updatesQts = -1;
int32 _updatesSeq = 0;
base::Timer _noUpdatesTimer;
base::Timer _onlineTimer;
PtsWaiter _ptsWaiter;
base::flat_map<not_null<ChannelData*>, crl::time> _whenGetDiffByPts;
base::flat_map<not_null<ChannelData*>, crl::time> _whenGetDiffAfterFail;
crl::time _getDifferenceTimeByPts = 0;
crl::time _getDifferenceTimeAfterFail = 0;
base::Timer _byPtsTimer;
base::flat_map<int32, MTPUpdates> _bySeqUpdates;
base::Timer _bySeqTimer;
base::Timer _byMinChannelTimer;
// growing timeout for getDifference calls, if it fails
crl::time _failDifferenceTimeout = 1;
// growing timeout for getChannelDifference calls, if it fails
base::flat_map<
not_null<ChannelData*>,
crl::time> _channelFailDifferenceTimeout;
base::Timer _failDifferenceTimer;
base::flat_map<
not_null<ChannelData*>,
mtpRequestId> _rangeDifferenceRequests;
crl::time _lastUpdateTime = 0;
bool _handlingChannelDifference = false;
base::flat_map<int, ActiveChatTracker> _activeChats;
mtpRequestId _onlineRequest = 0;
base::Timer _idleFinishTimer;
crl::time _lastSetOnline = 0;
bool _lastWasOnline = false;
bool _isIdle = false;
rpl::lifetime _lifetime;
};
} // namespace Api

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "api/api_self_destruct.h" #include "api/api_self_destruct.h"
#include "api/api_sensitive_content.h" #include "api/api_sensitive_content.h"
#include "api/api_updates.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "data/data_photo.h" #include "data/data_photo.h"
@ -272,6 +273,10 @@ Storage::Account &ApiWrap::local() const {
return _session->local(); return _session->local();
} }
Api::Updates &ApiWrap::updates() const {
return _session->updates();
}
void ApiWrap::setupSupportMode() { void ApiWrap::setupSupportMode() {
if (!_session->supportMode()) { if (!_session->supportMode()) {
return; return;
@ -491,12 +496,6 @@ void ApiWrap::importChatInvite(const QString &hash) {
}).send(); }).send();
} }
void ApiWrap::applyUpdates(
const MTPUpdates &updates,
uint64 sentMessageRandomId) {
App::main()->feedUpdates(updates, sentMessageRandomId);
}
void ApiWrap::savePinnedOrder(Data::Folder *folder) { void ApiWrap::savePinnedOrder(Data::Folder *folder) {
const auto &order = _session->data().pinnedChatsOrder( const auto &order = _session->data().pinnedChatsOrder(
folder, folder,
@ -2321,8 +2320,8 @@ void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) {
request(MTPmessages_DeleteChatUser( request(MTPmessages_DeleteChatUser(
chat->inputChat, chat->inputChat,
_session->user()->inputUser _session->user()->inputUser
)).done([=](const MTPUpdates &updates) { )).done([=](const MTPUpdates &result) {
applyUpdates(updates); applyUpdates(result);
deleteHistory(peer, false, revoke); deleteHistory(peer, false, revoke);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
deleteHistory(peer, false, revoke); deleteHistory(peer, false, revoke);
@ -2392,6 +2391,12 @@ void ApiWrap::deleteHistory(
} }
} }
void ApiWrap::applyUpdates(
const MTPUpdates &updates,
uint64 sentMessageRandomId) {
this->updates().applyUpdates(updates, sentMessageRandomId);
}
int ApiWrap::applyAffectedHistory( int ApiWrap::applyAffectedHistory(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result) { const MTPmessages_AffectedHistory &result) {
@ -2399,7 +2404,7 @@ int ApiWrap::applyAffectedHistory(
if (const auto channel = peer->asChannel()) { if (const auto channel = peer->asChannel()) {
channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v); channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v);
} else { } else {
App::main()->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v); updates().updateAndApply(data.vpts().v, data.vpts_count().v);
} }
return data.voffset().v; return data.voffset().v;
} }
@ -2418,7 +2423,26 @@ void ApiWrap::applyAffectedMessages(
void ApiWrap::applyAffectedMessages( void ApiWrap::applyAffectedMessages(
const MTPmessages_AffectedMessages &result) { const MTPmessages_AffectedMessages &result) {
const auto &data = result.c_messages_affectedMessages(); const auto &data = result.c_messages_affectedMessages();
App::main()->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v); updates().updateAndApply(data.vpts().v, data.vpts_count().v);
}
void ApiWrap::saveCurrentDraftToCloud() {
Core::App().saveCurrentDraftsToHistories();
for (const auto controller : session().windows()) {
if (const auto peer = controller->activeChatCurrent().peer()) {
if (const auto history = session().data().historyLoaded(peer)) {
session().local().writeDrafts(history);
const auto localDraft = history->localDraft();
const auto cloudDraft = history->cloudDraft();
if (!Data::draftsAreEqual(localDraft, cloudDraft)
&& !session().supportMode()) {
saveDraftToCloudDelayed(history);
}
}
}
}
} }
void ApiWrap::saveDraftsToCloud() { void ApiWrap::saveDraftsToCloud() {
@ -2713,98 +2737,6 @@ void ApiWrap::requestParticipantsCountDelayed(
[=] { channel->updateFullForced(); }); [=] { channel->updateFullForced(); });
} }
void ApiWrap::requestChannelRangeDifference(not_null<History*> history) {
Expects(history->isChannel());
const auto channel = history->peer->asChannel();
if (const auto requestId = _rangeDifferenceRequests.take(channel)) {
request(*requestId).cancel();
}
const auto range = history->rangeForDifferenceRequest();
if (!(range.from < range.till) || !channel->pts()) {
return;
}
MTP_LOG(0, ("getChannelDifference { good - "
"after channelDifferenceTooLong was received, "
"validating history part }%1").arg(cTestMode() ? " TESTMODE" : ""));
channelRangeDifferenceSend(channel, range, channel->pts());
}
void ApiWrap::channelRangeDifferenceSend(
not_null<ChannelData*> channel,
MsgRange range,
int32 pts) {
Expects(range.from < range.till);
const auto limit = range.till - range.from;
const auto filter = MTP_channelMessagesFilter(
MTP_flags(0),
MTP_vector<MTPMessageRange>(1, MTP_messageRange(
MTP_int(range.from),
MTP_int(range.till - 1))));
const auto requestId = request(MTPupdates_GetChannelDifference(
MTP_flags(MTPupdates_GetChannelDifference::Flag::f_force),
channel->inputChannel,
filter,
MTP_int(pts),
MTP_int(limit)
)).done([=](const MTPupdates_ChannelDifference &result) {
_rangeDifferenceRequests.remove(channel);
channelRangeDifferenceDone(channel, range, result);
}).fail([=](const RPCError &error) {
_rangeDifferenceRequests.remove(channel);
}).send();
_rangeDifferenceRequests.emplace(channel, requestId);
}
void ApiWrap::channelRangeDifferenceDone(
not_null<ChannelData*> channel,
MsgRange range,
const MTPupdates_ChannelDifference &result) {
auto nextRequestPts = int32(0);
auto isFinal = true;
switch (result.type()) {
case mtpc_updates_channelDifferenceEmpty: {
const auto &d = result.c_updates_channelDifferenceEmpty();
nextRequestPts = d.vpts().v;
isFinal = d.is_final();
} break;
case mtpc_updates_channelDifferenceTooLong: {
const auto &d = result.c_updates_channelDifferenceTooLong();
_session->data().processUsers(d.vusers());
_session->data().processChats(d.vchats());
nextRequestPts = d.vdialog().match([&](const MTPDdialog &data) {
return data.vpts().value_or_empty();
}, [&](const MTPDdialogFolder &data) {
return 0;
});
isFinal = d.is_final();
} break;
case mtpc_updates_channelDifference: {
const auto &d = result.c_updates_channelDifference();
App::main()->feedChannelDifference(d);
nextRequestPts = d.vpts().v;
isFinal = d.is_final();
} break;
}
if (!isFinal && nextRequestPts) {
MTP_LOG(0, ("getChannelDifference { "
"good - after not final channelDifference was received, "
"validating history part }%1"
).arg(cTestMode() ? " TESTMODE" : ""));
channelRangeDifferenceSend(channel, range, nextRequestPts);
}
}
template <typename Request> template <typename Request>
void ApiWrap::requestFileReference( void ApiWrap::requestFileReference(
Data::FileOrigin origin, Data::FileOrigin origin,
@ -3418,224 +3350,6 @@ void ApiWrap::parseRecentChannelParticipants(
}, std::move(callbackNotModified)); }, std::move(callbackNotModified));
} }
void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
switch (updates.type()) {
case mtpc_updateShortMessage: {
const auto &d = updates.c_updateShortMessage();
const auto flags = mtpCastFlags(d.vflags().v)
| MTPDmessage::Flag::f_from_id;
const auto peerUserId = d.is_out()
? d.vuser_id()
: MTP_int(_session->userId());
const auto fwd = d.vfwd_from();
_session->data().addNewMessage(
MTP_message(
MTP_flags(flags),
d.vid(),
d.is_out() ? MTP_int(_session->userId()) : d.vuser_id(),
MTP_peerUser(peerUserId),
fwd ? (*fwd) : MTPMessageFwdHeader(),
MTP_int(d.vvia_bot_id().value_or_empty()),
MTP_int(d.vreply_to_msg_id().value_or_empty()),
d.vdate(),
d.vmessage(),
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
MTPint(),
MTPint(),
MTPstring(),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
} break;
case mtpc_updateShortChatMessage: {
const auto &d = updates.c_updateShortChatMessage();
const auto flags = mtpCastFlags(d.vflags().v) | MTPDmessage::Flag::f_from_id;
const auto fwd = d.vfwd_from();
_session->data().addNewMessage(
MTP_message(
MTP_flags(flags),
d.vid(),
d.vfrom_id(),
MTP_peerChat(d.vchat_id()),
fwd ? (*fwd) : MTPMessageFwdHeader(),
MTP_int(d.vvia_bot_id().value_or_empty()),
MTP_int(d.vreply_to_msg_id().value_or_empty()),
d.vdate(),
d.vmessage(),
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
MTPint(),
MTPint(),
MTPstring(),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
} break;
case mtpc_updateShortSentMessage: {
auto &d = updates.c_updateShortSentMessage();
Q_UNUSED(d); // Sent message data was applied anyway.
} break;
default: Unexpected("Type in applyUpdatesNoPtsCheck()");
}
}
void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
switch (update.type()) {
case mtpc_updateNewMessage: {
auto &d = update.c_updateNewMessage();
auto needToAdd = true;
if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
const auto &data = d.vmessage().c_message();
if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
if (needToAdd && !data.is_from_scheduled()) {
// If we still need to add a new message,
// we should first check if this message is in
// the list of scheduled messages.
// This is necessary to correctly update the file reference.
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
_session->data().scheduledMessages().checkEntitiesAndUpdate(
data);
}
}
if (needToAdd) {
_session->data().addNewMessage(
d.vmessage(),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
}
} break;
case mtpc_updateReadMessagesContents: {
const auto &d = update.c_updateReadMessagesContents();
auto possiblyReadMentions = base::flat_set<MsgId>();
for (const auto &msgId : d.vmessages().v) {
if (const auto item = _session->data().message(NoChannel, msgId.v)) {
if (item->isUnreadMedia() || item->isUnreadMention()) {
item->markMediaRead();
_session->data().requestItemRepaint(item);
if (item->out()
&& item->history()->peer->isUser()
&& !App::main()->requestingDifference()) {
item->history()->peer->asUser()->madeAction(base::unixtime::now());
}
}
} else {
// Perhaps it was an unread mention!
possiblyReadMentions.insert(msgId.v);
}
}
checkForUnreadMentions(possiblyReadMentions);
} break;
case mtpc_updateReadHistoryInbox: {
const auto &d = update.c_updateReadHistoryInbox();
const auto peer = peerFromMTP(d.vpeer());
if (const auto history = _session->data().historyLoaded(peer)) {
const auto folderId = d.vfolder_id().value_or_empty();
history->applyInboxReadUpdate(
folderId,
d.vmax_id().v,
d.vstill_unread_count().v);
}
} break;
case mtpc_updateReadHistoryOutbox: {
const auto &d = update.c_updateReadHistoryOutbox();
const auto peer = peerFromMTP(d.vpeer());
if (const auto history = _session->data().historyLoaded(peer)) {
history->outboxRead(d.vmax_id().v);
if (!App::main()->requestingDifference()) {
if (const auto user = history->peer->asUser()) {
user->madeAction(base::unixtime::now());
}
}
}
} break;
case mtpc_updateWebPage: {
auto &d = update.c_updateWebPage();
Q_UNUSED(d); // Web page was updated anyway.
} break;
case mtpc_updateFolderPeers: {
const auto &data = update.c_updateFolderPeers();
auto &owner = _session->data();
for (const auto &peer : data.vfolder_peers().v) {
peer.match([&](const MTPDfolderPeer &data) {
const auto peerId = peerFromMTP(data.vpeer());
if (const auto history = owner.historyLoaded(peerId)) {
if (const auto folderId = data.vfolder_id().v) {
history->setFolder(owner.folder(folderId));
} else {
history->clearFolder();
}
}
});
}
} break;
case mtpc_updateDeleteMessages: {
auto &d = update.c_updateDeleteMessages();
_session->data().processMessagesDeleted(NoChannel, d.vmessages().v);
} break;
case mtpc_updateNewChannelMessage: {
auto &d = update.c_updateNewChannelMessage();
auto needToAdd = true;
if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
if (_session->data().checkEntitiesAndViewsUpdate(d.vmessage().c_message())) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
}
if (needToAdd) {
_session->data().addNewMessage(
d.vmessage(),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
}
} break;
case mtpc_updateEditChannelMessage: {
auto &d = update.c_updateEditChannelMessage();
_session->data().updateEditedMessage(d.vmessage());
} break;
case mtpc_updateEditMessage: {
auto &d = update.c_updateEditMessage();
_session->data().updateEditedMessage(d.vmessage());
} break;
case mtpc_updateChannelWebPage: {
auto &d = update.c_updateChannelWebPage();
Q_UNUSED(d); // Web page was updated anyway.
} break;
case mtpc_updateDeleteChannelMessages: {
auto &d = update.c_updateDeleteChannelMessages();
_session->data().processMessagesDeleted(d.vchannel_id().v, d.vmessages().v);
} break;
default: Unexpected("Type in applyUpdateNoPtsCheck()");
}
}
void ApiWrap::jumpToDate(Dialogs::Key chat, const QDate &date) { void ApiWrap::jumpToDate(Dialogs::Key chat, const QDate &date) {
if (const auto peer = chat.peer()) { if (const auto peer = chat.peer()) {
jumpToHistoryDate(peer, date); jumpToHistoryDate(peer, date);
@ -4346,9 +4060,8 @@ void ApiWrap::forwardMessages(
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
peer->input, peer->input,
MTP_int(action.options.scheduled) MTP_int(action.options.scheduled)
)).done([=]( )).done([=](const MTPUpdates &result) {
const MTPUpdates &updates) { applyUpdates(result);
applyUpdates(updates);
if (shared && !--shared->requestsLeft) { if (shared && !--shared->requestsLeft) {
shared->callback(); shared->callback();
} }

View file

@ -66,6 +66,8 @@ inline QString ToString(uint64 value) {
} // namespace details } // namespace details
class Updates;
template < template <
typename ...Types, typename ...Types,
typename = std::enable_if_t<(sizeof...(Types) > 0)>> typename = std::enable_if_t<(sizeof...(Types) > 0)>>
@ -139,6 +141,7 @@ public:
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
[[nodiscard]] Storage::Account &local() const; [[nodiscard]] Storage::Account &local() const;
[[nodiscard]] Api::Updates &updates() const;
void applyUpdates( void applyUpdates(
const MTPUpdates &updates, const MTPUpdates &updates,
@ -154,6 +157,8 @@ public:
MTPInputNotifyPeer peer, MTPInputNotifyPeer peer,
const MTPPeerNotifySettings &settings); const MTPPeerNotifySettings &settings);
void saveCurrentDraftToCloud();
void savePinnedOrder(Data::Folder *folder); void savePinnedOrder(Data::Folder *folder);
void toggleHistoryArchived( void toggleHistoryArchived(
not_null<History*> history, not_null<History*> history,
@ -194,7 +199,6 @@ public:
void requestBots(not_null<ChannelData*> channel); void requestBots(not_null<ChannelData*> channel);
void requestAdmins(not_null<ChannelData*> channel); void requestAdmins(not_null<ChannelData*> channel);
void requestParticipantsCountDelayed(not_null<ChannelData*> channel); void requestParticipantsCountDelayed(not_null<ChannelData*> channel);
void requestChannelRangeDifference(not_null<History*> history);
using UpdatedFileReferences = Data::UpdatedFileReferences; using UpdatedFileReferences = Data::UpdatedFileReferences;
using FileReferencesHandler = FnMut<void(const UpdatedFileReferences&)>; using FileReferencesHandler = FnMut<void(const UpdatedFileReferences&)>;
@ -310,9 +314,6 @@ public:
bool isQuitPrevent(); bool isQuitPrevent();
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
void jumpToDate(Dialogs::Key chat, const QDate &date); void jumpToDate(Dialogs::Key chat, const QDate &date);
void preloadEnoughUnreadMentions(not_null<History*> history); void preloadEnoughUnreadMentions(not_null<History*> history);
@ -554,15 +555,6 @@ private:
mtpRequestId req); mtpRequestId req);
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result); void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
void channelRangeDifferenceSend(
not_null<ChannelData*> channel,
MsgRange range,
int32 pts);
void channelRangeDifferenceDone(
not_null<ChannelData*> channel,
MsgRange range,
const MTPupdates_ChannelDifference &result);
void stickerSetDisenabled(mtpRequestId requestId); void stickerSetDisenabled(mtpRequestId requestId);
void stickersSaveOrder(); void stickersSaveOrder();
@ -709,10 +701,6 @@ private:
base::flat_set<not_null<ChannelData*>> _selfParticipantRequests; base::flat_set<not_null<ChannelData*>> _selfParticipantRequests;
base::flat_map<
not_null<ChannelData*>,
mtpRequestId> _rangeDifferenceRequests;
QMap<WebPageData*, mtpRequestId> _webPagesPending; QMap<WebPageData*, mtpRequestId> _webPagesPending;
base::Timer _webPagesTimer; base::Timer _webPagesTimer;

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_updates.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "lang/lang_file_parser.h" #include "lang/lang_file_parser.h"
#include "lang/lang_translator.h" #include "lang/lang_translator.h"
@ -464,13 +465,14 @@ void Application::forceLogOut(const TextWithEntities &explanation) {
} }
void Application::checkLocalTime() { void Application::checkLocalTime() {
if (crl::adjust_time()) { const auto adjusted = crl::adjust_time();
if (adjusted) {
base::Timer::Adjust(); base::Timer::Adjust();
base::ConcurrentTimerEnvironment::Adjust(); base::ConcurrentTimerEnvironment::Adjust();
base::unixtime::http_invalidate(); base::unixtime::http_invalidate();
if (App::main()) App::main()->checkLastUpdate(true); }
} else { if (activeAccount().sessionExists()) {
if (App::main()) App::main()->checkLastUpdate(false); activeAccount().session().updates().checkLastUpdate(adjusted);
} }
} }
@ -697,6 +699,9 @@ bool Application::passcodeLocked() const {
void Application::updateNonIdle() { void Application::updateNonIdle() {
_lastNonIdleTime = crl::now(); _lastNonIdleTime = crl::now();
if (activeAccount().sessionExists()) {
activeAccount().session().updates().checkIdleFinish();
}
} }
crl::time Application::lastNonIdleTime() const { crl::time Application::lastNonIdleTime() const {
@ -794,6 +799,25 @@ rpl::producer<bool> Application::lockValue() const {
_1 || _2); _1 || _2);
} }
bool Application::hasActiveWindow(not_null<Main::Session*> session) const {
if (App::quitting() || !_window) {
return false;
} else if (const auto controller = _window->sessionController()) {
if (&controller->session() == session) {
return _window->widget()->isActive();
}
}
return false;
}
void Application::saveCurrentDraftsToHistories() {
if (!_window) {
return;
} else if (const auto controller = _window->sessionController()) {
controller->content()->saveFieldToHistoryLocalDraft();
}
}
Window::Controller *Application::activeWindow() const { Window::Controller *Application::activeWindow() const {
return _window.get(); return _window.get();
} }
@ -897,10 +921,8 @@ void Application::QuitAttempt() {
if (IsAppLaunched() if (IsAppLaunched()
&& App().activeAccount().sessionExists() && App().activeAccount().sessionExists()
&& !Sandbox::Instance().isSavingSession()) { && !Sandbox::Instance().isSavingSession()) {
if (const auto mainwidget = App::main()) { if (App().activeAccount().session().updates().isQuitPrevent()) {
if (mainwidget->isQuitPrevent()) { prevents = true;
prevents = true;
}
} }
if (App().activeAccount().session().api().isQuitPrevent()) { if (App().activeAccount().session().api().isQuitPrevent()) {
prevents = true; prevents = true;

View file

@ -37,6 +37,7 @@ void quit();
namespace Main { namespace Main {
class Account; class Account;
class Session;
} // namespace Main } // namespace Main
namespace Ui { namespace Ui {
@ -96,6 +97,8 @@ public:
} }
// Windows interface. // Windows interface.
bool hasActiveWindow(not_null<Main::Session*> session) const;
void saveCurrentDraftsToHistories();
[[nodiscard]] Window::Controller *activeWindow() const; [[nodiscard]] Window::Controller *activeWindow() const;
bool closeActiveWindow(); bool closeActiveWindow();
bool minimizeActiveWindow(); bool minimizeActiveWindow();

View file

@ -46,7 +46,7 @@ void MegagroupInfo::setLocation(const ChannelLocation &location) {
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id) ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id) : PeerData(owner, id)
, inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) , inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0)))
, _ptsWaiter(&owner->session()) { , _ptsWaiter(&owner->session().updates()) {
Data::PeerFlagValue( Data::PeerFlagValue(
this, this,
MTPDchannel::Flag::f_megagroup MTPDchannel::Flag::f_megagroup

View file

@ -7,12 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "data/data_pts_waiter.h" #include "data/data_pts_waiter.h"
#include "mainwidget.h" #include "api/api_updates.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "app.h"
PtsWaiter::PtsWaiter(not_null<Main::Session*> session) : _session(session) { PtsWaiter::PtsWaiter(not_null<Api::Updates*> owner) : _owner(owner) {
} }
uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue, int32 pts) { uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue, int32 pts) {
@ -22,11 +19,9 @@ uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue, int32 pts) {
).first->first; ).first->first;
} }
void PtsWaiter::setWaitingForSkipped(ChannelData *channel, int32 ms) { void PtsWaiter::setWaitingForSkipped(ChannelData *channel, crl::time ms) {
if (ms >= 0) { if (ms >= 0) {
if (App::main()) { _owner->ptsWaiterStartTimerFor(channel, ms);
App::main()->ptsWaiterStartTimerFor(channel, ms);
}
_waitingForSkipped = true; _waitingForSkipped = true;
} else { } else {
_waitingForSkipped = false; _waitingForSkipped = false;
@ -34,11 +29,9 @@ void PtsWaiter::setWaitingForSkipped(ChannelData *channel, int32 ms) {
} }
} }
void PtsWaiter::setWaitingForShortPoll(ChannelData *channel, int32 ms) { void PtsWaiter::setWaitingForShortPoll(ChannelData *channel, crl::time ms) {
if (ms >= 0) { if (ms >= 0) {
if (App::main()) { _owner->ptsWaiterStartTimerFor(channel, ms);
App::main()->ptsWaiterStartTimerFor(channel, ms);
}
_waitingForShortPoll = true; _waitingForShortPoll = true;
} else { } else {
_waitingForShortPoll = false; _waitingForShortPoll = false;
@ -47,8 +40,8 @@ void PtsWaiter::setWaitingForShortPoll(ChannelData *channel, int32 ms) {
} }
void PtsWaiter::checkForWaiting(ChannelData *channel) { void PtsWaiter::checkForWaiting(ChannelData *channel) {
if (!_waitingForSkipped && !_waitingForShortPoll && App::main()) { if (!_waitingForSkipped && !_waitingForShortPoll) {
App::main()->ptsWaiterStartTimerFor(channel, -1); _owner->ptsWaiterStartTimerFor(channel, -1);
} }
} }
@ -67,10 +60,10 @@ void PtsWaiter::applySkippedUpdates(ChannelData *channel) {
for (auto i = _queue.cbegin(), e = _queue.cend(); i != e; ++i) { for (auto i = _queue.cbegin(), e = _queue.cend(); i != e; ++i) {
switch (i->second) { switch (i->second) {
case SkippedUpdate: { case SkippedUpdate: {
_session->api().applyUpdateNoPtsCheck(_updateQueue[i->first]); _owner->applyUpdateNoPtsCheck(_updateQueue[i->first]);
} break; } break;
case SkippedUpdates: { case SkippedUpdates: {
_session->api().applyUpdatesNoPtsCheck(_updatesQueue[i->first]); _owner->applyUpdatesNoPtsCheck(_updatesQueue[i->first]);
} break; } break;
} }
} }
@ -136,7 +129,7 @@ bool PtsWaiter::updateAndApply(
} }
if (!_waitingForSkipped || _queue.empty()) { if (!_waitingForSkipped || _queue.empty()) {
// Optimization - no need to put in queue and back. // Optimization - no need to put in queue and back.
_session->api().applyUpdatesNoPtsCheck(updates); _owner->applyUpdatesNoPtsCheck(updates);
} else { } else {
_updatesQueue.emplace(ptsKey(SkippedUpdates, pts), updates); _updatesQueue.emplace(ptsKey(SkippedUpdates, pts), updates);
applySkippedUpdates(channel); applySkippedUpdates(channel);
@ -154,7 +147,7 @@ bool PtsWaiter::updateAndApply(
} }
if (!_waitingForSkipped || _queue.empty()) { if (!_waitingForSkipped || _queue.empty()) {
// Optimization - no need to put in queue and back. // Optimization - no need to put in queue and back.
_session->api().applyUpdateNoPtsCheck(update); _owner->applyUpdateNoPtsCheck(update);
} else { } else {
_updateQueue.emplace(ptsKey(SkippedUpdate, pts), update); _updateQueue.emplace(ptsKey(SkippedUpdate, pts), update);
applySkippedUpdates(channel); applySkippedUpdates(channel);

View file

@ -7,9 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace Main { namespace Api {
class Session; class Updates;
} // namespace Main } // namespace Api
enum PtsSkippedQueue { enum PtsSkippedQueue {
SkippedUpdate, SkippedUpdate,
@ -18,7 +18,7 @@ enum PtsSkippedQueue {
class PtsWaiter { class PtsWaiter {
public: public:
explicit PtsWaiter(not_null<Main::Session*> session); explicit PtsWaiter(not_null<Api::Updates*> owner);
// 1s wait for skipped seq or pts in updates. // 1s wait for skipped seq or pts in updates.
static constexpr auto kWaitForSkippedTimeout = 1000; static constexpr auto kWaitForSkippedTimeout = 1000;
@ -45,8 +45,8 @@ public:
bool waitingForShortPoll() const { bool waitingForShortPoll() const {
return _waitingForShortPoll; return _waitingForShortPoll;
} }
void setWaitingForSkipped(ChannelData *channel, int32 ms); // < 0 - not waiting void setWaitingForSkipped(ChannelData *channel, crl::time ms); // < 0 - not waiting
void setWaitingForShortPoll(ChannelData *channel, int32 ms); // < 0 - not waiting void setWaitingForShortPoll(ChannelData *channel, crl::time ms); // < 0 - not waiting
int32 current() const{ int32 current() const{
return _good; return _good;
} }
@ -88,7 +88,7 @@ private:
uint64 ptsKey(PtsSkippedQueue queue, int32 pts); uint64 ptsKey(PtsSkippedQueue queue, int32 pts);
void checkForWaiting(ChannelData *channel); void checkForWaiting(ChannelData *channel);
const not_null<Main::Session*> _session; const not_null<Api::Updates*> _owner;
base::flat_map<uint64, PtsSkippedQueue> _queue; base::flat_map<uint64, PtsSkippedQueue> _queue;
base::flat_map<uint64, MTPUpdate> _updateQueue; base::flat_map<uint64, MTPUpdate> _updateQueue;
base::flat_map<uint64, MTPUpdates> _updatesQueue; base::flat_map<uint64, MTPUpdates> _updatesQueue;

View file

@ -3272,6 +3272,7 @@ void Session::notifyPollUpdateDelayed(not_null<PollData*> poll) {
void Session::sendWebPageGamePollNotifications() { void Session::sendWebPageGamePollNotifications() {
for (const auto page : base::take(_webpagesUpdated)) { for (const auto page : base::take(_webpagesUpdated)) {
_webpageUpdates.fire_copy(page);
const auto i = _webpageViews.find(page); const auto i = _webpageViews.find(page);
if (i != _webpageViews.end()) { if (i != _webpageViews.end()) {
for (const auto view : i->second) { for (const auto view : i->second) {
@ -3295,6 +3296,26 @@ void Session::sendWebPageGamePollNotifications() {
} }
} }
rpl::producer<not_null<WebPageData*>> Session::webPageUpdates() const {
return _webpageUpdates.events();
}
void Session::channelDifferenceTooLong(not_null<ChannelData*> channel) {
_channelDifferenceTooLong.fire_copy(channel);
}
rpl::producer<not_null<ChannelData*>> Session::channelDifferenceTooLong() const {
return _channelDifferenceTooLong.events();
}
void Session::historyOutboxRead(not_null<History*> history) {
_historyOutboxReads.fire_copy(history);
}
rpl::producer<not_null<History*>> Session::historyOutboxReads() const {
return _historyOutboxReads.events();
}
void Session::registerItemView(not_null<ViewElement*> view) { void Session::registerItemView(not_null<ViewElement*> view) {
_views[view->data()].push_back(view); _views[view->data()].push_back(view);
} }

View file

@ -549,8 +549,15 @@ public:
void notifyWebPageUpdateDelayed(not_null<WebPageData*> page); void notifyWebPageUpdateDelayed(not_null<WebPageData*> page);
void notifyGameUpdateDelayed(not_null<GameData*> game); void notifyGameUpdateDelayed(not_null<GameData*> game);
void notifyPollUpdateDelayed(not_null<PollData*> poll); void notifyPollUpdateDelayed(not_null<PollData*> poll);
bool hasPendingWebPageGamePollNotification() const; [[nodiscard]] bool hasPendingWebPageGamePollNotification() const;
void sendWebPageGamePollNotifications(); void sendWebPageGamePollNotifications();
[[nodiscard]] rpl::producer<not_null<WebPageData*>> webPageUpdates() const;
void channelDifferenceTooLong(not_null<ChannelData*> channel);
[[nodiscard]] rpl::producer<not_null<ChannelData*>> channelDifferenceTooLong() const;
void historyOutboxRead(not_null<History*> history);
[[nodiscard]] rpl::producer<not_null<History*>> historyOutboxReads() const;
void registerItemView(not_null<ViewElement*> view); void registerItemView(not_null<ViewElement*> view);
void unregisterItemView(not_null<ViewElement*> view); void unregisterItemView(not_null<ViewElement*> view);
@ -868,6 +875,10 @@ private:
base::flat_set<not_null<GameData*>> _gamesUpdated; base::flat_set<not_null<GameData*>> _gamesUpdated;
base::flat_set<not_null<PollData*>> _pollsUpdated; base::flat_set<not_null<PollData*>> _pollsUpdated;
rpl::event_stream<not_null<WebPageData*>> _webpageUpdates;
rpl::event_stream<not_null<ChannelData*>> _channelDifferenceTooLong;
rpl::event_stream<not_null<History*>> _historyOutboxReads;
base::flat_multi_map<TimeId, not_null<PollData*>> _pollsClosings; base::flat_multi_map<TimeId, not_null<PollData*>> _pollsClosings;
base::Timer _pollsClosingTimer; base::Timer _pollsClosingTimer;

View file

@ -320,6 +320,24 @@ Widget::Widget(
}, lifetime()); }, lifetime());
} }
void Widget::setGeometryWithTopMoved(
const QRect &newGeometry,
int topDelta) {
_topDelta = topDelta;
bool willBeResized = (size() != newGeometry.size());
if (geometry() != newGeometry) {
auto weak = Ui::MakeWeak(this);
setGeometry(newGeometry);
if (!weak) {
return;
}
}
if (!willBeResized) {
resizeEvent(nullptr);
}
_topDelta = 0;
}
void Widget::setupScrollUpButton() { void Widget::setupScrollUpButton() {
_scrollToTop->setClickedCallback([=] { _scrollToTop->setClickedCallback([=] {
if (_scrollToAnimation.animating()) { if (_scrollToAnimation.animating()) {
@ -707,8 +725,10 @@ void Widget::escape() {
void Widget::refreshLoadMoreButton(bool mayBlock, bool isBlocked) { void Widget::refreshLoadMoreButton(bool mayBlock, bool isBlocked) {
if (!mayBlock) { if (!mayBlock) {
_loadMoreChats.destroy(); if (_loadMoreChats) {
updateControlsGeometry(); _loadMoreChats.destroy();
updateControlsGeometry();
}
return; return;
} }
if (!_loadMoreChats) { if (!_loadMoreChats) {
@ -1560,8 +1580,7 @@ void Widget::updateControlsGeometry() {
right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _filter->y()); right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _filter->y());
auto scrollTop = filterAreaTop + filterAreaHeight; auto scrollTop = filterAreaTop + filterAreaHeight;
auto addToScroll = controller()->content()->contentScrollAddToY(); auto newScrollTop = _scroll->scrollTop() + _topDelta;
auto newScrollTop = _scroll->scrollTop() + addToScroll;
auto scrollHeight = height() - scrollTop; auto scrollHeight = height() - scrollTop;
const auto putBottomButton = [&](object_ptr<BottomButton> &button) { const auto putBottomButton = [&](object_ptr<BottomButton> &button) {
if (button && !button->isHidden()) { if (button && !button->isHidden()) {
@ -1582,7 +1601,7 @@ void Widget::updateControlsGeometry() {
if (scrollHeight != wasScrollHeight) { if (scrollHeight != wasScrollHeight) {
controller()->floatPlayerAreaUpdated().notify(true); controller()->floatPlayerAreaUpdated().notify(true);
} }
if (addToScroll) { if (_topDelta) {
_scroll->scrollToY(newScrollTop); _scroll->scrollToY(newScrollTop);
} else { } else {
onListScroll(); onListScroll();

View file

@ -55,6 +55,11 @@ class Widget : public Window::AbstractSectionWidget, public RPCSender {
public: public:
Widget(QWidget *parent, not_null<Window::SessionController*> controller); Widget(QWidget *parent, not_null<Window::SessionController*> controller);
// When resizing the widget with top edge moved up or down and we
// want to add this top movement to the scroll position, so inner
// content will not move.
void setGeometryWithTopMoved(const QRect &newGeometry, int topDelta);
void updateDragInScroll(bool inScroll); void updateDragInScroll(bool inScroll);
void searchInChat(Key chat); void searchInChat(Key chat);
@ -233,6 +238,8 @@ private:
object_ptr<QTimer> _draggingScrollTimer = { nullptr }; object_ptr<QTimer> _draggingScrollTimer = { nullptr };
int _draggingScrollDelta = 0; int _draggingScrollDelta = 0;
int _topDelta = 0;
}; };
} // namespace Dialogs } // namespace Dialogs

View file

@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "storage/storage_account.h"
//#include "storage/storage_feed_messages.h" // #feed //#include "storage/storage_feed_messages.h" // #feed
#include "support/support_helper.h" #include "support/support_helper.h"
#include "ui/image/image.h" #include "ui/image/image.h"
@ -336,7 +337,7 @@ void History::clearEditDraft() {
void History::draftSavedToCloud() { void History::draftSavedToCloud() {
updateChatListEntry(); updateChatListEntry();
if (App::main()) App::main()->writeDrafts(this); session().local().writeDrafts(this);
} }
HistoryItemsList History::validateForwardDraft() { HistoryItemsList History::validateForwardDraft() {
@ -1760,6 +1761,7 @@ void History::outboxRead(MsgId upTo) {
} }
} }
updateChatListEntry(); updateChatListEntry();
session().data().historyOutboxRead(this);
} }
void History::outboxRead(not_null<const HistoryItem*> wasRead) { void History::outboxRead(not_null<const HistoryItem*> wasRead) {

View file

@ -198,6 +198,12 @@ HistoryInner::HistoryInner(
}) | rpl::start_with_next([this](not_null<const Element*> view) { }) | rpl::start_with_next([this](not_null<const Element*> view) {
mouseActionUpdate(); mouseActionUpdate();
}, lifetime()); }, lifetime());
session().data().historyOutboxReads(
) | rpl::filter([=](not_null<History*> history) {
return (_history == history);
}) | rpl::start_with_next([=] {
update();
}, lifetime());
} }
Main::Session &HistoryInner::session() const { Main::Session &HistoryInner::session() const {

View file

@ -512,6 +512,21 @@ HistoryWidget::HistoryWidget(
} }
}, lifetime()); }, lifetime());
session().data().webPageUpdates(
) | rpl::filter([=](not_null<WebPageData*> page) {
return (_previewData == page.get());
}) | rpl::start_with_next([=] {
updatePreview();
}, lifetime());
session().data().channelDifferenceTooLong(
) | rpl::filter([=](not_null<ChannelData*> channel) {
return _peer == channel.get();
}) | rpl::start_with_next([=] {
updateHistoryDownVisibility();
preloadHistoryIfNeeded();
}, lifetime());
session().data().userIsBotChanges( session().data().userIsBotChanges(
) | rpl::filter([=](not_null<UserData*> user) { ) | rpl::filter([=](not_null<UserData*> user) {
return (_peer == user.get()); return (_peer == user.get());
@ -681,6 +696,24 @@ HistoryWidget::HistoryWidget(
setupShortcuts(); setupShortcuts();
} }
void HistoryWidget::setGeometryWithTopMoved(
const QRect &newGeometry,
int topDelta) {
_topDelta = topDelta;
bool willBeResized = (size() != newGeometry.size());
if (geometry() != newGeometry) {
auto weak = Ui::MakeWeak(this);
setGeometry(newGeometry);
if (!weak) {
return;
}
}
if (!willBeResized) {
resizeEvent(nullptr);
}
_topDelta = 0;
}
void HistoryWidget::refreshTabbedPanel() { void HistoryWidget::refreshTabbedPanel() {
if (_peer && controller()->hasTabbedSelectorOwnership()) { if (_peer && controller()->hasTabbedSelectorOwnership()) {
createTabbedPanel(); createTabbedPanel();
@ -1230,7 +1263,7 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
} }
void HistoryWidget::onCloudDraftSave() { void HistoryWidget::onCloudDraftSave() {
controller()->content()->saveDraftToCloud(); controller()->session().api().saveCurrentDraftToCloud();
} }
void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraft) { void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraft) {
@ -1735,7 +1768,7 @@ void HistoryWidget::showHistory(
// Removing focus from list clears selected and updates top bar. // Removing focus from list clears selected and updates top bar.
setFocus(); setFocus();
} }
controller()->content()->saveDraftToCloud(); controller()->session().api().saveCurrentDraftToCloud();
if (_migrated) { if (_migrated) {
_migrated->clearLocalDraft(); // use migrated draft only once _migrated->clearLocalDraft(); // use migrated draft only once
_migrated->clearEditDraft(); _migrated->clearEditDraft();
@ -2550,7 +2583,7 @@ bool HistoryWidget::doWeReadMentions() const {
&& !_firstLoadRequest && !_firstLoadRequest
&& !_delayedShowAtRequest && !_delayedShowAtRequest
&& !_a_show.animating() && !_a_show.animating()
&& App::wnd()->doWeMarkAsRead(); && controller()->widget()->doWeMarkAsRead();
} }
void HistoryWidget::checkHistoryActivation() { void HistoryWidget::checkHistoryActivation() {
@ -3022,7 +3055,7 @@ void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates,
if (auto editDraft = history->editDraft()) { if (auto editDraft = history->editDraft()) {
if (editDraft->saveRequestId == req) { if (editDraft->saveRequestId == req) {
history->clearEditDraft(); history->clearEditDraft();
controller()->content()->writeDrafts(history); session().local().writeDrafts(history);
} }
} }
} }
@ -5028,7 +5061,7 @@ void HistoryWidget::updateControlsGeometry() {
} }
} }
updateHistoryGeometry(false, false, { ScrollChangeAdd, controller()->content()->contentScrollAddToY() }); updateHistoryGeometry(false, false, { ScrollChangeAdd, _topDelta });
updateFieldSize(); updateFieldSize();

View file

@ -112,6 +112,11 @@ public:
void historyLoaded(); void historyLoaded();
// When resizing the widget with top edge moved up or down and we
// want to add this top movement to the scroll position, so inner
// content will not move.
void setGeometryWithTopMoved(const QRect &newGeometry, int topDelta);
void windowShown(); void windowShown();
[[nodiscard]] bool doWeReadServerHistory() const; [[nodiscard]] bool doWeReadServerHistory() const;
[[nodiscard]] bool doWeReadMentions() const; [[nodiscard]] bool doWeReadMentions() const;
@ -820,4 +825,6 @@ private:
object_ptr<Ui::PlainShadow> _topShadow; object_ptr<Ui::PlainShadow> _topShadow;
bool _inGrab = false; bool _inGrab = false;
int _topDelta = 0;
}; };

View file

@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "api/api_updates.h"
#include "apiwrap.h"
#include "main/main_app_config.h" #include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "facades.h" #include "facades.h"
@ -353,9 +355,11 @@ void Account::startMtp() {
Global::RefConnectionTypeChanged().notify(); Global::RefConnectionTypeChanged().notify();
} }
}); });
_mtp->setSessionResetHandler([](MTP::ShiftedDcId shiftedDcId) { _mtp->setSessionResetHandler([=](MTP::ShiftedDcId shiftedDcId) {
if (App::main() && shiftedDcId == MTP::maindc()) { if (sessionExists()) {
App::main()->getDifference(); if (shiftedDcId == session().api().instance()->mainDcId()) {
session().updates().getDifference();
}
} }
}); });

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_updates.h"
#include "core/application.h" #include "core/application.h"
#include "core/changelogs.h" #include "core/changelogs.h"
#include "main/main_account.h" #include "main/main_account.h"
@ -22,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/window_session_controller.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
//#include "platform/platform_specific.h" //#include "platform/platform_specific.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
@ -48,6 +50,7 @@ Session::Session(
, _settings(std::move(settings)) , _settings(std::move(settings))
, _saveSettingsTimer([=] { local().writeSettings(); }) , _saveSettingsTimer([=] { local().writeSettings(); })
, _api(std::make_unique<ApiWrap>(this)) , _api(std::make_unique<ApiWrap>(this))
, _updates(std::make_unique<Api::Updates>(this))
, _calls(std::make_unique<Calls::Instance>(this)) , _calls(std::make_unique<Calls::Instance>(this))
, _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get())) , _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get()))
, _uploader(std::make_unique<Storage::Uploader>(_api.get())) , _uploader(std::make_unique<Storage::Uploader>(_api.get()))
@ -176,4 +179,20 @@ void Session::saveSettingsNowIfNeeded() {
} }
} }
void Session::addWindow(not_null<Window::SessionController*> controller) {
_windows.emplace(controller);
controller->lifetime().add([=] {
_windows.remove(controller);
});
updates().addActiveChat(controller->activeChatChanges(
) | rpl::map([=](const Dialogs::Key &chat) {
return chat.peer();
}) | rpl::distinct_until_changed());
}
auto Session::windows() const
-> const base::flat_set<not_null<Window::SessionController*>> & {
return _windows;
}
} // namespace Main } // namespace Main

View file

@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ApiWrap; class ApiWrap;
namespace Api {
class Updates;
} // namespace Api
namespace MTP { namespace MTP {
class Instance; class Instance;
} // namespace MTP } // namespace MTP
@ -39,6 +43,7 @@ namespace Window {
namespace Notifications { namespace Notifications {
class System; class System;
} // namespace Notifications } // namespace Notifications
class SessionController;
} // namespace Window } // namespace Window
namespace Calls { namespace Calls {
@ -81,13 +86,16 @@ public:
} }
bool validateSelf(const MTPUser &user); bool validateSelf(const MTPUser &user);
[[nodiscard]] Storage::DownloadManagerMtproto &downloader() { [[nodiscard]] Api::Updates &updates() const {
return *_updates;
}
[[nodiscard]] Storage::DownloadManagerMtproto &downloader() const {
return *_downloader; return *_downloader;
} }
[[nodiscard]] Storage::Uploader &uploader() { [[nodiscard]] Storage::Uploader &uploader() const {
return *_uploader; return *_uploader;
} }
[[nodiscard]] Storage::Facade &storage() { [[nodiscard]] Storage::Facade &storage() const {
return *_storage; return *_storage;
} }
[[nodiscard]] Stickers::EmojiPack &emojiStickersPack() const { [[nodiscard]] Stickers::EmojiPack &emojiStickersPack() const {
@ -112,6 +120,10 @@ public:
void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay); void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
void saveSettingsNowIfNeeded(); void saveSettingsNowIfNeeded();
void addWindow(not_null<Window::SessionController*> controller);
[[nodiscard]] auto windows() const
-> const base::flat_set<not_null<Window::SessionController*>> &;
[[nodiscard]] not_null<MTP::Instance*> mtp(); [[nodiscard]] not_null<MTP::Instance*> mtp();
[[nodiscard]] ApiWrap &api() { [[nodiscard]] ApiWrap &api() {
return *_api; return *_api;
@ -143,6 +155,7 @@ private:
base::Timer _saveSettingsTimer; base::Timer _saveSettingsTimer;
const std::unique_ptr<ApiWrap> _api; const std::unique_ptr<ApiWrap> _api;
const std::unique_ptr<Api::Updates> _updates;
const std::unique_ptr<Calls::Instance> _calls; const std::unique_ptr<Calls::Instance> _calls;
const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader; const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader;
const std::unique_ptr<Storage::Uploader> _uploader; const std::unique_ptr<Storage::Uploader> _uploader;
@ -162,6 +175,8 @@ private:
const std::unique_ptr<Support::Helper> _supportHelper; const std::unique_ptr<Support::Helper> _supportHelper;
base::flat_set<not_null<Window::SessionController*>> _windows;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };

File diff suppressed because it is too large Load diff

View file

@ -111,6 +111,7 @@ public:
MainWidget( MainWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller); not_null<Window::SessionController*> controller);
~MainWidget();
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
[[nodiscard]] not_null<Window::SessionController*> controller() const; [[nodiscard]] not_null<Window::SessionController*> controller() const;
@ -118,8 +119,6 @@ public:
[[nodiscard]] bool isMainSectionShown() const; [[nodiscard]] bool isMainSectionShown() const;
[[nodiscard]] bool isThirdSectionShown() const; [[nodiscard]] bool isThirdSectionShown() const;
[[nodiscard]] int contentScrollAddToY() const;
void returnTabbedSelector(); void returnTabbedSelector();
void showAnimated(const QPixmap &bgAnimCache, bool back = false); void showAnimated(const QPixmap &bgAnimCache, bool back = false);
@ -146,10 +145,6 @@ public:
void windowShown(); void windowShown();
void sentUpdatesReceived(uint64 randomId, const MTPUpdates &updates);
void sentUpdatesReceived(const MTPUpdates &updates) {
return sentUpdatesReceived(0, updates);
}
void historyToDown(History *hist); void historyToDown(History *hist);
void dialogsToUp(); void dialogsToUp();
void checkHistoryActivation(); void checkHistoryActivation();
@ -174,16 +169,11 @@ public:
const std::optional<FullMsgId> &oldId); const std::optional<FullMsgId> &oldId);
bool onSendSticker(DocumentData *sticker); bool onSendSticker(DocumentData *sticker);
void updateOnlineDisplayIn(int32 msecs);
bool isActive() const; bool isActive() const;
[[nodiscard]] bool doWeMarkAsRead() const; [[nodiscard]] bool doWeMarkAsRead() const;
bool lastWasOnline() const;
crl::time lastSetOnline() const;
void saveDraftToCloud();
void applyCloudDraft(History *history); void applyCloudDraft(History *history);
void writeDrafts(History *history); void saveFieldToHistoryLocalDraft();
int32 dlgsWidth() const; int32 dlgsWidth() const;
@ -222,10 +212,6 @@ public:
void searchMessages(const QString &query, Dialogs::Key inChat); void searchMessages(const QString &query, Dialogs::Key inChat);
void itemEdited(not_null<HistoryItem*> item); void itemEdited(not_null<HistoryItem*> item);
void checkLastUpdate(bool afterSleep);
bool isIdle() const;
QPixmap cachedBackground(const QRect &forRect, int &x, int &y); QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
void updateScrollColors(); void updateScrollColors();
@ -248,31 +234,13 @@ public:
void choosePeer(PeerId peerId, MsgId showAtMsgId); void choosePeer(PeerId peerId, MsgId showAtMsgId);
void clearBotStartToken(PeerData *peer); void clearBotStartToken(PeerData *peer);
void ptsWaiterStartTimerFor(ChannelData *channel, int32 ms); // ms <= 0 - stop timer
void feedUpdates(const MTPUpdates &updates, uint64 randomId = 0);
void ctrlEnterSubmitUpdated(); void ctrlEnterSubmitUpdated();
void setInnerFocus(); void setInnerFocus();
void scheduleViewIncrement(HistoryItem *item); void scheduleViewIncrement(HistoryItem *item);
void feedChannelDifference(const MTPDupdates_channelDifference &data);
// Made public for ApiWrap, while it is still here.
// Better would be for this to be moved to ApiWrap.
bool requestingDifference() const {
return _ptsWaiter.requesting();
}
void getDifference();
void updateOnline(bool gotOtherOffline = false);
void checkIdleFinish();
bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(const QRect &globalRect);
bool ptsUpdateAndApply(int32 pts, int32 ptsCount, const MTPUpdates &updates);
bool ptsUpdateAndApply(int32 pts, int32 ptsCount, const MTPUpdate &update);
bool ptsUpdateAndApply(int32 pts, int32 ptsCount);
void searchInChat(Dialogs::Key chat); void searchInChat(Dialogs::Key chat);
void app_sendBotCallback( void app_sendBotCallback(
@ -293,10 +261,6 @@ public:
void closeBothPlayers(); void closeBothPlayers();
bool isQuitPrevent();
~MainWidget();
signals: signals:
void dialogsUpdated(); void dialogsUpdated();
@ -313,27 +277,7 @@ protected:
bool eventFilter(QObject *o, QEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override;
private: private:
using ChannelGetDifferenceTime = QMap<ChannelData*, crl::time>;
enum class ChannelDifferenceRequest {
Unknown,
PtsGapOrShortPoll,
AfterFail,
};
struct DeleteHistoryRequest {
PeerData *peer;
bool justClearHistory;
};
struct DeleteAllFromUserParams {
ChannelData *channel;
UserData *from;
};
void viewsIncrement(); void viewsIncrement();
void sendPing();
void getDifferenceByPts();
void getDifferenceAfterFail();
void animationCallback(); void animationCallback();
void handleAdaptiveLayoutUpdate(); void handleAdaptiveLayoutUpdate();
@ -349,7 +293,6 @@ private:
[[nodiscard]] bool saveThirdSectionToStackBack() const; [[nodiscard]] bool saveThirdSectionToStackBack() const;
[[nodiscard]] auto thirdSectionForCurrentMainSection(Dialogs::Key key) [[nodiscard]] auto thirdSectionForCurrentMainSection(Dialogs::Key key)
-> std::unique_ptr<Window::SectionMemento>; -> std::unique_ptr<Window::SectionMemento>;
void userIsContactUpdated(not_null<UserData*> user);
void setupConnectingWidget(); void setupConnectingWidget();
void createPlayer(); void createPlayer();
@ -381,28 +324,6 @@ private:
void saveSectionInStack(); void saveSectionInStack();
void getChannelDifference(
not_null<ChannelData*> channel,
ChannelDifferenceRequest from = ChannelDifferenceRequest::Unknown);
void gotDifference(const MTPupdates_Difference &diff);
bool failDifference(const RPCError &e);
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
void gotState(const MTPupdates_State &state);
void updSetState(int32 pts, int32 date, int32 qts, int32 seq);
void gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff);
bool failChannelDifference(ChannelData *channel, const RPCError &err);
void failDifferenceStartTimerFor(ChannelData *channel);
void mtpUpdateReceived(const MTPUpdates &updates);
void mtpNewSessionCreated();
void feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds = false);
// Doesn't call sendHistoryChangeNotifications itself.
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
void usernameResolveDone(QPair<MsgId, QString> msgIdAndStartToken, const MTPcontacts_ResolvedPeer &result); void usernameResolveDone(QPair<MsgId, QString> msgIdAndStartToken, const MTPcontacts_ResolvedPeer &result);
bool usernameResolveFail(QString name, const RPCError &error); bool usernameResolveFail(QString name, const RPCError &error);
@ -427,14 +348,9 @@ private:
bool floatPlayerIsVisible(not_null<HistoryItem*> item) override; bool floatPlayerIsVisible(not_null<HistoryItem*> item) override;
void floatPlayerClosed(FullMsgId itemId); void floatPlayerClosed(FullMsgId itemId);
bool getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, crl::time &curTime);
void viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req); void viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req);
bool viewsIncrementFail(const RPCError &error, mtpRequestId req); bool viewsIncrementFail(const RPCError &error, mtpRequestId req);
void updateStatusDone(const MTPBool &result);
bool updateStatusFail(const RPCError &error);
void refreshResizeAreas(); void refreshResizeAreas();
template <typename MoveCallback, typename FinishCallback> template <typename MoveCallback, typename FinishCallback>
void createResizeArea( void createResizeArea(
@ -498,38 +414,6 @@ private:
int _exportTopBarHeight = 0; int _exportTopBarHeight = 0;
int _contentScrollAddToY = 0; int _contentScrollAddToY = 0;
int32 updDate = 0;
int32 updQts = -1;
int32 updSeq = 0;
base::Timer _noUpdatesTimer;
PtsWaiter _ptsWaiter;
ChannelGetDifferenceTime _channelGetDifferenceTimeByPts, _channelGetDifferenceTimeAfterFail;
crl::time _getDifferenceTimeByPts = 0;
crl::time _getDifferenceTimeAfterFail = 0;
base::Timer _byPtsTimer;
QMap<int32, MTPUpdates> _bySeqUpdates;
base::Timer _bySeqTimer;
base::Timer _byMinChannelTimer;
mtpRequestId _onlineRequest = 0;
base::Timer _onlineTimer;
base::Timer _idleFinishTimer;
bool _lastWasOnline = false;
crl::time _lastSetOnline = 0;
bool _isIdle = false;
int32 _failDifferenceTimeout = 1; // growing timeout for getDifference calls, if it fails
QMap<ChannelData*, int32> _channelFailDifferenceTimeout; // growing timeout for getChannelDifference calls, if it fails
base::Timer _failDifferenceTimer;
crl::time _lastUpdateTime = 0;
bool _handlingChannelDifference = false;
QPixmap _cachedBackground; QPixmap _cachedBackground;
QRect _cachedFor, _willCacheFor; QRect _cachedFor, _willCacheFor;
int _cachedX = 0; int _cachedX = 0;

View file

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h" #include "storage/storage_account.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_updates.h"
#include "settings/settings_intro.h" #include "settings/settings_intro.h"
#include "platform/platform_notifications_manager.h" #include "platform/platform_notifications_manager.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
@ -556,9 +557,12 @@ bool MainWindow::eventFilter(QObject *object, QEvent *e) {
case QEvent::MouseMove: { case QEvent::MouseMove: {
const auto position = static_cast<QMouseEvent*>(e)->globalPos(); const auto position = static_cast<QMouseEvent*>(e)->globalPos();
if (_main && _main->isIdle() && _lastMousePosition != position) { if (_lastMousePosition != position) {
Core::App().updateNonIdle(); if (const auto controller = sessionController()) {
_main->checkIdleFinish(); if (controller->session().updates().isIdle()) {
Core::App().updateNonIdle();
}
}
} }
_lastMousePosition = position; _lastMousePosition = position;
} break; } break;
@ -1007,7 +1011,9 @@ void MainWindow::sendPaths() {
} }
void MainWindow::updateIsActiveHook() { void MainWindow::updateIsActiveHook() {
if (_main) _main->updateOnline(); if (const auto controller = sessionController()) {
controller->session().updates().updateOnline();
}
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {

View file

@ -271,7 +271,9 @@ public:
public: public:
void cancel() { void cancel() {
_sender->senderRequestCancel(_requestId); if (_requestId) {
_sender->senderRequestCancel(_requestId);
}
} }
private: private:

View file

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "media/audio/media_audio_track.h" #include "media/audio/media_audio_track.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "api/api_updates.h"
#include "facades.h" #include "facades.h"
namespace Settings { namespace Settings {
@ -87,8 +88,8 @@ auto GenerateCodes() {
})); }));
}); });
codes.emplace(qsl("getdifference"), [](SessionController *window) { codes.emplace(qsl("getdifference"), [](SessionController *window) {
if (auto main = App::main()) { if (window) {
main->getDifference(); window->session().updates().getDifference();
} }
}); });
codes.emplace(qsl("loadcolors"), [](SessionController *window) { codes.emplace(qsl("loadcolors"), [](SessionController *window) {

View file

@ -968,6 +968,35 @@ void Account::readMtpData() {
applyReadContext(std::move(context)); applyReadContext(std::move(context));
} }
void Account::writeDrafts(not_null<History*> history) {
Storage::MessageDraft storedLocalDraft, storedEditDraft;
MessageCursor localCursor, editCursor;
if (const auto localDraft = history->localDraft()) {
if (_owner->session().supportMode()
|| !Data::draftsAreEqual(localDraft, history->cloudDraft())) {
storedLocalDraft = Storage::MessageDraft{
localDraft->msgId,
localDraft->textWithTags,
localDraft->previewCancelled
};
localCursor = localDraft->cursor;
}
}
if (const auto editDraft = history->editDraft()) {
storedEditDraft = Storage::MessageDraft{
editDraft->msgId,
editDraft->textWithTags,
editDraft->previewCancelled
};
editCursor = editDraft->cursor;
}
writeDrafts(
history->peer->id,
storedLocalDraft,
storedEditDraft);
writeDraftCursors(history->peer->id, localCursor, editCursor);
}
void Account::writeDrafts( void Account::writeDrafts(
const PeerId &peer, const PeerId &peer,
const MessageDraft &localDraft, const MessageDraft &localDraft,

View file

@ -71,6 +71,7 @@ public:
void writeBackground(const Data::WallPaper &paper, const QImage &image); void writeBackground(const Data::WallPaper &paper, const QImage &image);
bool readBackground(); bool readBackground();
void writeDrafts(not_null<History*> history);
void writeDrafts( void writeDrafts(
const PeerId &peer, const PeerId &peer,
const MessageDraft &localDraft, const MessageDraft &localDraft,

View file

@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "core/application.h" #include "core/application.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "mainwidget.h" #include "api/api_updates.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "facades.h" #include "facades.h"
@ -120,9 +120,10 @@ void System::schedule(not_null<HistoryItem*> item) {
auto delay = item->Has<HistoryMessageForwarded>() ? 500 : 100; auto delay = item->Has<HistoryMessageForwarded>() ? 500 : 100;
const auto t = base::unixtime::now(); const auto t = base::unixtime::now();
const auto ms = crl::now(); const auto ms = crl::now();
const bool isOnline = App::main()->lastWasOnline(); const auto &updates = history->session().updates();
const bool isOnline = updates.lastWasOnline();
const auto otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL); const auto otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL);
const bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - App::main()->lastSetOnline()) > t * 1000LL); const bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - updates.lastSetOnline()) > t * 1000LL);
if (!isOnline && otherNotOld && otherLaterThanMe) { if (!isOnline && otherNotOld && otherLaterThanMe) {
delay = Global::NotifyCloudDelay(); delay = Global::NotifyCloudDelay();
} else if (cOtherOnline() >= t) { } else if (cOtherOnline() >= t) {

View file

@ -144,6 +144,8 @@ SessionController::SessionController(
refreshFiltersMenu(); refreshFiltersMenu();
}); });
}, session->lifetime()); }, session->lifetime());
session->addWindow(this);
} }
not_null<::MainWindow*> SessionController::widget() const { not_null<::MainWindow*> SessionController::widget() const {