Update API scheme, rich preview drafts.

This commit is contained in:
John Preston 2023-10-19 18:20:27 +04:00
parent b2e8e0431e
commit b1823d981b
21 changed files with 268 additions and 174 deletions

View File

@ -2135,8 +2135,10 @@ void ApiWrap::saveDraftsToCloud() {
auto flags = MTPmessages_SaveDraft::Flags(0);
auto &textWithTags = cloudDraft->textWithTags;
if (cloudDraft->previewState != Data::PreviewState::Allowed) {
if (cloudDraft->webpage.removed) {
flags |= MTPmessages_SaveDraft::Flag::f_no_webpage;
} else if (!cloudDraft->webpage.url.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_media;
}
if (cloudDraft->reply.messageId || cloudDraft->reply.topicRootId) {
flags |= MTPmessages_SaveDraft::Flag::f_reply_to;
@ -2149,6 +2151,7 @@ void ApiWrap::saveDraftsToCloud() {
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
Api::ConvertOption::SkipLocal);
using PageFlag = MTPDinputMediaWebPage::Flag;
history->startSavingCloudDraft(topicRootId);
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
MTP_flags(flags),
@ -2156,7 +2159,15 @@ void ApiWrap::saveDraftsToCloud() {
history->peer->input,
MTP_string(textWithTags.text),
entities,
MTPInputMedia()
MTP_inputMediaWebPage(
MTP_flags(PageFlag::f_optional
| (cloudDraft->webpage.forceLargeMedia
? PageFlag::f_force_large_media
: PageFlag())
| (cloudDraft->webpage.forceSmallMedia
? PageFlag::f_force_small_media
: PageFlag())),
MTP_string(cloudDraft->webpage.url))
)).done([=](const MTPBool &result, const MTP::Response &response) {
const auto requestId = response.requestId;
history->finishSavingCloudDraft(
@ -2243,7 +2254,7 @@ void ApiWrap::gotStickerSet(
}
void ApiWrap::requestWebPageDelayed(not_null<WebPageData*> page) {
if (page->pendingTill <= 0) {
if (page->failed || !page->pendingTill) {
return;
}
_webPagesPending.emplace(page, 0);
@ -2548,7 +2559,8 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &resu
for (auto i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
if (i->second == req) {
if (i->first->pendingTill > 0) {
i->first->pendingTill = -1;
i->first->pendingTill = 0;
i->first->failed = 1;
_session->data().notifyWebPageUpdateDelayed(i->first);
}
i = _webPagesPending.erase(i);

View File

@ -15,33 +15,53 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_web_page.h"
#include "mainwidget.h"
#include "storage/localstorage.h"
namespace Data {
WebPageDraft WebPageDraft::FromItem(not_null<HistoryItem*> item) {
const auto previewMedia = item->media();
const auto previewPage = previewMedia
? previewMedia->webpage()
: nullptr;
using PageFlag = MediaWebPageFlag;
const auto previewFlags = previewMedia
? previewMedia->webpageFlags()
: PageFlag();
return {
.id = previewPage ? previewPage->id : 0,
.url = previewPage ? previewPage->url : QString(),
.forceLargeMedia = !!(previewFlags & PageFlag::ForceLargeMedia),
.forceSmallMedia = !!(previewFlags & PageFlag::ForceSmallMedia),
.invert = item->invertMedia(),
.manual = !!(previewFlags & PageFlag::Manual),
};
}
Draft::Draft(
const TextWithTags &textWithTags,
FullReplyTo reply,
const MessageCursor &cursor,
PreviewState previewState,
WebPageDraft webpage,
mtpRequestId saveRequestId)
: textWithTags(textWithTags)
, reply(std::move(reply))
, cursor(cursor)
, previewState(previewState)
, webpage(webpage)
, saveRequestId(saveRequestId) {
}
Draft::Draft(
not_null<const Ui::InputField*> field,
FullReplyTo reply,
PreviewState previewState,
WebPageDraft webpage,
mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags())
, reply(std::move(reply))
, cursor(field)
, previewState(previewState) {
, webpage(webpage) {
}
void ApplyPeerCloudDraft(
@ -67,6 +87,23 @@ void ApplyPeerCloudDraft(
const auto replyPeerId = reply.externalPeerId
? reply.externalPeerId
: peerId;
auto webpage = WebPageDraft{
.invert = draft.is_invert_media(),
.removed = draft.is_no_webpage(),
};
if (const auto media = draft.vmedia()) {
media->match([&](const MTPDmessageMediaWebPage &data) {
const auto parsed = session->data().processWebpage(
data.vwebpage());
if (!parsed->failed) {
webpage.forceLargeMedia = data.is_force_large_media();
webpage.forceSmallMedia = data.is_force_small_media();
webpage.manual = data.is_manual();
webpage.url = parsed->url;
webpage.id = parsed->id;
}
}, [](const auto &) {});
}
auto cloudDraft = std::make_unique<Draft>(
textWithTags,
FullReplyTo{
@ -78,9 +115,7 @@ void ApplyPeerCloudDraft(
.topicRootId = topicRootId,
},
MessageCursor(Ui::kQFixedMax, Ui::kQFixedMax, Ui::kQFixedMax),
(draft.is_no_webpage()
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed));
std::move(webpage));
cloudDraft->date = date;
history->setCloudDraft(std::move(cloudDraft));

View File

@ -30,10 +30,19 @@ void ClearPeerCloudDraft(
MsgId topicRootId,
TimeId date);
enum class PreviewState : char {
Allowed,
Cancelled,
EmptyOnEdit,
struct WebPageDraft {
[[nodiscard]] static WebPageDraft FromItem(not_null<HistoryItem*> item);
WebPageId id = 0;
QString url;
bool forceLargeMedia : 1 = false;
bool forceSmallMedia : 1 = false;
bool invert : 1 = false;
bool manual : 1 = false;
bool removed : 1 = false;
friend inline bool operator==(const WebPageDraft&, const WebPageDraft&)
= default;
};
struct Draft {
@ -42,19 +51,19 @@ struct Draft {
const TextWithTags &textWithTags,
FullReplyTo reply,
const MessageCursor &cursor,
PreviewState previewState,
WebPageDraft webpage,
mtpRequestId saveRequestId = 0);
Draft(
not_null<const Ui::InputField*> field,
FullReplyTo reply,
PreviewState previewState,
WebPageDraft webpage,
mtpRequestId saveRequestId = 0);
TimeId date = 0;
TextWithTags textWithTags;
FullReplyTo reply; // reply.messageId.msg is editMsgId for edit draft.
MessageCursor cursor;
PreviewState previewState = PreviewState::Allowed;
WebPageDraft webpage;
mtpRequestId saveRequestId = 0;
};
@ -180,7 +189,7 @@ using HistoryDrafts = base::flat_map<DraftKey, std::unique_ptr<Draft>>;
}
return (a->textWithTags == b->textWithTags)
&& (a->reply == b->reply)
&& (a->previewState == b->previewState);
&& (a->webpage == b->webpage);
}
} // namespace Data

View File

@ -404,6 +404,10 @@ WebPageData *Media::webpage() const {
return nullptr;
}
MediaWebPageFlags Media::webpageFlags() const {
return {};
}
const SharedContact *Media::sharedContact() const {
return nullptr;
}
@ -1462,6 +1466,10 @@ WebPageData *MediaWebPage::webpage() const {
return _page;
}
MediaWebPageFlags MediaWebPage::webpageFlags() const {
return _flags;
}
bool MediaWebPage::hasReplyPreview() const {
if (const auto document = MediaWebPage::document()) {
return document->hasThumbnail()

View File

@ -122,6 +122,7 @@ public:
virtual DocumentData *document() const;
virtual PhotoData *photo() const;
virtual WebPageData *webpage() const;
virtual MediaWebPageFlags webpageFlags() const;
virtual const SharedContact *sharedContact() const;
virtual const Call *call() const;
virtual GameData *game() const;
@ -381,6 +382,7 @@ public:
DocumentData *document() const override;
PhotoData *photo() const override;
WebPageData *webpage() const override;
MediaWebPageFlags webpageFlags() const override;
bool hasReplyPreview() const override;
Image *replyPreview() const override;

View File

@ -3261,7 +3261,8 @@ not_null<WebPageData*> Session::processWebpage(const MTPWebPage &data) {
case mtpc_webPageEmpty: {
const auto result = webpage(data.c_webPageEmpty().vid().v);
if (result->pendingTill > 0) {
result->pendingTill = -1; // failed
result->pendingTill = 0;
result->failed = 1;
notifyWebPageUpdateDelayed(result);
}
return result;

View File

@ -227,7 +227,7 @@ bool WebPageData::applyChanges(
const QString &newAuthor,
int newPendingTill) {
if (newPendingTill != 0
&& (!url.isEmpty() || pendingTill < 0)
&& (!url.isEmpty() || failed)
&& (!pendingTill
|| pendingTill == newPendingTill
|| newPendingTill < -1)) {

View File

@ -100,8 +100,9 @@ struct WebPageData {
DocumentData *document = nullptr;
WebPageCollage collage;
int duration = 0;
int pendingTill = 0;
int version = 0;
TimeId pendingTill = 0;
uint32 version : 31 = 0;
uint32 failed : 1 = 0;
private:
void replaceDocumentGoodThumbnail();

View File

@ -203,13 +203,13 @@ void History::createLocalDraftFromCloud(MsgId topicRootId) {
draft->textWithTags,
draft->reply,
draft->cursor,
draft->previewState));
draft->webpage));
existing = localDraft(topicRootId);
} else if (existing != draft) {
existing->textWithTags = draft->textWithTags;
existing->reply = draft->reply;
existing->cursor = draft->cursor;
existing->previewState = draft->previewState;
existing->webpage = draft->webpage;
}
existing->date = draft->date;
}
@ -277,7 +277,7 @@ Data::Draft *History::createCloudDraft(
TextWithTags(),
FullReplyTo(),
MessageCursor(),
Data::PreviewState::Allowed));
Data::WebPageDraft()));
cloudDraft(topicRootId)->date = TimeId(0);
} else {
auto existing = cloudDraft(topicRootId);
@ -286,13 +286,13 @@ Data::Draft *History::createCloudDraft(
fromDraft->textWithTags,
fromDraft->reply,
fromDraft->cursor,
fromDraft->previewState));
fromDraft->webpage));
existing = cloudDraft(topicRootId);
} else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags;
existing->reply = fromDraft->reply;
existing->cursor = fromDraft->cursor;
existing->previewState = fromDraft->previewState;
existing->webpage = fromDraft->webpage;
}
existing->date = base::unixtime::now();
existing->reply.topicRootId = topicRootId;

View File

@ -206,7 +206,6 @@ HistoryWidget::HistoryWidget(
, _updateEditTimeLeftDisplay([=] { updateField(); })
, _fieldBarCancel(this, st::historyReplyCancel)
, _previewTimer([=] { requestPreview(); })
, _previewState(Data::PreviewState::Allowed)
, _topBar(this, controller)
, _scroll(
this,
@ -443,10 +442,6 @@ HistoryWidget::HistoryWidget(
_fieldLinksParser = std::make_unique<MessageLinksParser>(_field);
_fieldLinksParser->list().changes(
) | rpl::start_with_next([=](QStringList &&parsed) {
if (_previewState == Data::PreviewState::EmptyOnEdit
&& _parsedLinks != parsed) {
_previewState = Data::PreviewState::Allowed;
}
_parsedLinks = std::move(parsed);
checkPreview();
}, lifetime());
@ -1604,7 +1599,7 @@ void HistoryWidget::fieldChanged() {
updateSendButtonType();
if (!HasSendText(_field)) {
_previewState = Data::PreviewState::Allowed;
_previewDraft = {};
_fieldIsEmpty = true;
} else if (_fieldIsEmpty) {
_fieldIsEmpty = false;
@ -1668,14 +1663,14 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
.messageId = FullMsgId(_history->peer->id, _editMsgId),
.topicRootId = topicRootId,
},
_previewState,
_previewDraft,
_saveEditMsgRequestId));
} else {
if (_replyTo || !_field->empty()) {
_history->setLocalDraft(std::make_unique<Data::Draft>(
_field,
_replyTo,
_previewState));
_previewDraft));
} else {
_history->clearLocalDraft(topicRootId);
}
@ -1783,7 +1778,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(
textWithTags,
FullReplyTo(),
cursor,
Data::PreviewState::Allowed));
Data::WebPageDraft()));
applyDraft();
return true;
}
@ -1935,7 +1930,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
// Save links from _field to _parsedLinks without generating preview.
_previewState = Data::PreviewState::Cancelled;
_previewDraft = { .removed = true };
if (_editMsgId) {
_fieldLinksParser->setDisabled(!_replyEditMsg
|| (_replyEditMsg->media()
@ -1943,7 +1938,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
_fieldLinksParser->parseNow();
_parsedLinks = _fieldLinksParser->list().current();
_previewState = draft->previewState;
_previewDraft = draft->webpage;
checkPreview();
return true;
@ -2471,7 +2466,7 @@ void HistoryWidget::registerDraftSource() {
? FullReplyTo{ FullMsgId(peerId, editMsgId) }
: _replyTo),
_field->getTextWithTags(),
_previewState,
_previewDraft,
};
};
auto draftSource = Storage::MessageDraftSource{
@ -2909,7 +2904,7 @@ void HistoryWidget::updateControlsVisibility() {
if (_editMsgId
|| _replyTo
|| readyToForward()
|| (_previewData && _previewData->pendingTill >= 0)
|| (_previewData && !_previewData->failed)
|| _kbReplyTo) {
if (_fieldBarCancel->isHidden()) {
_fieldBarCancel->show();
@ -3766,11 +3761,11 @@ void HistoryWidget::saveEditMsg() {
cancelEdit();
return;
}
const auto webPageId = (_previewState != Data::PreviewState::Allowed)
const auto webPageId = _previewDraft.removed
? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id
: WebPageId(0));
: (_previewData && !_previewData->failed)
? _previewData->id
: WebPageId();
const auto textWithTags = _field->getTextWithAppliedMarkdown();
const auto prepareFlags = Ui::ItemTextOptions(
@ -3925,11 +3920,11 @@ void HistoryWidget::send(Api::SendOptions options) {
_cornerButtons.clearReplyReturns();
}
const auto webPageId = (_previewState != Data::PreviewState::Allowed)
const auto webPageId = _previewDraft.removed
? CancelledWebPageId
: ((_previewData && _previewData->pendingTill >= 0)
? _previewData->id
: WebPageId(0));
: (_previewData && !_previewData->failed)
? _previewData->id
: WebPageId();
auto message = Api::MessageToSend(prepareSendAction(options));
message.textWithTags = _field->getTextWithAppliedMarkdown();
@ -3950,7 +3945,9 @@ void HistoryWidget::send(Api::SendOptions options) {
hideSelectorControlsAnimated();
if (_previewData && _previewData->pendingTill) previewCancel();
if (_previewData && _previewData->pendingTill) {
previewCancel();
}
setInnerFocus();
if (!_keyboard->hasMarkup() && _keyboard->forceReply() && !_kbReplyTo) {
@ -4790,7 +4787,10 @@ void HistoryWidget::toggleKeyboard(bool manual) {
_field->setMaxHeight(computeMaxFieldHeight());
_kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyTo) {
if (!readyToForward()
&& (!_previewData || _previewData->failed)
&& !_editMsgId
&& !_replyTo) {
_fieldBarCancel->hide();
updateMouseTracking();
}
@ -5775,7 +5775,10 @@ void HistoryWidget::updateHistoryGeometry(
} else if (writeRestriction().has_value()) {
newScrollHeight -= _unblock->height();
}
if (_editMsgId || replyTo() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) {
if (_editMsgId
|| replyTo()
|| readyToForward()
|| (_previewData && !_previewData->failed)) {
newScrollHeight -= st::historyReplyHeight;
}
if (_kbShown) {
@ -6077,7 +6080,9 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_field->setMaxHeight(computeMaxFieldHeight());
_kbShown = false;
_kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyTo) {
if (!readyToForward()
&& (!_previewData || _previewData->failed)
&& !_replyTo) {
_fieldBarCancel->hide();
updateMouseTracking();
}
@ -6093,7 +6098,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_field->setMaxHeight(computeMaxFieldHeight());
_kbShown = false;
_kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyTo && !_editMsgId) {
if (!readyToForward()
&& (!_previewData || _previewData->failed)
&& !_replyTo
&& !_editMsgId) {
_fieldBarCancel->hide();
updateMouseTracking();
}
@ -6138,7 +6146,7 @@ int HistoryWidget::computeMaxFieldHeight() const {
- ((_editMsgId
|| replyTo()
|| readyToForward()
|| (_previewData && _previewData->pendingTill >= 0))
|| (_previewData && !_previewData->failed))
? st::historyReplyHeight
: 0)
- (2 * st::historySendPadding)
@ -6242,6 +6250,15 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
_peer,
Window::SectionShow::Way::Forward,
_editMsgId);
} else if (_previewData
&& !_previewData->failed
&& !_previewData->pendingTill) {
//const auto history = _history;
//using namespace HistoryView::Controls;
//EditWebPageOptions(
// controller()->uiShow(),
// _previewData,
// _previewDraft);
}
}
}
@ -7057,7 +7074,7 @@ void HistoryWidget::setFieldText(
| TextUpdateEvent::SendTyping;
previewCancel();
_previewState = Data::PreviewState::Allowed;
_previewDraft = {};
}
void HistoryWidget::clearFieldText(
@ -7171,7 +7188,7 @@ void HistoryWidget::setReplyFieldsFromProcessing() {
TextWithTags(),
id,
MessageCursor(),
Data::PreviewState::Allowed));
Data::WebPageDraft()));
}
} else {
_replyEditMsg = item;
@ -7218,7 +7235,7 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
_history->setLocalDraft(std::make_unique<Data::Draft>(
_field,
_replyTo,
_previewState));
_previewDraft));
} else {
_history->clearLocalDraft({});
}
@ -7230,23 +7247,17 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
int(editData.text.size()),
Ui::kQFixedMax
};
const auto previewPage = [&]() -> WebPageData* {
if (const auto media = item->media()) {
return media->webpage();
}
return nullptr;
}();
const auto previewState = previewPage
? Data::PreviewState::Allowed
: Data::PreviewState::EmptyOnEdit;
const auto previewDraft = Data::WebPageDraft::FromItem(item);
_history->setLocalEditDraft(std::make_unique<Data::Draft>(
editData,
FullReplyTo{ item->fullId() },
cursor,
previewState));
previewDraft));
applyDraft();
_previewData = previewPage;
_previewData = previewDraft.id
? session().data().webpage(previewDraft.id).get()
: nullptr;
if (_previewData) {
updatePreview();
}
@ -7316,7 +7327,9 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
_processingReplyItem = _replyEditMsg = nullptr;
_processingReplyTo = _replyTo = FullReplyTo();
mouseMoveEvent(0);
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) {
if (!readyToForward()
&& (!_previewData || _previewData->failed)
&& !_kbReplyTo) {
_fieldBarCancel->hide();
updateMouseTracking();
}
@ -7387,7 +7400,9 @@ void HistoryWidget::cancelEdit() {
saveDraft();
mouseMoveEvent(nullptr);
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !replyTo()) {
if (!readyToForward()
&& (!_previewData || _previewData->failed)
&& !replyTo()) {
_fieldBarCancel->hide();
updateMouseTracking();
}
@ -7408,8 +7423,8 @@ void HistoryWidget::cancelEdit() {
void HistoryWidget::cancelFieldAreaState() {
controller()->hideLayer();
_replyForwardPressed = false;
if (_previewData && _previewData->pendingTill >= 0) {
_previewState = Data::PreviewState::Cancelled;
if (_previewData && !_previewData->failed) {
_previewDraft = { .removed = true };
previewCancel();
_saveDraftText = true;
@ -7437,7 +7452,7 @@ void HistoryWidget::checkPreview() {
const auto previewRestricted = [&] {
return _peer && _peer->amRestricted(ChatRestriction::EmbedLinks);
}();
if (_previewState != Data::PreviewState::Allowed || previewRestricted) {
if (_previewDraft.removed || previewRestricted) {
previewCancel();
return;
}
@ -7446,7 +7461,7 @@ void HistoryWidget::checkPreview() {
_api.request(base::take(_previewRequest)).cancel();
_previewLinks = links;
if (_previewLinks.isEmpty()) {
if (_previewData && _previewData->pendingTill >= 0) {
if (_previewData && !_previewData->failed) {
previewCancel();
}
} else {
@ -7462,7 +7477,7 @@ void HistoryWidget::checkPreview() {
} else if (i.value()) {
_previewData = session().data().webpage(i.value());
updatePreview();
} else if (_previewData && _previewData->pendingTill >= 0) {
} else if (_previewData && !_previewData->failed) {
previewCancel();
}
}
@ -7470,9 +7485,7 @@ void HistoryWidget::checkPreview() {
}
void HistoryWidget::requestPreview() {
if (!_previewData
|| (_previewData->pendingTill <= 0)
|| _previewLinks.isEmpty()) {
if (!_previewData || _previewData->failed || _previewLinks.isEmpty()) {
return;
}
const auto links = _previewLinks;
@ -7498,11 +7511,11 @@ void HistoryWidget::gotPreview(
_previewCache.insert(links, page->id);
if (page->pendingTill > 0
&& page->pendingTill <= base::unixtime::now()) {
page->pendingTill = -1;
page->pendingTill = 0;
page->failed = true;
}
if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
_previewData = (page->id && page->pendingTill >= 0)
if (links == _previewLinks && !_previewDraft.removed) {
_previewData = (page->id && !page->failed)
? page.get()
: nullptr;
updatePreview();
@ -7510,8 +7523,7 @@ void HistoryWidget::gotPreview(
session().data().sendWebPageGamePollNotifications();
} else if (result.type() == mtpc_messageMediaEmpty) {
_previewCache.insert(links, 0);
if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
if (links == _previewLinks && !_previewDraft.removed) {
_previewData = nullptr;
updatePreview();
}
@ -7520,7 +7532,7 @@ void HistoryWidget::gotPreview(
void HistoryWidget::updatePreview() {
_previewTimer.cancel();
if (_previewData && _previewData->pendingTill >= 0) {
if (_previewData && !_previewData->failed) {
_fieldBarCancel->show();
updateMouseTracking();
if (_previewData->pendingTill) {
@ -7924,11 +7936,12 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
} else if (hasForward) {
backy -= st::historyReplyHeight;
backh += st::historyReplyHeight;
} else if (_previewData && _previewData->pendingTill >= 0) {
} else if (_previewData && !_previewData->failed) {
backy -= st::historyReplyHeight;
backh += st::historyReplyHeight;
}
auto drawWebPagePreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed;
auto drawWebPagePreview = (_previewData && !_previewData->failed)
&& !_replyForwardPressed;
p.setInactive(
controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
p.fillRect(myrtlrect(0, backy, width(), backh), st::historyReplyBg);

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "chat_helpers/bot_command.h"
#include "chat_helpers/field_autocomplete.h"
#include "data/data_drafts.h"
#include "window/section_widget.h"
#include "ui/widgets/fields/input_field.h"
#include "mtproto/sender.h"
@ -30,7 +31,6 @@ class Error;
} // namespace MTP
namespace Data {
enum class PreviewState : char;
class PhotoMedia;
} // namespace Data
@ -682,7 +682,7 @@ private:
Ui::Text::String _previewTitle;
Ui::Text::String _previewDescription;
base::Timer _previewTimer;
Data::PreviewState _previewState = Data::PreviewState();
Data::WebPageDraft _previewDraft;
bool _replyForwardPressed = false;

View File

@ -102,7 +102,7 @@ using VoiceRecordBar = Controls::VoiceRecordBar;
using ForwardPanel = Controls::ForwardPanel;
[[nodiscard]] auto ShowWebPagePreview(WebPageData *page) {
return page && (page->pendingTill >= 0);
return page && !page->failed;
}
WebPageText ProcessWebPageData(WebPageData *page) {
@ -128,9 +128,9 @@ public:
void cancel();
void checkPreview();
[[nodiscard]] Data::PreviewState state() const;
void setState(Data::PreviewState value);
void refreshState(Data::PreviewState value, bool disable);
[[nodiscard]] Data::WebPageDraft draft() const;
void setAllowed(bool allowed);
void refreshDraft(Data::WebPageDraft draft, bool disable);
[[nodiscard]] rpl::producer<> paintRequests() const;
[[nodiscard]] rpl::producer<QString> titleChanges() const;
@ -145,7 +145,7 @@ private:
MTP::Sender _api;
MessageLinksParser _fieldLinksParser;
Data::PreviewState _previewState = Data::PreviewState();
Data::WebPageDraft _previewDraft;
QStringList _parsedLinks;
QString _previewLinks;
@ -172,7 +172,6 @@ WebpageProcessor::WebpageProcessor(
: _history(history)
, _api(&history->session().mtp())
, _fieldLinksParser(field)
, _previewState(Data::PreviewState::Allowed)
, _timer([=] {
if (!ShowWebPagePreview(_previewData)
|| _previewLinks.isEmpty()) {
@ -198,12 +197,7 @@ WebpageProcessor::WebpageProcessor(
_fieldLinksParser.list().changes(
) | rpl::start_with_next([=](QStringList &&parsed) {
if (_previewState == Data::PreviewState::EmptyOnEdit
&& _parsedLinks != parsed) {
_previewState = Data::PreviewState::Allowed;
}
_parsedLinks = std::move(parsed);
checkPreview();
}, _lifetime);
}
@ -212,23 +206,23 @@ rpl::producer<> WebpageProcessor::paintRequests() const {
return _paintRequests.events();
}
Data::PreviewState WebpageProcessor::state() const {
return _previewState;
Data::WebPageDraft WebpageProcessor::draft() const {
return _previewDraft;
}
void WebpageProcessor::setState(Data::PreviewState value) {
_previewState = value;
void WebpageProcessor::setAllowed(bool allowed) {
_previewDraft.removed = !allowed;
}
void WebpageProcessor::refreshState(
Data::PreviewState value,
void WebpageProcessor::refreshDraft(
Data::WebPageDraft draft,
bool disable) {
// Save links from _field to _parsedLinks without generating preview.
_previewState = Data::PreviewState::Cancelled;
_previewDraft = { .removed = true };
_fieldLinksParser.setDisabled(disable);
_fieldLinksParser.parseNow();
_parsedLinks = _fieldLinksParser.list().current();
_previewState = value;
_previewDraft = draft;
checkPreview();
}
@ -275,21 +269,20 @@ void WebpageProcessor::getWebPagePreview() {
result.match([=](const MTPDmessageMediaWebPage &d) {
const auto page = _history->owner().processWebpage(d.vwebpage());
_previewCache.insert({ links, page->id });
auto &till = page->pendingTill;
if (till > 0 && till <= base::unixtime::now()) {
till = -1;
if (page->pendingTill > 0
&& page->pendingTill <= base::unixtime::now()) {
page->pendingTill = 0;
page->failed = true;
}
if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
_previewData = (page->id && page->pendingTill >= 0)
if (links == _previewLinks && !_previewDraft.removed) {
_previewData = (page->id && !page->failed)
? page.get()
: nullptr;
updatePreview();
}
}, [=](const MTPDmessageMediaEmpty &d) {
_previewCache.insert({ links, 0 });
if (links == _previewLinks
&& _previewState == Data::PreviewState::Allowed) {
if (links == _previewLinks && !_previewDraft.removed) {
_previewData = nullptr;
updatePreview();
}
@ -303,8 +296,7 @@ void WebpageProcessor::getWebPagePreview() {
void WebpageProcessor::checkPreview() {
const auto previewRestricted = _history->peer
&& _history->peer->amRestricted(ChatRestriction::EmbedLinks);
if (_previewState != Data::PreviewState::Allowed
|| previewRestricted) {
if (_previewDraft.removed || previewRestricted) {
cancel();
return;
}
@ -1476,7 +1468,7 @@ void ComposeControls::setFieldText(
if (_preview) {
_preview->cancel();
_preview->setState(Data::PreviewState::Allowed);
_preview->setAllowed(true);
}
}
@ -1490,7 +1482,7 @@ void ComposeControls::saveFieldToHistoryLocalDraft() {
const auto key = draftKeyCurrent();
_history->setDraft(
key,
std::make_unique<Data::Draft>(_field, id, _preview->state()));
std::make_unique<Data::Draft>(_field, id, _preview->draft()));
} else {
_history->clearDraft(draftKeyCurrent());
}
@ -1625,7 +1617,7 @@ void ComposeControls::init() {
_header->previewCancelled(
) | rpl::start_with_next([=] {
if (_preview) {
_preview->setState(Data::PreviewState::Cancelled);
_preview->setAllowed(false);
}
_saveDraftText = true;
_saveDraftStart = crl::now();
@ -2009,7 +2001,7 @@ void ComposeControls::fieldChanged() {
updateSendButtonType();
_hasSendText = HasSendText(_field);
if (!_hasSendText.current() && _preview) {
_preview->setState(Data::PreviewState::Allowed);
_preview->setAllowed(true);
}
if (updateBotCommandShown() || updateLikeShown()) {
updateControlsVisibility();
@ -2113,7 +2105,7 @@ void ComposeControls::registerDraftSource() {
return Storage::MessageDraft{
_header->getDraftReply(),
_field->getTextWithTags(),
_preview->state(),
_preview->draft(),
};
};
auto draftSource = Storage::MessageDraftSource{
@ -2179,7 +2171,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
_header->editMessage({});
_header->replyToMessage({});
_preview->refreshState(Data::PreviewState::Allowed, false);
_preview->refreshDraft({}, false);
_canReplaceMedia = false;
_photoEditMedia = nullptr;
return;
@ -2194,7 +2186,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
_textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping;
if (_preview) {
const auto disablePreview = (editDraft != nullptr);
_preview->refreshState(draft->previewState, disablePreview);
_preview->refreshDraft(draft->webpage, disablePreview);
}
if (draft == editDraft) {
@ -2215,7 +2207,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
item->fullId());
}
_header->editMessage(editingId, _photoEditMedia != nullptr);
_preview->refreshState(_preview->state(), disablePreview);
_preview->refreshDraft(_preview->draft(), disablePreview);
return true;
}
_canReplaceMedia = false;
@ -2892,15 +2884,6 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
int(editData.text.size()),
Ui::kQFixedMax
};
const auto previewPage = [&]() -> WebPageData* {
if (const auto media = item->media()) {
return media->webpage();
}
return nullptr;
}();
const auto previewState = previewPage
? Data::PreviewState::Allowed
: Data::PreviewState::EmptyOnEdit;
const auto key = draftKey(DraftType::Edit);
_history->setDraft(
key,
@ -2911,7 +2894,7 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
.topicRootId = key.topicRootId(),
},
cursor,
previewState));
Data::WebPageDraft::FromItem(item)));
applyDraft();
if (updateReplaceMediaButton()) {
updateControlsVisibility();
@ -2997,7 +2980,7 @@ void ComposeControls::replyToMessage(FullReplyTo id) {
TextWithTags(),
id,
MessageCursor(),
Data::PreviewState::Allowed));
Data::WebPageDraft()));
}
} else {
_header->replyToMessage(id);

View File

@ -43,7 +43,6 @@ namespace Data {
struct MessagePosition;
struct Draft;
class DraftKey;
enum class PreviewState : char;
class PhotoMedia;
} // namespace Data

View File

@ -493,7 +493,7 @@ void ShowReplyToChatBox(
textWithTags,
reply,
cursor,
Data::PreviewState::Allowed));
Data::WebPageDraft()));
history->clearLocalEditDraft(topicRootId);
history->session().changes().entryUpdated(
thread,

View File

@ -604,7 +604,7 @@ bool MainWidget::shareUrl(
textWithTags,
FullReplyTo{ .topicRootId = topicRootId },
cursor,
Data::PreviewState::Allowed));
Data::WebPageDraft()));
history->clearLocalEditDraft(topicRootId);
history->session().changes().entryUpdated(
thread,

View File

@ -575,7 +575,7 @@ chatInviteExported#ab4a819 flags:# revoked:flags.0?true permanent:flags.5?true r
chatInvitePublicJoinRequests#ed107ab7 = ExportedChatInvite;
chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
chatInvite#3033e855 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true verified:flags.7?true scam:flags.8?true fake:flags.9?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector<User> color:flags.10?int = ChatInvite;
chatInvite#cde0ec40 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true verified:flags.7?true scam:flags.8?true fake:flags.9?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector<User> color:int = ChatInvite;
chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
@ -589,7 +589,7 @@ inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true text_color:flags.9?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
@ -705,7 +705,7 @@ botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaInvoice#354a9b09 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument currency:string total_amount:long reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaWebPage#809ad9a6 flags:# invert_media:flags.3?true force_large_media:flags.4?true force_small_media:flags.5?true manual:flags.7?true message:string entities:flags.1?Vector<MessageEntity> url:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaWebPage#809ad9a6 flags:# invert_media:flags.3?true force_large_media:flags.4?true force_small_media:flags.5?true manual:flags.7?true safe:flags.8?true message:string entities:flags.1?Vector<MessageEntity> url:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
@ -1586,7 +1586,7 @@ payments.giveawayInfoResults#cd5570 flags:# winner:flags.0?true refunded:flags.1
prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway;
boost#53c300c8 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string = Boost;
boost#2a1c8c71 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int = Boost;
premium.boostsList#86f8613c flags:# count:int boosts:Vector<Boost> next_offset:flags.0?string users:Vector<User> = premium.BoostsList;
@ -1594,7 +1594,7 @@ myBoost#c448415c flags:# slot:int peer:flags.0?Peer date:int expires:int cooldow
premium.myBoosts#9ae228e2 my_boosts:Vector<MyBoost> chats:Vector<Chat> users:Vector<User> = premium.MyBoosts;
premium.boostsStatus#3d5daae6 flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int gift_boosts:flags.3?int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string prepaid_giveaways:flags.3?Vector<PrepaidGiveaway> my_boost_slots:flags.2?Vector<int> = premium.BoostsStatus;
premium.boostsStatus#4959427a flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int gift_boosts:flags.4?int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string prepaid_giveaways:flags.3?Vector<PrepaidGiveaway> my_boost_slots:flags.2?Vector<int> = premium.BoostsStatus;
---functions---
@ -2168,5 +2168,5 @@ stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
premium.getMyBoosts#be77b4a = premium.MyBoosts;
premium.applyBoost#184bc3b9 flags:# slots:flags.0?Vector<int> peer:InputPeer = Bool;
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;

View File

@ -1044,7 +1044,7 @@ void EnumerateDrafts(
key,
draft->reply,
draft->textWithTags,
draft->previewState,
draft->webpage,
draft->cursor);
}
for (const auto &[key, source] : sources) {
@ -1057,7 +1057,7 @@ void EnumerateDrafts(
key,
draft.reply,
draft.textWithTags,
draft.previewState,
draft.webpage,
cursor);
}
}
@ -1122,13 +1122,18 @@ void Account::writeDrafts(not_null<History*> history) {
auto&&, // key
const FullReplyTo &reply,
const TextWithTags &text,
Data::PreviewState,
const Data::WebPageDraft &webpage,
auto&&) { // cursor
size += sizeof(qint64) // key
+ Serialize::stringSize(text.text)
+ TextUtilities::SerializeTagsSize(text.tags)
+ sizeof(qint64) + sizeof(qint64) // messageId
+ sizeof(qint32); // previewState
+ Serialize::stringSize(webpage.url)
+ sizeof(qint32) // webpage.forceLargeMedia
+ sizeof(qint32) // webpage.forceSmallMedia
+ sizeof(qint32) // webpage.invert
+ sizeof(qint32) // webpage.manual
+ sizeof(qint32); // webpage.removed
};
EnumerateDrafts(
map,
@ -1146,7 +1151,7 @@ void Account::writeDrafts(not_null<History*> history) {
const Data::DraftKey &key,
const FullReplyTo &reply,
const TextWithTags &text,
Data::PreviewState previewState,
const Data::WebPageDraft &webpage,
auto&&) { // cursor
data.stream
<< key.serialize()
@ -1154,7 +1159,12 @@ void Account::writeDrafts(not_null<History*> history) {
<< TextUtilities::SerializeTags(text.tags)
<< qint64(reply.messageId.peer.value)
<< qint64(reply.messageId.msg.bare)
<< qint32(previewState);
<< webpage.url
<< qint32(webpage.forceLargeMedia ? 1 : 0)
<< qint32(webpage.forceSmallMedia ? 1 : 0)
<< qint32(webpage.invert ? 1 : 0)
<< qint32(webpage.manual ? 1 : 0)
<< qint32(webpage.removed ? 1 : 0);
};
EnumerateDrafts(
map,
@ -1206,7 +1216,7 @@ void Account::writeDraftCursors(not_null<History*> history) {
const Data::DraftKey &key,
auto&&, // reply
auto&&, // text
Data::PreviewState,
auto&&, // webpage
const MessageCursor &cursor) { // cursor
data.stream
<< key.serialize()
@ -1370,18 +1380,33 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
QByteArray textTagsSerialized;
qint64 keyValue = 0;
qint64 messageIdPeer = 0, messageIdMsg = 0;
qint32 keyValueOld = 0, uncheckedPreviewState = 0;
qint32 keyValueOld = 0;
QString webpageUrl;
qint32 webpageForceLargeMedia = 0;
qint32 webpageForceSmallMedia = 0;
qint32 webpageInvert = 0;
qint32 webpageManual = 0;
qint32 webpageRemoved = 0;
if (keysOld) {
draft.stream >> keyValueOld;
} else {
draft.stream >> keyValue;
}
if (!rich) {
qint32 uncheckedPreviewState = 0;
draft.stream
>> text.text
>> textTagsSerialized
>> messageIdMsg
>> uncheckedPreviewState;
enum class PreviewState : char {
Allowed,
Cancelled,
EmptyOnEdit,
};
if (uncheckedPreviewState == int(PreviewState::Cancelled)) {
webpageRemoved = 1;
}
messageIdPeer = peerId.value;
} else {
draft.stream
@ -1389,17 +1414,16 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
>> textTagsSerialized
>> messageIdPeer
>> messageIdMsg
>> uncheckedPreviewState;
>> webpageUrl
>> webpageForceLargeMedia
>> webpageForceSmallMedia
>> webpageInvert
>> webpageManual
>> webpageRemoved;
}
text.tags = TextUtilities::DeserializeTags(
textTagsSerialized,
text.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 = keysOld
? Data::DraftKey::FromSerializedOld(keyValueOld)
: Data::DraftKey::FromSerialized(keyValue);
@ -1413,7 +1437,14 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
.topicRootId = key.topicRootId(),
},
MessageCursor(),
previewState));
Data::WebPageDraft{
.url = webpageUrl,
.forceLargeMedia = (webpageForceLargeMedia == 1),
.forceSmallMedia = (webpageForceSmallMedia == 1),
.invert = (webpageInvert == 1),
.manual = (webpageManual == 1),
.removed = (webpageRemoved == 1),
}));
}
}
if (draft.stream.status() != QDataStream::Ok) {
@ -1478,9 +1509,9 @@ void Account::readDraftsWithCursorsLegacy(
msgData,
FullReplyTo{ FullMsgId(peerId, MsgId(msgReplyTo)) },
MessageCursor(),
(msgPreviewCancelled
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed)));
Data::WebPageDraft{
.removed = (msgPreviewCancelled == 1),
}));
}
if (editMsgId) {
map.emplace(
@ -1489,9 +1520,9 @@ void Account::readDraftsWithCursorsLegacy(
editData,
FullReplyTo{ FullMsgId(peerId, editMsgId) },
MessageCursor(),
(editPreviewCancelled
? Data::PreviewState::Cancelled
: Data::PreviewState::Allowed)));
Data::WebPageDraft{
.removed = (editPreviewCancelled == 1),
}));
}
readDraftCursors(peerId, map);
history->setDraftsMap(std::move(map));

View File

@ -53,7 +53,7 @@ enum class StartResult : uchar;
struct MessageDraft {
FullReplyTo reply;
TextWithTags textWithTags;
Data::PreviewState previewState = Data::PreviewState::Allowed;
Data::WebPageDraft webpage;
};
struct MessageDraftSource {

View File

@ -161,7 +161,7 @@ Data::Draft OccupiedDraft(const QString &normalizedName) {
+ normalizedName },
FullReplyTo(),
MessageCursor(),
Data::PreviewState::Allowed
Data::WebPageDraft()
};
}

View File

@ -1073,7 +1073,7 @@ void Manager::notificationActivated(
int(reply.text.size()),
Ui::kQFixedMax,
},
Data::PreviewState::Allowed);
Data::WebPageDraft());
history->setLocalDraft(std::move(draft));
}
window->widget()->showFromTray();

View File

@ -787,7 +787,7 @@ void SessionNavigation::applyBoostChecked(
MTP_flags(0),
MTPVector<MTPint>(), // slots
channel->input
)).done([=](const MTPBool &result) {
)).done([=](const MTPpremium_MyBoosts &result) {
done(true);
}).fail([=](const MTP::Error &error) {
showToast(u"Error: "_q + error.type());
@ -1556,7 +1556,7 @@ bool SessionController::switchInlineQuery(
textWithTags,
to.currentReplyTo,
cursor,
Data::PreviewState::Allowed);
Data::WebPageDraft());
auto params = Window::SectionShow();
params.reapplyLocalDraft = true;