203 lines
5.8 KiB
C++
203 lines
5.8 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "chat_helpers/rich_paste_toast.h"
|
|
|
|
#include "boxes/premium_preview_box.h"
|
|
#include "chat_helpers/message_field.h"
|
|
#include "iv/editor/iv_editor_clipboard_import.h"
|
|
#include "iv/editor/iv_editor_session.h"
|
|
#include "iv/iv_rich_page.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "main/main_session.h"
|
|
#include "window/window_session_controller.h"
|
|
#include "ui/text/text_utilities.h"
|
|
#include "ui/toast/toast.h"
|
|
#include "ui/toast/toast_widget.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "styles/style_chat.h"
|
|
#include "styles/style_chat_helpers.h"
|
|
|
|
#include <QtCore/QMimeData>
|
|
|
|
namespace ChatHelpers {
|
|
namespace {
|
|
|
|
constexpr auto kToastDuration = 9 * crl::time(1000);
|
|
|
|
} // namespace
|
|
|
|
std::optional<RichPasteDecision> MimeDataRichPasteOffer(
|
|
not_null<Main::Session*> session,
|
|
not_null<const QMimeData*> data) {
|
|
if (PasteAsPlainTextRequested()
|
|
|| !Iv::Editor::CanAuthorRichMessages(session)) {
|
|
return std::nullopt;
|
|
}
|
|
const auto limits = Iv::ResolveRichMessageLimits(session);
|
|
if (Iv::Editor::MimeDataHasRichStructure(session, data, limits)) {
|
|
return RichPasteDecision{ .offer = RichPasteOffer::Editor };
|
|
} else if (data->hasHtml() || !data->hasText()) {
|
|
return std::nullopt;
|
|
}
|
|
const auto text = data->text();
|
|
if (auto markdown = Iv::Editor::ComposeFieldMarkdown(
|
|
session,
|
|
text,
|
|
limits)) {
|
|
return RichPasteDecision{
|
|
.offer = RichPasteOffer::Field,
|
|
.markdown = std::move(*markdown),
|
|
};
|
|
} else if (Iv::Editor::TextHasMarkdownStructure(text, limits)) {
|
|
return RichPasteDecision{ .offer = RichPasteOffer::Markdown };
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
TextWithTags TextWithTagsReplaced(
|
|
const TextWithTags &original,
|
|
int from,
|
|
int till,
|
|
const TextWithTags &with) {
|
|
auto result = TextWithTags();
|
|
result.text = original.text.mid(0, from)
|
|
+ with.text
|
|
+ original.text.mid(till);
|
|
const auto shift = int(with.text.size()) - (till - from);
|
|
for (const auto &tag : original.tags) {
|
|
if (tag.offset + tag.length <= from) {
|
|
result.tags.push_back(tag);
|
|
}
|
|
}
|
|
for (const auto &tag : with.tags) {
|
|
result.tags.push_back({ from + tag.offset, tag.length, tag.id });
|
|
}
|
|
for (const auto &tag : original.tags) {
|
|
if (tag.offset >= till) {
|
|
result.tags.push_back({ tag.offset + shift, tag.length, tag.id });
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::shared_ptr<QMimeData> CloneMimeData(not_null<const QMimeData*> data) {
|
|
auto result = std::make_shared<QMimeData>();
|
|
for (const auto &format : data->formats()) {
|
|
result->setData(format, data->data(format));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ShowRichPasteToast(RichPasteToastArgs &&args) {
|
|
const auto session = args.session;
|
|
const auto undo = (args.offer == RichPasteOffer::Plain);
|
|
const auto field = (args.offer == RichPasteOffer::Field);
|
|
const auto markdown = (args.offer == RichPasteOffer::Markdown);
|
|
const auto locked = !undo
|
|
&& !field
|
|
&& !Iv::Editor::SessionPremium(session);
|
|
const auto button = locked
|
|
? QString()
|
|
: undo
|
|
? tr::lng_rich_paste_toast_undo(tr::now)
|
|
: field
|
|
? tr::lng_rich_paste_toast_apply(tr::now)
|
|
: tr::lng_rich_paste_toast_open(tr::now);
|
|
auto text = tr::bold(undo
|
|
? tr::lng_rich_paste_toast_markdown(tr::now)
|
|
: tr::lng_rich_paste_toast(tr::now)
|
|
).append('\n').append(locked
|
|
? tr::lng_rich_paste_toast_premium(
|
|
tr::now,
|
|
lt_link,
|
|
tr::link(tr::bold(
|
|
tr::lng_rich_paste_toast_premium_link(tr::now))),
|
|
tr::marked)
|
|
: undo
|
|
? tr::lng_rich_paste_toast_plain(tr::now, tr::rich)
|
|
: field
|
|
? tr::lng_rich_paste_toast_field(tr::now, tr::rich)
|
|
: markdown
|
|
? tr::lng_rich_paste_toast_editor_markdown(tr::now, tr::rich)
|
|
: tr::lng_rich_paste_toast_editor(tr::now, tr::rich));
|
|
auto filter = Ui::Toast::ClickHandlerFilter();
|
|
if (locked) {
|
|
filter = [=](const ClickHandlerPtr &handler, Qt::MouseButton mouse) {
|
|
if (mouse != Qt::LeftButton) {
|
|
return false;
|
|
} else if (auto show = Iv::Editor::ActiveWindowShow(session)) {
|
|
ShowPremiumPreviewToBuy(
|
|
std::move(show),
|
|
PremiumFeature::RichFormatting);
|
|
} else if (const auto window = session->tryResolveWindow()) {
|
|
ShowPremiumPreviewToBuy(
|
|
window,
|
|
PremiumFeature::RichFormatting);
|
|
}
|
|
return true;
|
|
};
|
|
}
|
|
const auto st = std::make_shared<style::Toast>(st::historyPremiumToast);
|
|
if (!button.isEmpty()) {
|
|
st->padding.setRight(
|
|
st::historyPremiumViewSet.style.font->width(button)
|
|
- st::historyPremiumViewSet.width);
|
|
}
|
|
const auto weak = Ui::Toast::Show(args.parent, Ui::Toast::Config{
|
|
.text = std::move(text),
|
|
.filter = std::move(filter),
|
|
.similarLines = true,
|
|
.iconLottie = (locked ? u"toast/star_premium_2"_q : QString()),
|
|
.iconLottieSize = st::toastLottieIconSize,
|
|
.st = st.get(),
|
|
.attach = RectPart::Bottom,
|
|
.addToAttachSide = std::move(args.bottomOffset),
|
|
.acceptinput = true,
|
|
.duration = kToastDuration,
|
|
});
|
|
const auto strong = weak.get();
|
|
if (!strong) {
|
|
return;
|
|
}
|
|
const auto widget = strong->widget();
|
|
widget->lifetime().add([st] {});
|
|
if (args.cancel) {
|
|
std::move(args.cancel) | rpl::on_next([=] {
|
|
if (const auto strong = weak.get()) {
|
|
strong->hideAnimated();
|
|
}
|
|
}, widget->lifetime());
|
|
}
|
|
if (button.isEmpty()) {
|
|
return;
|
|
}
|
|
const auto activate = Ui::CreateChild<Ui::RoundButton>(
|
|
widget.get(),
|
|
rpl::single(button),
|
|
st::historyPremiumViewSet);
|
|
activate->show();
|
|
activate->setClickedCallback([=, action = std::move(args.action)] {
|
|
if (const auto strong = weak.get()) {
|
|
strong->hideAnimated();
|
|
}
|
|
if (action) {
|
|
action();
|
|
}
|
|
});
|
|
rpl::combine(
|
|
widget->sizeValue(),
|
|
activate->sizeValue()
|
|
) | rpl::on_next([=](QSize outer, QSize inner) {
|
|
activate->moveToRight(
|
|
0,
|
|
(outer.height() - inner.height()) / 2,
|
|
outer.width());
|
|
}, widget->lifetime());
|
|
}
|
|
|
|
} // namespace ChatHelpers
|