mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Show toggle-able factcheck footer.
This commit is contained in:
parent
b299881bf8
commit
923a9ec6a8
9 changed files with 456 additions and 190 deletions
|
@ -466,6 +466,8 @@ PRIVATE
|
|||
data/business/data_business_info.h
|
||||
data/business/data_shortcut_messages.cpp
|
||||
data/business/data_shortcut_messages.h
|
||||
data/components/factchecks.cpp
|
||||
data/components/factchecks.h
|
||||
data/components/recent_peers.cpp
|
||||
data/components/recent_peers.h
|
||||
data/components/scheduled_messages.cpp
|
||||
|
|
|
@ -3287,6 +3287,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_context_spoiler_effect" = "Hide with Spoiler";
|
||||
"lng_context_disable_spoiler" = "Remove Spoiler";
|
||||
"lng_context_add_factcheck" = "Add Fact Check";
|
||||
|
||||
"lng_factcheck_title" = "Fact Check";
|
||||
"lng_factcheck_placeholder" = "Add Facts or Context";
|
||||
"lng_factcheck_whats_this" = "what's this?";
|
||||
"lng_factcheck_about" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country ({country}) responsible for combatting misinformation.";
|
||||
|
||||
"lng_translate_show_original" = "Show Original";
|
||||
"lng_translate_bar_to" = "Translate to {name}";
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
#include "base/random.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "history/view/media/history_view_web_page.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/history.h"
|
||||
|
@ -126,6 +127,7 @@ std::unique_ptr<HistoryView::WebPage> Factchecks::makeMedia(
|
|||
base::RandomValue<WebPageId>(),
|
||||
tr::lng_factcheck_title(tr::now),
|
||||
factcheck->data.text);
|
||||
factcheck->page->type = WebPageType::Factcheck;
|
||||
}
|
||||
return std::make_unique<HistoryView::WebPage>(
|
||||
view,
|
||||
|
|
|
@ -54,6 +54,8 @@ enum class WebPageType : uint8 {
|
|||
|
||||
VoiceChat,
|
||||
Livestream,
|
||||
|
||||
Factcheck,
|
||||
};
|
||||
[[nodiscard]] WebPageType ParseWebPageType(const MTPDwebPage &type);
|
||||
[[nodiscard]] bool IgnoreIv(WebPageType type);
|
||||
|
|
|
@ -111,7 +111,8 @@ MTPMessage PrepareLogMessage(const MTPMessage &message, TimeId newDate) {
|
|||
| MTPDmessage::Flag::f_forwards
|
||||
//| MTPDmessage::Flag::f_reactions
|
||||
| MTPDmessage::Flag::f_restriction_reason
|
||||
| MTPDmessage::Flag::f_ttl_period;
|
||||
| MTPDmessage::Flag::f_ttl_period
|
||||
| MTPDmessage::Flag::f_factcheck;
|
||||
return MTP_message(
|
||||
MTP_flags(data.vflags().v & ~removeFlags),
|
||||
data.vid(),
|
||||
|
@ -141,7 +142,7 @@ MTPMessage PrepareLogMessage(const MTPMessage &message, TimeId newDate) {
|
|||
MTPint(), // ttl_period
|
||||
MTPint(), // quick_reply_shortcut_id
|
||||
MTP_long(data.veffect().value_or_empty()),
|
||||
data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck());
|
||||
MTPFactCheck());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -586,10 +586,11 @@ void Message::animateReaction(Ui::ReactionFlyAnimationArgs &&args) {
|
|||
}
|
||||
|
||||
if (bubble) {
|
||||
auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
const auto entry = logEntryOriginal();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto inner = g;
|
||||
|
@ -636,10 +637,11 @@ void Message::animateEffect(Ui::ReactionFlyAnimationArgs &&args) {
|
|||
_bottomInfo.animateEffect(args.translated(-bottomRight), repainter);
|
||||
};
|
||||
if (bubble) {
|
||||
auto entry = logEntryOriginal();
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto inner = g;
|
||||
|
@ -732,10 +734,11 @@ QRect Message::effectIconGeometry() const {
|
|||
bottomRight - QPoint(size.width(), size.height()));
|
||||
};
|
||||
if (bubble) {
|
||||
auto entry = logEntryOriginal();
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto inner = g;
|
||||
|
@ -806,14 +809,6 @@ QSize Message::performCountOptimalSize() {
|
|||
Get<Factcheck>()->page = history()->session().factchecks().makeMedia(
|
||||
this,
|
||||
factcheck);
|
||||
|
||||
auto copy = data()->originalText();
|
||||
if (!copy.text.contains("FACT CHECK")) {
|
||||
copy.append("\n\nFACT CHECK!!\n\n").append(factcheck->data.text);
|
||||
crl::on_main(this, [=] {
|
||||
data()->setText(std::move(copy));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
RemoveComponents(Factcheck::Bit());
|
||||
}
|
||||
|
@ -863,6 +858,7 @@ QSize Message::performCountOptimalSize() {
|
|||
const auto forwarded = item->Get<HistoryMessageForwarded>();
|
||||
const auto via = item->Get<HistoryMessageVia>();
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
if (forwarded) {
|
||||
forwarded->create(via, item);
|
||||
}
|
||||
|
@ -872,13 +868,16 @@ QSize Message::performCountOptimalSize() {
|
|||
mediaDisplayed = media->isDisplayed();
|
||||
media->initDimensions();
|
||||
}
|
||||
if (check) {
|
||||
check->initDimensions();
|
||||
}
|
||||
if (entry) {
|
||||
entry->initDimensions();
|
||||
}
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
const auto withVisibleText = hasVisibleText();
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
maxWidth = plainMaxWidth();
|
||||
if (context() == Context::Replies && item->isDiscussionPost()) {
|
||||
|
@ -918,6 +917,7 @@ QSize Message::performCountOptimalSize() {
|
|||
minHeight += st::msgPadding.top();
|
||||
if (mediaDisplayed) minHeight += st::mediaInBubbleSkip;
|
||||
if (entry) minHeight += st::mediaInBubbleSkip;
|
||||
if (check) minHeight += st::mediaInBubbleSkip;
|
||||
}
|
||||
if (mediaDisplayed) {
|
||||
// Parts don't participate in maxWidth() in case of media message.
|
||||
|
@ -1002,6 +1002,10 @@ QSize Message::performCountOptimalSize() {
|
|||
+ st::msgPadding.right();
|
||||
accumulate_max(maxWidth, replyw);
|
||||
}
|
||||
if (check) {
|
||||
accumulate_max(maxWidth, check->maxWidth());
|
||||
minHeight += check->minHeight();
|
||||
}
|
||||
if (entry) {
|
||||
accumulate_max(maxWidth, entry->maxWidth());
|
||||
minHeight += entry->minHeight();
|
||||
|
@ -1121,11 +1125,12 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
return;
|
||||
}
|
||||
|
||||
auto entry = logEntryOriginal();
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
auto mediaDisplayed = media && media->isDisplayed();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
const auto displayInfo = needInfoDisplay();
|
||||
|
@ -1150,6 +1155,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) {
|
||||
localMediaBottom -= st::msgPadding.bottom();
|
||||
}
|
||||
if (check) {
|
||||
localMediaBottom -= check->height();
|
||||
}
|
||||
if (entry) {
|
||||
localMediaBottom -= entry->height();
|
||||
}
|
||||
|
@ -1299,6 +1307,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
if (entry) {
|
||||
trect.setHeight(trect.height() - entry->height());
|
||||
}
|
||||
if (check) {
|
||||
trect.setHeight(trect.height() - check->height());
|
||||
}
|
||||
if (displayInfo) {
|
||||
trect.setHeight(trect.height()
|
||||
- (_bottomInfo.height() - st::msgDateFont->height));
|
||||
|
@ -1358,6 +1369,19 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (check) {
|
||||
auto checkLeft = inner.left();
|
||||
auto checkTop = trect.y() + trect.height();
|
||||
p.translate(checkLeft, checkTop);
|
||||
auto checkContext = context.translated(checkLeft, -checkTop);
|
||||
checkContext.selection = skipTextSelection(context.selection);
|
||||
if (mediaDisplayed) {
|
||||
checkContext.selection = media->skipSelection(
|
||||
checkContext.selection);
|
||||
}
|
||||
check->draw(p, checkContext);
|
||||
p.translate(-checkLeft, -checkTop);
|
||||
}
|
||||
if (entry) {
|
||||
auto entryLeft = inner.left();
|
||||
auto entryTop = trect.y() + trect.height();
|
||||
|
@ -1924,10 +1948,11 @@ PointState Message::pointState(QPoint point) const {
|
|||
}
|
||||
if (const auto mediaDisplayed = media && media->isDisplayed()) {
|
||||
// Hack for grouped media point state.
|
||||
auto entry = logEntryOriginal();
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
|
||||
if (item->repliesAreComments() || item->externalReply()) {
|
||||
g.setHeight(g.height() - st::historyCommentsButtonHeight);
|
||||
|
@ -1959,6 +1984,10 @@ PointState Message::pointState(QPoint point) const {
|
|||
// if (getStateReplyInfo(point, trect, &result)) return result;
|
||||
// if (getStateViaBotIdInfo(point, trect, &result)) return result;
|
||||
//}
|
||||
if (check) {
|
||||
auto checkHeight = check->height();
|
||||
trect.setHeight(trect.height() - checkHeight);
|
||||
}
|
||||
if (entry) {
|
||||
auto entryHeight = entry->height();
|
||||
trect.setHeight(trect.height() - entryHeight);
|
||||
|
@ -1995,6 +2024,9 @@ void Message::clickHandlerPressedChanged(
|
|||
}
|
||||
}
|
||||
Element::clickHandlerPressedChanged(handler, pressed);
|
||||
if (const auto check = factcheckBlock()) {
|
||||
check->clickHandlerPressedChanged(handler, pressed);
|
||||
}
|
||||
if (!handler) {
|
||||
return;
|
||||
} else if (_rightAction && (handler == _rightAction->link)) {
|
||||
|
@ -2306,10 +2338,11 @@ TextState Message::textState(
|
|||
|
||||
if (bubble) {
|
||||
const auto inBubble = g.contains(point);
|
||||
auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
const auto entry = logEntryOriginal();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto inner = g;
|
||||
|
@ -2393,9 +2426,23 @@ TextState Message::textState(
|
|||
+ visibleMediaTextLength();
|
||||
}
|
||||
}
|
||||
if (check) {
|
||||
auto checkHeight = check->height();
|
||||
trect.setHeight(trect.height() - checkHeight);
|
||||
auto checkLeft = inner.left();
|
||||
auto checkTop = trect.y() + trect.height();
|
||||
if (point.y() >= checkTop && point.y() < checkTop + checkHeight) {
|
||||
result = check->textState(
|
||||
point - QPoint(checkLeft, checkTop),
|
||||
request);
|
||||
result.symbol += visibleTextLength()
|
||||
+ visibleMediaTextLength();
|
||||
}
|
||||
}
|
||||
|
||||
auto checkBottomInfoState = [&] {
|
||||
if (mediaOnBottom && (entry || media->customInfoLayout())) {
|
||||
if (mediaOnBottom
|
||||
&& (check || entry || media->customInfoLayout())) {
|
||||
return;
|
||||
}
|
||||
const auto bottomInfoResult = bottomInfoTextState(
|
||||
|
@ -2862,6 +2909,7 @@ void Message::updatePressed(QPoint point) {
|
|||
TextForMimeData Message::selectedText(TextSelection selection) const {
|
||||
const auto media = this->media();
|
||||
auto logEntryOriginalResult = TextForMimeData();
|
||||
auto factcheckResult = TextForMimeData();
|
||||
const auto mediaDisplayed = (media && media->isDisplayed());
|
||||
const auto mediaBefore = mediaDisplayed && invertMedia();
|
||||
const auto textSelection = mediaBefore
|
||||
|
@ -2876,7 +2924,15 @@ TextForMimeData Message::selectedText(TextSelection selection) const {
|
|||
auto mediaResult = (mediaDisplayed || isHiddenByGroup())
|
||||
? media->selectedText(mediaSelection)
|
||||
: TextForMimeData();
|
||||
if (auto entry = logEntryOriginal()) {
|
||||
if (const auto check = factcheckBlock()) {
|
||||
const auto checkSelection = mediaBefore
|
||||
? skipTextSelection(textSelection)
|
||||
: mediaDisplayed
|
||||
? media->skipSelection(mediaSelection)
|
||||
: skipTextSelection(selection);
|
||||
factcheckResult = check->selectedText(checkSelection);
|
||||
}
|
||||
if (const auto entry = logEntryOriginal()) {
|
||||
const auto originalSelection = mediaBefore
|
||||
? skipTextSelection(textSelection)
|
||||
: mediaDisplayed
|
||||
|
@ -2892,6 +2948,11 @@ TextForMimeData Message::selectedText(TextSelection selection) const {
|
|||
} else if (!second.empty()) {
|
||||
result.append(u"\n\n"_q).append(std::move(second));
|
||||
}
|
||||
if (result.empty()) {
|
||||
result = std::move(factcheckResult);
|
||||
} else if (!factcheckResult.empty()) {
|
||||
result.append(u"\n\n"_q).append(std::move(factcheckResult));
|
||||
}
|
||||
if (result.empty()) {
|
||||
result = std::move(logEntryOriginalResult);
|
||||
} else if (!logEntryOriginalResult.empty()) {
|
||||
|
@ -2981,6 +3042,21 @@ TextSelection Message::adjustSelection(
|
|||
? mediaAdjusted
|
||||
: unskipTextSelection(mediaAdjusted);
|
||||
}
|
||||
auto checkResult = TextSelection();
|
||||
if (const auto check = factcheckBlock()) {
|
||||
auto checkSelection = !mediaDisplayed
|
||||
? skipTextSelection(selection)
|
||||
: mediaBefore
|
||||
? skipTextSelection(textSelection)
|
||||
: media->skipSelection(mediaSelection);
|
||||
auto checkAdjusted = useSelection(checkSelection, true)
|
||||
? check->adjustSelection(checkSelection, type)
|
||||
: checkSelection;
|
||||
checkResult = unskipTextSelection(checkAdjusted);
|
||||
if (mediaDisplayed) {
|
||||
checkResult = media->unskipSelection(checkResult);
|
||||
}
|
||||
}
|
||||
auto entryResult = TextSelection();
|
||||
if (const auto entry = logEntryOriginal()) {
|
||||
auto entrySelection = !mediaDisplayed
|
||||
|
@ -3003,6 +3079,12 @@ TextSelection Message::adjustSelection(
|
|||
std::max(result.to, mediaResult.to),
|
||||
};
|
||||
}
|
||||
if (!checkResult.empty()) {
|
||||
result = result.empty() ? checkResult : TextSelection{
|
||||
std::min(result.from, checkResult.from),
|
||||
std::max(result.to, checkResult.to),
|
||||
};
|
||||
}
|
||||
if (!entryResult.empty()) {
|
||||
result = result.empty() ? entryResult : TextSelection{
|
||||
std::min(result.from, entryResult.from),
|
||||
|
@ -3410,6 +3492,13 @@ WebPage *Message::logEntryOriginal() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
WebPage *Message::factcheckBlock() const {
|
||||
if (const auto entry = Get<Factcheck>()) {
|
||||
return entry->page.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Message::toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &handler) const {
|
||||
if (_comments && _comments->link == handler) {
|
||||
|
@ -3539,7 +3628,9 @@ bool Message::drawBubble() const {
|
|||
const auto item = data();
|
||||
if (isHidden()) {
|
||||
return false;
|
||||
} else if (logEntryOriginal() || item->isFakeAboutView()) {
|
||||
} else if (logEntryOriginal()
|
||||
|| factcheckBlock()
|
||||
|| item->isFakeAboutView()) {
|
||||
return true;
|
||||
}
|
||||
const auto media = this->media();
|
||||
|
@ -3560,7 +3651,7 @@ bool Message::unwrapped() const {
|
|||
const auto item = data();
|
||||
if (isHidden()) {
|
||||
return true;
|
||||
} else if (logEntryOriginal()) {
|
||||
} else if (logEntryOriginal() || factcheckBlock()) {
|
||||
return false;
|
||||
}
|
||||
const auto media = this->media();
|
||||
|
@ -3921,8 +4012,22 @@ void Message::updateMediaInBubbleState() {
|
|||
|| Has<Reply>()
|
||||
|| item->Has<HistoryMessageVia>();
|
||||
};
|
||||
auto entry = logEntryOriginal();
|
||||
if (entry) {
|
||||
const auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
if (check) {
|
||||
mediaHasSomethingBelow = true;
|
||||
mediaHasSomethingAbove = getMediaHasSomethingAbove();
|
||||
auto checkState = (mediaHasSomethingAbove
|
||||
|| hasVisibleText()
|
||||
|| (media && media->isDisplayed()))
|
||||
? MediaInBubbleState::Bottom
|
||||
: MediaInBubbleState::None;
|
||||
check->setInBubbleState(checkState);
|
||||
if (!media) {
|
||||
check->setBubbleRounding(countBubbleRounding());
|
||||
return;
|
||||
}
|
||||
} else if (entry) {
|
||||
mediaHasSomethingBelow = true;
|
||||
mediaHasSomethingAbove = getMediaHasSomethingAbove();
|
||||
auto entryState = (mediaHasSomethingAbove
|
||||
|
@ -3947,7 +4052,7 @@ void Message::updateMediaInBubbleState() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
if (!check && !entry) {
|
||||
mediaHasSomethingAbove = getMediaHasSomethingAbove();
|
||||
}
|
||||
if (!invertMedia() && hasVisibleText()) {
|
||||
|
@ -4214,10 +4319,11 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
if (bubble) {
|
||||
auto reply = Get<Reply>();
|
||||
auto via = item->Get<HistoryMessageVia>();
|
||||
auto entry = logEntryOriginal();
|
||||
const auto check = factcheckBlock();
|
||||
const auto entry = logEntryOriginal();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
if (reactionsInBubble) {
|
||||
|
@ -4226,12 +4332,20 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
|
||||
if (contentWidth == maxWidth()) {
|
||||
if (mediaDisplayed) {
|
||||
if (check) {
|
||||
newHeight += check->resizeGetHeight(contentWidth);
|
||||
}
|
||||
if (entry) {
|
||||
newHeight += entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
} else if (entry) {
|
||||
// In case of text-only message it is counted in minHeight already.
|
||||
entry->resizeGetHeight(contentWidth);
|
||||
} else {
|
||||
if (check) {
|
||||
check->resizeGetHeight(contentWidth);
|
||||
}
|
||||
if (entry) {
|
||||
// In case of text-only message it is counted in minHeight already.
|
||||
entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto withVisibleText = hasVisibleText();
|
||||
|
@ -4251,15 +4365,24 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
if (!mediaOnTop) {
|
||||
newHeight += st::msgPadding.top();
|
||||
if (mediaDisplayed) newHeight += st::mediaInBubbleSkip;
|
||||
if (check) newHeight += st::mediaInBubbleSkip;
|
||||
if (entry) newHeight += st::mediaInBubbleSkip;
|
||||
}
|
||||
if (mediaDisplayed) {
|
||||
newHeight += media->height();
|
||||
if (check) {
|
||||
newHeight += check->resizeGetHeight(contentWidth);
|
||||
}
|
||||
if (entry) {
|
||||
newHeight += entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
} else {
|
||||
if (check) {
|
||||
newHeight += check->resizeGetHeight(contentWidth);
|
||||
}
|
||||
if (entry) {
|
||||
newHeight += entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
} else if (entry) {
|
||||
newHeight += entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
if (reactionsInBubble) {
|
||||
if (!mediaDisplayed || _viewButton) {
|
||||
|
@ -4343,9 +4466,12 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
bool Message::needInfoDisplay() const {
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media ? media->isDisplayed() : false;
|
||||
const auto check = factcheckBlock();
|
||||
const auto entry = logEntryOriginal();
|
||||
return entry
|
||||
? !entry->customInfoLayout()
|
||||
: check
|
||||
? !check->customInfoLayout()
|
||||
: ((mediaDisplayed && media->isBubbleBottom())
|
||||
? !media->customInfoLayout()
|
||||
: true);
|
||||
|
|
|
@ -47,6 +47,7 @@ struct LogEntryOriginal
|
|||
struct Factcheck
|
||||
: public RuntimeComponent<Factcheck, Element> {
|
||||
std::unique_ptr<WebPage> page;
|
||||
bool expanded = false;
|
||||
};
|
||||
|
||||
struct PsaTooltipState : public RuntimeComponent<PsaTooltipState, Element> {
|
||||
|
@ -294,6 +295,7 @@ private:
|
|||
[[nodiscard]] int viewButtonHeight() const;
|
||||
|
||||
[[nodiscard]] WebPage *logEntryOriginal() const;
|
||||
[[nodiscard]] WebPage *factcheckBlock() const;
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr createGoToCommentsLink() const;
|
||||
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_web_page.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "countries/countries_instance.h"
|
||||
#include "base/qt/qt_key_modifiers.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "iv/iv_instance.h"
|
||||
|
@ -19,13 +20,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_reply.h"
|
||||
#include "history/view/history_view_sponsored_click_handler.h"
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/view/history_view_reply.h"
|
||||
#include "history/view/history_view_sponsored_click_handler.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "menu/menu_sponsored.h"
|
||||
|
@ -36,13 +38,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxOriginalEntryLines = 8192;
|
||||
constexpr auto kFactcheckCollapsedLines = 3;
|
||||
constexpr auto kStickerSetLines = 3;
|
||||
constexpr auto kFactcheckAboutDuration = 5 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] int ArticleThumbWidth(not_null<PhotoData*> thumb, int height) {
|
||||
const auto size = thumb->location(Data::PhotoSize::Thumbnail);
|
||||
|
@ -148,6 +153,39 @@ constexpr auto kStickerSetLines = 3;
|
|||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr AboutFactcheckClickHandler(QString iso2) {
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
const auto show = my.show
|
||||
? my.show
|
||||
: controller
|
||||
? controller->uiShow()
|
||||
: nullptr;
|
||||
if (show) {
|
||||
const auto name = Countries::Instance().countryNameByISO2(iso2);
|
||||
const auto use = name.isEmpty() ? iso2 : name;
|
||||
show->showToast({
|
||||
.text = { tr::lng_factcheck_about(tr::now, lt_country, use) },
|
||||
.duration = kFactcheckAboutDuration,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr ToggleFactcheckClickHandler(
|
||||
not_null<Element*> view) {
|
||||
const auto weak = base::make_weak(view);
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
if (const auto strong = weak.get()) {
|
||||
if (const auto factcheck = strong->Get<Factcheck>()) {
|
||||
factcheck->expanded = !factcheck->expanded;
|
||||
strong->history()->owner().requestViewResize(strong);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] TextWithEntities PageToPhrase(not_null<WebPageData*> page) {
|
||||
const auto type = page->type;
|
||||
const auto text = Ui::Text::Upper(page->iv
|
||||
|
@ -234,32 +272,55 @@ WebPage::WebPage(
|
|||
: Media(parent)
|
||||
, _st(st::historyPagePreview)
|
||||
, _data(data)
|
||||
, _sponsoredData([&]() -> std::optional<SponsoredData> {
|
||||
if (!(flags & MediaWebPageFlag::Sponsored)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto &session = _parent->data()->history()->session();
|
||||
const auto details = session.sponsoredMessages().lookupDetails(
|
||||
_parent->data()->fullId());
|
||||
auto result = std::make_optional<SponsoredData>();
|
||||
result->buttonText = details.buttonText;
|
||||
result->isLinkInternal = details.isLinkInternal;
|
||||
result->backgroundEmojiId = details.backgroundEmojiId;
|
||||
result->colorIndex = details.colorIndex;
|
||||
result->canReport = details.canReport;
|
||||
return result;
|
||||
}())
|
||||
, _flags(flags)
|
||||
, _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _title(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right())
|
||||
, _flags(flags) {
|
||||
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right()) {
|
||||
history()->owner().registerWebPageView(_data, _parent);
|
||||
}
|
||||
|
||||
void WebPage::setupAdditionalData() {
|
||||
if (_flags & MediaWebPageFlag::Sponsored) {
|
||||
_additionalData = std::make_unique<AdditionalData>(SponsoredData());
|
||||
const auto raw = sponsoredData();
|
||||
const auto &session = _parent->data()->history()->session();
|
||||
const auto details = session.sponsoredMessages().lookupDetails(
|
||||
_parent->data()->fullId());
|
||||
raw->buttonText = details.buttonText;
|
||||
raw->isLinkInternal = details.isLinkInternal ? 1 : 0;
|
||||
raw->backgroundEmojiId = details.backgroundEmojiId;
|
||||
raw->colorIndex = details.colorIndex;
|
||||
raw->canReport = details.canReport ? 1 : 0;
|
||||
} else if (_data->stickerSet) {
|
||||
_additionalData = std::make_unique<AdditionalData>(StickerSetData());
|
||||
const auto raw = stickerSetData();
|
||||
for (const auto &sticker : _data->stickerSet->items) {
|
||||
if (!sticker->sticker()) {
|
||||
continue;
|
||||
}
|
||||
raw->views.push_back(
|
||||
std::make_unique<Sticker>(_parent, sticker, true));
|
||||
}
|
||||
const auto side = std::ceil(std::sqrt(raw->views.size()));
|
||||
const auto box = UnitedLineHeight() * kStickerSetLines;
|
||||
const auto single = box / side;
|
||||
for (const auto &view : raw->views) {
|
||||
view->setWebpagePart();
|
||||
view->initSize(single);
|
||||
}
|
||||
} else if (_data->type == WebPageType::Factcheck) {
|
||||
_additionalData = std::make_unique<AdditionalData>(FactcheckData());
|
||||
}
|
||||
}
|
||||
|
||||
QSize WebPage::countOptimalSize() {
|
||||
if (_data->pendingTill || _data->failed) {
|
||||
return { 0, 0 };
|
||||
}
|
||||
setupAdditionalData();
|
||||
|
||||
const auto sponsored = sponsoredData();
|
||||
const auto factcheck = factcheckData();
|
||||
|
||||
// Detect _openButtonWidth before counting paddings.
|
||||
_openButton = Ui::Text::String();
|
||||
|
@ -274,12 +335,10 @@ QSize WebPage::countOptimalSize() {
|
|||
PageToPhrase(_data),
|
||||
kMarkupTextOptions,
|
||||
context);
|
||||
} else if (_sponsoredData) {
|
||||
if (!_sponsoredData->buttonText.isEmpty()) {
|
||||
_openButton.setText(
|
||||
st::semiboldTextStyle,
|
||||
Ui::Text::Upper(_sponsoredData->buttonText));
|
||||
}
|
||||
} else if (sponsored && !sponsored->buttonText.isEmpty()) {
|
||||
_openButton.setText(
|
||||
st::semiboldTextStyle,
|
||||
Ui::Text::Upper(sponsored->buttonText));
|
||||
}
|
||||
|
||||
const auto padding = inBubblePadding() + innerMargin();
|
||||
|
@ -296,25 +355,7 @@ QSize WebPage::countOptimalSize() {
|
|||
}
|
||||
const auto lineHeight = UnitedLineHeight();
|
||||
|
||||
if (_data->stickerSet && !_stickerSet) {
|
||||
_stickerSet = std::make_unique<StickerSet>();
|
||||
for (const auto &sticker : _data->stickerSet->items) {
|
||||
if (!sticker->sticker()) {
|
||||
continue;
|
||||
}
|
||||
_stickerSet->views.push_back(
|
||||
std::make_unique<Sticker>(_parent, sticker, true));
|
||||
}
|
||||
const auto side = std::ceil(std::sqrt(_stickerSet->views.size()));
|
||||
const auto box = lineHeight * kStickerSetLines;
|
||||
const auto single = box / side;
|
||||
for (const auto &view : _stickerSet->views) {
|
||||
view->setWebpagePart();
|
||||
view->initSize(single);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_openl && (!_data->url.isEmpty() || _sponsoredData)) {
|
||||
if (!_openl && (!_data->url.isEmpty() || sponsored || factcheck)) {
|
||||
const auto original = _parent->data()->originalText();
|
||||
const auto previewOfHiddenUrl = [&] {
|
||||
if (_data->type == WebPageType::BotApp) {
|
||||
|
@ -352,28 +393,31 @@ QSize WebPage::countOptimalSize() {
|
|||
}
|
||||
return true;
|
||||
}();
|
||||
_openl = _data->iv
|
||||
? IvClickHandler(_data, original)
|
||||
: (previewOfHiddenUrl || UrlClickHandler::IsSuspicious(
|
||||
_data->url))
|
||||
? std::make_shared<HiddenUrlClickHandler>(_data->url)
|
||||
: std::make_shared<UrlClickHandler>(_data->url, true);
|
||||
if (_data->document
|
||||
if (sponsored) {
|
||||
_openl = SponsoredLink(_data->url, sponsored->isLinkInternal);
|
||||
if (sponsored->canReport) {
|
||||
sponsored->hint.link = AboutSponsoredClickHandler();
|
||||
}
|
||||
} else if (factcheck) {
|
||||
const auto item = _parent->data();
|
||||
if (const auto info = item->Get<HistoryMessageFactcheck>()) {
|
||||
const auto country = info->data.country;
|
||||
factcheck->hint.link = AboutFactcheckClickHandler(country);
|
||||
}
|
||||
} else if (_data->document
|
||||
&& (_data->document->isWallPaper()
|
||||
|| _data->document->isTheme())) {
|
||||
_openl = std::make_shared<DocumentWrappedClickHandler>(
|
||||
std::move(_openl),
|
||||
_data->document,
|
||||
_parent->data()->fullId());
|
||||
}
|
||||
if (_sponsoredData) {
|
||||
_openl = SponsoredLink(
|
||||
_data->url,
|
||||
_sponsoredData->isLinkInternal);
|
||||
|
||||
if (_sponsoredData->canReport) {
|
||||
_sponsoredData->hintLink = AboutSponsoredClickHandler();
|
||||
}
|
||||
} else {
|
||||
_openl = _data->iv
|
||||
? IvClickHandler(_data, original)
|
||||
: (previewOfHiddenUrl || UrlClickHandler::IsSuspicious(
|
||||
_data->url))
|
||||
? std::make_shared<HiddenUrlClickHandler>(_data->url)
|
||||
: std::make_shared<UrlClickHandler>(_data->url, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,7 +500,12 @@ QSize WebPage::countOptimalSize() {
|
|||
|
||||
const auto siteNameHeight = _siteName.isEmpty() ? 0 : lineHeight;
|
||||
const auto titleMinHeight = _title.isEmpty() ? 0 : lineHeight;
|
||||
const auto descMaxLines = isLogEntryOriginal()
|
||||
const auto factcheckMetrics = factcheck
|
||||
? computeFactcheckMetrics(_description.minHeight())
|
||||
: FactcheckMetrics();
|
||||
const auto descMaxLines = factcheck
|
||||
? factcheckMetrics.lines
|
||||
: isLogEntryOriginal()
|
||||
? kMaxOriginalEntryLines
|
||||
: (3 + (siteNameHeight ? 0 : 1) + (titleMinHeight ? 0 : 1));
|
||||
const auto descriptionMinHeight = _description.isEmpty()
|
||||
|
@ -517,15 +566,16 @@ QSize WebPage::countOptimalSize() {
|
|||
if (_asArticle) {
|
||||
minHeight = resizeGetHeight(maxWidth);
|
||||
}
|
||||
if (_sponsoredData && _sponsoredData->canReport) {
|
||||
_sponsoredData->widthBeforeHint
|
||||
= st::webPageTitleStyle.font->width(siteName);
|
||||
if (const auto hint = hintData()) {
|
||||
hint->widthBefore = st::webPageTitleStyle.font->width(siteName);
|
||||
const auto &font = st::webPageSponsoredHintFont;
|
||||
_sponsoredData->hintSize = QSize(
|
||||
font->width(tr::lng_sponsored_message_revenue_button(tr::now))
|
||||
+ font->height,
|
||||
hint->text = sponsored
|
||||
? tr::lng_sponsored_message_revenue_button(tr::now)
|
||||
: tr::lng_factcheck_whats_this(tr::now);
|
||||
hint->size = QSize(
|
||||
font->width(hint->text) + font->height,
|
||||
font->height);
|
||||
maxWidth += _sponsoredData->hintSize.width();
|
||||
maxWidth += hint->size.width();
|
||||
}
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
@ -539,9 +589,22 @@ QSize WebPage::countCurrentSize(int newWidth) {
|
|||
const auto innerWidth = newWidth - rect::m::sum::h(padding);
|
||||
auto newHeight = 0;
|
||||
|
||||
const auto specialRightPix = (_sponsoredData || _stickerSet);
|
||||
const auto stickerSet = stickerSetData();
|
||||
const auto factcheck = factcheckData();
|
||||
const auto specialRightPix = (sponsoredData() || stickerSet);
|
||||
const auto lineHeight = UnitedLineHeight();
|
||||
const auto linesMax = (specialRightPix || isLogEntryOriginal())
|
||||
const auto factcheckMetrics = factcheck
|
||||
? computeFactcheckMetrics(_description.countHeight(innerWidth))
|
||||
: FactcheckMetrics();
|
||||
if (factcheck) {
|
||||
factcheck->expandable = factcheckMetrics.expandable;
|
||||
_openl = factcheck->expandable
|
||||
? ToggleFactcheckClickHandler(_parent)
|
||||
: nullptr;
|
||||
}
|
||||
const auto linesMax = factcheck
|
||||
? (factcheckMetrics.lines + 1)
|
||||
: (specialRightPix || isLogEntryOriginal())
|
||||
? kMaxOriginalEntryLines
|
||||
: 5;
|
||||
const auto siteNameHeight = _siteNameLines ? lineHeight : 0;
|
||||
|
@ -550,7 +613,7 @@ QSize WebPage::countCurrentSize(int newWidth) {
|
|||
if (asArticle() || specialRightPix) {
|
||||
constexpr auto kSponsoredUserpicLines = 2;
|
||||
_pixh = lineHeight
|
||||
* (_stickerSet
|
||||
* (stickerSet
|
||||
? kStickerSetLines
|
||||
: specialRightPix
|
||||
? kSponsoredUserpicLines
|
||||
|
@ -673,8 +736,14 @@ void WebPage::ensurePhotoMediaCreated() const {
|
|||
}
|
||||
|
||||
bool WebPage::hasHeavyPart() const {
|
||||
if (const auto stickerSet = stickerSetData()) {
|
||||
for (const auto &part : stickerSet->views) {
|
||||
if (part->hasHeavyPart()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _photoMedia
|
||||
|| (_stickerSet)
|
||||
|| (_attach ? _attach->hasHeavyPart() : false);
|
||||
}
|
||||
|
||||
|
@ -684,6 +753,11 @@ void WebPage::unloadHeavyPart() {
|
|||
}
|
||||
_description.unloadPersistentAnimation();
|
||||
_photoMedia = nullptr;
|
||||
if (const auto stickerSet = stickerSetData()) {
|
||||
for (const auto &part : stickerSet->views) {
|
||||
part->unloadHeavyPart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebPage::draw(Painter &p, const PaintContext &context) const {
|
||||
|
@ -704,22 +778,22 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
auto tshift = inner.top();
|
||||
auto paintw = inner.width();
|
||||
|
||||
const auto asSponsored = (!!_sponsoredData);
|
||||
const auto sponsored = sponsoredData();
|
||||
|
||||
const auto selected = context.selected();
|
||||
const auto view = parent();
|
||||
const auto from = view->data()->contentColorsFrom();
|
||||
const auto colorIndex = (asSponsored && _sponsoredData->colorIndex)
|
||||
? _sponsoredData->colorIndex
|
||||
const auto colorIndex = (sponsored && sponsored->colorIndex)
|
||||
? sponsored->colorIndex
|
||||
: from
|
||||
? from->colorIndex()
|
||||
: view->colorIndex();
|
||||
const auto cache = context.outbg
|
||||
? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
|
||||
: st->coloredReplyCache(selected, colorIndex).get();
|
||||
const auto backgroundEmojiId = (asSponsored
|
||||
&& _sponsoredData->backgroundEmojiId)
|
||||
? _sponsoredData->backgroundEmojiId
|
||||
const auto backgroundEmojiId = (sponsored
|
||||
&& sponsored->backgroundEmojiId)
|
||||
? sponsored->backgroundEmojiId
|
||||
: from
|
||||
? from->backgroundEmojiId()
|
||||
: DocumentId();
|
||||
|
@ -755,8 +829,8 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
|
||||
auto lineHeight = UnitedLineHeight();
|
||||
if (_stickerSet) {
|
||||
const auto viewsCount = _stickerSet->views.size();
|
||||
if (const auto stickerSet = stickerSetData()) {
|
||||
const auto viewsCount = stickerSet->views.size();
|
||||
const auto box = _pixh;
|
||||
const auto topLeft = QPoint(inner.left() + paintw - box, tshift);
|
||||
const auto side = std::ceil(std::sqrt(viewsCount));
|
||||
|
@ -767,7 +841,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
if (viewsCount <= index) {
|
||||
break;
|
||||
}
|
||||
const auto &view = _stickerSet->views[index];
|
||||
const auto &view = stickerSet->views[index];
|
||||
const auto size = view->countOptimalSize();
|
||||
const auto offsetX = (single - size.width()) / 2.;
|
||||
const auto offsetY = (single - size.height()) / 2.;
|
||||
|
@ -822,7 +896,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
st->msgSelectOverlay(),
|
||||
st->msgSelectOverlayCorners(Ui::CachedCornerRadius::Small));
|
||||
}
|
||||
if (!asSponsored) {
|
||||
if (!sponsored) {
|
||||
// Ignore photo width in sponsored messages,
|
||||
// as its width only affects the title.
|
||||
paintw -= pw + st::webPagePhotoDelta;
|
||||
|
@ -850,35 +924,31 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
endskip,
|
||||
false,
|
||||
context.selection);
|
||||
if (asSponsored
|
||||
&& _sponsoredData->canReport
|
||||
&& (paintw >
|
||||
_sponsoredData->widthBeforeHint
|
||||
+ _sponsoredData->hintSize.width())) {
|
||||
if (_sponsoredData->hintRipple) {
|
||||
_sponsoredData->hintRipple->paint(
|
||||
p,
|
||||
_sponsoredData->lastHintPos.x(),
|
||||
_sponsoredData->lastHintPos.y(),
|
||||
width(),
|
||||
&cache->bg);
|
||||
if (_sponsoredData->hintRipple->empty()) {
|
||||
_sponsoredData->hintRipple = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const auto hint = hintData();
|
||||
if (hint && (paintw > hint->widthBefore + hint->size.width())) {
|
||||
auto color = cache->icon;
|
||||
color.setAlphaF(color.alphaF() * 0.15);
|
||||
|
||||
const auto height = st::webPageSponsoredHintFont->height;
|
||||
const auto radius = height / 2;
|
||||
|
||||
_sponsoredData->lastHintPos = QPointF(
|
||||
radius + inner.left() + _sponsoredData->widthBeforeHint,
|
||||
hint->lastPosition = QPointF(
|
||||
radius + inner.left() + hint->widthBefore,
|
||||
tshift + (_siteName.style()->font->height - height) / 2.);
|
||||
const auto rect = QRectF(
|
||||
_sponsoredData->lastHintPos,
|
||||
_sponsoredData->hintSize);
|
||||
|
||||
if (hint->ripple) {
|
||||
hint->ripple->paint(
|
||||
p,
|
||||
hint->lastPosition.x(),
|
||||
hint->lastPosition.y(),
|
||||
width(),
|
||||
&cache->bg);
|
||||
if (hint->ripple->empty()) {
|
||||
hint->ripple = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const auto rect = QRectF(hint->lastPosition, hint->size);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(color);
|
||||
|
@ -887,10 +957,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
p.setPen(cache->icon);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setFont(st::webPageSponsoredHintFont);
|
||||
p.drawText(
|
||||
rect,
|
||||
tr::lng_sponsored_message_revenue_button(tr::now),
|
||||
style::al_center);
|
||||
p.drawText(rect, hint->text, style::al_center);
|
||||
}
|
||||
tshift += lineHeight;
|
||||
|
||||
|
@ -901,7 +968,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
|||
const auto endskip = _title.hasSkipBlock()
|
||||
? _parent->skipBlockWidth()
|
||||
: 0;
|
||||
const auto titleWidth = asSponsored
|
||||
const auto titleWidth = sponsored
|
||||
? (paintw - _pixh - st::webPagePhotoDelta)
|
||||
: paintw;
|
||||
_title.drawLeftElided(
|
||||
|
@ -1051,16 +1118,38 @@ bool WebPage::asArticle() const {
|
|||
return _asArticle && (_data->photo != nullptr);
|
||||
}
|
||||
|
||||
WebPage::StickerSetData *WebPage::stickerSetData() const {
|
||||
return std::get_if<StickerSetData>(_additionalData.get());
|
||||
}
|
||||
|
||||
WebPage::SponsoredData *WebPage::sponsoredData() const {
|
||||
return std::get_if<SponsoredData>(_additionalData.get());
|
||||
}
|
||||
|
||||
WebPage::FactcheckData *WebPage::factcheckData() const {
|
||||
return std::get_if<FactcheckData>(_additionalData.get());
|
||||
}
|
||||
|
||||
WebPage::HintData *WebPage::hintData() const {
|
||||
if (const auto sponsored = sponsoredData()) {
|
||||
return sponsored->hint.link ? &sponsored->hint : nullptr;
|
||||
} else if (const auto factcheck = factcheckData()) {
|
||||
return factcheck->hint.link ? &factcheck->hint : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextState WebPage::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
|
||||
if (width() < rect::m::sum::h(st::msgPadding) + 1) {
|
||||
return result;
|
||||
}
|
||||
const auto sponsored = sponsoredData();
|
||||
const auto bubble = _attach ? _attach->bubbleMargins() : QMargins();
|
||||
const auto full = Rect(currentSize());
|
||||
auto outer = full - inBubblePadding();
|
||||
if (_sponsoredData) {
|
||||
if (sponsored) {
|
||||
outer.translate(0, st::msgDateFont->height);
|
||||
}
|
||||
const auto inner = outer - innerMargin();
|
||||
|
@ -1175,16 +1264,15 @@ TextState WebPage::textState(QPoint point, StateRequest request) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ((!result.link || _sponsoredData) && outer.contains(point)) {
|
||||
if ((!result.link || sponsored) && outer.contains(point)) {
|
||||
result.link = _openl;
|
||||
}
|
||||
if (_sponsoredData && _sponsoredData->canReport) {
|
||||
const auto contains = QRectF(
|
||||
_sponsoredData->lastHintPos,
|
||||
_sponsoredData->hintSize).contains(point
|
||||
- QPoint(0, st::msgDateFont->height));
|
||||
if (contains) {
|
||||
result.link = _sponsoredData->hintLink;
|
||||
if (const auto hint = hintData()) {
|
||||
const auto check = point
|
||||
- QPoint(0, sponsored ? st::msgDateFont->height : 0);
|
||||
const auto hintRect = QRectF(hint->lastPosition, hint->size);
|
||||
if (hintRect.contains(check)) {
|
||||
result.link = hint->link;
|
||||
}
|
||||
}
|
||||
_lastPoint = point - outer.topLeft();
|
||||
|
@ -1256,25 +1344,25 @@ void WebPage::clickHandlerActiveChanged(
|
|||
void WebPage::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool pressed) {
|
||||
if (_sponsoredData && _sponsoredData->hintLink == p) {
|
||||
const auto hint = hintData();
|
||||
if (hint && hint->link == p) {
|
||||
if (pressed) {
|
||||
if (!_sponsoredData->hintRipple) {
|
||||
if (!hint->ripple) {
|
||||
const auto owner = &parent()->history()->owner();
|
||||
auto ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
hint->ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
st::defaultRippleAnimation,
|
||||
Ui::RippleAnimation::RoundRectMask(
|
||||
_sponsoredData->hintSize,
|
||||
hint->size,
|
||||
_st.radius),
|
||||
[=] { owner->requestViewRepaint(parent()); });
|
||||
_sponsoredData->hintRipple = std::move(ripple);
|
||||
}
|
||||
const auto full = Rect(currentSize());
|
||||
const auto outer = full - inBubblePadding();
|
||||
_sponsoredData->hintRipple->add(_lastPoint
|
||||
hint->ripple->add(_lastPoint
|
||||
+ outer.topLeft()
|
||||
- _sponsoredData->lastHintPos.toPoint());
|
||||
} else if (_sponsoredData->hintRipple) {
|
||||
_sponsoredData->hintRipple->lastStop();
|
||||
- hint->lastPosition.toPoint());
|
||||
} else if (hint->ripple) {
|
||||
hint->ripple->lastStop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1387,6 +1475,19 @@ bool WebPage::isLogEntryOriginal() const {
|
|||
return _parent->data()->isAdminLogEntry() && _parent->media() != this;
|
||||
}
|
||||
|
||||
WebPage::FactcheckMetrics WebPage::computeFactcheckMetrics(
|
||||
int fullHeight) const {
|
||||
const auto possible = fullHeight / st::normalFont->height;
|
||||
const auto expandable = (possible > kFactcheckCollapsedLines + 1);
|
||||
const auto check = _parent->Get<Factcheck>();
|
||||
const auto expanded = check && check->expanded;
|
||||
const auto allowExpanding = (expanded || !expandable);
|
||||
return {
|
||||
.lines = allowExpanding ? possible : kFactcheckCollapsedLines,
|
||||
.expandable = expandable,
|
||||
};
|
||||
}
|
||||
|
||||
int WebPage::bottomInfoPadding() const {
|
||||
if (!isBubbleBottom()) {
|
||||
return 0;
|
||||
|
|
|
@ -101,6 +101,40 @@ public:
|
|||
~WebPage();
|
||||
|
||||
private:
|
||||
struct FactcheckMetrics {
|
||||
int lines = 0;
|
||||
bool expandable = false;
|
||||
};
|
||||
struct HintData {
|
||||
QSize size;
|
||||
QPointF lastPosition;
|
||||
QString text;
|
||||
int widthBefore = 0;
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
ClickHandlerPtr link;
|
||||
};
|
||||
struct StickerSetData {
|
||||
std::vector<std::unique_ptr<Sticker>> views;
|
||||
};
|
||||
struct SponsoredData {
|
||||
QString buttonText;
|
||||
|
||||
uint64 backgroundEmojiId = 0;
|
||||
uint8 colorIndex : 6 = 0;
|
||||
uint8 isLinkInternal : 1 = 0;
|
||||
uint8 canReport : 1 = 0;
|
||||
|
||||
HintData hint;
|
||||
};
|
||||
struct FactcheckData {
|
||||
HintData hint;
|
||||
bool expandable = false;
|
||||
};
|
||||
using AdditionalData = std::variant<
|
||||
StickerSetData,
|
||||
SponsoredData,
|
||||
FactcheckData>;
|
||||
|
||||
void playAnimation(bool autoplay) override;
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
@ -124,36 +158,26 @@ private:
|
|||
const ClickHandlerPtr &link) const;
|
||||
[[nodiscard]] bool asArticle() const;
|
||||
|
||||
[[nodiscard]] StickerSetData *stickerSetData() const;
|
||||
[[nodiscard]] SponsoredData *sponsoredData() const;
|
||||
[[nodiscard]] FactcheckData *factcheckData() const;
|
||||
[[nodiscard]] HintData *hintData() const;
|
||||
|
||||
[[nodiscard]] FactcheckMetrics computeFactcheckMetrics(
|
||||
int fullHeight) const;
|
||||
|
||||
void setupAdditionalData();
|
||||
|
||||
const style::QuoteStyle &_st;
|
||||
const not_null<WebPageData*> _data;
|
||||
const MediaWebPageFlags _flags;
|
||||
|
||||
std::vector<std::unique_ptr<Data::Media>> _collage;
|
||||
ClickHandlerPtr _openl;
|
||||
std::unique_ptr<Media> _attach;
|
||||
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||
mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||
|
||||
struct StickerSet final {
|
||||
std::vector<std::unique_ptr<Sticker>> views;
|
||||
};
|
||||
|
||||
std::unique_ptr<StickerSet> _stickerSet;
|
||||
|
||||
struct SponsoredData final {
|
||||
QString buttonText;
|
||||
bool isLinkInternal = false;
|
||||
|
||||
uint64 backgroundEmojiId = 0;
|
||||
uint8 colorIndex : 6 = 0;
|
||||
|
||||
bool canReport = false;
|
||||
QSize hintSize;
|
||||
QPointF lastHintPos;
|
||||
int widthBeforeHint = 0;
|
||||
std::unique_ptr<Ui::RippleAnimation> hintRipple;
|
||||
ClickHandlerPtr hintLink;
|
||||
};
|
||||
mutable std::optional<SponsoredData> _sponsoredData;
|
||||
|
||||
int _dataVersion = -1;
|
||||
int _siteNameLines = 0;
|
||||
int _descriptionLines = 0;
|
||||
|
@ -172,7 +196,7 @@ private:
|
|||
int _pixw = 0;
|
||||
int _pixh = 0;
|
||||
|
||||
const MediaWebPageFlags _flags;
|
||||
std::unique_ptr<AdditionalData> _additionalData;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue