From 6796ac688a33bcc1fe9115c18e9c6e84c415513e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sun, 31 Dec 2017 12:39:21 +0300
Subject: [PATCH] Apply web page media updates.

---
 Telegram/SourceFiles/app.cpp                  |  94 ++++++++------
 Telegram/SourceFiles/app.h                    |  47 ++++++-
 Telegram/SourceFiles/data/data_web_page.cpp   | 122 ++++++++++++++++++
 Telegram/SourceFiles/data/data_web_page.h     |  22 +++-
 .../history/history_media_types.cpp           |  35 +++--
 .../SourceFiles/history/history_media_types.h |   8 +-
 Telegram/gyp/telegram_sources.txt             |   1 +
 7 files changed, 261 insertions(+), 68 deletions(-)
 create mode 100644 Telegram/SourceFiles/data/data_web_page.cpp

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<MTPDocumentAttribute> &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<MTPDocumentAttribute> &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<HistorySticker>(_parent, _data->document);
@@ -3664,6 +3658,9 @@ void HistoryWebPage::initDimensions() {
 		} else if (_data->photo) {
 			_attach = std::make_unique<HistoryPhoto>(_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<HistoryMedia> _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