diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 76c058914..bd97e7d5e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1290,7 +1290,23 @@ namespace { } WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) { - return App::webPageSet(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), TextWithEntities(), nullptr, nullptr, 0, QString(), webpage.vdate.v); + constexpr auto kDefaultPendingTimeout = 60; + return App::webPageSet( + webpage.vid.v, + convert, + QString(), + QString(), + QString(), + QString(), + QString(), + TextWithEntities(), + nullptr, + nullptr, + 0, + QString(), + webpage.vdate.v + ? webpage.vdate.v + : (unixtime() + kDefaultPendingTimeout)); } WebPageData *feedWebPage(const MTPWebPage &webpage) { @@ -1603,9 +1619,9 @@ namespace { const TextWithEntities &description, PhotoData *photo, DocumentData *document, - int32 duration, + int duration, const QString &author, - int32 pendingTill) { + int pendingTill) { if (convert) { if (convert->id != webPage) { const auto i = webPagesData.find(convert->id); @@ -1614,23 +1630,18 @@ namespace { } convert->id = webPage; } - if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) { - convert->type = toWebPageType(type); - convert->url = TextUtilities::Clean(url); - convert->displayUrl = TextUtilities::Clean(displayUrl); - convert->siteName = TextUtilities::Clean(siteName); - convert->title = TextUtilities::SingleLine(title); - convert->description = description; - convert->photo = photo; - convert->document = document; - convert->duration = duration; - convert->author = TextUtilities::Clean(author); - if (convert->pendingTill > 0 && pendingTill <= 0) { - Auth().api().clearWebPageRequest(convert); - } - convert->pendingTill = pendingTill; - if (App::main()) App::main()->webPageUpdated(convert); - } + convert->applyChanges( + type, + url, + displayUrl, + siteName, + title, + description, + photo, + document, + duration, + author, + pendingTill); } const auto i = webPagesData.constFind(webPage); WebPageData *result; @@ -1638,7 +1649,19 @@ namespace { if (convert) { result = convert; } else { - result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, document, photo, duration, author, (pendingTill >= -1) ? pendingTill : -1); + result = new WebPageData( + webPage, + toWebPageType(type), + url, + displayUrl, + siteName, + title, + description, + document, + photo, + duration, + author, + (pendingTill >= -1) ? pendingTill : -1); if (pendingTill > 0) { Auth().api().requestWebPageDelayed(result); } @@ -1647,23 +1670,18 @@ namespace { } else { result = i.value(); if (result != convert) { - if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) { - result->type = toWebPageType(type); - result->url = TextUtilities::Clean(url); - result->displayUrl = TextUtilities::Clean(displayUrl); - result->siteName = TextUtilities::Clean(siteName); - result->title = TextUtilities::SingleLine(title); - result->description = description; - result->photo = photo; - result->document = document; - result->duration = duration; - result->author = TextUtilities::Clean(author); - if (result->pendingTill > 0 && pendingTill <= 0) { - Auth().api().clearWebPageRequest(result); - } - result->pendingTill = pendingTill; - if (App::main()) App::main()->webPageUpdated(result); - } + result->applyChanges( + type, + url, + displayUrl, + siteName, + title, + description, + photo, + document, + duration, + author, + pendingTill); } } return result; diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 316f641a4..289efdce2 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -133,13 +133,52 @@ namespace App { PeerData *peerByName(const QString &username); QString peerName(const PeerData *peer, bool forDialogs = false); PhotoData *photo(const PhotoId &photo); - PhotoData *photoSet(const PhotoId &photo, PhotoData *convert, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full); + PhotoData *photoSet( + const PhotoId &photo, + PhotoData *convert, + const uint64 &access, + int32 date, + const ImagePtr &thumb, + const ImagePtr &medium, + const ImagePtr &full); DocumentData *document(const DocumentId &document); - DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 version, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation); + DocumentData *documentSet( + const DocumentId &document, + DocumentData *convert, + const uint64 &access, + int32 version, + int32 date, + const QVector &attributes, + const QString &mime, + const ImagePtr &thumb, + int32 dc, + int32 size, + const StorageImageLocation &thumbLocation); WebPageData *webPage(const WebPageId &webPage); - WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const TextWithEntities &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill); + WebPageData *webPageSet( + const WebPageId &webPage, + WebPageData *convert, + const QString &type, + const QString &url, + const QString &displayUrl, + const QString &siteName, + const QString &title, + const TextWithEntities &description, + PhotoData *photo, + DocumentData *document, + int duration, + const QString &author, + int pendingTill); GameData *game(const GameId &game); - GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc); + GameData *gameSet( + const GameId &game, + GameData *convert, + const uint64 &accessHash, + const QString &shortName, + const QString &title, + const QString &description, + PhotoData *photo, + DocumentData *document); LocationData *location(const LocationCoords &coords); void forgetMedia(); diff --git a/Telegram/SourceFiles/data/data_web_page.cpp b/Telegram/SourceFiles/data/data_web_page.cpp new file mode 100644 index 000000000..c17b077a1 --- /dev/null +++ b/Telegram/SourceFiles/data/data_web_page.cpp @@ -0,0 +1,122 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "data/data_web_page.h" + +#include "auth_session.h" +#include "apiwrap.h" +#include "mainwidget.h" +#include "ui/text/text_entity.h" + +namespace { + +QString SiteNameFromUrl(const QString &url) { + QUrl u(url); + QString pretty = u.isValid() ? u.toDisplayString() : url; + QRegularExpressionMatch m = QRegularExpression(qsl("^[a-zA-Z0-9]+://")).match(pretty); + if (m.hasMatch()) pretty = pretty.mid(m.capturedLength()); + int32 slash = pretty.indexOf('/'); + if (slash > 0) pretty = pretty.mid(0, slash); + QStringList components = pretty.split('.', QString::SkipEmptyParts); + if (components.size() >= 2) { + components = components.mid(components.size() - 2); + return components.at(0).at(0).toUpper() + components.at(0).mid(1) + '.' + components.at(1); + } + return QString(); +} + +} // namespace + +bool WebPageData::applyChanges( + const QString &newType, + const QString &newUrl, + const QString &newDisplayUrl, + const QString &newSiteName, + const QString &newTitle, + const TextWithEntities &newDescription, + PhotoData *newPhoto, + DocumentData *newDocument, + int newDuration, + const QString &newAuthor, + int newPendingTill) { + if (newPendingTill != 0 + && (!url.isEmpty() || newUrl.isEmpty()) + && (!pendingTill + || pendingTill == newPendingTill + || newPendingTill < -1)) { + return false; + } + + const auto resultType = toWebPageType(newType); + const auto resultUrl = TextUtilities::Clean(newUrl); + const auto resultDisplayUrl = TextUtilities::Clean( + newDisplayUrl); + const auto possibleSiteName = TextUtilities::Clean( + newSiteName); + const auto resultTitle = TextUtilities::SingleLine( + newTitle); + const auto resultAuthor = TextUtilities::Clean(newAuthor); + + const auto viewTitleText = resultTitle.isEmpty() + ? TextUtilities::SingleLine(resultAuthor) + : resultTitle; + const auto resultSiteName = [&] { + if (!possibleSiteName.isEmpty()) { + return possibleSiteName; + } else if (!newDescription.text.isEmpty() + && viewTitleText.isEmpty() + && !resultUrl.isEmpty()) { + return SiteNameFromUrl(resultUrl); + } + return QString(); + }(); + + if (type == resultType + && url == resultUrl + && displayUrl == resultDisplayUrl + && siteName == resultSiteName + && title == resultTitle + && description.text == newDescription.text + && photo == newPhoto + && document == newDocument + && duration == newDuration + && author == resultAuthor + && pendingTill == newPendingTill) { + return false; + } + if (pendingTill > 0 && newPendingTill <= 0) { + Auth().api().clearWebPageRequest(this); + } + type = resultType; + url = resultUrl; + displayUrl = resultDisplayUrl; + siteName = resultSiteName; + title = resultTitle; + description = newDescription; + photo = newPhoto; + document = newDocument; + duration = newDuration; + author = resultAuthor; + pendingTill = newPendingTill; + ++version; + if (App::main()) App::main()->webPageUpdated(this); + + return true; +} \ No newline at end of file diff --git a/Telegram/SourceFiles/data/data_web_page.h b/Telegram/SourceFiles/data/data_web_page.h index ccf3a0147..3c3369ef3 100644 --- a/Telegram/SourceFiles/data/data_web_page.h +++ b/Telegram/SourceFiles/data/data_web_page.h @@ -50,9 +50,9 @@ struct WebPageData { const TextWithEntities &description, DocumentData *document, PhotoData *photo, - int32 duration, + int duration, const QString &author, - int32 pendingTill) + int pendingTill) : id(id) , type(type) , url(url) @@ -72,6 +72,19 @@ struct WebPageData { if (photo) photo->forget(); } + bool applyChanges( + const QString &newType, + const QString &newUrl, + const QString &newDisplayUrl, + const QString &newSiteName, + const QString &newTitle, + const TextWithEntities &newDescription, + PhotoData *newPhoto, + DocumentData *newDocument, + int newDuration, + const QString &newAuthor, + int newPendingTill); + WebPageId id = 0; WebPageType type = WebPageArticle; QString url; @@ -79,10 +92,11 @@ struct WebPageData { QString siteName; QString title; TextWithEntities description; - int32 duration = 0; + int duration = 0; QString author; PhotoData *photo = nullptr; DocumentData *document = nullptr; - int32 pendingTill = 0; + int pendingTill = 0; + int version = 0; }; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 9b080a743..86e676f83 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -3567,21 +3567,6 @@ TextWithEntities HistoryCall::selectedText(TextSelection selection) const { namespace { -QString siteNameFromUrl(const QString &url) { - QUrl u(url); - QString pretty = u.isValid() ? u.toDisplayString() : url; - QRegularExpressionMatch m = QRegularExpression(qsl("^[a-zA-Z0-9]+://")).match(pretty); - if (m.hasMatch()) pretty = pretty.mid(m.capturedLength()); - int32 slash = pretty.indexOf('/'); - if (slash > 0) pretty = pretty.mid(0, slash); - QStringList components = pretty.split('.', QString::SkipEmptyParts); - if (components.size() >= 2) { - components = components.mid(components.size() - 2); - return components.at(0).at(0).toUpper() + components.at(0).mid(1) + '.' + components.at(1); - } - return QString(); -} - int32 articleThumbWidth(PhotoData *thumb, int32 height) { int32 w = thumb->medium->width(), h = thumb->medium->height(); return qMax(qMin(height * w / h, height), 1); @@ -3623,6 +3608,18 @@ void HistoryWebPage::initDimensions() { _maxw = _minh = _height = 0; return; } + const auto versionChanged = (_dataVersion != _data->version); + if (versionChanged) { + _dataVersion = _data->version; + _openl = nullptr; + if (_attach) { + _attach->detachFromParent(); + _attach = nullptr; + } + _title = Text(st::msgMinWidth - st::webPageLeft); + _description = Text(st::msgMinWidth - st::webPageLeft); + _siteNameWidth = 0; + } auto lineHeight = unitedLineHeight(); if (!_openl && !_data->url.isEmpty()) { @@ -3631,9 +3628,6 @@ void HistoryWebPage::initDimensions() { // init layout auto title = TextUtilities::SingleLine(_data->title.isEmpty() ? _data->author : _data->title); - if (!_data->description.text.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) { - _data->siteName = siteNameFromUrl(_data->url); - } if (!_data->document && _data->photo && _data->type != WebPagePhoto && _data->type != WebPageVideo) { if (_data->type == WebPageProfile) { _asArticle = true; @@ -3650,7 +3644,7 @@ void HistoryWebPage::initDimensions() { } // init attach - if (!_asArticle && !_attach) { + if (!_attach && !_asArticle) { if (_data->document) { if (_data->document->sticker()) { _attach = std::make_unique(_parent, _data->document); @@ -3664,6 +3658,9 @@ void HistoryWebPage::initDimensions() { } else if (_data->photo) { _attach = std::make_unique(_parent, _data->photo, QString()); } + if (_attach) { + _attach->attachToParent(); + } } auto textFloatsAroundInfo = !_asArticle && !_attach && isBubbleBottom(); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index ccd62ab8e..117768ece 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -942,13 +942,15 @@ private: std::unique_ptr _attach; bool _asArticle = false; - int32 _titleLines, _descriptionLines; + int _dataVersion = -1; + int _titleLines = 0; + int _descriptionLines = 0; Text _title, _description; - int32 _siteNameWidth = 0; + int _siteNameWidth = 0; QString _duration; - int32 _durationWidth = 0; + int _durationWidth = 0; int16 _pixw = 0; int16 _pixh = 0; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 32317724f..11b5c6ff6 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -187,6 +187,7 @@ <(src_loc)/data/data_types.h <(src_loc)/data/data_user_photos.cpp <(src_loc)/data/data_user_photos.h +<(src_loc)/data/data_web_page.cpp <(src_loc)/data/data_web_page.h <(src_loc)/dialogs/dialogs_common.h <(src_loc)/dialogs/dialogs_indexed_list.cpp