Cloud stored message drafts support added.

This commit is contained in:
John Preston 2016-06-03 21:24:27 +03:00
parent 307e529ccf
commit cd2615d8d0
16 changed files with 431 additions and 151 deletions

View File

@ -705,6 +705,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_willbe_history" = "Please select a chat to start messaging";
"lng_message_with_from" = "[c]{from}:[/c] {message}";
"lng_from_you" = "You";
"lng_from_draft" = "Draft";
"lng_bot_description" = "What can this bot do?";
"lng_unblock_button" = "Unblock";
"lng_channel_join" = "Join Channel";

View File

@ -151,6 +151,7 @@ enum {
WriteMapTimeout = 1000,
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
SaveCloudDraftTimeout = 14000, // save draft to the cloud after 14 more seconds
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
SetOnlineAfterActivity = 30, // user with hidden last seen stays online for such amount of seconds in the interface

View File

@ -136,6 +136,10 @@ MTPint toServerTime(const TimeId &clientTime) {
return MTP_int(clientTime + unixtimeDelta);
}
QDateTime dateFromServerTime(TimeId time) {
return dateFromServerTime(MTP_int(time));
}
// Precise timing functions / rand init
struct CRYPTO_dynlock_value {

View File

@ -474,10 +474,16 @@ inline QDateTime date(int32 time = -1) {
return result;
}
inline QDateTime date(const MTPint &time) {
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) {
#ifdef Q_OS_WIN
localtime_s(_Tm, _Time);

View File

@ -21,13 +21,31 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "data/drafts.h"
#include "historywidget.h"
#include "mainwidget.h"
namespace Data {
namespace {
} // namespace
void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
auto history = App::history(peerId);
auto text = qs(draft.vmessage);
auto entities = draft.has_entities() ? entitiesFromMTP(draft.ventities.c_vector().v) : EntitiesInText();
TextWithTags textWithTags = { textApplyEntities(text, entities), textTagsFromEntities(entities) };
MsgId replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : 0;
auto cloudDraft = std_::make_unique<HistoryDraft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
cloudDraft->date = ::date(draft.vdate);
history->setCloudDraft(std_::move(cloudDraft));
history->createLocalDraftFromCloud();
history->updateChatListSortPosition();
history->updateChatListEntry();
if (auto main = App::main()) {
main->applyCloudDraft(history);
}
}
} // namespace Data

View File

@ -31,8 +31,26 @@ namespace Layout {
namespace {
void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool active) {
QDateTime now(QDateTime::currentDateTime()), lastTime(date);
QDate nowDate(now.date()), lastDate(lastTime.date());
QString dt;
if (lastDate == nowDate) {
dt = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
dt = langDayOfWeek(lastDate);
} else {
dt = lastDate.toString(qsl("d.MM.yy"));
}
int32 dtWidth = st::dlgDateFont->width(dt);
rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip);
p.setFont(st::dlgDateFont);
p.setPen(active ? st::dlgActiveDateColor : st::dlgDateColor);
p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt);
}
template <typename PaintItemCallback>
void paintRow(Painter &p, History *history, HistoryItem *item, int w, bool active, bool selected, bool onlyBackground, PaintItemCallback paintItemCallback) {
void paintRow(Painter &p, History *history, HistoryItem *item, HistoryDraft *draft, int w, bool active, bool selected, bool onlyBackground, PaintItemCallback paintItemCallback) {
QRect fullRect(0, 0, w, st::dlgHeight);
p.fillRect(fullRect, active ? st::dlgActiveBG : (selected ? st::dlgHoverBG : st::dlgBG));
if (onlyBackground) return;
@ -53,31 +71,56 @@ void paintRow(Painter &p, History *history, HistoryItem *item, int w, bool activ
rectForName.setLeft(rectForName.left() + st::dlgImgSkip);
}
if (!item) {
int texttop = st::dlgPaddingVer + st::dlgFont->height + st::dlgSep;
if (draft) {
paintRowDate(p, draft->date, rectForName, active);
// draw check
if (draft->saveRequestId) {
auto check = active ? &st::dlgActiveSendImg : &st::dlgSendImg;
rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip);
p.drawSprite(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), *check);
}
bool hasDraftIcon = !active;
if (hasDraftIcon) {
QString counter;
bool mutedCounter = false;
int unreadRight = w - st::dlgPaddingHor;
int unreadTop = texttop + st::dlgHistFont->ascent - st::dlgUnreadFont->ascent - st::dlgUnreadTop;
int unreadWidth = 0;
paintUnreadCount(p, counter, unreadRight, unreadTop, style::al_right, active, mutedCounter, &unreadWidth);
st::dialogsDraft.paint(p, QPoint(w - st::dlgPaddingHor - st::dlgUnreadHeight, unreadTop), w);
namewidth -= unreadWidth + st::dlgUnreadPaddingHor;
}
p.setFont(st::dlgHistFont);
p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor);
if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
p.drawText(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgFont->ascent + st::dlgSep, lang(lng_empty_history));
if (history->cloudDraftTextCache.isEmpty()) {
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
QString msg = lng_message_with_from(lt_from, textRichPrepare(lang(lng_from_draft)), lt_message, textRichPrepare(draft->textWithTags.text));
history->cloudDraftTextCache.setRichText(st::dlgHistFont, msg, _textDlgOptions, custom);
}
textstyleSet(&(active ? st::dlgActiveTextStyle : st::dlgTextStyle));
p.setFont(st::dlgHistFont);
p.setPen(active ? st::dlgActiveColor : st::dlgTextColor);
history->cloudDraftTextCache.drawElided(p, nameleft, texttop, namewidth, st::dlgFont->height / st::dlgHistFont->height);
textstyleRestore();
} else {
history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth);
history->typingText.drawElided(p, nameleft, texttop, namewidth);
}
} else if (!item) {
p.setFont(st::dlgHistFont);
p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor);
if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
p.drawText(nameleft, texttop + st::dlgFont->ascent, lang(lng_empty_history));
} else {
history->typingText.drawElided(p, nameleft, texttop, namewidth);
}
} else {
// draw date
QDateTime now(QDateTime::currentDateTime()), lastTime(item->date);
QDate nowDate(now.date()), lastDate(lastTime.date());
QString dt;
if (lastDate == nowDate) {
dt = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
dt = langDayOfWeek(lastDate);
} else {
dt = lastDate.toString(qsl("d.MM.yy"));
}
int32 dtWidth = st::dlgDateFont->width(dt);
rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip);
p.setFont(st::dlgDateFont);
p.setPen(active ? st::dlgActiveDateColor : st::dlgDateColor);
p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt);
paintRowDate(p, item->date, rectForName, active);
// draw check
if (item->needCheck()) {
@ -186,7 +229,11 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, style::alig
void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground) {
auto history = row->history();
auto item = history->lastMsg;
paintRow(p, history, item, w, active, selected, onlyBackground, [&p, w, active, history](int nameleft, int namewidth, HistoryItem *item) {
auto cloudDraft = history->cloudDraft();
if (item && cloudDraft && cloudDraft->date < item->date) {
cloudDraft = nullptr; // Draw item, if draft is older.
}
paintRow(p, history, item, cloudDraft, w, active, selected, onlyBackground, [&p, w, active, history](int nameleft, int namewidth, HistoryItem *item) {
int32 unread = history->unreadCount();
if (history->peer->migrateFrom()) {
if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) {
@ -195,7 +242,8 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele
}
int availableWidth = namewidth;
int texttop = st::dlgPaddingVer + st::dlgFont->height + st::dlgSep;
bool hasDraftIcon = active ? false : Local::hasDraft(history->peer->id);
auto cloudDraft = history->cloudDraft();
bool hasDraftIcon = active ? false : (cloudDraft && cloudDraft->date.isValid());
if (unread || hasDraftIcon) {
QString counter;
bool mutedCounter = false;
@ -208,10 +256,10 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele
int unreadTop = texttop + st::dlgHistFont->ascent - st::dlgUnreadFont->ascent - st::dlgUnreadTop;
int unreadWidth = 0;
paintUnreadCount(p, counter, unreadRight, unreadTop, style::al_right, active, mutedCounter, &unreadWidth);
availableWidth -= unreadWidth + st::dlgUnreadPaddingHor;
if (!showUnreadCounter) {
st::dialogsDraft.paint(p, QPoint(w - st::dlgPaddingHor - st::dlgUnreadHeight, unreadTop), w);
}
availableWidth -= unreadWidth + st::dlgUnreadPaddingHor;
}
if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dlgFont->height), active, history->textCachedFor, history->lastItemTextCache);
@ -225,7 +273,7 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele
void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground) {
auto item = row->item();
auto history = item->history();
paintRow(p, history, item, w, active, selected, onlyBackground, [&p, row, active](int nameleft, int namewidth, HistoryItem *item) {
paintRow(p, history, item, nullptr, w, active, selected, onlyBackground, [&p, row, active](int nameleft, int namewidth, HistoryItem *item) {
int lastWidth = namewidth, texttop = st::dlgPaddingVer + st::dlgFont->height + st::dlgSep;
item->drawInDialog(p, QRect(nameleft, texttop, lastWidth, st::dlgFont->height), active, row->_cacheFor, row->_cache);
});

View File

@ -162,6 +162,59 @@ void History::setHasPendingResizedItems() {
Global::RefHandleHistoryUpdate().call();
}
void History::createLocalDraftFromCloud() {
auto draft = cloudDraft();
if (historyDraftIsNull(draft) || !draft->date.isValid()) return;
auto existing = localDraft();
if (historyDraftIsNull(existing) || !existing->date.isValid() || draft->date > existing->date) {
if (!existing) {
setLocalDraft(std_::make_unique<HistoryDraft>(draft->textWithTags, draft->msgId, draft->cursor, draft->previewCancelled));
existing = localDraft();
} else if (existing != draft) {
existing->textWithTags = draft->textWithTags;
existing->msgId = draft->msgId;
existing->cursor = draft->cursor;
existing->previewCancelled = draft->previewCancelled;
}
existing->date = draft->date;
}
}
HistoryDraft *History::createCloudDraft(HistoryDraft *fromDraft) {
if (historyDraftIsNull(fromDraft)) {
setCloudDraft(std_::make_unique<HistoryDraft>(TextWithTags(), 0, MessageCursor(), false));
cloudDraft()->date = QDateTime();
} else {
auto existing = cloudDraft();
if (!existing) {
setCloudDraft(std_::make_unique<HistoryDraft>(fromDraft->textWithTags, fromDraft->msgId, fromDraft->cursor, fromDraft->previewCancelled));
existing = cloudDraft();
} else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags;
existing->msgId = fromDraft->msgId;
existing->cursor = fromDraft->cursor;
existing->previewCancelled = fromDraft->previewCancelled;
}
existing->date = ::date(myunixtime());
}
cloudDraftTextCache.clear();
updateChatListSortPosition();
updateChatListEntry();
return cloudDraft();
}
void History::clearCloudDraft() {
if (_cloudDraft) {
_cloudDraft = nullptr;
cloudDraftTextCache.clear();
updateChatListSortPosition();
updateChatListEntry();
}
}
bool History::updateTyping(uint64 ms, bool force) {
bool changed = force;
for (TypingUsers::iterator i = typing.begin(), e = typing.end(); i != e;) {
@ -1037,6 +1090,7 @@ void History::newItemAdded(HistoryItem *item) {
if (!item->unread()) {
outboxRead(item);
}
item->history()->clearCloudDraft();
} else if (item->unread()) {
bool skip = false;
if (!isChannel() || peer->asChannel()->amIn()) {
@ -1679,19 +1733,38 @@ void History::setLastMessage(HistoryItem *msg) {
updateChatListEntry();
}
void History::setChatsListDate(const QDateTime &date) {
bool updateDialog = (App::main() && (!peer->isChannel() || peer->asChannel()->amIn() || inChatList(Dialogs::Mode::All)));
if (peer->migrateTo() && !inChatList(Dialogs::Mode::All)) {
updateDialog = false;
bool History::needUpdateInChatList() const {
if (inChatList(Dialogs::Mode::All)) {
return true;
} else if (peer->migrateTo()) {
return false;
}
return (!peer->isChannel() || peer->asChannel()->amIn());
}
void History::setChatsListDate(const QDateTime &date) {
bool updateDialog = needUpdateInChatList();
if (!lastMsgDate.isNull() && lastMsgDate >= date) {
if (!updateDialog || !inChatList(Dialogs::Mode::All)) {
return;
}
}
lastMsgDate = date;
_sortKeyInChatList = dialogPosFromDate(lastMsgDate);
if (updateDialog) {
updateChatListSortPosition();
}
void History::updateChatListSortPosition() {
auto chatListDate = [this]() {
if (auto draft = cloudDraft()) {
if (draft->date > lastMsgDate) {
return draft->date;
}
}
return lastMsgDate;
};
_sortKeyInChatList = dialogPosFromDate(chatListDate());
if (App::main() && needUpdateInChatList()) {
App::main()->createDialog(this);
}
}

View File

@ -155,40 +155,44 @@ struct SendAction {
using TextWithTags = FlatTextarea::TextWithTags;
struct HistoryDraft {
HistoryDraft() : msgId(0), previewCancelled(false) {
HistoryDraft() {
}
HistoryDraft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled)
HistoryDraft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequestId = 0)
: textWithTags(textWithTags)
, msgId(msgId)
, cursor(cursor)
, previewCancelled(previewCancelled) {
, previewCancelled(previewCancelled)
, saveRequestId(saveRequestId) {
}
HistoryDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled)
HistoryDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0)
: textWithTags(field.getTextWithTags())
, msgId(msgId)
, cursor(field)
, previewCancelled(previewCancelled) {
}
QDateTime date;
TextWithTags textWithTags;
MsgId msgId; // replyToId for message draft, editMsgId for edit draft
MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft
MessageCursor cursor;
bool previewCancelled;
bool previewCancelled = false;
mtpRequestId saveRequestId = 0;
};
struct HistoryEditDraft : public HistoryDraft {
HistoryEditDraft()
: HistoryDraft()
, saveRequest(0) {
inline bool historyDraftIsNull(HistoryDraft *draft) {
return (!draft || (!draft->msgId && draft->textWithTags.text.isEmpty()));
}
inline bool historyDraftsAreEqual(HistoryDraft *a, HistoryDraft *b) {
bool aIsNull = historyDraftIsNull(a);
bool bIsNull = historyDraftIsNull(b);
if (aIsNull) {
return bIsNull;
} else if (bIsNull) {
return false;
}
HistoryEditDraft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequest = 0)
: HistoryDraft(textWithTags, msgId, cursor, previewCancelled)
, saveRequest(saveRequest) {
}
HistoryEditDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequest = 0)
: HistoryDraft(field, msgId, previewCancelled)
, saveRequest(saveRequest) {
}
mtpRequestId saveRequest;
};
return (a->textWithTags == b->textWithTags) && (a->msgId == b->msgId) && (a->previewCancelled == b->previewCancelled);
}
class HistoryMedia;
class HistoryMessage;
@ -279,6 +283,8 @@ public:
void setLastMessage(HistoryItem *msg);
void fixLastMessage(bool wasAtBottom);
bool needUpdateInChatList() const;
void updateChatListSortPosition();
void setChatsListDate(const QDateTime &date);
uint64 sortKeyInChatList() const {
return _sortKeyInChatList;
@ -367,35 +373,48 @@ public:
typedef QList<HistoryItem*> NotifyQueue;
NotifyQueue notifies;
HistoryDraft *msgDraft() {
return _msgDraft.get();
HistoryDraft *localDraft() {
return _localDraft.get();
}
HistoryEditDraft *editDraft() {
HistoryDraft *cloudDraft() {
return _cloudDraft.get();
}
HistoryDraft *editDraft() {
return _editDraft.get();
}
void setMsgDraft(std_::unique_ptr<HistoryDraft> &&draft) {
_msgDraft = std_::move(draft);
void setLocalDraft(std_::unique_ptr<HistoryDraft> &&draft) {
_localDraft = std_::move(draft);
}
void takeMsgDraft(History *from) {
if (auto &draft = from->_msgDraft) {
if (!draft->textWithTags.text.isEmpty() && !_msgDraft) {
_msgDraft = std_::move(draft);
_msgDraft->msgId = 0; // edit and reply to drafts can't migrate
void takeLocalDraft(History *from) {
if (auto &draft = from->_localDraft) {
if (!draft->textWithTags.text.isEmpty() && !_localDraft) {
_localDraft = std_::move(draft);
// Edit and reply to drafts can't migrate.
// Cloud drafts do not migrate automatically.
_localDraft->msgId = 0;
}
from->clearMsgDraft();
from->clearLocalDraft();
}
}
void setEditDraft(std_::unique_ptr<HistoryEditDraft> &&draft) {
void createLocalDraftFromCloud();
void setCloudDraft(std_::unique_ptr<HistoryDraft> &&draft) {
_cloudDraft = std_::move(draft);
cloudDraftTextCache.clear();
}
HistoryDraft *createCloudDraft(HistoryDraft *fromDraft);
void setEditDraft(std_::unique_ptr<HistoryDraft> &&draft) {
_editDraft = std_::move(draft);
}
void clearMsgDraft() {
_msgDraft = nullptr;
void clearLocalDraft() {
_localDraft = nullptr;
}
void clearCloudDraft();
void clearEditDraft() {
_editDraft = nullptr;
}
HistoryDraft *draft() {
return _editDraft ? editDraft() : msgDraft();
return _editDraft ? editDraft() : localDraft();
}
// some fields below are a property of a currently displayed instance of this
@ -485,6 +504,8 @@ public:
void changeMsgId(MsgId oldId, MsgId newId);
Text cloudDraftTextCache = Text { int(st::dlgRichMinWidth) };
protected:
void clearOnDestroy();
@ -578,8 +599,8 @@ private:
// Depending on isBuildingFrontBlock() gets front or back block.
HistoryBlock *prepareBlockForAddingItem();
std_::unique_ptr<HistoryDraft> _msgDraft;
std_::unique_ptr<HistoryEditDraft> _editDraft;
std_::unique_ptr<HistoryDraft> _localDraft, _cloudDraft;
std_::unique_ptr<HistoryDraft> _editDraft;
};

View File

@ -2872,6 +2872,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_saveDraftTimer.setSingleShot(true);
connect(&_saveDraftTimer, SIGNAL(timeout()), this, SLOT(onDraftSave()));
_saveCloudDraftTimer.setSingleShot(true);
connect(&_saveCloudDraftTimer, SIGNAL(timeout()), this, SLOT(onCloudDraftSave()));
connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed()));
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed()));
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onCheckFieldAutocomplete()), Qt::QueuedConnection);
@ -3074,7 +3076,9 @@ void HistoryWidget::onTextChange() {
update();
}
_saveCloudDraftTimer.stop();
if (!_peer || !(_textUpdateEvents.testFlag(TextUpdateEvent::SaveDraft))) return;
_saveDraftText = true;
onDraftSave(true);
}
@ -3103,31 +3107,52 @@ void HistoryWidget::onDraftSave(bool delayed) {
writeDrafts(nullptr, nullptr);
}
void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft) {
HistoryDraft *historyMsgDraft = _history ? _history->msgDraft() : nullptr;
if (!msgDraft && _editMsgId) msgDraft = &historyMsgDraft;
void HistoryWidget::saveFieldToHistoryLocalDraft() {
if (!_history) return;
if (_editMsgId) {
_history->setEditDraft(std_::make_unique<HistoryDraft>(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId));
} else {
if (_replyToId || !_field.isEmpty()) {
_history->setLocalDraft(std_::make_unique<HistoryDraft>(_field, _replyToId, _previewCancelled));
} else {
_history->clearLocalDraft();
}
_history->clearEditDraft();
}
}
void HistoryWidget::onCloudDraftSave() {
if (App::main()) {
App::main()->saveDraftToCloud();
}
}
void HistoryWidget::writeDrafts(HistoryDraft **localDraft, HistoryDraft **editDraft) {
HistoryDraft *historyLocalDraft = _history ? _history->localDraft() : nullptr;
if (!localDraft && _editMsgId) localDraft = &historyLocalDraft;
bool save = _peer && (_saveDraftStart > 0);
_saveDraftStart = 0;
_saveDraftTimer.stop();
if (_saveDraftText) {
if (save) {
Local::MessageDraft localMsgDraft, localEditDraft;
if (msgDraft) {
if (*msgDraft) {
localMsgDraft = Local::MessageDraft((*msgDraft)->msgId, (*msgDraft)->textWithTags, (*msgDraft)->previewCancelled);
Local::MessageDraft storedLocalDraft, storedEditDraft;
if (localDraft) {
if (*localDraft) {
storedLocalDraft = Local::MessageDraft((*localDraft)->msgId, (*localDraft)->textWithTags, (*localDraft)->previewCancelled);
}
} else {
localMsgDraft = Local::MessageDraft(_replyToId, _field.getTextWithTags(), _previewCancelled);
storedLocalDraft = Local::MessageDraft(_replyToId, _field.getTextWithTags(), _previewCancelled);
}
if (editDraft) {
if (*editDraft) {
localEditDraft = Local::MessageDraft((*editDraft)->msgId, (*editDraft)->textWithTags, (*editDraft)->previewCancelled);
storedEditDraft = Local::MessageDraft((*editDraft)->msgId, (*editDraft)->textWithTags, (*editDraft)->previewCancelled);
}
} else if (_editMsgId) {
localEditDraft = Local::MessageDraft(_editMsgId, _field.getTextWithTags(), _previewCancelled);
storedEditDraft = Local::MessageDraft(_editMsgId, _field.getTextWithTags(), _previewCancelled);
}
Local::writeDrafts(_peer->id, localMsgDraft, localEditDraft);
Local::writeDrafts(_peer->id, storedLocalDraft, storedEditDraft);
if (_migrated) {
Local::writeDrafts(_migrated->peer->id, Local::MessageDraft(), Local::MessageDraft());
}
@ -3135,13 +3160,13 @@ void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **edit
_saveDraftText = false;
}
if (save) {
MessageCursor msgCursor, editCursor;
if (msgDraft) {
if (*msgDraft) {
msgCursor = (*msgDraft)->cursor;
MessageCursor localCursor, editCursor;
if (localDraft) {
if (*localDraft) {
localCursor = (*localDraft)->cursor;
}
} else {
msgCursor = MessageCursor(_field);
localCursor = MessageCursor(_field);
}
if (editDraft) {
if (*editDraft) {
@ -3150,26 +3175,30 @@ void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **edit
} else if (_editMsgId) {
editCursor = MessageCursor(_field);
}
Local::writeDraftCursors(_peer->id, msgCursor, editCursor);
Local::writeDraftCursors(_peer->id, localCursor, editCursor);
if (_migrated) {
Local::writeDraftCursors(_migrated->peer->id, MessageCursor(), MessageCursor());
}
}
if (!_editMsgId) {
_saveCloudDraftTimer.start(SaveCloudDraftTimeout);
}
}
void HistoryWidget::writeDrafts(History *history) {
Local::MessageDraft localMsgDraft, localEditDraft;
MessageCursor msgCursor, editCursor;
if (auto msgDraft = history->msgDraft()) {
localMsgDraft = Local::MessageDraft(msgDraft->msgId, msgDraft->textWithTags, msgDraft->previewCancelled);
msgCursor = msgDraft->cursor;
Local::MessageDraft storedLocalDraft, storedEditDraft;
MessageCursor localCursor, editCursor;
if (auto localDraft = history->localDraft()) {
storedLocalDraft = Local::MessageDraft(localDraft->msgId, localDraft->textWithTags, localDraft->previewCancelled);
localCursor = localDraft->cursor;
}
if (auto editDraft = history->editDraft()) {
localEditDraft = Local::MessageDraft(editDraft->msgId, editDraft->textWithTags, editDraft->previewCancelled);
storedEditDraft = Local::MessageDraft(editDraft->msgId, editDraft->textWithTags, editDraft->previewCancelled);
editCursor = editDraft->cursor;
}
Local::writeDrafts(history->peer->id, localMsgDraft, localEditDraft);
Local::writeDraftCursors(history->peer->id, msgCursor, editCursor);
Local::writeDrafts(history->peer->id, storedLocalDraft, storedEditDraft);
Local::writeDraftCursors(history->peer->id, localCursor, editCursor);
}
void HistoryWidget::cancelSendAction(History *history, SendActionType type) {
@ -3342,7 +3371,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query) {
History *h = App::history(toPeerId);
TextWithTags textWithTags = { '@' + bot->username + ' ' + query, TextWithTags::Tags() };
MessageCursor cursor = { textWithTags.text.size(), textWithTags.text.size(), QFIXED_MAX };
h->setMsgDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->setLocalDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
if (h == _history) {
applyDraft();
} else {
@ -3652,7 +3681,7 @@ void HistoryWidget::applyDraft(bool parseLinks) {
_replyToId = 0;
} else {
_editMsgId = 0;
_replyToId = readyToForward() ? 0 : _history->msgDraft()->msgId;
_replyToId = readyToForward() ? 0 : _history->localDraft()->msgId;
}
if (parseLinks) {
onPreviewParse();
@ -3665,6 +3694,12 @@ void HistoryWidget::applyDraft(bool parseLinks) {
}
}
void HistoryWidget::applyCloudDraft(History *history) {
if (_history == history) {
applyDraft();
}
}
void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool reload) {
MsgId wasMsgId = _showAtMsgId;
History *wasHistory = _history;
@ -3708,7 +3743,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
if (startBot && _peer->isUser() && _peer->asUser()->botInfo) {
if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id;
onBotStart();
_history->clearMsgDraft();
_history->clearLocalDraft();
applyDraft();
}
return;
@ -3726,24 +3761,15 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
clearAllLoadRequests();
if (_history) {
if (_editMsgId) {
_history->setEditDraft(std_::make_unique<HistoryEditDraft>(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId));
} else {
if (_replyToId || !_field.isEmpty()) {
_history->setMsgDraft(std_::make_unique<HistoryDraft>(_field, _replyToId, _previewCancelled));
} else {
_history->clearMsgDraft();
}
_history->clearEditDraft();
}
if (App::main()) App::main()->saveDraftToCloud();
if (_migrated) {
_migrated->clearEditDraft(); // use migrated draft only once
_migrated->clearLocalDraft(); // use migrated draft only once
_migrated->clearEditDraft();
}
auto msgDraft = _history->msgDraft();
auto localDraft = _history->localDraft();
auto editDraft = _history->editDraft();
writeDrafts(&msgDraft, &editDraft);
writeDrafts(&localDraft, &editDraft);
_history->showAtMsgId = _showAtMsgId;
@ -3848,7 +3874,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
if (_migrated) {
Local::readDraftsWithCursors(_migrated);
_migrated->clearEditDraft();
_history->takeMsgDraft(_migrated);
_history->takeLocalDraft(_migrated);
}
applyDraft(false);
@ -4725,7 +4751,7 @@ void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates,
cancelEdit();
}
if (auto editDraft = history->editDraft()) {
if (editDraft->saveRequest == req) {
if (editDraft->saveRequestId == req) {
history->clearEditDraft();
writeDrafts(history);
}
@ -4738,8 +4764,8 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp
_saveEditMsgRequestId = 0;
}
if (auto editDraft = history->editDraft()) {
if (editDraft->saveRequest == req) {
editDraft->saveRequest = 0;
if (editDraft->saveRequestId == req) {
editDraft->saveRequestId = 0;
}
}
@ -7204,10 +7230,10 @@ void HistoryWidget::onReplyToMessage() {
App::main()->cancelForwarding();
if (_editMsgId) {
if (auto msgDraft = _history->msgDraft()) {
msgDraft->msgId = to->id;
if (auto localDraft = _history->localDraft()) {
localDraft->msgId = to->id;
} else {
_history->setMsgDraft(std_::make_unique<HistoryDraft>(TextWithTags(), to->id, MessageCursor(), false));
_history->setLocalDraft(std_::make_unique<HistoryDraft>(TextWithTags(), to->id, MessageCursor(), false));
}
} else {
_replyEditMsg = to;
@ -7241,9 +7267,9 @@ void HistoryWidget::onEditMessage() {
delete box;
if (_replyToId || !_field.isEmpty()) {
_history->setMsgDraft(std_::make_unique<HistoryDraft>(_field, _replyToId, _previewCancelled));
_history->setLocalDraft(std_::make_unique<HistoryDraft>(_field, _replyToId, _previewCancelled));
} else {
_history->clearMsgDraft();
_history->clearLocalDraft();
}
auto original = to->originalText();
@ -7251,7 +7277,7 @@ void HistoryWidget::onEditMessage() {
auto editTags = textTagsFromEntities(original.entities);
TextWithTags editData = { editText, editTags };
MessageCursor cursor = { editText.size(), editText.size(), QFIXED_MAX };
_history->setEditDraft(std_::make_unique<HistoryEditDraft>(editData, to->id, cursor, false));
_history->setEditDraft(std_::make_unique<HistoryDraft>(editData, to->id, cursor, false));
applyDraft(false);
_previewData = nullptr;
@ -7368,12 +7394,12 @@ void HistoryWidget::cancelReply(bool lastKeyboardUsed) {
resizeEvent(0);
update();
} else if (auto msgDraft = (_history ? _history->msgDraft() : nullptr)) {
if (msgDraft->msgId) {
if (msgDraft->textWithTags.text.isEmpty()) {
_history->clearMsgDraft();
} else if (auto localDraft = (_history ? _history->localDraft() : nullptr)) {
if (localDraft->msgId) {
if (localDraft->textWithTags.text.isEmpty()) {
_history->clearLocalDraft();
} else {
msgDraft->msgId = 0;
localDraft->msgId = 0;
}
}
}

View File

@ -632,6 +632,9 @@ public:
void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false);
void clearDelayedShowAt();
void clearAllLoadRequests();
void saveFieldToHistoryLocalDraft();
void applyCloudDraft(History *history);
void contactsReceived();
void updateToEndVisibility();
@ -781,6 +784,7 @@ public slots:
void onDraftSaveDelayed();
void onDraftSave(bool delayed = false);
void onCloudDraftSave();
void updateStickers();
@ -959,7 +963,7 @@ private:
Q_DECLARE_FLAGS(TextUpdateEvents, TextUpdateEvent);
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(TextUpdateEvents);
void writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft);
void writeDrafts(HistoryDraft **localDraft, HistoryDraft **editDraft);
void writeDrafts(History *history);
void setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory);
void clearFieldText(TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory) {
@ -1088,7 +1092,7 @@ private:
uint64 _saveDraftStart = 0;
bool _saveDraftText = false;
QTimer _saveDraftTimer;
QTimer _saveDraftTimer, _saveCloudDraftTimer;
PlainShadow _sideShadow, _topShadow;
bool _inGrab = false;

View File

@ -2270,10 +2270,10 @@ namespace Local {
return _oldSettingsVersion;
}
void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft) {
void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft) {
if (!_working()) return;
if (msgDraft.msgId <= 0 && msgDraft.textWithTags.text.isEmpty() && editDraft.msgId <= 0) {
if (localDraft.msgId <= 0 && localDraft.textWithTags.text.isEmpty() && editDraft.msgId <= 0) {
auto i = _draftsMap.find(peer);
if (i != _draftsMap.cend()) {
clearKey(i.value());
@ -2291,17 +2291,17 @@ namespace Local {
_writeMap(WriteMapFast);
}
auto msgTags = FlatTextarea::serializeTagsList(msgDraft.textWithTags.tags);
auto msgTags = FlatTextarea::serializeTagsList(localDraft.textWithTags.tags);
auto editTags = FlatTextarea::serializeTagsList(editDraft.textWithTags.tags);
int size = sizeof(quint64);
size += Serialize::stringSize(msgDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32);
size += Serialize::stringSize(localDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32);
size += Serialize::stringSize(editDraft.textWithTags.text) + Serialize::bytearraySize(editTags) + 2 * sizeof(qint32);
EncryptedDescriptor data(size);
data.stream << quint64(peer);
data.stream << msgDraft.textWithTags.text << msgTags;
data.stream << qint32(msgDraft.msgId) << qint32(msgDraft.previewCancelled ? 1 : 0);
data.stream << localDraft.textWithTags.text << msgTags;
data.stream << qint32(localDraft.msgId) << qint32(localDraft.previewCancelled ? 1 : 0);
data.stream << editDraft.textWithTags.text << editTags;
data.stream << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0);
@ -2322,7 +2322,7 @@ namespace Local {
}
}
void _readDraftCursors(const PeerId &peer, MessageCursor &msgCursor, MessageCursor &editCursor) {
void _readDraftCursors(const PeerId &peer, MessageCursor &localCursor, MessageCursor &editCursor) {
DraftsMap::iterator j = _draftCursorsMap.find(peer);
if (j == _draftCursorsMap.cend()) {
return;
@ -2334,9 +2334,9 @@ namespace Local {
return;
}
quint64 draftPeer;
qint32 msgPosition = 0, msgAnchor = 0, msgScroll = QFIXED_MAX;
qint32 localPosition = 0, localAnchor = 0, localScroll = QFIXED_MAX;
qint32 editPosition = 0, editAnchor = 0, editScroll = QFIXED_MAX;
draft.stream >> draftPeer >> msgPosition >> msgAnchor >> msgScroll;
draft.stream >> draftPeer >> localPosition >> localAnchor >> localScroll;
if (!draft.stream.atEnd()) {
draft.stream >> editPosition >> editAnchor >> editScroll;
}
@ -2346,7 +2346,7 @@ namespace Local {
return;
}
msgCursor = MessageCursor(msgPosition, msgAnchor, msgScroll);
localCursor = MessageCursor(localPosition, localAnchor, localScroll);
editCursor = MessageCursor(editPosition, editAnchor, editScroll);
}
@ -2404,15 +2404,17 @@ namespace Local {
MessageCursor msgCursor, editCursor;
_readDraftCursors(peer, msgCursor, editCursor);
if (msgData.text.isEmpty() && !msgReplyTo) {
h->clearMsgDraft();
} else {
h->setMsgDraft(std_::make_unique<HistoryDraft>(msgData, msgReplyTo, msgCursor, msgPreviewCancelled));
if (!h->localDraft()) {
if (msgData.text.isEmpty() && !msgReplyTo) {
h->clearLocalDraft();
} else {
h->setLocalDraft(std_::make_unique<HistoryDraft>(msgData, msgReplyTo, msgCursor, msgPreviewCancelled));
}
}
if (!editMsgId) {
h->clearEditDraft();
} else {
h->setEditDraft(std_::make_unique<HistoryEditDraft>(editData, editMsgId, editCursor, editPreviewCancelled));
h->setEditDraft(std_::make_unique<HistoryDraft>(editData, editMsgId, editCursor, editPreviewCancelled));
}
}

View File

@ -116,9 +116,9 @@ namespace Local {
TextWithTags textWithTags;
bool previewCancelled;
};
void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft);
void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft);
void readDraftsWithCursors(History *h);
void writeDraftCursors(const PeerId &peer, const MessageCursor &msgCursor, const MessageCursor &editCursor);
void writeDraftCursors(const PeerId &peer, const MessageCursor &localCursor, const MessageCursor &editCursor);
bool hasDraftCursors(const PeerId &peer);
bool hasDraft(const PeerId &peer);

View File

@ -161,7 +161,7 @@ bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QStrin
History *h = App::history(peer);
TextWithTags textWithTags = { url + '\n' + text, TextWithTags::Tags() };
MessageCursor cursor = { url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX };
h->setMsgDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->setLocalDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->clearEditDraft();
bool opened = _history->peer() && (_history->peer()->id == peer);
if (opened) {
@ -181,7 +181,7 @@ bool MainWidget::onInlineSwitchChosen(const PeerId &peer, const QString &botAndQ
History *h = App::history(peer);
TextWithTags textWithTags = { botAndQuery, TextWithTags::Tags() };
MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX };
h->setMsgDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->setLocalDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->clearEditDraft();
bool opened = _history->peer() && (_history->peer()->id == peer);
if (opened) {
@ -916,8 +916,9 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
if (!v) return;
if (v->isEmpty()) {
if (peer->isChat() && peer->asChat()->haveLeft()) {
deleteConversation(peer, false);
if (peer->isChat() && !peer->asChat()->haveLeft()) {
History *h = App::historyLoaded(peer->id);
if (h) Local::addSavedPeer(peer, h->lastMsgDate);
} else if (peer->isChannel()) {
if (peer->asChannel()->inviter > 0 && peer->asChannel()->amIn()) {
if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) {
@ -929,8 +930,7 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu
}
}
} else {
History *h = App::historyLoaded(peer->id);
if (h) Local::addSavedPeer(peer, h->lastMsgDate);
deleteConversation(peer, false);
}
} else {
History *h = App::history(peer->id);
@ -1114,6 +1114,10 @@ void MainWidget::sendMessage(const MessageToSend &message) {
if (!sentEntities.c_vector().v.isEmpty()) {
sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
}
if (message.clearDraft) {
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
history->clearCloudDraft();
}
lastMessage = history->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(newId.msg), MTP_int(showFromName ? MTP::authedId() : 0), peerToMTP(history->peer->id), MTPnullFwdHeader, MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, localEntities, MTP_int(1), MTPint()), NewMessageUnread);
history->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_flags(sendFlags), history->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, sentEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, history->sendRequestId);
}
@ -3695,9 +3699,14 @@ void MainWidget::updateOnline(bool gotOtherOffline) {
_lastSetOnline = ms;
_onlineRequest = MTP::send(MTPaccount_UpdateStatus(MTP_bool(!isOnline)));
if (App::self()) App::self()->onlineTill = unixtime() + (isOnline ? (Global::OnlineUpdatePeriod() / 1000) : -1);
if (App::self()) {
App::self()->onlineTill = unixtime() + (isOnline ? (Global::OnlineUpdatePeriod() / 1000) : -1);
}
if (!isOnline) { // Went offline, so we need to save message draft to the cloud.
saveDraftToCloud();
}
_lastSetOnline = getms(true);
_lastSetOnline = ms;
updateOnlineDisplay();
} else if (isOnline) {
@ -3706,6 +3715,64 @@ void MainWidget::updateOnline(bool gotOtherOffline) {
_onlineTimer.start(updateIn);
}
void MainWidget::saveDraftToCloud() {
_history->saveFieldToHistoryLocalDraft();
auto peer = _history->peer();
if (auto history = App::historyLoaded(peer)) {
auto localDraft = history->localDraft();
auto cloudDraft = history->cloudDraft();
if (!historyDraftsAreEqual(localDraft, cloudDraft)) {
if (cloudDraft && cloudDraft->saveRequestId) {
MTP::cancel(cloudDraft->saveRequestId);
}
cloudDraft = history->createCloudDraft(localDraft);
MTPmessages_SaveDraft::Flags flags = 0;
auto &textWithTags = cloudDraft->textWithTags;
if (cloudDraft->previewCancelled) {
flags |= MTPmessages_SaveDraft::Flag::f_no_webpage;
}
if (cloudDraft->msgId) {
flags |= MTPmessages_SaveDraft::Flag::f_reply_to_msg_id;
}
if (!textWithTags.tags.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_entities;
}
auto entities = linksToMTP(entitiesFromTextTags(textWithTags.tags), true);
cloudDraft->saveRequestId = MTP::send(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), peer->input, MTP_string(textWithTags.text), entities), rpcDone(&MainWidget::saveCloudDraftDone, peer), rpcFail(&MainWidget::saveCloudDraftFail, peer));
}
}
}
void MainWidget::applyCloudDraft(History *history) {
_history->applyCloudDraft(history);
}
void MainWidget::saveCloudDraftDone(PeerData *peer, const MTPBool &result, mtpRequestId requestId) {
if (auto history = App::historyLoaded(peer)) {
if (auto cloudDraft = history->cloudDraft()) {
if (cloudDraft->saveRequestId == requestId) {
cloudDraft->saveRequestId = 0;
history->updateChatListEntry();
}
}
}
}
bool MainWidget::saveCloudDraftFail(PeerData *peer, const RPCError &error, mtpRequestId requestId) {
if (MTP::isDefaultHandledError(error)) return false;
if (auto history = App::historyLoaded(peer)) {
if (auto cloudDraft = history->cloudDraft()) {
if (cloudDraft->saveRequestId == requestId) {
history->clearCloudDraft();
}
}
}
return true;
}
void MainWidget::checkIdleFinish() {
if (this != App::main()) return;
if (psIdleTime() < uint64(Global::OfflineIdleTimeout())) {

View File

@ -232,6 +232,9 @@ public:
bool lastWasOnline() const;
uint64 lastSetOnline() const;
void saveDraftToCloud();
void applyCloudDraft(History *history);
int32 dlgsWidth() const;
void forwardLayer(int32 forwardSelected = 0); // -1 - send paths
@ -287,6 +290,7 @@ public:
MsgId replyTo = 0;
bool silent = false;
WebPageId webPageId = 0;
bool clearDraft = true;
};
void sendMessage(const MessageToSend &message);
void saveRecentHashtags(const QString &text);
@ -489,6 +493,9 @@ private:
void messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result);
void overviewLoaded(History *history, const MTPmessages_Messages &result, mtpRequestId req);
void saveCloudDraftDone(PeerData *peer, const MTPBool &result, mtpRequestId requestId);
bool saveCloudDraftFail(PeerData *peer, const RPCError &error, mtpRequestId requestId);
bool _started = false;
uint64 failedObjId = 0;

View File

@ -89,6 +89,7 @@ void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *s
message.textWithTags = { QString::fromUtf8(str), TextWithTags::Tags() };
message.replyTo = (msgid > 0 && !history->peer->isUser()) ? msgid : 0;
message.silent = false;
message.clearDraft = false;
App::main()->sendMessage(message);
}

View File

@ -3068,6 +3068,7 @@ void Text::clear() {
delete *i;
}
clearFields();
_text.clear();
}
void Text::clearFields() {