Improve editing messages with link previews.

Now preview state can be one of (allowed, cancelled, empty-in-edit).

In case of editing a message without preview we set the state to
empty-in-edit and it changes to allowed if the links in the message
are changed somehow.

That way we don't need to cancel the preview when editing a message
with a cancelled preview and at the same time adding a link to
a message that had no preview in the first place will add a preview.
This commit is contained in:
John Preston 2021-01-29 15:27:17 +04:00
parent fc4ed2ff91
commit 8f0e23bb25
16 changed files with 159 additions and 82 deletions

View file

@ -2449,7 +2449,7 @@ void ApiWrap::saveDraftsToCloud() {
auto flags = MTPmessages_SaveDraft::Flags(0); auto flags = MTPmessages_SaveDraft::Flags(0);
auto &textWithTags = cloudDraft->textWithTags; auto &textWithTags = cloudDraft->textWithTags;
if (cloudDraft->previewCancelled) { if (cloudDraft->previewState != Data::PreviewState::Allowed) {
flags |= MTPmessages_SaveDraft::Flag::f_no_webpage; flags |= MTPmessages_SaveDraft::Flag::f_no_webpage;
} }
if (cloudDraft->msgId) { if (cloudDraft->msgId) {

View file

@ -501,6 +501,11 @@ MessageLinksParser::MessageLinksParser(not_null<Ui::InputField*> field)
_field->installEventFilter(this); _field->installEventFilter(this);
} }
void MessageLinksParser::parseNow() {
_timer.cancel();
parse();
}
bool MessageLinksParser::eventFilter(QObject *object, QEvent *event) { bool MessageLinksParser::eventFilter(QObject *object, QEvent *event) {
if (object == _field) { if (object == _field) {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {

View file

@ -71,7 +71,9 @@ class MessageLinksParser : private QObject {
public: public:
MessageLinksParser(not_null<Ui::InputField*> field); MessageLinksParser(not_null<Ui::InputField*> field);
const rpl::variable<QStringList> &list() const; void parseNow();
[[nodiscard]] const rpl::variable<QStringList> &list() const;
protected: protected:
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;

View file

@ -18,32 +18,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h" #include "storage/localstorage.h"
namespace Data { namespace Data {
namespace {
} // namespace
Draft::Draft( Draft::Draft(
const TextWithTags &textWithTags, const TextWithTags &textWithTags,
MsgId msgId, MsgId msgId,
const MessageCursor &cursor, const MessageCursor &cursor,
bool previewCancelled, PreviewState previewState,
mtpRequestId saveRequestId) mtpRequestId saveRequestId)
: textWithTags(textWithTags) : textWithTags(textWithTags)
, msgId(msgId) , msgId(msgId)
, cursor(cursor) , cursor(cursor)
, previewCancelled(previewCancelled) , previewState(previewState)
, saveRequestId(saveRequestId) { , saveRequestId(saveRequestId) {
} }
Draft::Draft( Draft::Draft(
not_null<const Ui::InputField*> field, not_null<const Ui::InputField*> field,
MsgId msgId, MsgId msgId,
bool previewCancelled, PreviewState previewState,
mtpRequestId saveRequestId) mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags()) : textWithTags(field->getTextWithTags())
, msgId(msgId) , msgId(msgId)
, cursor(field) , cursor(field)
, previewCancelled(previewCancelled) { , previewState(previewState) {
} }
void ApplyPeerCloudDraft( void ApplyPeerCloudDraft(
@ -66,7 +63,9 @@ void ApplyPeerCloudDraft(
textWithTags, textWithTags,
replyTo, replyTo,
MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX),
draft.is_no_webpage()); (draft.is_no_webpage()
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed));
cloudDraft->date = draft.vdate().v; cloudDraft->date = draft.vdate().v;
history->setCloudDraft(std::move(cloudDraft)); history->setCloudDraft(std::move(cloudDraft));

View file

@ -26,25 +26,31 @@ void ClearPeerCloudDraft(
PeerId peerId, PeerId peerId,
TimeId date); TimeId date);
enum class PreviewState : char {
Allowed,
Cancelled,
EmptyOnEdit,
};
struct Draft { struct Draft {
Draft() = default; Draft() = default;
Draft( Draft(
const TextWithTags &textWithTags, const TextWithTags &textWithTags,
MsgId msgId, MsgId msgId,
const MessageCursor &cursor, const MessageCursor &cursor,
bool previewCancelled, PreviewState previewState,
mtpRequestId saveRequestId = 0); mtpRequestId saveRequestId = 0);
Draft( Draft(
not_null<const Ui::InputField*> field, not_null<const Ui::InputField*> field,
MsgId msgId, MsgId msgId,
bool previewCancelled, PreviewState previewState,
mtpRequestId saveRequestId = 0); mtpRequestId saveRequestId = 0);
TimeId date = 0; 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;
bool previewCancelled = false; PreviewState previewState = PreviewState::Allowed;
mtpRequestId saveRequestId = 0; mtpRequestId saveRequestId = 0;
}; };
@ -144,7 +150,7 @@ inline bool draftsAreEqual(const Draft *a, const Draft *b) {
return (a->textWithTags == b->textWithTags) return (a->textWithTags == b->textWithTags)
&& (a->msgId == b->msgId) && (a->msgId == b->msgId)
&& (a->previewCancelled == b->previewCancelled); && (a->previewState == b->previewState);
} }
} // namespace Data } // namespace Data

View file

@ -215,13 +215,13 @@ void History::createLocalDraftFromCloud() {
draft->textWithTags, draft->textWithTags,
draft->msgId, draft->msgId,
draft->cursor, draft->cursor,
draft->previewCancelled)); draft->previewState));
existing = localDraft(); existing = localDraft();
} else if (existing != draft) { } else if (existing != draft) {
existing->textWithTags = draft->textWithTags; existing->textWithTags = draft->textWithTags;
existing->msgId = draft->msgId; existing->msgId = draft->msgId;
existing->cursor = draft->cursor; existing->cursor = draft->cursor;
existing->previewCancelled = draft->previewCancelled; existing->previewState = draft->previewState;
} }
existing->date = draft->date; existing->date = draft->date;
} }
@ -280,7 +280,7 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
TextWithTags(), TextWithTags(),
0, 0,
MessageCursor(), MessageCursor(),
false)); Data::PreviewState::Allowed));
cloudDraft()->date = TimeId(0); cloudDraft()->date = TimeId(0);
} else { } else {
auto existing = cloudDraft(); auto existing = cloudDraft();
@ -289,13 +289,13 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
fromDraft->textWithTags, fromDraft->textWithTags,
fromDraft->msgId, fromDraft->msgId,
fromDraft->cursor, fromDraft->cursor,
fromDraft->previewCancelled)); fromDraft->previewState));
existing = cloudDraft(); existing = cloudDraft();
} else if (existing != fromDraft) { } else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags; existing->textWithTags = fromDraft->textWithTags;
existing->msgId = fromDraft->msgId; existing->msgId = fromDraft->msgId;
existing->cursor = fromDraft->cursor; existing->cursor = fromDraft->cursor;
existing->previewCancelled = fromDraft->previewCancelled; existing->previewState = fromDraft->previewState;
} }
existing->date = base::unixtime::now(); existing->date = base::unixtime::now();
} }

View file

@ -174,6 +174,7 @@ HistoryWidget::HistoryWidget(
, _updateEditTimeLeftDisplay([=] { updateField(); }) , _updateEditTimeLeftDisplay([=] { updateField(); })
, _fieldBarCancel(this, st::historyReplyCancel) , _fieldBarCancel(this, st::historyReplyCancel)
, _previewTimer([=] { requestPreview(); }) , _previewTimer([=] { requestPreview(); })
, _previewState(Data::PreviewState::Allowed)
, _topBar(this, controller) , _topBar(this, controller)
, _scroll(this, st::historyScroll, false) , _scroll(this, st::historyScroll, false)
, _updateHistoryItems([=] { updateHistoryItemsByTimer(); }) , _updateHistoryItems([=] { updateHistoryItemsByTimer(); })
@ -335,6 +336,10 @@ HistoryWidget::HistoryWidget(
_fieldLinksParser = std::make_unique<MessageLinksParser>(_field); _fieldLinksParser = std::make_unique<MessageLinksParser>(_field);
_fieldLinksParser->list().changes( _fieldLinksParser->list().changes(
) | rpl::start_with_next([=](QStringList &&parsed) { ) | rpl::start_with_next([=](QStringList &&parsed) {
if (_previewState == Data::PreviewState::EmptyOnEdit
&& _parsedLinks != parsed) {
_previewState = Data::PreviewState::Allowed;
}
_parsedLinks = std::move(parsed); _parsedLinks = std::move(parsed);
checkPreview(); checkPreview();
}, lifetime()); }, lifetime());
@ -1328,7 +1333,7 @@ void HistoryWidget::fieldChanged() {
updateSendButtonType(); updateSendButtonType();
if (!HasSendText(_field)) { if (!HasSendText(_field)) {
_previewCancelled = false; _previewState = Data::PreviewState::Allowed;
} }
if (updateCmdStartShown()) { if (updateCmdStartShown()) {
updateControlsVisibility(); updateControlsVisibility();
@ -1377,10 +1382,17 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
if (!_history) return; if (!_history) return;
if (_editMsgId) { if (_editMsgId) {
_history->setLocalEditDraft(std::make_unique<Data::Draft>(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); _history->setLocalEditDraft(std::make_unique<Data::Draft>(
_field,
_editMsgId,
_previewState,
_saveEditMsgRequestId));
} else { } else {
if (_replyToId || !_field->empty()) { if (_replyToId || !_field->empty()) {
_history->setLocalDraft(std::make_unique<Data::Draft>(_field, _replyToId, _previewCancelled)); _history->setLocalDraft(std::make_unique<Data::Draft>(
_field,
_replyToId,
_previewState));
} else { } else {
_history->clearLocalDraft(); _history->clearLocalDraft();
} }
@ -1401,7 +1413,7 @@ void HistoryWidget::writeDraftTexts() {
Storage::MessageDraft{ Storage::MessageDraft{
_editMsgId ? _editMsgId : _replyToId, _editMsgId ? _editMsgId : _replyToId,
_field->getTextWithTags(), _field->getTextWithTags(),
_previewCancelled, _previewState,
}); });
if (_migrated) { if (_migrated) {
_migrated->clearDrafts(); _migrated->clearDrafts();
@ -1476,7 +1488,11 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
if (_history) { if (_history) {
TextWithTags textWithTags = { '@' + samePeerBot->username + ' ' + query, TextWithTags::Tags() }; TextWithTags textWithTags = { '@' + samePeerBot->username + ' ' + query, TextWithTags::Tags() };
MessageCursor cursor = { textWithTags.text.size(), textWithTags.text.size(), QFIXED_MAX }; MessageCursor cursor = { textWithTags.text.size(), textWithTags.text.size(), QFIXED_MAX };
_history->setLocalDraft(std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); _history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags,
0,
cursor,
Data::PreviewState::Allowed));
applyDraft(); applyDraft();
return true; return true;
} }
@ -1497,7 +1513,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
textWithTags, textWithTags,
to.currentReplyToId, to.currentReplyToId,
cursor, cursor,
false); Data::PreviewState::Allowed);
if (to.section == Section::Replies) { if (to.section == Section::Replies) {
history->setDraft( history->setDraft(
@ -1653,8 +1669,15 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
setFieldText(draft->textWithTags, 0, fieldHistoryAction); setFieldText(draft->textWithTags, 0, fieldHistoryAction);
_field->setFocus(); _field->setFocus();
draft->cursor.applyTo(_field); draft->cursor.applyTo(_field);
_textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; _textUpdateEvents = TextUpdateEvent::SaveDraft
_previewCancelled = draft->previewCancelled; | TextUpdateEvent::SendTyping;
// Save links from _field to _parsedLinks without generating preview.
_previewState = Data::PreviewState::Cancelled;
_fieldLinksParser->parseNow();
_parsedLinks = _fieldLinksParser->list().current();
_previewState = draft->previewState;
_replyEditMsg = nullptr; _replyEditMsg = nullptr;
if (const auto editDraft = _history->localEditDraft()) { if (const auto editDraft = _history->localEditDraft()) {
_editMsgId = editDraft->msgId; _editMsgId = editDraft->msgId;
@ -2986,7 +3009,7 @@ void HistoryWidget::saveEditMsg() {
cancelEdit(); cancelEdit();
return; return;
} }
const auto webPageId = _previewCancelled const auto webPageId = (_previewState != Data::PreviewState::Allowed)
? CancelledWebPageId ? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0) : ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id ? _previewData->id
@ -3106,7 +3129,7 @@ void HistoryWidget::send(Api::SendOptions options) {
return; return;
} }
const auto webPageId = _previewCancelled const auto webPageId = (_previewState != Data::PreviewState::Allowed)
? CancelledWebPageId ? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0) : ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id ? _previewData->id
@ -5580,7 +5603,7 @@ void HistoryWidget::setFieldText(
| TextUpdateEvent::SendTyping; | TextUpdateEvent::SendTyping;
previewCancel(); previewCancel();
_previewCancelled = false; _previewState = Data::PreviewState::Allowed;
} }
void HistoryWidget::clearFieldText( void HistoryWidget::clearFieldText(
@ -5623,7 +5646,7 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
TextWithTags(), TextWithTags(),
item->id, item->id,
MessageCursor(), MessageCursor(),
false)); Data::PreviewState::Allowed));
} }
} else { } else {
_replyEditMsg = item; _replyEditMsg = item;
@ -5670,7 +5693,7 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
_field, _field,
_replyToId, _replyToId,
_previewCancelled)); _previewState));
} else { } else {
_history->clearLocalDraft(); _history->clearLocalDraft();
} }
@ -5688,12 +5711,14 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
} }
return nullptr; return nullptr;
}(); }();
const auto previewCancelled = !previewPage; const auto previewState = previewPage
? Data::PreviewState::Allowed
: Data::PreviewState::EmptyOnEdit;
_history->setLocalEditDraft(std::make_unique<Data::Draft>( _history->setLocalEditDraft(std::make_unique<Data::Draft>(
editData, editData,
item->id, item->id,
cursor, cursor,
previewCancelled)); previewState));
applyDraft(); applyDraft();
_previewData = previewPage; _previewData = previewPage;
@ -5853,7 +5878,7 @@ void HistoryWidget::cancelFieldAreaState() {
Ui::hideLayer(); Ui::hideLayer();
_replyForwardPressed = false; _replyForwardPressed = false;
if (_previewData && _previewData->pendingTill >= 0) { if (_previewData && _previewData->pendingTill >= 0) {
_previewCancelled = true; _previewState = Data::PreviewState::Cancelled;
previewCancel(); previewCancel();
_saveDraftText = true; _saveDraftText = true;
@ -5881,7 +5906,7 @@ void HistoryWidget::checkPreview() {
const auto previewRestricted = [&] { const auto previewRestricted = [&] {
return _peer && _peer->amRestricted(ChatRestriction::f_embed_links); return _peer && _peer->amRestricted(ChatRestriction::f_embed_links);
}(); }();
if (_previewCancelled || previewRestricted) { if (_previewState != Data::PreviewState::Allowed || previewRestricted) {
previewCancel(); previewCancel();
return; return;
} }
@ -5929,7 +5954,10 @@ void HistoryWidget::requestPreview() {
}).send(); }).send();
} }
void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtpRequestId req) { void HistoryWidget::gotPreview(
QString links,
const MTPMessageMedia &result,
mtpRequestId req) {
if (req == _previewRequest) { if (req == _previewRequest) {
_previewRequest = 0; _previewRequest = 0;
} }
@ -5937,10 +5965,12 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp
const auto &data = result.c_messageMediaWebPage().vwebpage(); const auto &data = result.c_messageMediaWebPage().vwebpage();
const auto page = session().data().processWebpage(data); const auto page = session().data().processWebpage(data);
_previewCache.insert(links, page->id); _previewCache.insert(links, page->id);
if (page->pendingTill > 0 && page->pendingTill <= base::unixtime::now()) { if (page->pendingTill > 0
&& page->pendingTill <= base::unixtime::now()) {
page->pendingTill = -1; page->pendingTill = -1;
} }
if (links == _previewLinks && !_previewCancelled) { if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
_previewData = (page->id && page->pendingTill >= 0) _previewData = (page->id && page->pendingTill >= 0)
? page.get() ? page.get()
: nullptr; : nullptr;
@ -5949,7 +5979,8 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp
session().data().sendWebPageGamePollNotifications(); session().data().sendWebPageGamePollNotifications();
} else if (result.type() == mtpc_messageMediaEmpty) { } else if (result.type() == mtpc_messageMediaEmpty) {
_previewCache.insert(links, 0); _previewCache.insert(links, 0);
if (links == _previewLinks && !_previewCancelled) { if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
_previewData = nullptr; _previewData = nullptr;
updatePreview(); updatePreview();
} }

View file

@ -25,6 +25,10 @@ struct SendingAlbum;
enum class SendMediaType; enum class SendMediaType;
class MessageLinksParser; class MessageLinksParser;
namespace Data {
enum class PreviewState : char;
} // namespace Data
namespace SendMenu { namespace SendMenu {
enum class Type; enum class Type;
} // namespace SendMenu } // namespace SendMenu
@ -617,7 +621,7 @@ private:
Ui::Text::String _previewTitle; Ui::Text::String _previewTitle;
Ui::Text::String _previewDescription; Ui::Text::String _previewDescription;
base::Timer _previewTimer; base::Timer _previewTimer;
bool _previewCancelled = false; Data::PreviewState _previewState = Data::PreviewState();
bool _replyForwardPressed = false; bool _replyForwardPressed = false;

View file

@ -612,7 +612,8 @@ ComposeControls::ComposeControls(
_send, _send,
st::historySendSize.height())) st::historySendSize.height()))
, _sendMenuType(sendMenuType) , _sendMenuType(sendMenuType)
, _saveDraftTimer([=] { saveDraft(); }) { , _saveDraftTimer([=] { saveDraft(); })
, _previewState(Data::PreviewState::Allowed) {
init(); init();
} }
@ -865,7 +866,7 @@ void ComposeControls::setFieldText(
| TextUpdateEvent::SendTyping; | TextUpdateEvent::SendTyping;
_previewCancel(); _previewCancel();
_previewCancelled = false; _previewState = Data::PreviewState::Allowed;
} }
void ComposeControls::saveFieldToHistoryLocalDraft() { void ComposeControls::saveFieldToHistoryLocalDraft() {
@ -880,7 +881,7 @@ void ComposeControls::saveFieldToHistoryLocalDraft() {
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
_field, _field,
_header->getDraftMessageId(), _header->getDraftMessageId(),
_previewCancelled)); _previewState));
} else { } else {
_history->clearDraft(draftKeyCurrent()); _history->clearDraft(draftKeyCurrent());
} }
@ -964,7 +965,7 @@ void ComposeControls::init() {
_header->previewCancelled( _header->previewCancelled(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_previewCancelled = true; _previewState = Data::PreviewState::Cancelled;
_saveDraftText = true; _saveDraftText = true;
_saveDraftStart = crl::now(); _saveDraftStart = crl::now();
saveDraft(); saveDraft();
@ -1224,7 +1225,7 @@ void ComposeControls::fieldChanged() {
} }
updateSendButtonType(); updateSendButtonType();
if (!HasSendText(_field)) { if (!HasSendText(_field)) {
_previewCancelled = false; _previewState = Data::PreviewState::Allowed;
} }
if (updateBotCommandShown()) { if (updateBotCommandShown()) {
updateControlsVisibility(); updateControlsVisibility();
@ -1294,7 +1295,7 @@ void ComposeControls::writeDraftTexts() {
Storage::MessageDraft{ Storage::MessageDraft{
_header->getDraftMessageId(), _header->getDraftMessageId(),
_field->getTextWithTags(), _field->getTextWithTags(),
_previewCancelled, _previewState,
}); });
} }
@ -1353,7 +1354,8 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
_field->setFocus(); _field->setFocus();
draft->cursor.applyTo(_field); draft->cursor.applyTo(_field);
_textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; _textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping;
_previewCancelled = draft->previewCancelled; _previewSetState(draft->previewState);
if (draft == editDraft) { if (draft == editDraft) {
_header->editMessage({ _history->channelId(), draft->msgId }); _header->editMessage({ _history->channelId(), draft->msgId });
_header->replyToMessage({}); _header->replyToMessage({});
@ -1837,14 +1839,16 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
} }
return nullptr; return nullptr;
}(); }();
const auto previewCancelled = !previewPage; const auto previewState = previewPage
? Data::PreviewState::Allowed
: Data::PreviewState::EmptyOnEdit;
_history->setDraft( _history->setDraft(
draftKey(DraftType::Edit), draftKey(DraftType::Edit),
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
editData, editData,
item->id, item->id,
cursor, cursor,
previewCancelled)); previewState));
applyDraft(); applyDraft();
if (_autocomplete) { if (_autocomplete) {
@ -1883,7 +1887,7 @@ void ComposeControls::replyToMessage(FullMsgId id) {
TextWithTags(), TextWithTags(),
id.msg, id.msg,
MessageCursor(), MessageCursor(),
false)); Data::PreviewState::Allowed));
} }
} else { } else {
_header->replyToMessage(id); _header->replyToMessage(id);
@ -1995,7 +1999,8 @@ void ComposeControls::initWebpageProcess() {
if (till > 0 && till <= base::unixtime::now()) { if (till > 0 && till <= base::unixtime::now()) {
till = -1; till = -1;
} }
if (links == *previewLinks && !_previewCancelled) { if (links == *previewLinks
&& _previewState == Data::PreviewState::Allowed) {
*previewData = (page->id && page->pendingTill >= 0) *previewData = (page->id && page->pendingTill >= 0)
? page.get() ? page.get()
: nullptr; : nullptr;
@ -2003,7 +2008,8 @@ void ComposeControls::initWebpageProcess() {
} }
}, [=](const MTPDmessageMediaEmpty &d) { }, [=](const MTPDmessageMediaEmpty &d) {
previewCache->insert({ links, 0 }); previewCache->insert({ links, 0 });
if (links == *previewLinks && !_previewCancelled) { if (links == *previewLinks
&& _previewState == Data::PreviewState::Allowed) {
*previewData = nullptr; *previewData = nullptr;
updatePreview(); updatePreview();
} }
@ -2032,7 +2038,8 @@ void ComposeControls::initWebpageProcess() {
const auto checkPreview = crl::guard(_wrap.get(), [=] { const auto checkPreview = crl::guard(_wrap.get(), [=] {
const auto previewRestricted = peer const auto previewRestricted = peer
&& peer->amRestricted(ChatRestriction::f_embed_links); && peer->amRestricted(ChatRestriction::f_embed_links);
if (_previewCancelled || previewRestricted) { if (_previewState != Data::PreviewState::Allowed
|| previewRestricted) {
_previewCancel(); _previewCancel();
return; return;
} }
@ -2105,8 +2112,20 @@ void ComposeControls::initWebpageProcess() {
const auto fieldLinksParser = const auto fieldLinksParser =
lifetime.make_state<MessageLinksParser>(_field); lifetime.make_state<MessageLinksParser>(_field);
_previewSetState = [=](Data::PreviewState state) {
// Save links from _field to _parsedLinks without generating preview.
_previewState = Data::PreviewState::Cancelled;
fieldLinksParser->parseNow();
*parsedLinks = fieldLinksParser->list().current();
_previewState = state;
};
fieldLinksParser->list().changes( fieldLinksParser->list().changes(
) | rpl::start_with_next([=](QStringList &&parsed) { ) | rpl::start_with_next([=](QStringList &&parsed) {
if (_previewState == Data::PreviewState::EmptyOnEdit
&& *parsedLinks != parsed) {
_previewState = Data::PreviewState::Allowed;
}
*parsedLinks = std::move(parsed); *parsedLinks = std::move(parsed);
checkPreview(); checkPreview();

View file

@ -34,6 +34,7 @@ namespace Data {
struct MessagePosition; struct MessagePosition;
struct Draft; struct Draft;
class DraftKey; class DraftKey;
enum class PreviewState : char;
} // namespace Data } // namespace Data
namespace InlineBots { namespace InlineBots {
@ -307,7 +308,8 @@ private:
bool _botCommandShown = false; bool _botCommandShown = false;
Fn<void()> _previewCancel; Fn<void()> _previewCancel;
bool _previewCancelled = false; Fn<void(Data::PreviewState)> _previewSetState;
Data::PreviewState _previewState = Data::PreviewState();
rpl::lifetime _uploaderSubscriptions; rpl::lifetime _uploaderSubscriptions;

View file

@ -902,11 +902,7 @@ void RepliesWidget::send(Api::SendOptions options) {
return; return;
} }
const auto webPageId = _composeControls->webPageId();/* _previewCancelled const auto webPageId = _composeControls->webPageId();
? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id
: WebPageId(0));*/
auto message = ApiWrap::MessageToSend(_history); auto message = ApiWrap::MessageToSend(_history);
message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); message.textWithTags = _composeControls->getTextWithAppliedMarkdown();

View file

@ -523,11 +523,7 @@ void ScheduledWidget::send() {
} }
void ScheduledWidget::send(Api::SendOptions options) { void ScheduledWidget::send(Api::SendOptions options) {
const auto webPageId = _composeControls->webPageId();/* _previewCancelled const auto webPageId = _composeControls->webPageId();
? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id
: WebPageId(0));*/
auto message = ApiWrap::MessageToSend(_history); auto message = ApiWrap::MessageToSend(_history);
message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); message.textWithTags = _composeControls->getTextWithAppliedMarkdown();

View file

@ -548,8 +548,11 @@ bool MainWidget::shareUrl(
QFIXED_MAX QFIXED_MAX
}; };
auto history = peer->owner().history(peer); auto history = peer->owner().history(peer);
history->setLocalDraft( history->setLocalDraft(std::make_unique<Data::Draft>(
std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); textWithTags,
0,
cursor,
Data::PreviewState::Allowed));
history->clearLocalEditDraft(); history->clearLocalEditDraft();
history->session().changes().historyUpdated( history->session().changes().historyUpdated(
history, history,
@ -576,7 +579,11 @@ bool MainWidget::inlineSwitchChosen(PeerId peerId, const QString &botAndQuery) {
const auto h = peer->owner().history(peer); const auto h = peer->owner().history(peer);
TextWithTags textWithTags = { botAndQuery, TextWithTags::Tags() }; TextWithTags textWithTags = { botAndQuery, TextWithTags::Tags() };
MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX }; MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX };
h->setLocalDraft(std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); h->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags,
0,
cursor,
Data::PreviewState::Allowed));
h->clearLocalEditDraft(); h->clearLocalEditDraft();
h->session().changes().historyUpdated( h->session().changes().historyUpdated(
h, h,

View file

@ -958,7 +958,7 @@ void EnumerateDrafts(
key, key,
draft->msgId, draft->msgId,
draft->textWithTags, draft->textWithTags,
draft->previewCancelled, draft->previewState,
draft->cursor); draft->cursor);
} }
if (replaceKey if (replaceKey
@ -969,7 +969,7 @@ void EnumerateDrafts(
replaceKey, replaceKey,
replaceDraft.msgId, replaceDraft.msgId,
replaceDraft.textWithTags, replaceDraft.textWithTags,
replaceDraft.previewCancelled, replaceDraft.previewState,
replaceCursor); replaceCursor);
} }
} }
@ -1017,12 +1017,12 @@ void Account::writeDrafts(
auto&&, // key auto&&, // key
MsgId, // msgId MsgId, // msgId
const TextWithTags &text, const TextWithTags &text,
bool, // previewCancelled Data::PreviewState,
auto&&) { // cursor auto&&) { // cursor
size += sizeof(qint32) // key size += sizeof(qint32) // key
+ Serialize::stringSize(text.text) + Serialize::stringSize(text.text)
+ sizeof(quint32) + TextUtilities::SerializeTagsSize(text.tags) + sizeof(quint32) + TextUtilities::SerializeTagsSize(text.tags)
+ 2 * sizeof(qint32); // msgId, previewCancelled + 2 * sizeof(qint32); // msgId, previewState
}; };
EnumerateDrafts( EnumerateDrafts(
map, map,
@ -1043,14 +1043,14 @@ void Account::writeDrafts(
const Data::DraftKey &key, const Data::DraftKey &key,
MsgId msgId, MsgId msgId,
const TextWithTags &text, const TextWithTags &text,
bool previewCancelled, Data::PreviewState previewState,
auto&&) { // cursor auto&&) { // cursor
data.stream data.stream
<< key.serialize() << key.serialize()
<< text.text << text.text
<< TextUtilities::SerializeTags(text.tags) << TextUtilities::SerializeTags(text.tags)
<< qint32(msgId) << qint32(msgId)
<< qint32(previewCancelled ? 1 : 0); << qint32(previewState);
}; };
EnumerateDrafts( EnumerateDrafts(
map, map,
@ -1109,7 +1109,7 @@ void Account::writeDraftCursors(
auto&&, // key auto&&, // key
MsgId, // msgId MsgId, // msgId
auto&&, // text auto&&, // text
bool, // previewCancelled Data::PreviewState,
const MessageCursor &cursor) { // cursor const MessageCursor &cursor) { // cursor
data.stream data.stream
<< qint32(cursor.position) << qint32(cursor.position)
@ -1249,23 +1249,29 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
TextWithTags data; TextWithTags data;
QByteArray tagsSerialized; QByteArray tagsSerialized;
qint32 keyValue = 0, messageId = 0, previewCancelled = 0; qint32 keyValue = 0, messageId = 0, uncheckedPreviewState = 0;
draft.stream draft.stream
>> keyValue >> keyValue
>> data.text >> data.text
>> tagsSerialized >> tagsSerialized
>> messageId >> messageId
>> previewCancelled; >> uncheckedPreviewState;
data.tags = TextUtilities::DeserializeTags( data.tags = TextUtilities::DeserializeTags(
tagsSerialized, tagsSerialized,
data.text.size()); data.text.size());
auto previewState = Data::PreviewState::Allowed;
switch (static_cast<Data::PreviewState>(uncheckedPreviewState)) {
case Data::PreviewState::Cancelled:
case Data::PreviewState::EmptyOnEdit:
previewState = Data::PreviewState(uncheckedPreviewState);
}
const auto key = Data::DraftKey::FromSerialized(keyValue); const auto key = Data::DraftKey::FromSerialized(keyValue);
if (key && key != Data::DraftKey::Cloud()) { if (key && key != Data::DraftKey::Cloud()) {
map.emplace(key, std::make_unique<Data::Draft>( map.emplace(key, std::make_unique<Data::Draft>(
data, data,
messageId, messageId,
MessageCursor(), MessageCursor(),
previewCancelled)); previewState));
} }
} }
if (draft.stream.status() != QDataStream::Ok) { if (draft.stream.status() != QDataStream::Ok) {
@ -1326,14 +1332,18 @@ void Account::readDraftsWithCursorsLegacy(
msgData, msgData,
msgReplyTo, msgReplyTo,
MessageCursor(), MessageCursor(),
msgPreviewCancelled)); (msgPreviewCancelled
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed)));
} }
if (editMsgId) { if (editMsgId) {
map.emplace(Data::DraftKey::LocalEdit(), std::make_unique<Data::Draft>( map.emplace(Data::DraftKey::LocalEdit(), std::make_unique<Data::Draft>(
editData, editData,
editMsgId, editMsgId,
MessageCursor(), MessageCursor(),
editPreviewCancelled)); (editPreviewCancelled
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed)));
} }
readDraftCursors(peerId, map); readDraftCursors(peerId, map);
history->setDraftsMap(std::move(map)); history->setDraftsMap(std::move(map));

View file

@ -52,7 +52,7 @@ enum class StartResult : uchar;
struct MessageDraft { struct MessageDraft {
MsgId msgId = 0; MsgId msgId = 0;
TextWithTags textWithTags; TextWithTags textWithTags;
bool previewCancelled = false; Data::PreviewState previewState = Data::PreviewState::Allowed;
}; };
class Account final { class Account final {

View file

@ -155,7 +155,7 @@ Data::Draft OccupiedDraft(const QString &normalizedName) {
+ normalizedName }, + normalizedName },
MsgId(0), MsgId(0),
MessageCursor(), MessageCursor(),
false Data::PreviewState::Allowed
}; };
} }