diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2ea05b6494..9ffb523029 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 8c9393ce7a..1ee892abe4 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -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 diff --git a/Telegram/SourceFiles/core/basic_types.cpp b/Telegram/SourceFiles/core/basic_types.cpp index 79851ca379..2ce138a993 100644 --- a/Telegram/SourceFiles/core/basic_types.cpp +++ b/Telegram/SourceFiles/core/basic_types.cpp @@ -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 { diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index def3b712b4..9d1bab3a6f 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -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); diff --git a/Telegram/SourceFiles/data/drafts.cpp b/Telegram/SourceFiles/data/drafts.cpp index 68b51bf9c5..84e6ef917a 100644 --- a/Telegram/SourceFiles/data/drafts.cpp +++ b/Telegram/SourceFiles/data/drafts.cpp @@ -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(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 diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 9d836fb58e..5057ec3df9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -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 -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); }); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index a050f5582e..c1d09de456 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -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(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(TextWithTags(), 0, MessageCursor(), false)); + cloudDraft()->date = QDateTime(); + } else { + auto existing = cloudDraft(); + if (!existing) { + setCloudDraft(std_::make_unique(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); } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 4487816435..b2ba3dbea6 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -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 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 &&draft) { - _msgDraft = std_::move(draft); + void setLocalDraft(std_::unique_ptr &&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 &&draft) { + void createLocalDraftFromCloud(); + void setCloudDraft(std_::unique_ptr &&draft) { + _cloudDraft = std_::move(draft); + cloudDraftTextCache.clear(); + } + HistoryDraft *createCloudDraft(HistoryDraft *fromDraft); + void setEditDraft(std_::unique_ptr &&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 _msgDraft; - std_::unique_ptr _editDraft; + std_::unique_ptr _localDraft, _cloudDraft; + std_::unique_ptr _editDraft; }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 0c4a05c3cc..738ab6eac6 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -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(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); + } else { + if (_replyToId || !_field.isEmpty()) { + _history->setLocalDraft(std_::make_unique(_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(textWithTags, 0, cursor, false)); + h->setLocalDraft(std_::make_unique(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(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); - } else { - if (_replyToId || !_field.isEmpty()) { - _history->setMsgDraft(std_::make_unique(_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(TextWithTags(), to->id, MessageCursor(), false)); + _history->setLocalDraft(std_::make_unique(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(_field, _replyToId, _previewCancelled)); + _history->setLocalDraft(std_::make_unique(_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(editData, to->id, cursor, false)); + _history->setEditDraft(std_::make_unique(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; } } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 8b2bd6ede2..d84f6ec4e8 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -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; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 7b233f1d4a..b4962429d1 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -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(msgData, msgReplyTo, msgCursor, msgPreviewCancelled)); + if (!h->localDraft()) { + if (msgData.text.isEmpty() && !msgReplyTo) { + h->clearLocalDraft(); + } else { + h->setLocalDraft(std_::make_unique(msgData, msgReplyTo, msgCursor, msgPreviewCancelled)); + } } if (!editMsgId) { h->clearEditDraft(); } else { - h->setEditDraft(std_::make_unique(editData, editMsgId, editCursor, editPreviewCancelled)); + h->setEditDraft(std_::make_unique(editData, editMsgId, editCursor, editPreviewCancelled)); } } diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index de30ce75e9..0f9a0fd11c 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -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); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 3d9b6c7df8..d99c08848b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -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(textWithTags, 0, cursor, false)); + h->setLocalDraft(std_::make_unique(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(textWithTags, 0, cursor, false)); + h->setLocalDraft(std_::make_unique(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())) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 881071b306..0dbebf701b 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -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; diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index cd38b00159..629b9767f8 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -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); } diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index f828a2f28d..d04c0caeaa 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -3068,6 +3068,7 @@ void Text::clear() { delete *i; } clearFields(); + _text.clear(); } void Text::clearFields() {