Request dialog list entries when needed.

Also save the original server-side int32 date in HistoryItems.
This commit is contained in:
John Preston 2018-02-03 22:52:35 +03:00
parent 0c5efb935d
commit 8bacc74d8b
50 changed files with 719 additions and 549 deletions

View file

@ -377,24 +377,6 @@ void ApiWrap::requestDialogEntry(not_null<Data::Feed*> feed) {
auto peers = QVector<MTPInputDialogPeer>( auto peers = QVector<MTPInputDialogPeer>(
1, 1,
MTP_inputDialogPeerFeed(MTP_int(feed->id()))); MTP_inputDialogPeerFeed(MTP_int(feed->id())));
if (feed->channelsLoaded()) {
const auto &channels = feed->channels();
peers.reserve(channels.size() + 1);
for (const auto history : feed->channels()) {
peers.push_back(MTP_inputDialogPeer(history->peer->input));
}
} else {
request(MTPmessages_GetDialogs(
MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id),
MTP_int(feed->id()),
MTP_int(0), // offset_date
MTP_int(0), // offset_id
MTP_inputPeerEmpty(), // offset_peer
MTP_int(Data::Feed::kChannelsLimit)
)).done([=](const MTPmessages_Dialogs &result) {
// applyFeedChannels(result);
}).send();
}
request(MTPmessages_GetPeerDialogs( request(MTPmessages_GetPeerDialogs(
MTP_vector(std::move(peers)) MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) { )).done([=](const MTPmessages_PeerDialogs &result) {
@ -405,6 +387,27 @@ void ApiWrap::requestDialogEntry(not_null<Data::Feed*> feed) {
}).send(); }).send();
} }
//void ApiWrap::requestFeedDialogsEntries(not_null<Data::Feed*> feed) {
// if (_dialogFeedRequests.contains(feed)) {
// return;
// }
// _dialogFeedRequests.emplace(feed);
//
// request(MTPmessages_GetDialogs(
// MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id),
// MTP_int(feed->id()),
// MTP_int(0), // offset_date
// MTP_int(0), // offset_id
// MTP_inputPeerEmpty(), // offset_peer
// MTP_int(Data::Feed::kChannelsLimit)
// )).done([=](const MTPmessages_Dialogs &result) {
// applyFeedDialogs(feed, result);
// _dialogFeedRequests.remove(feed);
// }).fail([=](const RPCError &error) {
// _dialogFeedRequests.remove(feed);
// }).send();
//}
void ApiWrap::requestDialogEntry(not_null<History*> history) { void ApiWrap::requestDialogEntry(not_null<History*> history) {
if (_dialogRequests.contains(history)) { if (_dialogRequests.contains(history)) {
return; return;
@ -417,43 +420,7 @@ void ApiWrap::requestDialogEntry(not_null<History*> history) {
MTP_vector(std::move(peers)) MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) { )).done([=](const MTPmessages_PeerDialogs &result) {
applyPeerDialogs(result); applyPeerDialogs(result);
historyDialogEntryApplied(history);
if (history->lastMessage()) {
if (!history->chatsListDate().isNull()
&& history->loadedAtBottom()) {
if (const auto channel = history->peer->asChannel()) {
const auto inviter = channel->inviter;
if (inviter != 0
&& history->chatsListDate() <= channel->inviteDate
&& channel->amIn()) {
if (const auto from = App::userLoaded(inviter)) {
history->insertJoinedMessage(true);
}
}
}
}
history->updateChatListExistence();
} else {
if (const auto chat = history->peer->asChat()) {
if (!chat->haveLeft()) {
Local::addSavedPeer(
history->peer,
history->chatsListDate());
}
} else if (const auto channel = history->peer->asChannel()) {
const auto inviter = channel->inviter;
if (inviter != 0 && channel->amIn()) {
if (const auto from = App::userLoaded(inviter)) {
history->unloadBlocks();
history->addNewerSlice(QVector<MTPMessage>());
history->insertJoinedMessage(
true);
}
}
} else {
App::main()->deleteConversation(history->peer, false);
}
}
_dialogRequests.remove(history); _dialogRequests.remove(history);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_dialogRequests.remove(history); _dialogRequests.remove(history);
@ -486,6 +453,95 @@ void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
_session->data().sendHistoryChangeNotifications(); _session->data().sendHistoryChangeNotifications();
} }
void ApiWrap::historyDialogEntryApplied(not_null<History*> history) {
if (!history->lastMessage()) {
if (const auto chat = history->peer->asChat()) {
if (!chat->haveLeft()) {
Local::addSavedPeer(
history->peer,
history->chatsListDate());
}
} else if (const auto channel = history->peer->asChannel()) {
const auto inviter = channel->inviter;
if (inviter != 0 && channel->amIn()) {
if (const auto from = App::userLoaded(inviter)) {
history->unloadBlocks();
history->addNewerSlice(QVector<MTPMessage>());
history->insertJoinedMessage(true);
}
}
} else {
App::main()->deleteConversation(history->peer, false);
}
return;
}
if (!history->chatsListDate().isNull()
&& history->loadedAtBottom()) {
if (const auto channel = history->peer->asChannel()) {
const auto inviter = channel->inviter;
if (inviter != 0
&& history->chatsListDate() <= ParseDateTime(channel->inviteDate)
&& channel->amIn()) {
if (const auto from = App::userLoaded(inviter)) {
history->insertJoinedMessage(true);
}
}
}
}
history->updateChatListExistence();
}
void ApiWrap::applyFeedDialogs(
not_null<Data::Feed*> feed,
const MTPmessages_Dialogs &dialogs) {
const auto [dialogsList, messagesList] = [&] {
const auto process = [&](const auto &data) {
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
return std::make_tuple(&data.vdialogs.v, &data.vmessages.v);
};
switch (dialogs.type()) {
case mtpc_messages_dialogs:
return process(dialogs.c_messages_dialogs());
case mtpc_messages_dialogsSlice:
LOG(("API Error: "
"Unexpected dialogsSlice in feed dialogs list."));
return process(dialogs.c_messages_dialogsSlice());
}
Unexpected("Type in DialogsWidget::dialogsReceived");
}();
App::feedMsgs(*messagesList, NewMessageLast);
auto channels = std::vector<not_null<ChannelData*>>();
channels.reserve(dialogsList->size());
for (const auto &dialog : *dialogsList) {
switch (dialog.type()) {
case mtpc_dialog: {
if (const auto peerId = peerFromMTP(dialog.c_dialog().vpeer)) {
if (peerIsChannel(peerId)) {
const auto history = App::history(peerId);
history->applyDialog(dialog.c_dialog());
channels.push_back(history->peer->asChannel());
} else {
LOG(("API Error: "
"Unexpected non-channel in feed dialogs list."));
}
}
} break;
case mtpc_dialogFeed: {
LOG(("API Error: Unexpected dialogFeed in feed dialogs list."));
} break;
default: Unexpected("Type in DialogsInner::dialogsReceived");
}
}
feed->setChannels(channels);
_session->data().sendHistoryChangeNotifications();
}
void ApiWrap::requestFullPeer(PeerData *peer) { void ApiWrap::requestFullPeer(PeerData *peer) {
if (!peer || _fullPeerRequests.contains(peer)) return; if (!peer || _fullPeerRequests.contains(peer)) return;
@ -1118,12 +1174,12 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
case mtpc_channelParticipantSelf: { case mtpc_channelParticipantSelf: {
auto &d = p.vparticipant.c_channelParticipantSelf(); auto &d = p.vparticipant.c_channelParticipantSelf();
channel->inviter = d.vinviter_id.v; channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate); channel->inviteDate = d.vdate.v;
} break; } break;
case mtpc_channelParticipantCreator: { case mtpc_channelParticipantCreator: {
auto &d = p.vparticipant.c_channelParticipantCreator(); auto &d = p.vparticipant.c_channelParticipantCreator();
channel->inviter = _session->userId(); channel->inviter = _session->userId();
channel->inviteDate = date(MTP_int(channel->date)); channel->inviteDate = channel->date;
if (channel->mgInfo) { if (channel->mgInfo) {
channel->mgInfo->creator = App::self(); channel->mgInfo->creator = App::self();
} }
@ -1131,7 +1187,7 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
case mtpc_channelParticipantAdmin: { case mtpc_channelParticipantAdmin: {
auto &d = p.vparticipant.c_channelParticipantAdmin(); auto &d = p.vparticipant.c_channelParticipantAdmin();
channel->inviter = d.vinviter_id.v; channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate); channel->inviteDate = d.vdate.v;
} break; } break;
} }
@ -1670,7 +1726,7 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer) {
if (auto history = App::historyLoaded(peer->id)) { if (auto history = App::historyLoaded(peer->id)) {
if (const auto last = history->lastMessage()) { if (const auto last = history->lastMessage()) {
deleteTillId = last->id; deleteTillId = last->id;
Local::addSavedPeer(history->peer, last->date); Local::addSavedPeer(history->peer, ItemDateTime(last));
} }
history->clear(); history->clear();
history->newLoaded = history->oldLoaded = true; history->newLoaded = history->oldLoaded = true;
@ -2909,8 +2965,10 @@ void ApiWrap::requestFeedChannels(not_null<Data::Feed*> feed) {
case mtpc_channels_feedSources: { case mtpc_channels_feedSources: {
const auto &data = result.c_channels_feedSources(); const auto &data = result.c_channels_feedSources();
App::feedUsers(data.vusers);
App::feedChats(data.vchats); // First we set channels without reading them from data.
// This allows us to apply them all at once without registering
// them one by one.
for (const auto &broadcasts : data.vfeeds.v) { for (const auto &broadcasts : data.vfeeds.v) {
if (broadcasts.type() == mtpc_feedBroadcasts) { if (broadcasts.type() == mtpc_feedBroadcasts) {
const auto &list = broadcasts.c_feedBroadcasts(); const auto &list = broadcasts.c_feedBroadcasts();
@ -2923,6 +2981,10 @@ void ApiWrap::requestFeedChannels(not_null<Data::Feed*> feed) {
feed->setChannels(std::move(channels)); feed->setChannels(std::move(channels));
} }
} }
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
if (feed->channelsLoaded()) { if (feed->channelsLoaded()) {
feedChannelsDone(feed); feedChannelsDone(feed);
} else { } else {
@ -3198,7 +3260,7 @@ void ApiWrap::forwardMessages(
history->addNewForwarded( history->addNewForwarded(
newId.msg, newId.msg,
flags, flags,
date(MTP_int(unixtime())), unixtime(),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
message); message);

View file

@ -69,6 +69,7 @@ public:
void requestContacts(); void requestContacts();
void requestDialogEntry(not_null<Data::Feed*> feed); void requestDialogEntry(not_null<Data::Feed*> feed);
//void requestFeedDialogsEntries(not_null<Data::Feed*> feed);
void requestDialogEntry(not_null<History*> history); void requestDialogEntry(not_null<History*> history);
void requestFullPeer(PeerData *peer); void requestFullPeer(PeerData *peer);
@ -282,6 +283,10 @@ private:
QVector<MTPint> collectMessageIds(const MessageDataRequests &requests); QVector<MTPint> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs); void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void historyDialogEntryApplied(not_null<History*> history);
void applyFeedDialogs(
not_null<Data::Feed*> feed,
const MTPmessages_Dialogs &dialogs);
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req); void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req); void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req);

View file

@ -460,7 +460,7 @@ void DeleteMessagesBox::prepare() {
} else { } else {
text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size()); text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size());
auto canDeleteAllForEveryone = true; auto canDeleteAllForEveryone = true;
auto now = ::date(unixtime()); auto now = unixtime();
auto deleteForUser = (UserData*)nullptr; auto deleteForUser = (UserData*)nullptr;
auto peer = (PeerData*)nullptr; auto peer = (PeerData*)nullptr;
auto forEveryoneText = lang(lng_delete_for_everyone_check); auto forEveryoneText = lang(lng_delete_for_everyone_check);
@ -588,7 +588,7 @@ void DeleteMessagesBox::deleteAndClear() {
if (wasOnServer) { if (wasOnServer) {
idsByPeer[history->peer].push_back(MTP_int(itemId.msg)); idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
} else if (wasLast) { } else if (wasLast && !history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history); Auth().api().requestDialogEntry(history);
} }
} }

View file

@ -415,7 +415,7 @@ MTPChannelBannedRights EditRestrictedBox::DefaultRights(not_null<ChannelData*> c
void EditRestrictedBox::showRestrictUntil() { void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1); auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever() ? tomorrow : date(getRealUntilValue()).date(); auto highlighted = isUntilForever() ? tomorrow : ParseDateTime(getRealUntilValue()).date();
auto month = highlighted; auto month = highlighted;
_restrictUntilBox = Ui::show( _restrictUntilBox = Ui::show(
Box<CalendarBox>( Box<CalendarBox>(
@ -471,7 +471,11 @@ void EditRestrictedBox::createUntilVariants() {
}; };
auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) { auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) {
if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) { if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) {
addVariant(until, lng_rights_chat_banned_custom_date(lt_date, langDayOfMonthFull(date(until).date()))); addVariant(
until,
lng_rights_chat_banned_custom_date(
lt_date,
langDayOfMonthFull(ParseDateTime(until).date())));
} }
}; };
auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) { auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) {

View file

@ -364,15 +364,15 @@ void ContactsBoxController::checkForEmptyRows() {
} }
} }
std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(not_null<PeerData*> peer) { std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(
if (auto user = peer->asUser()) { not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
return createRow(user); return createRow(user);
} }
return nullptr; return nullptr;
} }
void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) { void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
Auth().api().requestDialogEntry(App::history(row->peer()));
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId); Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
} }

View file

@ -784,7 +784,11 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
prepareThumb(preview); prepareThumb(preview);
const auto filepath = file.path; const auto filepath = file.path;
if (filepath.isEmpty()) { if (filepath.isEmpty()) {
auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); auto filename = filedialogDefaultName(
qsl("image"),
qsl(".png"),
QString(),
true);
_nameText.setText( _nameText.setText(
st::semiboldTextStyle, st::semiboldTextStyle,
filename, filename,

View file

@ -119,8 +119,9 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
//CountriesByISO2::const_iterator j = countries.constFind(country); //CountriesByISO2::const_iterator j = countries.constFind(country);
//if (j != countries.cend()) country = QString::fromUtf8(j.value()->name); //if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
MTPint active = d.vdate_active.v ? d.vdate_active : d.vdate_created; const auto active = data.activeTime = d.vdate_active.v
data.activeTime = active.v; ? d.vdate_active.v
: d.vdate_created.v;
data.info = qs(d.vdevice_model) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(d.vsystem_version); data.info = qs(d.vdevice_model) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(d.vsystem_version);
data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country); data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
@ -144,12 +145,15 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
} }
_current = data; _current = data;
} else { } else {
QDateTime now(QDateTime::currentDateTime()), lastTime(date(active)); const auto now = QDateTime::currentDateTime();
QDate nowDate(now.date()), lastDate(lastTime.date()); const auto lastTime = ParseDateTime(active);
const auto nowDate = now.date();
const auto lastDate = lastTime.date();
QString dt; QString dt;
if (lastDate == nowDate) { if (lastDate == nowDate) {
data.active = lastTime.toString(cTimeFormat()); data.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) { } else if (lastDate.year() == nowDate.year()
&& lastDate.weekNumber() == nowDate.weekNumber()) {
data.active = langDayOfWeek(lastDate); data.active = langDayOfWeek(lastDate);
} else { } else {
data.active = lastDate.toString(qsl("d.MM.yy")); data.active = lastDate.toString(qsl("d.MM.yy"));

View file

@ -39,7 +39,8 @@ public:
}; };
bool canAddItem(not_null<const HistoryItem*> item) const { bool canAddItem(not_null<const HistoryItem*> item) const {
return (ComputeType(item) == _type && item->date.date() == _date); return (ComputeType(item) == _type)
&& (ItemDateTime(item).date() == _date);
} }
void addItem(not_null<HistoryItem*> item) { void addItem(not_null<HistoryItem*> item) {
Expects(canAddItem(item)); Expects(canAddItem(item));
@ -117,7 +118,7 @@ private:
BoxController::Row::Row(not_null<HistoryItem*> item) BoxController::Row::Row(not_null<HistoryItem*> item)
: PeerListRow(item->history()->peer, item->id) : PeerListRow(item->history()->peer, item->id)
, _items(1, item) , _items(1, item)
, _date(item->date.date()) , _date(ItemDateTime(item).date())
, _type(ComputeType(item)) { , _type(ComputeType(item)) {
refreshStatus(); refreshStatus();
} }
@ -162,7 +163,7 @@ void BoxController::Row::refreshStatus() {
return; return;
} }
auto text = [this] { auto text = [this] {
auto time = _items.front()->date.time().toString(cTimeFormat()); auto time = ItemDateTime(_items.front()).time().toString(cTimeFormat());
auto today = QDateTime::currentDateTime().date(); auto today = QDateTime::currentDateTime().date();
if (_date == today) { if (_date == today) {
return lng_call_box_status_today(lt_time, time); return lng_call_box_status_today(lt_time, time);

View file

@ -29,7 +29,7 @@ QString filedialogDefaultName(
const QString &extension, const QString &extension,
const QString &path, const QString &path,
bool skipExistance, bool skipExistance,
int fileTime) { TimeId fileTime) {
auto directoryPath = path; auto directoryPath = path;
if (directoryPath.isEmpty()) { if (directoryPath.isEmpty()) {
if (cDialogLastPath().isEmpty()) { if (cDialogLastPath().isEmpty()) {
@ -40,7 +40,8 @@ QString filedialogDefaultName(
QString base; QString base;
if (fileTime) { if (fileTime) {
base = prefix + ::date(fileTime).toString("_yyyy-MM-dd_HH-mm-ss"); const auto date = ParseDateTime(fileTime);
base = prefix + date.toString("_yyyy-MM-dd_HH-mm-ss");
} else { } else {
struct tm tm; struct tm tm;
time_t t = time(NULL); time_t t = time(NULL);

View file

@ -21,7 +21,7 @@ QString filedialogDefaultName(
const QString &extension, const QString &extension,
const QString &path = QString(), const QString &path = QString(),
bool skipExistance = false, bool skipExistance = false,
int fileTime = 0); TimeId fileTime = TimeId(0));
QString filedialogNextFilename( QString filedialogNextFilename(
const QString &name, const QString &name,
const QString &cur, const QString &cur,

View file

@ -80,7 +80,7 @@ namespace {
} }
} }
TimeId myunixtime() { TimeId LocalUnixtime() {
return (TimeId)time(NULL); return (TimeId)time(NULL);
} }
@ -103,31 +103,25 @@ void unixtimeSet(int32 serverTime, bool force) {
DEBUG_LOG(("MTP Info: setting client unixtime to %1").arg(serverTime)); DEBUG_LOG(("MTP Info: setting client unixtime to %1").arg(serverTime));
} }
unixtimeWasSet = true; unixtimeWasSet = true;
unixtimeDelta = serverTime + 1 - myunixtime(); unixtimeDelta = serverTime + 1 - LocalUnixtime();
DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(unixtimeDelta)); DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(unixtimeDelta));
} }
_initMsgIdConstants(); _initMsgIdConstants();
} }
TimeId unixtime() { TimeId unixtime() {
auto result = myunixtime(); auto result = LocalUnixtime();
QReadLocker locker(&unixtimeLock); QReadLocker locker(&unixtimeLock);
return result + unixtimeDelta; return result + unixtimeDelta;
} }
TimeId fromServerTime(const MTPint &serverTime) { QDateTime ParseDateTime(TimeId serverTime) {
if (serverTime <= 0) {
return QDateTime();
}
QReadLocker locker(&unixtimeLock); QReadLocker locker(&unixtimeLock);
return serverTime.v - unixtimeDelta; return QDateTime::fromTime_t(serverTime - unixtimeDelta);
}
MTPint toServerTime(const TimeId &clientTime) {
QReadLocker locker(&unixtimeLock);
return MTP_int(clientTime + unixtimeDelta);
}
QDateTime dateFromServerTime(TimeId time) {
return dateFromServerTime(MTP_int(time));
} }
// Precise timing functions / rand init // Precise timing functions / rand init
@ -224,7 +218,7 @@ namespace {
_msgIdCoef = float64(0xFFFF0000L) / 1000000000.; _msgIdCoef = float64(0xFFFF0000L) / 1000000000.;
_msStart = 1000LL * static_cast<TimeMs>(ts.tv_sec) + (static_cast<TimeMs>(ts.tv_nsec) / 1000000LL); _msStart = 1000LL * static_cast<TimeMs>(ts.tv_sec) + (static_cast<TimeMs>(ts.tv_nsec) / 1000000LL);
#endif #endif
_timeStart = myunixtime(); _timeStart = LocalUnixtime();
srand((uint32)(_msStart & 0xFFFFFFFFL)); srand((uint32)(_msStart & 0xFFFFFFFFL));
} }
}; };
@ -310,7 +304,7 @@ namespace ThirdParty {
} }
bool checkms() { bool checkms() {
auto unixms = (myunixtime() - _timeStart) * 1000LL + _msAddToUnixtime; auto unixms = (LocalUnixtime() - _timeStart) * 1000LL + _msAddToUnixtime;
auto ms = getms(true); auto ms = getms(true);
if (ms > unixms + 1000LL) { if (ms > unixms + 1000LL) {
_msAddToUnixtime = ((ms - unixms) / 1000LL) * 1000LL; _msAddToUnixtime = ((ms - unixms) / 1000LL) * 1000LL;

View file

@ -225,32 +225,14 @@ private:
}; };
class MTPint;
using TimeId = int32; using TimeId = int32;
TimeId myunixtime();
void unixtimeInit(); void unixtimeInit();
void unixtimeSet(TimeId servertime, bool force = false); void unixtimeSet(TimeId serverTime, bool force = false);
TimeId unixtime(); TimeId unixtime();
TimeId fromServerTime(const MTPint &serverTime);
MTPint toServerTime(const TimeId &clientTime);
uint64 msgid(); uint64 msgid();
int32 reqid(); int32 reqid();
inline QDateTime date(int32 time = -1) { QDateTime ParseDateTime(TimeId serverTime);
QDateTime result;
if (time >= 0) result.setTime_t(time);
return result;
}
inline QDateTime dateFromServerTime(const MTPint &time) {
return date(fromServerTime(time));
}
inline QDateTime date(const MTPint &time) {
return dateFromServerTime(time);
}
QDateTime dateFromServerTime(TimeId time);
inline void mylocaltime(struct tm * _Tm, const time_t * _Time) { inline void mylocaltime(struct tm * _Tm, const time_t * _Time) {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN

View file

@ -19,11 +19,28 @@ namespace {
} // namespace } // namespace
Draft::Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId) Draft::Draft(
: textWithTags(field->getTextWithTags()) const TextWithTags &textWithTags,
, msgId(msgId) MsgId msgId,
, cursor(field) const MessageCursor &cursor,
, previewCancelled(previewCancelled) { bool previewCancelled,
mtpRequestId saveRequestId)
: textWithTags(textWithTags)
, msgId(msgId)
, cursor(cursor)
, previewCancelled(previewCancelled)
, saveRequestId(saveRequestId) {
}
Draft::Draft(
not_null<const Ui::FlatTextarea*> field,
MsgId msgId,
bool previewCancelled,
mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags())
, msgId(msgId)
, cursor(field)
, previewCancelled(previewCancelled) {
} }
void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
@ -32,7 +49,7 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) }; auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) };
auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0); auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage()); auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
cloudDraft->date = ::date(draft.vdate); cloudDraft->date = draft.vdate.v;
history->setCloudDraft(std::move(cloudDraft)); history->setCloudDraft(std::move(cloudDraft));
history->createLocalDraftFromCloud(); history->createLocalDraftFromCloud();

View file

@ -17,18 +17,20 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft);
void clearPeerCloudDraft(PeerId peerId); void clearPeerCloudDraft(PeerId peerId);
struct Draft { struct Draft {
Draft() { Draft() = default;
} Draft(
Draft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequestId = 0) const TextWithTags &textWithTags,
: textWithTags(textWithTags) MsgId msgId,
, msgId(msgId) const MessageCursor &cursor,
, cursor(cursor) bool previewCancelled,
, previewCancelled(previewCancelled) mtpRequestId saveRequestId = 0);
, saveRequestId(saveRequestId) { Draft(
} not_null<const Ui::FlatTextarea*> field,
Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0); MsgId msgId,
bool previewCancelled,
mtpRequestId saveRequestId = 0);
QDateTime date; TimeId date = 0;
TextWithTags textWithTags; TextWithTags textWithTags;
MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft
MessageCursor cursor; MessageCursor cursor;

View file

@ -74,7 +74,7 @@ void Feed::registerOne(not_null<ChannelData*> channel) {
_channels.push_back(history); _channels.push_back(history);
if (history->lastMessageKnown()) { if (history->lastMessageKnown()) {
recountLastMessage(); recountLastMessage();
} else if (_channelsLoaded) { } else if (lastMessageKnown()) {
_parent->session().api().requestDialogEntry(history); _parent->session().api().requestDialogEntry(history);
} }
_parent->session().storage().remove( _parent->session().storage().remove(
@ -121,7 +121,7 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) {
void Feed::updateLastMessage(not_null<HistoryItem*> item) { void Feed::updateLastMessage(not_null<HistoryItem*> item) {
if (justUpdateLastMessage(item)) { if (justUpdateLastMessage(item)) {
if (_lastMessage && *_lastMessage) { if (_lastMessage && *_lastMessage) {
setChatsListDate((*_lastMessage)->date); setChatsListDate(ItemDateTime(*_lastMessage));
} }
} }
} }
@ -192,15 +192,22 @@ void Feed::setChannels(std::vector<not_null<ChannelData*>> channels) {
_channels, _channels,
channel.get(), channel.get(),
[](auto history) { return history->peer->asChannel(); } [](auto history) { return history->peer->asChannel(); }
) != end(_channels); ) == end(_channels);
}) | ranges::to_vector; }) | ranges::to_vector;
for (const auto channel : remove) { for (const auto channel : remove) {
channel->clearFeed(); channel->clearFeed();
} }
// We assume the last message was correct before requesting the list.
// So we save it and don't allow channels from the list to change it.
// After that we restore it.
const auto oldLastMessage = base::take(_lastMessage);
for (const auto channel : add) { for (const auto channel : add) {
_lastMessage = base::none;
channel->setFeed(this); channel->setFeed(this);
} }
_lastMessage = oldLastMessage;
_channels.clear(); _channels.clear();
for (const auto channel : channels) { for (const auto channel : channels) {
@ -256,7 +263,7 @@ void Feed::recountLastMessage() {
void Feed::updateChatsListDate() { void Feed::updateChatsListDate() {
if (_lastMessage && *_lastMessage) { if (_lastMessage && *_lastMessage) {
setChatsListDate((*_lastMessage)->date); setChatsListDate(ItemDateTime(*_lastMessage));
} }
} }
@ -268,6 +275,14 @@ bool Feed::lastMessageKnown() const {
return !!_lastMessage; return !!_lastMessage;
} }
int Feed::unreadCount() const {
return _unreadCount ? *_unreadCount : 0;
}
bool Feed::unreadCountKnown() const {
return !!_unreadCount;
}
void Feed::applyDialog(const MTPDdialogFeed &data) { void Feed::applyDialog(const MTPDdialogFeed &data) {
const auto addChannel = [&](ChannelId channelId) { const auto addChannel = [&](ChannelId channelId) {
if (const auto channel = App::channelLoaded(channelId)) { if (const auto channel = App::channelLoaded(channelId)) {
@ -299,8 +314,14 @@ void Feed::applyDialog(const MTPDdialogFeed &data) {
} }
void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) { void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
if (unreadCountKnown()
&& (*_unreadCount == unreadNonMutedCount + unreadMutedCount)
&& (_unreadMutedCount == unreadMutedCount)) {
return;
}
_unreadCount = unreadNonMutedCount + unreadMutedCount; _unreadCount = unreadNonMutedCount + unreadMutedCount;
_unreadMutedCount = unreadMutedCount; _unreadMutedCount = unreadMutedCount;
updateChatListEntry();
} }
void Feed::setUnreadPosition(const MessagePosition &position) { void Feed::setUnreadPosition(const MessagePosition &position) {
@ -312,7 +333,7 @@ void Feed::setUnreadPosition(const MessagePosition &position) {
void Feed::unreadCountChanged( void Feed::unreadCountChanged(
base::optional<int> unreadCountDelta, base::optional<int> unreadCountDelta,
int mutedCountDelta) { int mutedCountDelta) {
if (!_unreadCount) { if (!unreadCountKnown()) {
return; return;
} }
if (unreadCountDelta) { if (unreadCountDelta) {
@ -323,6 +344,9 @@ void Feed::unreadCountChanged(
*_unreadCount); *_unreadCount);
updateChatListEntry(); updateChatListEntry();
} else { } else {
// _parent->session().api().requestFeedDialogsEntries(this);
// Happens once for each channel with unknown unread count.
// Requesting all feed dialogs could be huge and even have slicing.
_parent->session().api().requestDialogEntry(this); _parent->session().api().requestDialogEntry(this);
} }
} }
@ -344,7 +368,7 @@ bool Feed::shouldBeInChatList() const {
} }
int Feed::chatListUnreadCount() const { int Feed::chatListUnreadCount() const {
return _unreadCount ? *_unreadCount : 0; return unreadCount();
} }
bool Feed::chatListMutedBadge() const { bool Feed::chatListMutedBadge() const {

View file

@ -55,6 +55,8 @@ public:
HistoryItem *lastMessage() const; HistoryItem *lastMessage() const;
bool lastMessageKnown() const; bool lastMessageKnown() const;
int unreadCount() const;
bool unreadCountKnown() const;
bool toImportant() const override; bool toImportant() const override;
bool shouldBeInChatList() const override; bool shouldBeInChatList() const override;

View file

@ -946,8 +946,8 @@ public:
|| amCreator(); || amCreator();
} }
int32 inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel UserId inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel
QDateTime inviteDate; TimeId inviteDate = 0;
void ptsInit(int32 pts) { void ptsInit(int32 pts) {
_ptsWaiter.init(pts); _ptsWaiter.init(pts);

View file

@ -14,6 +14,7 @@ namespace {
constexpr auto kMinOnlineChangeTimeout = TimeMs(1000); constexpr auto kMinOnlineChangeTimeout = TimeMs(1000);
constexpr auto kMaxOnlineChangeTimeout = 86400 * TimeMs(1000); constexpr auto kMaxOnlineChangeTimeout = 86400 * TimeMs(1000);
constexpr auto kSecondsInDay = 86400;
int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) { int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
if (online <= 0) { if (online <= 0) {
@ -33,7 +34,7 @@ int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
if (hours < 12) { if (hours < 12) {
return (hours + 1) * 3600 - (now - online); return (hours + 1) * 3600 - (now - online);
} }
const auto nowFull = ::date(now); const auto nowFull = ParseDateTime(now);
const auto tomorrow = QDateTime(nowFull.date().addDays(1)); const auto tomorrow = QDateTime(nowFull.date().addDays(1));
return static_cast<int32>(nowFull.secsTo(tomorrow)); return static_cast<int32>(nowFull.secsTo(tomorrow));
} }
@ -175,27 +176,21 @@ TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
return -1; return -1;
} }
const auto online = user->onlineTill; const auto online = user->onlineTill;
const auto fromDate = [](const QDate &date) {
return toServerTime(QDateTime(date).toTime_t()).v;
};
if (online <= 0) { if (online <= 0) {
switch (online) { switch (online) {
case 0: case 0:
case -1: return online; case -1: return online;
case -2: { case -2: {
const auto recently = date(now).date().addDays(-3); return now - 3 * kSecondsInDay;
return fromDate(recently);
} break; } break;
case -3: { case -3: {
const auto weekago = date(now).date().addDays(-7); return now - 7 * kSecondsInDay;
return fromDate(weekago);
} break; } break;
case -4: { case -4: {
const auto monthago = date(now).date().addDays(-30); return now - 30 * kSecondsInDay;
return fromDate(monthago);
} break; } break;
} }
return -online; return -online;
@ -233,8 +228,8 @@ QString OnlineText(TimeId online, TimeId now) {
if (hours < 12) { if (hours < 12) {
return lng_status_lastseen_hours(lt_count, hours); return lng_status_lastseen_hours(lt_count, hours);
} }
const auto onlineFull = ::date(online); const auto onlineFull = ParseDateTime(online);
const auto nowFull = ::date(now); const auto nowFull = ParseDateTime(now);
if (onlineFull.date() == nowFull.date()) { if (onlineFull.date() == nowFull.date()) {
const auto onlineTime = onlineFull.time().toString(cTimeFormat()); const auto onlineTime = onlineFull.time().toString(cTimeFormat());
return lng_status_lastseen_today(lt_time, onlineTime); return lng_status_lastseen_today(lt_time, onlineTime);
@ -259,8 +254,8 @@ QString OnlineTextFull(not_null<UserData*> user, TimeId now) {
} else if (const auto common = OnlineTextCommon(user->onlineTill, now)) { } else if (const auto common = OnlineTextCommon(user->onlineTill, now)) {
return *common; return *common;
} }
const auto onlineFull = ::date(user->onlineTill); const auto onlineFull = ParseDateTime(user->onlineTill);
const auto nowFull = ::date(now); const auto nowFull = ParseDateTime(now);
if (onlineFull.date() == nowFull.date()) { if (onlineFull.date() == nowFull.date()) {
const auto onlineTime = onlineFull.time().toString(cTimeFormat()); const auto onlineTime = onlineFull.time().toString(cTimeFormat());
return lng_status_lastseen_today(lt_time, onlineTime); return lng_status_lastseen_today(lt_time, onlineTime);

View file

@ -32,22 +32,6 @@ uint64 PinnedDialogPos(int pinnedIndex) {
} // namespace } // namespace
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b) {
if (a->date < b->date) {
return true;
} else if (b->date < a->date) {
return false;
}
const auto apeer = a->history()->peer->bareId();
const auto bpeer = b->history()->peer->bareId();
if (apeer < bpeer) {
return true;
} else if (bpeer < apeer) {
return false;
}
return a->id < b->id;
}
Entry::Entry(const Key &key) Entry::Entry(const Key &key)
: lastItemTextCache(st::dialogsTextWidthMin) : lastItemTextCache(st::dialogsTextWidthMin)
, _key(key) { , _key(key) {
@ -127,7 +111,7 @@ PositionChange Entry::adjustByPosInChatList(
return { movedFrom, movedTo }; return { movedFrom, movedTo };
} }
void Entry::setChatsListDate(const QDateTime &date) { void Entry::setChatsListDate(QDateTime date) {
if (!_lastMessageDate.isNull() && _lastMessageDate >= date) { if (!_lastMessageDate.isNull() && _lastMessageDate >= date) {
if (!inChatList(Dialogs::Mode::All)) { if (!inChatList(Dialogs::Mode::All)) {
return; return;

View file

@ -33,8 +33,6 @@ struct PositionChange {
int movedTo; int movedTo;
}; };
bool MessageIsLess(not_null<HistoryItem*> a, not_null<HistoryItem*> b);
class Entry { class Entry {
public: public:
Entry(const Key &key); Entry(const Key &key);
@ -62,7 +60,7 @@ public:
return _sortKeyInChatList; return _sortKeyInChatList;
} }
void updateChatListSortPosition(); void updateChatListSortPosition();
void setChatsListDate(const QDateTime &date); void setChatsListDate(QDateTime date);
virtual void updateChatListExistence(); virtual void updateChatListExistence();
bool needUpdateInChatList() const; bool needUpdateInChatList() const;

View file

@ -1716,7 +1716,6 @@ void DialogsInner::dialogsReceived(const QVector<MTPDialog> &added) {
default: Unexpected("Type in DialogsInner::dialogsReceived"); default: Unexpected("Type in DialogsInner::dialogsReceived");
} }
} }
Notify::unreadCounterUpdated();
refresh(); refresh();
} }
@ -1729,8 +1728,11 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) {
const auto history = App::history(peerId); const auto history = App::history(peerId);
history->applyDialog(dialog); history->applyDialog(dialog);
if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) { if (!history->isPinnedDialog()) {
addSavedPeersAfter(history->chatsListDate()); const auto date = history->chatsListDate();
if (!date.isNull()) {
addSavedPeersAfter(date);
}
} }
_contactsNoDialogs->del(history); _contactsNoDialogs->del(history);
if (const auto from = history->peer->migrateFrom()) { if (const auto from = history->peer->migrateFrom()) {
@ -1749,8 +1751,11 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
const auto feed = Auth().data().feed(feedId); const auto feed = Auth().data().feed(feedId);
feed->applyDialog(dialog); feed->applyDialog(dialog);
if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) { if (!feed->isPinnedDialog()) {
addSavedPeersAfter(feed->chatsListDate()); const auto date = feed->chatsListDate();
if (!date.isNull()) {
addSavedPeersAfter(date);
}
} }
} }

View file

@ -25,7 +25,7 @@ namespace {
// Show all dates that are in the last 20 hours in time format. // Show all dates that are in the last 20 hours in time format.
constexpr int kRecentlyInSeconds = 20 * 3600; constexpr int kRecentlyInSeconds = 20 * 3600;
void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool active, bool selected) { void paintRowDate(Painter &p, QDateTime date, QRect &rectForName, bool active, bool selected) {
auto now = QDateTime::currentDateTime(); auto now = QDateTime::currentDateTime();
auto lastTime = date; auto lastTime = date;
auto nowDate = now.date(); auto nowDate = now.date();
@ -389,13 +389,13 @@ void RowPainter::paint(
const auto displayDate = [item, cloudDraft] { const auto displayDate = [item, cloudDraft] {
if (item) { if (item) {
if (cloudDraft) { if (cloudDraft) {
return (item->date > cloudDraft->date) return (item->date() > cloudDraft->date)
? item->date ? ItemDateTime(item)
: cloudDraft->date; : ParseDateTime(cloudDraft->date);
} }
return item->date; return ItemDateTime(item);
} }
return cloudDraft ? cloudDraft->date : QDateTime(); return cloudDraft ? ParseDateTime(cloudDraft->date) : QDateTime();
}(); }();
const auto from = history const auto from = history
@ -581,7 +581,7 @@ void RowPainter::paint(
from, from,
item, item,
cloudDraft, cloudDraft,
item->date, ItemDateTime(item),
fullWidth, fullWidth,
flags, flags,
ms, ms,

View file

@ -455,7 +455,7 @@ QString InnerWidget::tooltipText() const {
if (_mouseCursorState == CursorState::Date if (_mouseCursorState == CursorState::Date
&& _mouseAction == MouseAction::None) { && _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) { if (const auto view = App::hoveredItem()) {
auto dateText = view->data()->date.toString( auto dateText = view->dateTime().toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat)); QLocale::system().dateTimeFormat(QLocale::LongFormat));
return dateText; return dateText;
} }
@ -666,7 +666,7 @@ void InnerWidget::itemsAdded(Direction direction, int addedCount) {
const auto view = _items[i - 1].get(); const auto view = _items[i - 1].get();
if (i < _items.size()) { if (i < _items.size()) {
const auto previous = _items[i].get(); const auto previous = _items[i].get();
view->setDisplayDate(view->data()->date.date() != previous->data()->date.date()); view->setDisplayDate(view->dateTime().date() != previous->dateTime().date());
const auto attach = view->computeIsAttachToPrevious(previous); const auto attach = view->computeIsAttachToPrevious(previous);
view->setAttachToPrevious(attach); view->setAttachToPrevious(attach);
previous->setAttachToNext(attach); previous->setAttachToNext(attach);
@ -796,7 +796,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
} else { } else {
HistoryView::ServiceMessagePainter::paintDate( HistoryView::ServiceMessagePainter::paintDate(
p, p,
view->data()->date, view->dateTime(),
dateY, dateY,
width); width);
} }

View file

@ -105,7 +105,7 @@ TextWithEntities ExtractEditedText(const MTPMessage &message) {
return { text, entities }; return { text, entities };
} }
PhotoData *GenerateChatPhoto(ChannelId channelId, uint64 logEntryId, MTPint date, const MTPDchatPhoto &photo) { PhotoData *GenerateChatPhoto(ChannelId channelId, uint64 logEntryId, TimeId date, const MTPDchatPhoto &photo) {
// We try to make a unique photoId that will stay the same for each pair (channelId, logEntryId). // We try to make a unique photoId that will stay the same for each pair (channelId, logEntryId).
static const auto RandomIdPart = rand_value<uint64>(); static const auto RandomIdPart = rand_value<uint64>();
auto mixinIdPart = (static_cast<uint64>(static_cast<uint32>(channelId)) << 32) ^ logEntryId; auto mixinIdPart = (static_cast<uint64>(static_cast<uint32>(channelId)) << 32) ^ logEntryId;
@ -119,7 +119,7 @@ PhotoData *GenerateChatPhoto(ChannelId channelId, uint64 logEntryId, MTPint date
MTP_flags(0), MTP_flags(0),
MTP_long(photoId), MTP_long(photoId),
MTP_long(0), MTP_long(0),
date, MTP_int(date),
MTP_vector<MTPPhotoSize>(photoSizes))); MTP_vector<MTPPhotoSize>(photoSizes)));
} }
@ -184,13 +184,21 @@ auto GenerateBannedChangeText(const TextWithEntities &user, const MTPChannelBann
Expects(!prevRights || prevRights->type() == mtpc_channelBannedRights); Expects(!prevRights || prevRights->type() == mtpc_channelBannedRights);
auto newFlags = newRights ? newRights->c_channelBannedRights().vflags.v : MTPDchannelBannedRights::Flags(0); auto newFlags = newRights ? newRights->c_channelBannedRights().vflags.v : MTPDchannelBannedRights::Flags(0);
auto prevFlags = prevRights ? prevRights->c_channelBannedRights().vflags.v : MTPDchannelBannedRights::Flags(0); auto prevFlags = prevRights ? prevRights->c_channelBannedRights().vflags.v : MTPDchannelBannedRights::Flags(0);
auto newUntil = newRights ? newRights->c_channelBannedRights().vuntil_date : MTP_int(0); auto newUntil = newRights ? newRights->c_channelBannedRights().vuntil_date.v : TimeId(0);
auto indefinitely = ChannelData::IsRestrictedForever(newUntil.v); auto indefinitely = ChannelData::IsRestrictedForever(newUntil);
if (newFlags & Flag::f_view_messages) { if (newFlags & Flag::f_view_messages) {
return lng_admin_log_banned__generic(lt_user, user); return lng_admin_log_banned__generic(lt_user, user);
} }
auto untilText = indefinitely ? lang(lng_admin_log_restricted_forever) : lng_admin_log_restricted_until(lt_date, langDateTime(::date(newUntil))); auto untilText = indefinitely
auto result = lng_admin_log_restricted__generic(lt_user, user, lt_until, TextWithEntities { untilText }); ? lang(lng_admin_log_restricted_forever)
: lng_admin_log_restricted_until(
lt_date,
langDateTime(ParseDateTime(newUntil)));
auto result = lng_admin_log_restricted__generic(
lt_user,
user,
lt_until,
TextWithEntities { untilText });
static auto phraseMap = std::map<Flags, LangKey> { static auto phraseMap = std::map<Flags, LangKey> {
{ Flag::f_view_messages, lng_admin_log_banned_view_messages }, { Flag::f_view_messages, lng_admin_log_banned_view_messages },
@ -321,7 +329,7 @@ void GenerateItems(
auto from = App::user(event.vuser_id.v); auto from = App::user(event.vuser_id.v);
auto channel = history->peer->asChannel(); auto channel = history->peer->asChannel();
auto &action = event.vaction; auto &action = event.vaction;
auto date = event.vdate; auto date = event.vdate.v;
auto addPart = [&](not_null<HistoryItem*> item) { auto addPart = [&](not_null<HistoryItem*> item) {
return callback(OwnedItem(delegate, item)); return callback(OwnedItem(delegate, item));
}; };
@ -334,7 +342,7 @@ void GenerateItems(
auto addSimpleServiceMessage = [&](const QString &text, PhotoData *photo = nullptr) { auto addSimpleServiceMessage = [&](const QString &text, PhotoData *photo = nullptr) {
auto message = HistoryService::PreparedText { text }; auto message = HistoryService::PreparedText { text };
message.links.push_back(fromLink); message.links.push_back(fromLink);
addPart(new HistoryService(history, idManager->next(), ::date(date), message, 0, peerToUser(from->id), photo)); addPart(new HistoryService(history, idManager->next(), date, message, 0, peerToUser(from->id), photo));
}; };
auto createChangeTitle = [&](const MTPDchannelAdminLogEventActionChangeTitle &action) { auto createChangeTitle = [&](const MTPDchannelAdminLogEventActionChangeTitle &action) {
@ -355,7 +363,7 @@ void GenerateItems(
auto bodyReplyTo = 0; auto bodyReplyTo = 0;
auto bodyViaBotId = 0; auto bodyViaBotId = 0;
auto newDescription = PrepareText(newValue, QString()); auto newDescription = PrepareText(newValue, QString());
auto body = new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), QString(), newDescription); auto body = new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, date, peerToUser(from->id), QString(), newDescription);
if (!oldValue.isEmpty()) { if (!oldValue.isEmpty()) {
auto oldDescription = PrepareText(oldValue, QString()); auto oldDescription = PrepareText(oldValue, QString());
body->addLogEntryOriginal(id, lang(lng_admin_log_previous_description), oldDescription); body->addLogEntryOriginal(id, lang(lng_admin_log_previous_description), oldDescription);
@ -376,7 +384,7 @@ void GenerateItems(
auto bodyReplyTo = 0; auto bodyReplyTo = 0;
auto bodyViaBotId = 0; auto bodyViaBotId = 0;
auto newLink = newValue.isEmpty() ? TextWithEntities() : PrepareText(Messenger::Instance().createInternalLinkFull(newValue), QString()); auto newLink = newValue.isEmpty() ? TextWithEntities() : PrepareText(Messenger::Instance().createInternalLinkFull(newValue), QString());
auto body = new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), QString(), newLink); auto body = new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, date, peerToUser(from->id), QString(), newLink);
if (!oldValue.isEmpty()) { if (!oldValue.isEmpty()) {
auto oldLink = PrepareText(Messenger::Instance().createInternalLinkFull(oldValue), QString()); auto oldLink = PrepareText(Messenger::Instance().createInternalLinkFull(oldValue), QString());
body->addLogEntryOriginal(id, lang(lng_admin_log_previous_link), oldLink); body->addLogEntryOriginal(id, lang(lng_admin_log_previous_link), oldLink);
@ -428,7 +436,7 @@ void GenerateItems(
PrepareLogMessage( PrepareLogMessage(
action.vmessage, action.vmessage,
idManager->next(), idManager->next(),
date.v), date),
detachExistingItem)); detachExistingItem));
} }
}; };
@ -448,7 +456,7 @@ void GenerateItems(
PrepareLogMessage( PrepareLogMessage(
action.vnew_message, action.vnew_message,
idManager->next(), idManager->next(),
date.v), date),
detachExistingItem); detachExistingItem);
if (oldValue.text.isEmpty()) { if (oldValue.text.isEmpty()) {
oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text)); oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text));
@ -463,7 +471,7 @@ void GenerateItems(
auto detachExistingItem = false; auto detachExistingItem = false;
addPart(history->createItem( addPart(history->createItem(
PrepareLogMessage(action.vmessage, idManager->next(), date.v), PrepareLogMessage(action.vmessage, idManager->next(), date),
detachExistingItem)); detachExistingItem));
}; };
@ -486,7 +494,7 @@ void GenerateItems(
auto bodyReplyTo = 0; auto bodyReplyTo = 0;
auto bodyViaBotId = 0; auto bodyViaBotId = 0;
auto bodyText = GenerateParticipantChangeText(channel, action.vparticipant); auto bodyText = GenerateParticipantChangeText(channel, action.vparticipant);
addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), QString(), bodyText)); addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, date, peerToUser(from->id), QString(), bodyText));
}; };
auto createParticipantToggleBan = [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) { auto createParticipantToggleBan = [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) {
@ -494,7 +502,7 @@ void GenerateItems(
auto bodyReplyTo = 0; auto bodyReplyTo = 0;
auto bodyViaBotId = 0; auto bodyViaBotId = 0;
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant); auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant);
addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), QString(), bodyText)); addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, date, peerToUser(from->id), QString(), bodyText));
}; };
auto createParticipantToggleAdmin = [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) { auto createParticipantToggleAdmin = [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) {
@ -502,7 +510,7 @@ void GenerateItems(
auto bodyReplyTo = 0; auto bodyReplyTo = 0;
auto bodyViaBotId = 0; auto bodyViaBotId = 0;
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant); auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant);
addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), QString(), bodyText)); addPart(new HistoryMessage(history, idManager->next(), bodyFlags, bodyReplyTo, bodyViaBotId, date, peerToUser(from->id), QString(), bodyText));
}; };
auto createChangeStickerSet = [&](const MTPDchannelAdminLogEventActionChangeStickerSet &action) { auto createChangeStickerSet = [&](const MTPDchannelAdminLogEventActionChangeStickerSet &action) {
@ -523,7 +531,7 @@ void GenerateItems(
auto message = HistoryService::PreparedText { text }; auto message = HistoryService::PreparedText { text };
message.links.push_back(fromLink); message.links.push_back(fromLink);
message.links.push_back(setLink); message.links.push_back(setLink);
addPart(new HistoryService(history, idManager->next(), ::date(date), message, 0, peerToUser(from->id))); addPart(new HistoryService(history, idManager->next(), date, message, 0, peerToUser(from->id)));
} }
}; };

View file

@ -322,12 +322,18 @@ void History::takeLocalDraft(History *from) {
void History::createLocalDraftFromCloud() { void History::createLocalDraftFromCloud() {
auto draft = cloudDraft(); auto draft = cloudDraft();
if (Data::draftIsNull(draft) || !draft->date.isValid()) return; if (Data::draftIsNull(draft) || !draft->date) {
return;
}
auto existing = localDraft(); auto existing = localDraft();
if (Data::draftIsNull(existing) || !existing->date.isValid() || draft->date >= existing->date) { if (Data::draftIsNull(existing) || !existing->date || draft->date >= existing->date) {
if (!existing) { if (!existing) {
setLocalDraft(std::make_unique<Data::Draft>(draft->textWithTags, draft->msgId, draft->cursor, draft->previewCancelled)); setLocalDraft(std::make_unique<Data::Draft>(
draft->textWithTags,
draft->msgId,
draft->cursor,
draft->previewCancelled));
existing = localDraft(); existing = localDraft();
} else if (existing != draft) { } else if (existing != draft) {
existing->textWithTags = draft->textWithTags; existing->textWithTags = draft->textWithTags;
@ -351,11 +357,15 @@ Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) {
0, 0,
MessageCursor(), MessageCursor(),
false)); false));
cloudDraft()->date = QDateTime(); cloudDraft()->date = TimeId(0);
} else { } else {
auto existing = cloudDraft(); auto existing = cloudDraft();
if (!existing) { if (!existing) {
setCloudDraft(std::make_unique<Data::Draft>(fromDraft->textWithTags, fromDraft->msgId, fromDraft->cursor, fromDraft->previewCancelled)); setCloudDraft(std::make_unique<Data::Draft>(
fromDraft->textWithTags,
fromDraft->msgId,
fromDraft->cursor,
fromDraft->previewCancelled));
existing = cloudDraft(); existing = cloudDraft();
} else if (existing != fromDraft) { } else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags; existing->textWithTags = fromDraft->textWithTags;
@ -363,7 +373,7 @@ Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) {
existing->cursor = fromDraft->cursor; existing->cursor = fromDraft->cursor;
existing->previewCancelled = fromDraft->previewCancelled; existing->previewCancelled = fromDraft->previewCancelled;
} }
existing->date = ::date(myunixtime()); existing->date = unixtime();
} }
cloudDraftTextCache.clear(); cloudDraftTextCache.clear();
@ -623,99 +633,9 @@ std::vector<not_null<HistoryItem*>> History::createItems(
return result; return result;
} }
not_null<HistoryItem*> History::createItemForwarded(
MsgId id,
MTPDmessage::Flags flags,
QDateTime date,
UserId from,
const QString &postAuthor,
HistoryMessage *original) {
return new HistoryMessage(
this,
id,
flags,
date,
from,
postAuthor,
original);
}
not_null<HistoryItem*> History::createItemDocument(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
QDateTime date,
UserId from,
const QString &postAuthor,
DocumentData *document,
const TextWithEntities &caption,
const MTPReplyMarkup &markup) {
return new HistoryMessage(
this,
id,
flags,
replyTo,
viaBotId,
date,
from,
postAuthor,
document,
caption,
markup);
}
not_null<HistoryItem*> History::createItemPhoto(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
QDateTime date,
UserId from,
const QString &postAuthor,
PhotoData *photo,
const TextWithEntities &caption,
const MTPReplyMarkup &markup) {
return new HistoryMessage(
this,
id,
flags,
replyTo,
viaBotId,
date,
from,
postAuthor,
photo,
caption,
markup);
}
not_null<HistoryItem*> History::createItemGame(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
QDateTime date,
UserId from,
const QString &postAuthor,
GameData *game,
const MTPReplyMarkup &markup) {
return new HistoryMessage(
this,
id,
flags,
replyTo,
viaBotId,
date,
from,
postAuthor,
game,
markup);
}
not_null<HistoryItem*> History::addNewService( not_null<HistoryItem*> History::addNewService(
MsgId msgId, MsgId msgId,
QDateTime date, TimeId date,
const QString &text, const QString &text,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
bool unread) { bool unread) {
@ -781,12 +701,19 @@ HistoryItem *History::addToHistory(const MTPMessage &msg) {
not_null<HistoryItem*> History::addNewForwarded( not_null<HistoryItem*> History::addNewForwarded(
MsgId id, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
HistoryMessage *original) { not_null<HistoryMessage*> original) {
return addNewItem( return addNewItem(
createItemForwarded(id, flags, date, from, postAuthor, original), new HistoryMessage(
this,
id,
flags,
date,
from,
postAuthor,
original),
true); true);
} }
@ -795,18 +722,19 @@ not_null<HistoryItem*> History::addNewDocument(
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
UserId viaBotId, UserId viaBotId,
MsgId replyTo, MsgId replyTo,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
DocumentData *document, not_null<DocumentData*> document,
const TextWithEntities &caption, const TextWithEntities &caption,
const MTPReplyMarkup &markup) { const MTPReplyMarkup &markup) {
return addNewItem( return addNewItem(
createItemDocument( new HistoryMessage(
this,
id, id,
flags, flags,
viaBotId,
replyTo, replyTo,
viaBotId,
date, date,
from, from,
postAuthor, postAuthor,
@ -821,18 +749,19 @@ not_null<HistoryItem*> History::addNewPhoto(
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
UserId viaBotId, UserId viaBotId,
MsgId replyTo, MsgId replyTo,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
PhotoData *photo, not_null<PhotoData*> photo,
const TextWithEntities &caption, const TextWithEntities &caption,
const MTPReplyMarkup &markup) { const MTPReplyMarkup &markup) {
return addNewItem( return addNewItem(
createItemPhoto( new HistoryMessage(
this,
id, id,
flags, flags,
viaBotId,
replyTo, replyTo,
viaBotId,
date, date,
from, from,
postAuthor, postAuthor,
@ -847,17 +776,18 @@ not_null<HistoryItem*> History::addNewGame(
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
UserId viaBotId, UserId viaBotId,
MsgId replyTo, MsgId replyTo,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
GameData *game, not_null<GameData*> game,
const MTPReplyMarkup &markup) { const MTPReplyMarkup &markup) {
return addNewItem( return addNewItem(
createItemGame( new HistoryMessage(
this,
id, id,
flags, flags,
viaBotId,
replyTo, replyTo,
viaBotId,
date, date,
from, from,
postAuthor, postAuthor,
@ -1291,8 +1221,7 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
if (from == item->author()) { if (from == item->author()) {
clearSendAction(from); clearSendAction(from);
} }
auto itemServerTime = toServerTime(item->date.toTime_t()); from->madeAction(item->date());
from->madeAction(itemServerTime.v);
} }
if (item->out()) { if (item->out()) {
destroyUnreadBar(); destroyUnreadBar();
@ -1670,6 +1599,10 @@ int History::unreadCount() const {
return _unreadCount ? *_unreadCount : 0; return _unreadCount ? *_unreadCount : 0;
} }
bool History::unreadCountKnown() const {
return !!_unreadCount;
}
void History::setUnreadCount(int newUnreadCount) { void History::setUnreadCount(int newUnreadCount) {
if (!_unreadCount || *_unreadCount != newUnreadCount) { if (!_unreadCount || *_unreadCount != newUnreadCount) {
const auto unreadCountDelta = _unreadCount | [&](int count) { const auto unreadCountDelta = _unreadCount | [&](int count) {
@ -1805,8 +1738,11 @@ std::shared_ptr<AdminLog::LocalIdManager> History::adminLogIdManager() {
QDateTime History::adjustChatListDate() const { QDateTime History::adjustChatListDate() const {
const auto result = chatsListDate(); const auto result = chatsListDate();
if (const auto draft = cloudDraft()) { if (const auto draft = cloudDraft()) {
if (!Data::draftIsNull(draft) && draft->date > result) { if (!Data::draftIsNull(draft)) {
return draft->date; const auto draftResult = ParseDateTime(draft->date);
if (draftResult > result) {
return draftResult;
}
} }
} }
return result; return result;
@ -2123,7 +2059,7 @@ void History::setLastMessage(HistoryItem *item) {
if (const auto feed = peer->feed()) { if (const auto feed = peer->feed()) {
feed->updateLastMessage(item); feed->updateLastMessage(item);
} }
setChatsListDate(item->date); setChatsListDate(ItemDateTime(item));
} else if (!_lastMessage || *_lastMessage) { } else if (!_lastMessage || *_lastMessage) {
_lastMessage = nullptr; _lastMessage = nullptr;
updateChatListEntry(); updateChatListEntry();
@ -2140,7 +2076,7 @@ bool History::lastMessageKnown() const {
void History::updateChatListExistence() { void History::updateChatListExistence() {
Entry::updateChatListExistence(); Entry::updateChatListExistence();
if (!lastMessageKnown() || !_unreadCount) { if (!lastMessageKnown() || !unreadCountKnown()) {
if (const auto channel = peer->asChannel()) { if (const auto channel = peer->asChannel()) {
if (!channel->feed()) { if (!channel->feed()) {
// After ungrouping from a feed we need to load dialog. // After ungrouping from a feed we need to load dialog.
@ -2197,7 +2133,7 @@ void History::applyDialog(const MTPDdialog &data) {
peerToChannel(channel->id), peerToChannel(channel->id),
data.vtop_message.v); data.vtop_message.v);
if (const auto item = App::histItemById(topMessageId)) { if (const auto item = App::histItemById(topMessageId)) {
if (item->date <= date(channel->date)) { if (item->date() <= channel->date) {
Auth().api().requestSelfParticipant(channel); Auth().api().requestSelfParticipant(channel);
} }
} }
@ -2384,7 +2320,7 @@ HistoryService *History::insertJoinedMessage(bool unread) {
// flags |= MTPDmessage::Flag::f_unread; // flags |= MTPDmessage::Flag::f_unread;
} }
auto inviteDate = peer->asChannel()->inviteDate; const auto inviteDate = peer->asChannel()->inviteDate;
if (isEmpty()) { if (isEmpty()) {
_joinedMessage = GenerateJoinedMessage( _joinedMessage = GenerateJoinedMessage(
this, this,
@ -2409,7 +2345,7 @@ HistoryService *History::insertJoinedMessage(bool unread) {
peer->asChannel()->mgInfo->joinedMessageFound = true; peer->asChannel()->mgInfo->joinedMessageFound = true;
return nullptr; return nullptr;
} }
if (item->date <= inviteDate) { if (item->date() <= inviteDate) {
++itemIndex; ++itemIndex;
_joinedMessage = GenerateJoinedMessage( _joinedMessage = GenerateJoinedMessage(
this, this,
@ -2418,7 +2354,7 @@ HistoryService *History::insertJoinedMessage(bool unread) {
flags); flags);
addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex); addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex);
const auto lastDate = chatsListDate(); const auto lastDate = chatsListDate();
if (lastDate.isNull() || inviteDate >= lastDate) { if (lastDate.isNull() || ParseDateTime(inviteDate) >= lastDate) {
setLastMessage(_joinedMessage); setLastMessage(_joinedMessage);
if (unread) { if (unread) {
newItemAdded(_joinedMessage); newItemAdded(_joinedMessage);
@ -2456,14 +2392,15 @@ void History::checkJoinedMessage(bool createUnread) {
} }
} }
QDateTime inviteDate = peer->asChannel()->inviteDate; const auto inviteDate = peer->asChannel()->inviteDate;
QDateTime firstDate, lastDate; auto firstDate = TimeId(0);
auto lastDate = TimeId(0);
if (!blocks.empty()) { if (!blocks.empty()) {
firstDate = blocks.front()->messages.front()->data()->date; firstDate = blocks.front()->messages.front()->data()->date();
lastDate = blocks.back()->messages.back()->data()->date; lastDate = blocks.back()->messages.back()->data()->date();
} }
if (!firstDate.isNull() if (firstDate
&& !lastDate.isNull() && lastDate
&& (firstDate <= inviteDate || loadedAtTop()) && (firstDate <= inviteDate || loadedAtTop())
&& (lastDate > inviteDate || loadedAtBottom())) { && (lastDate > inviteDate || loadedAtBottom())) {
const auto willBeLastMsg = (inviteDate >= lastDate); const auto willBeLastMsg = (inviteDate >= lastDate);
@ -2596,7 +2533,7 @@ void History::clearUpTill(MsgId availableMinId) {
MTP_int(fromId), MTP_int(fromId),
peerToMTP(peer->id), peerToMTP(peer->id),
MTP_int(replyToId), MTP_int(replyToId),
toServerTime(item->date.toTime_t()), MTP_int(item->date()),
MTP_messageActionHistoryClear() MTP_messageActionHistoryClear()
).c_messageService()); ).c_messageService());
} }

View file

@ -148,15 +148,56 @@ public:
void unloadBlocks(); void unloadBlocks();
void clearUpTill(MsgId availableMinId); void clearUpTill(MsgId availableMinId);
void applyGroupAdminChanges(const base::flat_map<UserId, bool> &changes); void applyGroupAdminChanges(
const base::flat_map<UserId, bool> &changes);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type); HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPMessage &msg); HistoryItem *addToHistory(const MTPMessage &msg);
not_null<HistoryItem*> addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags = 0, bool newMsg = true); not_null<HistoryItem*> addNewService(
not_null<HistoryItem*> addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, UserId from, const QString &postAuthor, HistoryMessage *item); MsgId msgId,
not_null<HistoryItem*> addNewDocument(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, DocumentData *doc, const TextWithEntities &caption, const MTPReplyMarkup &markup); TimeId date,
not_null<HistoryItem*> addNewPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const TextWithEntities &caption, const MTPReplyMarkup &markup); const QString &text,
not_null<HistoryItem*> addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); MTPDmessage::Flags flags = 0,
bool newMsg = true);
not_null<HistoryItem*> addNewForwarded(
MsgId id,
MTPDmessage::Flags flags,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<HistoryMessage*> original);
not_null<HistoryItem*> addNewDocument(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<DocumentData*> document,
const TextWithEntities &caption,
const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewPhoto(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<PhotoData*> photo,
const TextWithEntities &caption,
const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewGame(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
MsgId replyTo,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<GameData*> game,
const MTPReplyMarkup &markup);
// Used only internally and for channel admin log. // Used only internally and for channel admin log.
HistoryItem *createItem( HistoryItem *createItem(
@ -180,6 +221,7 @@ public:
MsgId loadAroundId() const; MsgId loadAroundId() const;
int unreadCount() const; int unreadCount() const;
bool unreadCountKnown() const;
void setUnreadCount(int newUnreadCount); void setUnreadCount(int newUnreadCount);
bool mute() const; bool mute() const;
bool changeMute(bool newMute); bool changeMute(bool newMute);
@ -376,11 +418,6 @@ private:
void clearBlocks(bool leaveItems); void clearBlocks(bool leaveItems);
not_null<HistoryItem*> createItemForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, UserId from, const QString &postAuthor, HistoryMessage *msg);
not_null<HistoryItem*> createItemDocument(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, DocumentData *doc, const TextWithEntities &caption, const MTPReplyMarkup &markup);
not_null<HistoryItem*> createItemPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const TextWithEntities &caption, const MTPReplyMarkup &markup);
not_null<HistoryItem*> createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewItem( not_null<HistoryItem*> addNewItem(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
bool unread); bool unread);

View file

@ -731,7 +731,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} else { } else {
HistoryView::ServiceMessagePainter::paintDate( HistoryView::ServiceMessagePainter::paintDate(
p, p,
view->data()->date, view->dateTime(),
dateY, dateY,
_contentWidth); _contentWidth);
} }
@ -1430,7 +1430,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_widget->replyToMessage(itemId); _widget->replyToMessage(itemId);
}); });
} }
if (item->allowsEdit(::date(unixtime()))) { if (item->allowsEdit(unixtime())) {
_menu->addAction(lang(lng_context_edit_msg), [=] { _menu->addAction(lang(lng_context_edit_msg), [=] {
_widget->editMessage(itemId); _widget->editMessage(itemId);
}); });
@ -1693,11 +1693,17 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
if (!photo->date || !photo->loaded()) return; if (!photo->date || !photo->loaded()) return;
auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter();
FileDialog::GetWritePath(lang(lng_save_photo), filter, filedialogDefaultName(qsl("photo"), qsl(".jpg")), base::lambda_guarded(this, [this, photo](const QString &result) { FileDialog::GetWritePath(
if (!result.isEmpty()) { lang(lng_save_photo),
photo->full->pix().toImage().save(result, "JPG"); filter,
} filedialogDefaultName(
})); qsl("photo"),
qsl(".jpg")),
base::lambda_guarded(this, [this, photo](const QString &result) {
if (!result.isEmpty()) {
photo->full->pix().toImage().save(result, "JPG");
}
}));
} }
void HistoryInner::copyContextImage(not_null<PhotoData*> photo) { void HistoryInner::copyContextImage(not_null<PhotoData*> photo) {
@ -1797,7 +1803,7 @@ TextWithEntities HistoryInner::getSelectedText() const {
const auto wrapItem = [&]( const auto wrapItem = [&](
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
TextWithEntities &&unwrapped) { TextWithEntities &&unwrapped) {
auto time = item->date.toString(timeFormat); auto time = ItemDateTime(item).toString(timeFormat);
auto part = TextWithEntities(); auto part = TextWithEntities();
auto size = item->author()->name.size() auto size = item->author()->name.size()
+ time.size() + time.size()
@ -1886,7 +1892,7 @@ void HistoryInner::recountHistoryGeometry() {
_historySkipHeight = 0; _historySkipHeight = 0;
if (_migrated) { if (_migrated) {
if (!_migrated->isEmpty() && !_history->isEmpty() && _migrated->loadedAtBottom() && _history->loadedAtTop()) { if (!_migrated->isEmpty() && !_history->isEmpty() && _migrated->loadedAtBottom() && _history->loadedAtTop()) {
if (_migrated->blocks.back()->messages.back()->data()->date.date() == _history->blocks.front()->messages.front()->data()->date.date()) { if (_migrated->blocks.back()->messages.back()->dateTime().date() == _history->blocks.front()->messages.front()->dateTime().date()) {
if (_migrated->blocks.back()->messages.back()->data()->isGroupMigrate() && _history->blocks.front()->messages.front()->data()->isGroupMigrate()) { if (_migrated->blocks.back()->messages.back()->data()->isGroupMigrate() && _history->blocks.front()->messages.front()->data()->isGroupMigrate()) {
_historySkipHeight += _history->blocks.front()->messages.front()->height(); _historySkipHeight += _history->blocks.front()->messages.front()->height();
} else { } else {
@ -2379,7 +2385,7 @@ void HistoryInner::mouseActionUpdate() {
if (const auto date = view->Get<HistoryView::DateBadge>()) { if (const auto date = view->Get<HistoryView::DateBadge>()) {
dateWidth = date->width; dateWidth = date->width;
} else { } else {
dateWidth = st::msgServiceFont->width(langDayOfMonthFull(item->date.date())); dateWidth = st::msgServiceFont->width(langDayOfMonthFull(view->dateTime().date()));
} }
dateWidth += st::msgServicePadding.left() + st::msgServicePadding.right(); dateWidth += st::msgServicePadding.left() + st::msgServicePadding.right();
auto dateLeft = st::msgServiceMargin.left(); auto dateLeft = st::msgServiceMargin.left();
@ -2393,9 +2399,9 @@ void HistoryInner::mouseActionUpdate() {
if (point.x() >= dateLeft && point.x() < dateLeft + dateWidth) { if (point.x() >= dateLeft && point.x() < dateLeft + dateWidth) {
if (!_scrollDateLink) { if (!_scrollDateLink) {
_scrollDateLink = std::make_shared<DateClickHandler>(item->history()->peer, item->date.date()); _scrollDateLink = std::make_shared<DateClickHandler>(item->history()->peer, view->dateTime().date());
} else { } else {
static_cast<DateClickHandler*>(_scrollDateLink.get())->setDate(item->date.date()); static_cast<DateClickHandler*>(_scrollDateLink.get())->setDate(view->dateTime().date());
} }
dragState = TextState( dragState = TextState(
nullptr, nullptr,
@ -2924,14 +2930,21 @@ QString HistoryInner::tooltipText() const {
if (_mouseCursorState == CursorState::Date if (_mouseCursorState == CursorState::Date
&& _mouseAction == MouseAction::None) { && _mouseAction == MouseAction::None) {
if (const auto view = App::hoveredItem()) { if (const auto view = App::hoveredItem()) {
auto dateText = view->data()->date.toString( auto dateText = view->dateTime().toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat)); QLocale::system().dateTimeFormat(QLocale::LongFormat));
auto editedDate = view->displayedEditDate(); if (const auto editedDate = view->displayedEditDate()) {
if (!editedDate.isNull()) { dateText += '\n' + lng_edited_date(
dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); lt_date,
ParseDateTime(editedDate).toString(
QLocale::system().dateTimeFormat(
QLocale::LongFormat)));
} }
if (const auto forwarded = view->data()->Get<HistoryMessageForwarded>()) { if (const auto forwarded = view->data()->Get<HistoryMessageForwarded>()) {
dateText += '\n' + lng_forwarded_date(lt_date, forwarded->originalDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); dateText += '\n' + lng_forwarded_date(
lt_date,
ParseDateTime(forwarded->originalDate).toString(
QLocale::system().dateTimeFormat(
QLocale::LongFormat)));
} }
return dateText; return dateText;
} }

View file

@ -47,7 +47,7 @@ not_null<HistoryItem*> CreateUnsupportedMessage(
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from) { UserId from) {
const auto siteLink = qsl("https://desktop.telegram.org"); const auto siteLink = qsl("https://desktop.telegram.org");
auto text = TextWithEntities{ auto text = TextWithEntities{
@ -75,16 +75,20 @@ HistoryItem::HistoryItem(
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
QDateTime date, TimeId date,
UserId from) UserId from)
: id(id) : id(id)
, date(date) , _date(date)
, _history(history) , _history(history)
, _from(from ? App::user(from) : history->peer) , _from(from ? App::user(from) : history->peer)
, _flags(flags) { , _flags(flags) {
App::historyRegItem(this); App::historyRegItem(this);
} }
TimeId HistoryItem::date() const {
return _date;
}
void HistoryItem::finishEdition(int oldKeyboardTop) { void HistoryItem::finishEdition(int oldKeyboardTop) {
Auth().data().requestItemViewRefresh(this); Auth().data().requestItemViewRefresh(this);
invalidateChatsListEntry(); invalidateChatsListEntry();
@ -153,8 +157,7 @@ void HistoryItem::finishEditionToEmpty() {
bool HistoryItem::isMediaUnread() const { bool HistoryItem::isMediaUnread() const {
if (!mentionsMe() && _history->peer->isChannel()) { if (!mentionsMe() && _history->peer->isChannel()) {
auto now = ::date(unixtime()); auto passed = unixtime() - date();
auto passed = date.secsTo(now);
if (passed >= Global::ChannelsReadMediaPeriod()) { if (passed >= Global::ChannelsReadMediaPeriod()) {
return false; return false;
} }
@ -332,7 +335,7 @@ bool HistoryItem::allowsForward() const {
return false; return false;
} }
bool HistoryItem::allowsEdit(const QDateTime &now) const { bool HistoryItem::allowsEdit(TimeId now) const {
return false; return false;
} }
@ -357,9 +360,11 @@ bool HistoryItem::canDelete() const {
return false; return false;
} }
bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { bool HistoryItem::canDeleteForEveryone(TimeId now) const {
auto messageToMyself = _history->peer->isSelf(); auto messageToMyself = _history->peer->isSelf();
auto messageTooOld = messageToMyself ? false : (date.secsTo(cur) >= Global::EditTimeLimit()); auto messageTooOld = messageToMyself
? false
: (now >= date() + Global::EditTimeLimit());
if (id < 0 || messageToMyself || messageTooOld || isPost()) { if (id < 0 || messageToMyself || messageTooOld || isPost()) {
return false; return false;
} }
@ -443,7 +448,7 @@ ChannelId HistoryItem::channelId() const {
} }
Data::MessagePosition HistoryItem::position() const { Data::MessagePosition HistoryItem::position() const {
return Data::MessagePosition(toServerTime(date.toTime_t()).v, fullId()); return Data::MessagePosition(date(), fullId());
} }
MsgId HistoryItem::replyToId() const { MsgId HistoryItem::replyToId() const {
@ -457,11 +462,11 @@ not_null<PeerData*> HistoryItem::author() const {
return isPost() ? history()->peer : from(); return isPost() ? history()->peer : from();
} }
QDateTime HistoryItem::dateOriginal() const { TimeId HistoryItem::dateOriginal() const {
if (const auto forwarded = Get<HistoryMessageForwarded>()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) {
return forwarded->originalDate; return forwarded->originalDate;
} }
return date; return date();
} }
not_null<PeerData*> HistoryItem::senderOriginal() const { not_null<PeerData*> HistoryItem::senderOriginal() const {
@ -621,6 +626,10 @@ HistoryItem::~HistoryItem() {
} }
} }
QDateTime ItemDateTime(not_null<const HistoryItem*> item) {
return ParseDateTime(item->date());
}
ClickHandlerPtr goToMessageClickHandler( ClickHandlerPtr goToMessageClickHandler(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
FullMsgId returnToId) { FullMsgId returnToId) {
@ -658,7 +667,7 @@ not_null<HistoryItem*> HistoryItem::Create(
const auto text = HistoryService::PreparedText { const auto text = HistoryService::PreparedText {
lang(lng_message_empty) lang(lng_message_empty)
}; };
return new HistoryService(history, data.vid.v, ::date(), text); return new HistoryService(history, data.vid.v, TimeId(0), text);
} break; } break;
case mtpc_message: { case mtpc_message: {
@ -750,7 +759,7 @@ not_null<HistoryItem*> HistoryItem::Create(
data.vflags.v, data.vflags.v,
data.vreply_to_msg_id.v, data.vreply_to_msg_id.v,
data.vvia_bot_id.v, data.vvia_bot_id.v,
::date(data.vdate), data.vdate.v,
data.vfrom_id.v); data.vfrom_id.v);
} else if (badMedia == MediaCheckResult::Empty) { } else if (badMedia == MediaCheckResult::Empty) {
const auto text = HistoryService::PreparedText { const auto text = HistoryService::PreparedText {
@ -759,7 +768,7 @@ not_null<HistoryItem*> HistoryItem::Create(
return new HistoryService( return new HistoryService(
history, history,
data.vid.v, data.vid.v,
::date(data.vdate), data.vdate.v,
text, text,
data.vflags.v, data.vflags.v,
data.has_from_id() ? data.vfrom_id.v : UserId(0)); data.has_from_id() ? data.vfrom_id.v : UserId(0));

View file

@ -207,9 +207,9 @@ public:
bool isPinned() const; bool isPinned() const;
bool canPin() const; bool canPin() const;
virtual bool allowsForward() const; virtual bool allowsForward() const;
virtual bool allowsEdit(const QDateTime &now) const; virtual bool allowsEdit(TimeId now) const;
bool canDelete() const; bool canDelete() const;
bool canDeleteForEveryone(const QDateTime &cur) const; bool canDeleteForEveryone(TimeId now) const;
bool suggestBanReport() const; bool suggestBanReport() const;
bool suggestDeleteAllReport() const; bool suggestDeleteAllReport() const;
@ -217,13 +217,13 @@ public:
QString directLink() const; QString directLink() const;
MsgId id; MsgId id;
QDateTime date;
ChannelId channelId() const; ChannelId channelId() const;
FullMsgId fullId() const { FullMsgId fullId() const {
return FullMsgId(channelId(), id); return FullMsgId(channelId(), id);
} }
Data::MessagePosition position() const; Data::MessagePosition position() const;
TimeId date() const;
Data::Media *media() const { Data::Media *media() const {
return _media.get(); return _media.get();
@ -244,7 +244,7 @@ public:
not_null<PeerData*> author() const; not_null<PeerData*> author() const;
QDateTime dateOriginal() const; TimeId dateOriginal() const;
not_null<PeerData*> senderOriginal() const; not_null<PeerData*> senderOriginal() const;
not_null<PeerData*> fromOriginal() const; not_null<PeerData*> fromOriginal() const;
QString authorOriginal() const; QString authorOriginal() const;
@ -264,7 +264,7 @@ protected:
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
QDateTime date, TimeId date,
UserId from); UserId from);
virtual void markMediaAsReadHook() { virtual void markMediaAsReadHook() {
@ -296,6 +296,8 @@ protected:
std::unique_ptr<Data::Media> _media; std::unique_ptr<Data::Media> _media;
private: private:
TimeId _date = 0;
HistoryView::Element *_mainView = nullptr; HistoryView::Element *_mainView = nullptr;
friend class HistoryView::Element; friend class HistoryView::Element;
@ -303,6 +305,8 @@ private:
}; };
QDateTime ItemDateTime(not_null<const HistoryItem*> item);
ClickHandlerPtr goToMessageClickHandler( ClickHandlerPtr goToMessageClickHandler(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId msgId, MsgId msgId,

View file

@ -45,14 +45,14 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, Hist
void refresh(const QString &date, bool displayed); void refresh(const QString &date, bool displayed);
int maxWidth() const; int maxWidth() const;
QDateTime date; TimeId date = 0;
Text text; Text text;
}; };
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded, HistoryItem> { struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded, HistoryItem> {
void create(const HistoryMessageVia *via) const; void create(const HistoryMessageVia *via) const;
QDateTime originalDate; TimeId originalDate = 0;
PeerData *originalSender = nullptr; PeerData *originalSender = nullptr;
QString originalAuthor; QString originalAuthor;
MsgId originalId = 0; MsgId originalId = 0;

View file

@ -3144,7 +3144,7 @@ HistoryCall::HistoryCall(
const auto item = parent->data(); const auto item = parent->data();
_text = Data::MediaCall::Text(item, _reason); _text = Data::MediaCall::Text(item, _reason);
_status = item->date.time().toString(cTimeFormat()); _status = parent->dateTime().time().toString(cTimeFormat());
if (_duration) { if (_duration) {
if (_reason != FinishReason::Missed if (_reason != FinishReason::Missed
&& _reason != FinishReason::Busy) { && _reason != FinishReason::Busy) {

View file

@ -271,8 +271,8 @@ struct HistoryMessage::CreateConfig {
PeerId savedFromPeer = 0; PeerId savedFromPeer = 0;
MsgId savedFromMsgId = 0; MsgId savedFromMsgId = 0;
QString authorOriginal; QString authorOriginal;
QDateTime originalDate; TimeId originalDate = 0;
QDateTime editDate; TimeId editDate = 0;
// For messages created from MTP structs. // For messages created from MTP structs.
const MTPReplyMarkup *mtpMarkup = nullptr; const MTPReplyMarkup *mtpMarkup = nullptr;
@ -283,13 +283,18 @@ struct HistoryMessage::CreateConfig {
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
const MTPDmessage &msg) const MTPDmessage &data)
: HistoryItem(history, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) { : HistoryItem(
history,
data.vid.v,
data.vflags.v,
data.vdate.v,
data.has_from_id() ? data.vfrom_id.v : UserId(0)) {
CreateConfig config; CreateConfig config;
if (msg.has_fwd_from() && msg.vfwd_from.type() == mtpc_messageFwdHeader) { if (data.has_fwd_from() && data.vfwd_from.type() == mtpc_messageFwdHeader) {
auto &f = msg.vfwd_from.c_messageFwdHeader(); auto &f = data.vfwd_from.c_messageFwdHeader();
config.originalDate = ::date(f.vdate); config.originalDate = f.vdate.v;
if (f.has_from_id() || f.has_channel_id()) { if (f.has_from_id() || f.has_channel_id()) {
config.senderOriginal = f.has_channel_id() config.senderOriginal = f.has_channel_id()
? peerFromChannel(f.vchannel_id) ? peerFromChannel(f.vchannel_id)
@ -302,45 +307,50 @@ HistoryMessage::HistoryMessage(
} }
} }
} }
if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v; if (data.has_reply_to_msg_id()) config.replyTo = data.vreply_to_msg_id.v;
if (msg.has_via_bot_id()) config.viaBotId = msg.vvia_bot_id.v; if (data.has_via_bot_id()) config.viaBotId = data.vvia_bot_id.v;
if (msg.has_views()) config.viewsCount = msg.vviews.v; if (data.has_views()) config.viewsCount = data.vviews.v;
if (msg.has_reply_markup()) config.mtpMarkup = &msg.vreply_markup; if (data.has_reply_markup()) config.mtpMarkup = &data.vreply_markup;
if (msg.has_edit_date()) config.editDate = ::date(msg.vedit_date); if (data.has_edit_date()) config.editDate = data.vedit_date.v;
if (msg.has_post_author()) config.author = qs(msg.vpost_author); if (data.has_post_author()) config.author = qs(data.vpost_author);
createComponents(config); createComponents(config);
if (msg.has_media()) { if (data.has_media()) {
setMedia(msg.vmedia); setMedia(data.vmedia);
} }
auto text = TextUtilities::Clean(qs(msg.vmessage)); auto text = TextUtilities::Clean(qs(data.vmessage));
auto entities = msg.has_entities() auto entities = data.has_entities()
? TextUtilities::EntitiesFromMTP(msg.ventities.v) ? TextUtilities::EntitiesFromMTP(data.ventities.v)
: EntitiesInText(); : EntitiesInText();
setText({ text, entities }); setText({ text, entities });
if (msg.has_grouped_id()) { if (data.has_grouped_id()) {
setGroupId(MessageGroupId::FromRaw(msg.vgrouped_id.v)); setGroupId(MessageGroupId::FromRaw(data.vgrouped_id.v));
} }
} }
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
const MTPDmessageService &msg) const MTPDmessageService &data)
: HistoryItem(history, msg.vid.v, mtpCastFlags(msg.vflags.v), ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) { : HistoryItem(
history,
data.vid.v,
mtpCastFlags(data.vflags.v),
data.vdate.v,
data.has_from_id() ? data.vfrom_id.v : UserId(0)) {
CreateConfig config; CreateConfig config;
if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v; if (data.has_reply_to_msg_id()) config.replyTo = data.vreply_to_msg_id.v;
createComponents(config); createComponents(config);
switch (msg.vaction.type()) { switch (data.vaction.type()) {
case mtpc_messageActionPhoneCall: { case mtpc_messageActionPhoneCall: {
_media = std::make_unique<Data::MediaCall>( _media = std::make_unique<Data::MediaCall>(
this, this,
msg.vaction.c_messageActionPhoneCall()); data.vaction.c_messageActionPhoneCall());
} break; } break;
default: Unexpected("Service message action type in HistoryMessage."); default: Unexpected("Service message action type in HistoryMessage.");
@ -353,21 +363,26 @@ HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<HistoryMessage*> fwd) not_null<HistoryMessage*> original)
: HistoryItem(history, id, NewForwardedFlags(history->peer, from, fwd) | flags, date, from) { : HistoryItem(
history,
id,
NewForwardedFlags(history->peer, from, original) | flags,
date,
from) {
CreateConfig config; CreateConfig config;
if (fwd->Has<HistoryMessageForwarded>() || !fwd->history()->peer->isSelf()) { if (original->Has<HistoryMessageForwarded>() || !original->history()->peer->isSelf()) {
// Server doesn't add "fwd_from" to non-forwarded messages from chat with yourself. // Server doesn't add "fwd_from" to non-forwarded messages from chat with yourself.
config.originalDate = fwd->dateOriginal(); config.originalDate = original->dateOriginal();
auto senderOriginal = fwd->senderOriginal(); auto senderOriginal = original->senderOriginal();
config.senderOriginal = senderOriginal->id; config.senderOriginal = senderOriginal->id;
config.authorOriginal = fwd->authorOriginal(); config.authorOriginal = original->authorOriginal();
if (senderOriginal->isChannel()) { if (senderOriginal->isChannel()) {
config.originalId = fwd->idOriginal(); config.originalId = original->idOriginal();
} }
} }
if (history->peer->isSelf()) { if (history->peer->isSelf()) {
@ -379,16 +394,16 @@ HistoryMessage::HistoryMessage(
// config.savedFromPeer = config.senderOriginal; // config.savedFromPeer = config.senderOriginal;
// config.savedFromMsgId = config.originalId; // config.savedFromMsgId = config.originalId;
//} else { //} else {
config.savedFromPeer = fwd->history()->peer->id; config.savedFromPeer = original->history()->peer->id;
config.savedFromMsgId = fwd->id; config.savedFromMsgId = original->id;
//} //}
} }
if (flags & MTPDmessage::Flag::f_post_author) { if (flags & MTPDmessage::Flag::f_post_author) {
config.author = postAuthor; config.author = postAuthor;
} }
auto fwdViaBot = fwd->viaBot(); auto fwdViaBot = original->viaBot();
if (fwdViaBot) config.viaBotId = peerToUser(fwdViaBot->id); if (fwdViaBot) config.viaBotId = peerToUser(fwdViaBot->id);
int fwdViewsCount = fwd->viewsCount(); int fwdViewsCount = original->viewsCount();
if (fwdViewsCount > 0) { if (fwdViewsCount > 0) {
config.viewsCount = fwdViewsCount; config.viewsCount = fwdViewsCount;
} else if (isPost()) { } else if (isPost()) {
@ -396,9 +411,9 @@ HistoryMessage::HistoryMessage(
} }
// Copy inline keyboard when forwarding messages with a game. // Copy inline keyboard when forwarding messages with a game.
auto mediaOriginal = fwd->media(); auto mediaOriginal = original->media();
if (mediaOriginal && mediaOriginal->game()) { if (mediaOriginal && mediaOriginal->game()) {
config.inlineMarkup = fwd->inlineReplyMarkup(); config.inlineMarkup = original->inlineReplyMarkup();
} }
createComponents(config); createComponents(config);
@ -416,7 +431,7 @@ HistoryMessage::HistoryMessage(
if (mediaOriginal && !ignoreMedia()) { if (mediaOriginal && !ignoreMedia()) {
_media = mediaOriginal->clone(this); _media = mediaOriginal->clone(this);
} }
setText(fwd->originalText()); setText(original->originalText());
} }
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
@ -425,7 +440,7 @@ HistoryMessage::HistoryMessage(
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
const TextWithEntities &textWithEntities) const TextWithEntities &textWithEntities)
@ -437,17 +452,17 @@ HistoryMessage::HistoryMessage(
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const TextWithEntities &caption, const TextWithEntities &caption,
const MTPReplyMarkup &markup) const MTPReplyMarkup &markup)
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { : HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
_media = std::make_unique<Data::MediaFile>(this, document); _media = std::make_unique<Data::MediaFile>(this, document);
@ -456,17 +471,17 @@ HistoryMessage::HistoryMessage(
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
const TextWithEntities &caption, const TextWithEntities &caption,
const MTPReplyMarkup &markup) const MTPReplyMarkup &markup)
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { : HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
_media = std::make_unique<Data::MediaPhoto>(this, photo); _media = std::make_unique<Data::MediaPhoto>(this, photo);
@ -475,16 +490,16 @@ HistoryMessage::HistoryMessage(
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<GameData*> game, not_null<GameData*> game,
const MTPReplyMarkup &markup) const MTPReplyMarkup &markup)
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { : HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
_media = std::make_unique<Data::MediaGame>(this, game); _media = std::make_unique<Data::MediaGame>(this, game);
@ -564,7 +579,7 @@ bool HistoryMessage::allowsForward() const {
return !_media || _media->allowsForward(); return !_media || _media->allowsForward();
} }
bool HistoryMessage::allowsEdit(const QDateTime &now) const { bool HistoryMessage::allowsEdit(TimeId now) const {
const auto peer = _history->peer; const auto peer = _history->peer;
const auto messageToMyself = peer->isSelf(); const auto messageToMyself = peer->isSelf();
const auto canPinInMegagroup = [&] { const auto canPinInMegagroup = [&] {
@ -575,7 +590,7 @@ bool HistoryMessage::allowsEdit(const QDateTime &now) const {
}(); }();
const auto messageTooOld = (messageToMyself || canPinInMegagroup) const auto messageTooOld = (messageToMyself || canPinInMegagroup)
? false ? false
: (date.secsTo(now) >= Global::EditTimeLimit()); : (now >= date() + Global::EditTimeLimit());
if (id < 0 || messageTooOld) { if (id < 0 || messageTooOld) {
return false; return false;
} }
@ -626,7 +641,7 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
} }
return (config.inlineMarkup != nullptr); return (config.inlineMarkup != nullptr);
}; };
if (!config.editDate.isNull()) { if (config.editDate != TimeId(0)) {
mask |= HistoryMessageEdited::Bit(); mask |= HistoryMessageEdited::Bit();
} }
if (config.senderOriginal) { if (config.senderOriginal) {
@ -866,7 +881,7 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
AddComponents(HistoryMessageEdited::Bit()); AddComponents(HistoryMessageEdited::Bit());
} }
auto edited = Get<HistoryMessageEdited>(); auto edited = Get<HistoryMessageEdited>();
edited->date = ::date(message.vedit_date); edited->date = message.vedit_date.v;
} }
TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() }; TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };

View file

@ -29,35 +29,35 @@ class HistoryMessage
public: public:
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
const MTPDmessage &msg); const MTPDmessage &data);
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
const MTPDmessageService &msg); const MTPDmessageService &data);
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<HistoryMessage*> fwd); // local forwarded not_null<HistoryMessage*> original); // local forwarded
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
const TextWithEntities &textWithEntities); // local message const TextWithEntities &textWithEntities); // local message
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<DocumentData*> document, not_null<DocumentData*> document,
@ -65,11 +65,11 @@ public:
const MTPReplyMarkup &markup); // local document const MTPReplyMarkup &markup); // local document
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
@ -77,11 +77,11 @@ public:
const MTPReplyMarkup &markup); // local photo const MTPReplyMarkup &markup); // local photo
HistoryMessage( HistoryMessage(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
MsgId replyTo, MsgId replyTo,
UserId viaBotId, UserId viaBotId,
QDateTime date, TimeId date,
UserId from, UserId from,
const QString &postAuthor, const QString &postAuthor,
not_null<GameData*> game, not_null<GameData*> game,
@ -95,7 +95,7 @@ public:
const MTPMessageMedia &media); const MTPMessageMedia &media);
bool allowsForward() const override; bool allowsForward() const override;
bool allowsEdit(const QDateTime &now) const override; bool allowsEdit(TimeId now) const override;
bool uploading() const; bool uploading() const;
void applyGroupAdminChanges( void applyGroupAdminChanges(

View file

@ -400,32 +400,39 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
return result; return result;
} }
HistoryService::HistoryService(not_null<History*> history, const MTPDmessage &message) :
HistoryItem(history, message.vid.v, message.vflags.v, ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
createFromMtp(message);
}
HistoryService::HistoryService( HistoryService::HistoryService(
not_null<History*> history, not_null<History*> history,
const MTPDmessageService &message) const MTPDmessage &data)
: HistoryItem( : HistoryItem(
history, history,
message.vid.v, data.vid.v,
mtpCastFlags(message.vflags.v), data.vflags.v,
::date(message.vdate), data.vdate.v,
message.has_from_id() ? message.vfrom_id.v : 0) { data.has_from_id() ? data.vfrom_id.v : UserId(0)) {
createFromMtp(message); createFromMtp(data);
} }
HistoryService::HistoryService( HistoryService::HistoryService(
not_null<History*> history, not_null<History*> history,
MsgId msgId, const MTPDmessageService &data)
QDateTime date, : HistoryItem(
history,
data.vid.v,
mtpCastFlags(data.vflags.v),
data.vdate.v,
data.has_from_id() ? data.vfrom_id.v : UserId(0)) {
createFromMtp(data);
}
HistoryService::HistoryService(
not_null<History*> history,
MsgId id,
TimeId date,
const PreparedText &message, const PreparedText &message,
MTPDmessage::Flags flags, MTPDmessage::Flags flags,
UserId from, UserId from,
PhotoData *photo) PhotoData *photo)
: HistoryItem(history, msgId, flags, date, from) { : HistoryItem(history, id, flags, date, from) {
setServiceText(message); setServiceText(message);
if (photo) { if (photo) {
_media = std::make_unique<Data::MediaPhoto>( _media = std::make_unique<Data::MediaPhoto>(
@ -665,7 +672,7 @@ HistoryService::PreparedText GenerateJoinedText(
HistoryService *GenerateJoinedMessage( HistoryService *GenerateJoinedMessage(
not_null<History*> history, not_null<History*> history,
const QDateTime &inviteDate, TimeId inviteDate,
not_null<UserData*> inviter, not_null<UserData*> inviter,
MTPDmessage::Flags flags) { MTPDmessage::Flags flags) {
return new HistoryService( return new HistoryService(

View file

@ -58,14 +58,14 @@ public:
QList<ClickHandlerPtr> links; QList<ClickHandlerPtr> links;
}; };
HistoryService(not_null<History*> history, const MTPDmessage &message); HistoryService(not_null<History*> history, const MTPDmessage &data);
HistoryService( HistoryService(
not_null<History*> history, not_null<History*> history,
const MTPDmessageService &message); const MTPDmessageService &data);
HistoryService( HistoryService(
not_null<History*> history, not_null<History*> history,
MsgId msgId, MsgId id,
QDateTime date, TimeId date,
const PreparedText &message, const PreparedText &message,
MTPDmessage::Flags flags = 0, MTPDmessage::Flags flags = 0,
UserId from = 0, UserId from = 0,
@ -153,6 +153,6 @@ private:
HistoryService *GenerateJoinedMessage( HistoryService *GenerateJoinedMessage(
not_null<History*> history, not_null<History*> history,
const QDateTime &inviteDate, TimeId inviteDate,
not_null<UserData*> inviter, not_null<UserData*> inviter,
MTPDmessage::Flags flags); MTPDmessage::Flags flags);

View file

@ -5137,7 +5137,9 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
} }
} else if (e->key() == Qt::Key_Up) { } else if (e->key() == Qt::Key_Up) {
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
if (_history && _history->lastSentMsg && _history->lastSentMsg->allowsEdit(::date(unixtime()))) { if (_history
&& _history->lastSentMsg
&& _history->lastSentMsg->allowsEdit(unixtime())) {
if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) { if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) {
editMessage(_history->lastSentMsg); editMessage(_history->lastSentMsg);
return; return;
@ -5490,7 +5492,7 @@ bool HistoryWidget::sendExistingDocument(
flags, flags,
0, 0,
options.replyTo, options.replyTo,
date(MTP_int(unixtime())), unixtime(),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
doc, doc,
@ -5586,7 +5588,7 @@ void HistoryWidget::sendExistingPhoto(
flags, flags,
0, 0,
options.replyTo, options.replyTo,
date(MTP_int(unixtime())), unixtime(),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
photo, photo,
@ -6539,8 +6541,7 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int
QString editTimeLeftText; QString editTimeLeftText;
int updateIn = -1; int updateIn = -1;
auto tmp = ::date(unixtime()); auto timeSinceMessage = ItemDateTime(_replyEditMsg).msecsTo(QDateTime::currentDateTime());
auto timeSinceMessage = _replyEditMsg->date.msecsTo(QDateTime::currentDateTime());
auto editTimeLeft = (Global::EditTimeLimit() * 1000LL) - timeSinceMessage; auto editTimeLeft = (Global::EditTimeLimit() * 1000LL) - timeSinceMessage;
if (editTimeLeft < 2) { if (editTimeLeft < 2) {
editTimeLeftText = qsl("0:00"); editTimeLeftText = qsl("0:00");

View file

@ -137,6 +137,7 @@ Element::Element(
not_null<HistoryItem*> data) not_null<HistoryItem*> data)
: _delegate(delegate) : _delegate(delegate)
, _data(data) , _data(data)
, _dateTime(ItemDateTime(data))
, _context(delegate->elementContext()) { , _context(delegate->elementContext()) {
Auth().data().registerItemView(this); Auth().data().registerItemView(this);
refreshMedia(); refreshMedia();
@ -153,6 +154,10 @@ not_null<HistoryItem*> Element::data() const {
return _data; return _data;
} }
QDateTime Element::dateTime() const {
return _dateTime;
}
HistoryMedia *Element::media() const { HistoryMedia *Element::media() const {
return _media.get(); return _media.get();
} }
@ -287,7 +292,7 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
const auto prev = previous->data(); const auto prev = previous->data();
const auto possible = !item->serviceMsg() && !prev->serviceMsg() const auto possible = !item->serviceMsg() && !prev->serviceMsg()
&& !item->isEmpty() && !prev->isEmpty() && !item->isEmpty() && !prev->isEmpty()
&& (qAbs(prev->date.secsTo(item->date)) < kAttachMessageToPreviousSecondsDelta) && (std::abs(prev->date() - item->date()) < kAttachMessageToPreviousSecondsDelta)
&& (_context == Context::Feed && (_context == Context::Feed
|| (!item->isPost() && !prev->isPost())); || (!item->isPost() && !prev->isPost()));
if (possible) { if (possible) {
@ -367,9 +372,10 @@ void Element::recountDisplayDateInBlocks() {
return false; return false;
} }
if (auto previous = previousInBlocks()) { if (const auto previous = previousInBlocks()) {
const auto prev = previous->data(); const auto prev = previous->data();
return prev->isEmpty() || (prev->date.date() != item->date.date()); return prev->isEmpty()
|| (previous->dateTime().date() != dateTime().date());
} }
return true; return true;
}()); }());
@ -391,7 +397,7 @@ void Element::setDisplayDate(bool displayDate) {
const auto item = data(); const auto item = data();
if (displayDate && !Has<DateBadge>()) { if (displayDate && !Has<DateBadge>()) {
AddComponents(DateBadge::Bit()); AddComponents(DateBadge::Bit());
Get<DateBadge>()->init(item->date); Get<DateBadge>()->init(dateTime());
setPendingResize(); setPendingResize();
} else if (!displayDate && Has<DateBadge>()) { } else if (!displayDate && Has<DateBadge>()) {
RemoveComponents(DateBadge::Bit()); RemoveComponents(DateBadge::Bit());
@ -478,8 +484,8 @@ bool Element::displayEditedBadge() const {
return false; return false;
} }
QDateTime Element::displayedEditDate() const { TimeId Element::displayedEditDate() const {
return QDateTime(); return TimeId(0);
} }
bool Element::hasVisibleText() const { bool Element::hasVisibleText() const {

View file

@ -121,6 +121,8 @@ public:
Context context() const; Context context() const;
void refreshDataId(); void refreshDataId();
QDateTime dateTime() const;
int y() const; int y() const;
void setY(int y); void setY(int y);
@ -221,7 +223,7 @@ public:
int outerWidth) const; int outerWidth) const;
virtual ClickHandlerPtr rightActionLink() const; virtual ClickHandlerPtr rightActionLink() const;
virtual bool displayEditedBadge() const; virtual bool displayEditedBadge() const;
virtual QDateTime displayedEditDate() const; virtual TimeId displayedEditDate() const;
virtual bool hasVisibleText() const; virtual bool hasVisibleText() const;
// Legacy blocks structure. // Legacy blocks structure.
@ -266,9 +268,10 @@ private:
const not_null<ElementDelegate*> _delegate; const not_null<ElementDelegate*> _delegate;
const not_null<HistoryItem*> _data; const not_null<HistoryItem*> _data;
std::unique_ptr<HistoryMedia> _media; std::unique_ptr<HistoryMedia> _media;
const QDateTime _dateTime;
int _y = 0; int _y = 0;
Context _context; Context _context = Context();
Flags _flags = Flag::NeedsResize; Flags _flags = Flag::NeedsResize;

View file

@ -908,7 +908,7 @@ QString ListWidget::tooltipText() const {
? _overElement->data().get() ? _overElement->data().get()
: nullptr; : nullptr;
if (_mouseCursorState == CursorState::Date && item) { if (_mouseCursorState == CursorState::Date && item) {
return item->date.toString( return _overElement->dateTime().toString(
QLocale::system().dateTimeFormat(QLocale::LongFormat)); QLocale::system().dateTimeFormat(QLocale::LongFormat));
} else if (_mouseCursorState == CursorState::Forwarded && item) { } else if (_mouseCursorState == CursorState::Forwarded && item) {
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
@ -1178,7 +1178,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
} else { } else {
ServiceMessagePainter::paintDate( ServiceMessagePainter::paintDate(
p, p,
view->data()->date, view->dateTime(),
dateY, dateY,
width); width);
} }
@ -1238,7 +1238,7 @@ TextWithEntities ListWidget::getSelectedText() const {
const auto wrapItem = [&]( const auto wrapItem = [&](
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
TextWithEntities &&unwrapped) { TextWithEntities &&unwrapped) {
auto time = item->date.toString(timeFormat); auto time = ItemDateTime(item).toString(timeFormat);
auto part = TextWithEntities(); auto part = TextWithEntities();
auto size = item->author()->name.size() auto size = item->author()->name.size()
+ time.size() + time.size()
@ -2158,8 +2158,8 @@ void ListWidget::refreshAttachmentsFromTill(int from, int till) {
if (next->isHidden()) { if (next->isHidden()) {
next->setDisplayDate(false); next->setDisplayDate(false);
} else { } else {
const auto viewDate = view->data()->date; const auto viewDate = view->dateTime();
const auto nextDate = next->data()->date; const auto nextDate = next->dateTime();
next->setDisplayDate(nextDate.date() != viewDate.date()); next->setDisplayDate(nextDate.date() != viewDate.date());
auto attached = next->computeIsAttachToPrevious(view); auto attached = next->computeIsAttachToPrevious(view);
next->setAttachToPrevious(attached); next->setAttachToPrevious(attached);

View file

@ -1682,12 +1682,12 @@ void Message::refreshEditedBadge() {
const auto item = message(); const auto item = message();
const auto edited = displayedEditBadge(); const auto edited = displayedEditBadge();
const auto editDate = displayedEditDate(); const auto editDate = displayedEditDate();
const auto dateText = item->date.toString(cTimeFormat()); const auto dateText = dateTime().toString(cTimeFormat());
if (edited) { if (edited) {
edited->refresh(dateText, !editDate.isNull()); edited->refresh(dateText, editDate != 0);
} }
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) { if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
const auto text = (!edited || editDate.isNull()) const auto text = (!edited || !editDate)
? dateText ? dateText
: edited->text.originalText(); : edited->text.originalText();
msgsigned->refresh(text); msgsigned->refresh(text);
@ -1702,7 +1702,7 @@ void Message::initTime() {
} else if (const auto edited = displayedEditBadge()) { } else if (const auto edited = displayedEditBadge()) {
item->_timeWidth = edited->maxWidth(); item->_timeWidth = edited->maxWidth();
} else { } else {
item->_timeText = item->date.toString(cTimeFormat()); item->_timeText = dateTime().toString(cTimeFormat());
item->_timeWidth = st::msgDateFont->width(item->_timeText); item->_timeWidth = st::msgDateFont->width(item->_timeText);
} }
if (const auto views = item->Get<HistoryMessageViews>()) { if (const auto views = item->Get<HistoryMessageViews>()) {
@ -1722,29 +1722,29 @@ void Message::initTime() {
} }
bool Message::displayEditedBadge() const { bool Message::displayEditedBadge() const {
return !displayedEditDate().isNull(); return (displayedEditDate() != TimeId(0));
} }
QDateTime Message::displayedEditDate() const { TimeId Message::displayedEditDate() const {
const auto item = message(); const auto item = message();
auto hasViaBotId = item->Has<HistoryMessageVia>(); auto hasViaBotId = item->Has<HistoryMessageVia>();
auto hasInlineMarkup = (item->inlineReplyMarkup() != nullptr); auto hasInlineMarkup = (item->inlineReplyMarkup() != nullptr);
return displayedEditDate(hasViaBotId || hasInlineMarkup); return displayedEditDate(hasViaBotId || hasInlineMarkup);
} }
QDateTime Message::displayedEditDate( TimeId Message::displayedEditDate(
bool hasViaBotOrInlineMarkup) const { bool hasViaBotOrInlineMarkup) const {
if (hasViaBotOrInlineMarkup) { if (hasViaBotOrInlineMarkup) {
return QDateTime(); return TimeId(0);
} else if (const auto fromUser = message()->from()->asUser()) { } else if (const auto fromUser = message()->from()->asUser()) {
if (fromUser->botInfo) { if (fromUser->botInfo) {
return QDateTime(); return TimeId(0);
} }
} }
if (const auto edited = displayedEditBadge()) { if (const auto edited = displayedEditBadge()) {
return edited->date; return edited->date;
} }
return QDateTime(); return TimeId(0);
} }
HistoryMessageEdited *Message::displayedEditBadge() { HistoryMessageEdited *Message::displayedEditBadge() {

View file

@ -79,7 +79,7 @@ public:
int outerWidth) const override; int outerWidth) const override;
ClickHandlerPtr rightActionLink() const override; ClickHandlerPtr rightActionLink() const override;
bool displayEditedBadge() const override; bool displayEditedBadge() const override;
QDateTime displayedEditDate() const override; TimeId displayedEditDate() const override;
int infoWidth() const override; int infoWidth() const override;
private: private:
@ -135,7 +135,7 @@ private:
bool displayFastShare() const; bool displayFastShare() const;
bool displayGoToOriginal() const; bool displayGoToOriginal() const;
ClickHandlerPtr fastReplyLink() const; ClickHandlerPtr fastReplyLink() const;
QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const; TimeId displayedEditDate(bool hasViaBotOrInlineMarkup) const;
const HistoryMessageEdited *displayedEditBadge() const; const HistoryMessageEdited *displayedEditBadge() const;
HistoryMessageEdited *displayedEditBadge(); HistoryMessageEdited *displayedEditBadge();
void initTime(); void initTime();

View file

@ -194,7 +194,7 @@ bool ListWidget::Section::addItem(not_null<BaseLayout*> item) {
void ListWidget::Section::setHeader(not_null<BaseLayout*> item) { void ListWidget::Section::setHeader(not_null<BaseLayout*> item) {
auto text = [&] { auto text = [&] {
auto date = item->getItem()->date.date(); auto date = item->dateTime().date();
switch (_type) { switch (_type) {
case Type::Photo: case Type::Photo:
case Type::Video: case Type::Video:
@ -217,8 +217,8 @@ void ListWidget::Section::setHeader(not_null<BaseLayout*> item) {
bool ListWidget::Section::belongsHere( bool ListWidget::Section::belongsHere(
not_null<BaseLayout*> item) const { not_null<BaseLayout*> item) const {
Expects(!_items.empty()); Expects(!_items.empty());
auto date = item->getItem()->date.date(); auto date = item->dateTime().date();
auto myDate = _items.back().second->getItem()->date.date(); auto myDate = _items.back().second->dateTime().date();
switch (_type) { switch (_type) {
case Type::Photo: case Type::Photo:

View file

@ -131,7 +131,7 @@ void SendPhoto::addToHistory(
flags, flags,
viaBotId, viaBotId,
replyToId, replyToId,
date(mtpDate), mtpDate.v,
fromId, fromId,
postAuthor, postAuthor,
_photo, _photo,
@ -166,7 +166,7 @@ void SendFile::addToHistory(
flags, flags,
viaBotId, viaBotId,
replyToId, replyToId,
date(mtpDate), mtpDate.v,
fromId, fromId,
postAuthor, postAuthor,
_document, _document,
@ -208,7 +208,7 @@ void SendGame::addToHistory(
flags, flags,
viaBotId, viaBotId,
replyToId, replyToId,
date(mtpDate), mtpDate.v,
fromId, fromId,
postAuthor, postAuthor,
_game, _game,

View file

@ -4860,16 +4860,20 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} break; } break;
case mtpc_updateContactRegistered: { case mtpc_updateContactRegistered: {
auto &d = update.c_updateContactRegistered(); const auto &d = update.c_updateContactRegistered();
if (auto user = App::userLoaded(d.vuser_id.v)) { if (const auto user = App::userLoaded(d.vuser_id.v)) {
if (App::history(user->id)->loadedAtBottom()) { if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addNewService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), 0); App::history(user->id)->addNewService(
clientMsgId(),
d.vdate.v,
lng_action_user_registered(lt_from, user->name),
MTPDmessage::Flags(0));
} }
} }
} break; } break;
case mtpc_updateContactLink: { case mtpc_updateContactLink: {
auto &d = update.c_updateContactLink(); const auto &d = update.c_updateContactLink();
App::feedUserLink(d.vuser_id, d.vmy_link, d.vforeign_link); App::feedUserLink(d.vuser_id, d.vmy_link, d.vforeign_link);
} break; } break;

View file

@ -468,22 +468,31 @@ void Widget::handleSongChange() {
TextWithEntities textWithEntities; TextWithEntities textWithEntities;
if (document->isVoiceMessage() || document->isVideoMessage()) { if (document->isVoiceMessage() || document->isVideoMessage()) {
if (auto item = App::histItemById(current.contextId())) { if (const auto item = App::histItemById(current.contextId())) {
auto name = App::peerName(item->fromOriginal()); const auto name = App::peerName(item->fromOriginal());
auto date = [item] { const auto date = [item] {
auto date = item->date.date(); const auto parsed = ItemDateTime(item);
auto time = item->date.time().toString(cTimeFormat()); const auto date = parsed.date();
auto today = QDateTime::currentDateTime().date(); const auto time = parsed.time().toString(cTimeFormat());
const auto today = QDateTime::currentDateTime().date();
if (date == today) { if (date == today) {
return lng_player_message_today(lt_time, time); return lng_player_message_today(lt_time, time);
} else if (date.addDays(1) == today) { } else if (date.addDays(1) == today) {
return lng_player_message_yesterday(lt_time, time); return lng_player_message_yesterday(lt_time, time);
} }
return lng_player_message_date(lt_date, langDayOfMonthFull(date), lt_time, time); return lng_player_message_date(
lt_date,
langDayOfMonthFull(date),
lt_time,
time);
}; };
textWithEntities.text = name + ' ' + date(); textWithEntities.text = name + ' ' + date();
textWithEntities.entities.append({ EntityInTextBold, 0, name.size(), QString() }); textWithEntities.entities.append(EntityInText(
EntityInTextBold,
0,
name.size(),
QString()));
} else { } else {
textWithEntities.text = lang(lng_media_audio); textWithEntities.text = lang(lng_media_audio);
} }

View file

@ -325,14 +325,17 @@ void MediaView::updateControls() {
_moreNav = myrtlrect(width() - st::mediaviewIconSize.width(), height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height()); _moreNav = myrtlrect(width() - st::mediaviewIconSize.width(), height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height());
_moreNavIcon = centerrect(_moreNav, st::mediaviewMore); _moreNavIcon = centerrect(_moreNav, st::mediaviewMore);
QDateTime d, dNow(date(unixtime())); const auto dNow = QDateTime::currentDateTime();
if (_photo) { const auto d = [&] {
d = date(_photo->date); if (_photo) {
} else if (_doc) { return ParseDateTime(_photo->date);
d = date(_doc->date); } else if (_doc) {
} else if (auto item = App::histItemById(_msgid)) { return ParseDateTime(_doc->date);
d = item->date; } else if (const auto item = App::histItemById(_msgid)) {
} return ItemDateTime(item);
}
return dNow;
}();
if (d.date() == dNow.date()) { if (d.date() == dNow.date()) {
_dateText = lng_mediaview_today(lt_time, d.time().toString(cTimeFormat())); _dateText = lng_mediaview_today(lt_time, d.time().toString(cTimeFormat()));
} else if (d.date().addDays(1) == dNow.date()) { } else if (d.date().addDays(1) == dNow.date()) {
@ -847,14 +850,23 @@ void MediaView::onSaveAs() {
psBringToBack(this); psBringToBack(this);
auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter();
FileDialog::GetWritePath(lang(lng_save_photo), filter, filedialogDefaultName(qsl("photo"), qsl(".jpg"), QString(), false, _photo->date), base::lambda_guarded(this, [this, photo = _photo](const QString &result) { FileDialog::GetWritePath(
if (!result.isEmpty() && _photo == photo && photo->loaded()) { lang(lng_save_photo),
photo->full->pix().toImage().save(result, "JPG"); filter,
} filedialogDefaultName(
psShowOverAll(this); qsl("photo"),
}), base::lambda_guarded(this, [this] { qsl(".jpg"),
psShowOverAll(this); QString(),
})); false,
_photo->date),
base::lambda_guarded(this, [this, photo = _photo](const QString &result) {
if (!result.isEmpty() && _photo == photo && photo->loaded()) {
photo->full->pix().toImage().save(result, "JPG");
}
psShowOverAll(this);
}), base::lambda_guarded(this, [this] {
psShowOverAll(this);
}));
} }
activateWindow(); activateWindow();
Sandbox::setActiveWindow(this); Sandbox::setActiveWindow(this);

View file

@ -124,7 +124,13 @@ MsgId AbstractItem::msgId() const {
return item ? item->id : 0; return item ? item->id : 0;
} }
ItemBase::ItemBase(not_null<HistoryItem*> parent) : _parent(parent) { ItemBase::ItemBase(not_null<HistoryItem*> parent)
: _parent(parent)
, _dateTime(ItemDateTime(parent)) {
}
QDateTime ItemBase::dateTime() const {
return _dateTime;
} }
void ItemBase::clickHandlerActiveChanged( void ItemBase::clickHandlerActiveChanged(
@ -564,9 +570,19 @@ Voice::Voice(
setDocumentLinks(_data); setDocumentLinks(_data);
updateName(); updateName();
QString d = textcmdLink(1, TextUtilities::EscapeForRichParsing(langDateTime(date(_data->date)))); const auto dateText = textcmdLink(
1,
TextUtilities::EscapeForRichParsing(
langDateTime(ParseDateTime(_data->date))));
TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto };
_details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); _details.setText(
st::defaultTextStyle,
lng_date_and_duration(
lt_date,
dateText,
lt_duration,
formatDurationText(_data->voice()->duration)),
opts);
_details.setLink(1, goToMessageClickHandler(parent)); _details.setLink(1, goToMessageClickHandler(parent));
} }
@ -804,7 +820,7 @@ Document::Document(
, _msgl(goToMessageClickHandler(parent)) , _msgl(goToMessageClickHandler(parent))
, _namel(std::make_shared<DocumentOpenClickHandler>(_data, parent->fullId())) , _namel(std::make_shared<DocumentOpenClickHandler>(_data, parent->fullId()))
, _st(st) , _st(st)
, _date(langDateTime(date(_data->date))) , _date(langDateTime(ParseDateTime(_data->date)))
, _datew(st::normalFont->width(_date)) , _datew(st::normalFont->width(_date))
, _colorIndex(documentColorIndex(_data, _ext)) { , _colorIndex(documentColorIndex(_data, _ext)) {
_name.setMarkedText(st::defaultTextStyle, ComposeNameWithEntities(_data), _documentNameOptions); _name.setMarkedText(st::defaultTextStyle, ComposeNameWithEntities(_data), _documentNameOptions);

View file

@ -64,6 +64,8 @@ class ItemBase : public AbstractItem {
public: public:
ItemBase(not_null<HistoryItem*> parent); ItemBase(not_null<HistoryItem*> parent);
QDateTime dateTime() const;
void setPosition(int position) { void setPosition(int position) {
_position = position; _position = position;
} }
@ -103,7 +105,8 @@ private:
void ensureCheckboxCreated(); void ensureCheckboxCreated();
int _position = 0; int _position = 0;
not_null<HistoryItem*> _parent; const not_null<HistoryItem*> _parent;
const QDateTime _dateTime;
std::unique_ptr<Checkbox> _check; std::unique_ptr<Checkbox> _check;
}; };

View file

@ -331,7 +331,9 @@ void System::showNext() {
if (nextNotify) { if (nextNotify) {
if (forwardedItem) { if (forwardedItem) {
auto nextForwarded = nextNotify->Has<HistoryMessageForwarded>() ? nextNotify : nullptr; auto nextForwarded = nextNotify->Has<HistoryMessageForwarded>() ? nextNotify : nullptr;
if (nextForwarded && forwardedItem->author() == nextForwarded->author() && qAbs(int64(nextForwarded->date.toTime_t()) - int64(forwardedItem->date.toTime_t())) < 2) { if (nextForwarded
&& forwardedItem->author() == nextForwarded->author()
&& qAbs(int64(nextForwarded->date()) - int64(forwardedItem->date())) < 2) {
forwardedItem = nextForwarded; forwardedItem = nextForwarded;
++forwardedCount; ++forwardedCount;
} else { } else {

View file

@ -309,13 +309,13 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
auto currentPeerDate = [peer] { auto currentPeerDate = [peer] {
if (auto history = App::historyLoaded(peer)) { if (auto history = App::historyLoaded(peer)) {
if (history->scrollTopItem) { if (history->scrollTopItem) {
return history->scrollTopItem->data()->date.date(); return history->scrollTopItem->dateTime().date();
} else if (history->loadedAtTop() && !history->isEmpty() && history->peer->migrateFrom()) { } else if (history->loadedAtTop() && !history->isEmpty() && history->peer->migrateFrom()) {
if (auto migrated = App::historyLoaded(history->peer->migrateFrom())) { if (auto migrated = App::historyLoaded(history->peer->migrateFrom())) {
if (migrated->scrollTopItem) { if (migrated->scrollTopItem) {
// We're up in the migrated history. // We're up in the migrated history.
// So current date is the date of first message here. // So current date is the date of first message here.
return history->blocks.front()->messages.front()->data()->date.date(); return history->blocks.front()->messages.front()->dateTime().date();
} }
} }
} else if (!history->chatsListDate().isNull()) { } else if (!history->chatsListDate().isNull()) {
@ -344,7 +344,7 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
if (auto history = App::historyLoaded(chat)) { if (auto history = App::historyLoaded(chat)) {
if (history->loadedAtTop()) { if (history->loadedAtTop()) {
if (!history->isEmpty()) { if (!history->isEmpty()) {
return history->blocks.front()->messages.front()->data()->date.date(); return history->blocks.front()->messages.front()->dateTime().date();
} }
} else { } else {
return startDate(); return startDate();
@ -354,7 +354,7 @@ void Controller::showJumpToDate(not_null<PeerData*> peer, QDate requestedDate) {
if (auto history = App::historyLoaded(peer)) { if (auto history = App::historyLoaded(peer)) {
if (history->loadedAtTop()) { if (history->loadedAtTop()) {
if (!history->isEmpty()) { if (!history->isEmpty()) {
return history->blocks.front()->messages.front()->data()->date.date(); return history->blocks.front()->messages.front()->dateTime().date();
} }
return QDate::currentDate(); return QDate::currentDate();
} }