diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css
index f9b086a4d..41a9709ca 100644
--- a/Telegram/Resources/iv_html/page.css
+++ b/Telegram/Resources/iv_html/page.css
@@ -306,7 +306,7 @@ article ol aside {
article blockquote cite,
article aside cite,
article footer cite,
-article .iv-pullquote cite {
+article .pullquote cite {
font-family: 'Helvetica Neue';
font-size: 15px;
display: block;
@@ -385,7 +385,7 @@ article table {
article table.bordered,
article table.bordered td,
article table.bordered th {
- border: 1px solid var(--td-box-divider-fg);
+ border: 1px solid var(--td-history-to-down-shadow);
}
article table.striped tr:nth-child(odd) td {
background-color: var(--td-box-divider-bg);
@@ -425,7 +425,7 @@ article details {
article details:before {
content: '';
display: block;
- border-bottom: 1px solid var(--td-box-divider-fg);
+ border-bottom: 1px solid var(--td-history-to-down-shadow);
position: absolute;
left: 18px;
right: 0;
@@ -818,6 +818,7 @@ section.embed-post small {
color: var(--td-window-sub-text-fg);
}
+
section.related {
margin: 7px 0 12px;
}
@@ -842,7 +843,7 @@ section.related a.related-link {
section.related a.related-link:after {
content: '';
display: block;
- border-bottom: 1px solid var(--td-box-divider-fg);
+ border-bottom: 1px solid var(--td-history-to-down-shadow);
position: absolute;
left: 18px;
right: 0;
@@ -975,19 +976,19 @@ section.channel > a > h4 {
padding: 7px 18px;
}
-.iv-pullquote {
+.pullquote {
text-align: center;
max-width: 420px;
font-size: 19px;
display: block;
margin: 0 auto;
}
-.iv-photo-wrap {
+.photo-wrap {
width: 100%;
background-size: 100%;
margin: 0 auto;
}
-.iv-photo {
+.photo {
background-size: 100%;
}
diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp
index e5b1f5be6..afe91b8a1 100644
--- a/Telegram/SourceFiles/data/data_document.cpp
+++ b/Telegram/SourceFiles/data/data_document.cpp
@@ -772,7 +772,11 @@ Storage::Cache::Key DocumentData::bigFileBaseCacheKey() const {
}
void DocumentData::forceToCache(bool force) {
- _flags |= Flag::ForceToCache;
+ if (force) {
+ _flags |= Flag::ForceToCache;
+ } else {
+ _flags &= ~Flag::ForceToCache;
+ }
}
bool DocumentData::saveToCache() const {
diff --git a/Telegram/SourceFiles/iv/iv_controller.cpp b/Telegram/SourceFiles/iv/iv_controller.cpp
index 6b1d6cc0f..c5aca31f5 100644
--- a/Telegram/SourceFiles/iv/iv_controller.cpp
+++ b/Telegram/SourceFiles/iv/iv_controller.cpp
@@ -482,18 +482,25 @@ void Controller::processKey(const QString &key, const QString &modifier) {
void Controller::processLink(const QString &url, const QString &context) {
const auto channelPrefix = u"channel"_q;
const auto joinPrefix = u"join_link"_q;
+ const auto webpagePrefix = u"webpage"_q;
if (context.startsWith(channelPrefix)) {
_events.fire({
- Event::Type::OpenChannel,
- context.mid(channelPrefix.size()),
+ .type = Event::Type::OpenChannel,
+ .context = context.mid(channelPrefix.size()),
});
} else if (context.startsWith(joinPrefix)) {
_events.fire({
- Event::Type::JoinChannel,
- context.mid(joinPrefix.size()),
+ .type = Event::Type::JoinChannel,
+ .context = context.mid(joinPrefix.size()),
+ });
+ } else if (context.startsWith(webpagePrefix)) {
+ _events.fire({
+ .type = Event::Type::OpenPage,
+ .url = url,
+ .context = context.mid(webpagePrefix.size()),
});
} else if (context.isEmpty()) {
- _events.fire({ Event::Type::OpenLink, url });
+ _events.fire({ .type = Event::Type::OpenLink, .url = url });
}
}
diff --git a/Telegram/SourceFiles/iv/iv_controller.h b/Telegram/SourceFiles/iv/iv_controller.h
index 202a5b17c..10d76befc 100644
--- a/Telegram/SourceFiles/iv/iv_controller.h
+++ b/Telegram/SourceFiles/iv/iv_controller.h
@@ -38,9 +38,11 @@ public:
Quit,
OpenChannel,
JoinChannel,
+ OpenPage,
OpenLink,
};
Type type = Type::Close;
+ QString url;
QString context;
};
diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp
index 2ac591c22..ae9cb4064 100644
--- a/Telegram/SourceFiles/iv/iv_instance.cpp
+++ b/Telegram/SourceFiles/iv/iv_instance.cpp
@@ -15,12 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_cloud_file.h"
#include "data/data_document.h"
+#include "data/data_document_media.h"
#include "data/data_file_origin.h"
#include "data/data_photo_media.h"
#include "data/data_session.h"
+#include "data/data_web_page.h"
#include "info/profile/info_profile_values.h"
#include "iv/iv_controller.h"
#include "iv/iv_data.h"
+#include "lottie/lottie_common.h" // Lottie::ReadContent.
#include "main/main_account.h"
#include "main/main_domain.h"
#include "main/main_session.h"
@@ -97,13 +100,17 @@ private:
std::vector loaded;
int64 offset = 0;
};
- struct FileLoad {
+ struct FileStream {
not_null document;
std::unique_ptr loader;
std::vector requests;
std::string mime;
rpl::lifetime lifetime;
};
+ struct FileLoad {
+ std::shared_ptr<::Data::DocumentMedia> media;
+ std::vector requests;
+ };
void showLocal(Prepared result);
void showWindowed(Prepared result);
@@ -123,9 +130,9 @@ private:
// Windowed.
void streamPhoto(PhotoId photoId, Webview::DataRequest request);
void streamFile(DocumentId documentId, Webview::DataRequest request);
- void streamFile(FileLoad &file, Webview::DataRequest request);
+ void streamFile(FileStream &file, Webview::DataRequest request);
void processPartInFile(
- FileLoad &file,
+ FileStream &file,
Media::Streaming::LoadedPart &&part);
bool finishRequestWithPart(
PartRequest &request,
@@ -134,6 +141,9 @@ private:
void sendEmbed(QByteArray hash, Webview::DataRequest request);
void fillChannelJoinedValues(const Prepared &result);
+ void subscribeToDocuments();
+ [[nodiscard]] QByteArray readFile(
+ const std::shared_ptr<::Data::DocumentMedia> &media);
void requestDone(
Webview::DataRequest request,
QByteArray bytes,
@@ -146,6 +156,7 @@ private:
std::shared_ptr _show;
QString _id;
std::unique_ptr _controller;
+ base::flat_map _streams;
base::flat_map _files;
base::flat_map> _inChannelValues;
@@ -157,6 +168,7 @@ private:
rpl::event_stream _events;
+ rpl::lifetime _documentLifetime;
rpl::lifetime _lifetime;
};
@@ -461,20 +473,37 @@ void Shown::streamFile(
Webview::DataRequest request) {
using namespace Data;
- const auto i = _files.find(documentId);
- if (i != end(_files)) {
+ const auto i = _streams.find(documentId);
+ if (i != end(_streams)) {
streamFile(i->second, std::move(request));
return;
}
const auto document = _session->data().document(documentId);
auto loader = document->createStreamingLoader(FileOrigin(), false);
if (!loader) {
- requestFail(std::move(request));
+ if (document->size >= Storage::kMaxFileInMemory) {
+ requestFail(std::move(request));
+ } else {
+ auto media = document->createMediaView();
+ if (const auto content = readFile(media); !content.isEmpty()) {
+ requestDone(
+ std::move(request),
+ content,
+ document->mimeString().toStdString());
+ } else {
+ subscribeToDocuments();
+ auto &file = _files[documentId];
+ file.media = std::move(media);
+ file.requests.push_back(std::move(request));
+ document->forceToCache(true);
+ document->save(::Data::FileOrigin(), QString());
+ }
+ }
return;
}
- auto &file = _files.emplace(
+ auto &file = _streams.emplace(
documentId,
- FileLoad{
+ FileStream{
.document = document,
.loader = std::move(loader),
.mime = document->mimeString().toStdString(),
@@ -482,15 +511,15 @@ void Shown::streamFile(
file.loader->parts(
) | rpl::start_with_next([=](Media::Streaming::LoadedPart &&part) {
- const auto i = _files.find(documentId);
- Assert(i != end(_files));
+ const auto i = _streams.find(documentId);
+ Assert(i != end(_streams));
processPartInFile(i->second, std::move(part));
}, file.lifetime);
streamFile(file, std::move(request));
}
-void Shown::streamFile(FileLoad &file, Webview::DataRequest request) {
+void Shown::streamFile(FileStream &file, Webview::DataRequest request) {
constexpr auto kPart = Media::Streaming::Loader::kPartSize;
const auto size = file.document->size;
const auto last = int((size + kPart - 1) / kPart);
@@ -519,8 +548,44 @@ void Shown::streamFile(FileLoad &file, Webview::DataRequest request) {
}
}
+void Shown::subscribeToDocuments() {
+ if (_documentLifetime) {
+ return;
+ }
+ _documentLifetime = _session->data().documentLoadProgress(
+ ) | rpl::filter([=](not_null document) {
+ return !document->loading();
+ }) | rpl::start_with_next([=](not_null document) {
+ const auto i = _files.find(document->id);
+ if (i == end(_files)) {
+ return;
+ }
+ auto requests = base::take(i->second.requests);
+ const auto content = readFile(i->second.media);
+ _files.erase(i);
+
+ if (!content.isEmpty()) {
+ for (auto &request : requests) {
+ requestDone(
+ std::move(request),
+ content,
+ document->mimeString().toStdString());
+ }
+ } else {
+ for (auto &request : requests) {
+ requestFail(std::move(request));
+ }
+ }
+ });
+}
+
+QByteArray Shown::readFile(
+ const std::shared_ptr<::Data::DocumentMedia> &media) {
+ return Lottie::ReadContent(media->bytes(), media->owner()->filepath());
+}
+
void Shown::processPartInFile(
- FileLoad &file,
+ FileStream &file,
Media::Streaming::LoadedPart &&part) {
for (auto i = begin(file.requests); i != end(file.requests);) {
if (finishRequestWithPart(*i, part)) {
@@ -725,8 +790,26 @@ void Instance::show(
case Type::JoinChannel:
processJoinChannel(event.context);
break;
+ case Type::OpenPage:
case Type::OpenLink:
- UrlClickHandler::Open(event.context);
+ _shownSession->api().request(MTPmessages_GetWebPage(
+ MTP_string(event.url),
+ MTP_int(0)
+ )).done([=](const MTPmessages_WebPage &result) {
+ _shownSession->data().processUsers(result.data().vusers());
+ _shownSession->data().processChats(result.data().vchats());
+ const auto page = _shownSession->data().processWebpage(
+ result.data().vwebpage());
+ if (page && page->iv) {
+ const auto parts = event.url.split('#');
+ const auto hash = (parts.size() > 1) ? parts[1] : u""_q;
+ this->show(show, page->iv.get(), hash);
+ } else {
+ UrlClickHandler::Open(event.url);
+ }
+ }).fail([=] {
+ UrlClickHandler::Open(event.url);
+ }).send();
break;
}
}, _shown->lifetime());
diff --git a/Telegram/SourceFiles/iv/iv_prepare.cpp b/Telegram/SourceFiles/iv/iv_prepare.cpp
index 7262f4ddb..4a46a7ab4 100644
--- a/Telegram/SourceFiles/iv/iv_prepare.cpp
+++ b/Telegram/SourceFiles/iv/iv_prepare.cpp
@@ -214,11 +214,11 @@ QByteArray Parser::block(const MTPDpageBlockUnsupported &data) {
}
QByteArray Parser::block(const MTPDpageBlockTitle &data) {
- return tag("h1", { { "class", "iv-title" } }, rich(data.vtext()));
+ return tag("h1", { { "class", "title" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockSubtitle &data) {
- return tag("h2", { { "class", "iv-subtitle" } }, rich(data.vtext()));
+ return tag("h2", { { "class", "subtitle" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockAuthorDate &data) {
@@ -232,11 +232,11 @@ QByteArray Parser::block(const MTPDpageBlockAuthorDate &data) {
}
QByteArray Parser::block(const MTPDpageBlockHeader &data) {
- return tag("h3", { { "class", "iv-header" } }, rich(data.vtext()));
+ return tag("h3", { { "class", "header" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockSubheader &data) {
- return tag("h4", { { "class", "iv-subheader" } }, rich(data.vtext()));
+ return tag("h4", { { "class", "subheader" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockParagraph &data) {
@@ -255,11 +255,11 @@ QByteArray Parser::block(const MTPDpageBlockPreformatted &data) {
}
QByteArray Parser::block(const MTPDpageBlockFooter &data) {
- return tag("footer", { { "class", "iv-footer" } }, rich(data.vtext()));
+ return tag("footer", { { "class", "footer" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockDivider &data) {
- return tag("hr", { { "class", "iv-divider" } });
+ return tag("hr", { { "class", "divider" } });
}
QByteArray Parser::block(const MTPDpageBlockAnchor &data) {
@@ -285,7 +285,7 @@ QByteArray Parser::block(const MTPDpageBlockPullquote &data) {
: tag("cite", caption);
return tag(
"div",
- { { "class", "iv-pullquote" } },
+ { { "class", "pullquote" } },
rich(data.vtext()) + cite);
}
@@ -313,11 +313,11 @@ QByteArray Parser::block(const MTPDpageBlockPhoto &data) {
const auto style = "background-image:url('" + src + "');"
"padding-top:" + QByteArray::number(paddingTopPercent) + "%";
const auto inner = tag("div", {
- { "class", "iv-photo" },
+ { "class", "photo" },
{ "style", style } });
auto result = tag(
"div",
- { { "class", "iv-photo-wrap" }, { "style", wrapStyle } },
+ { { "class", "photo-wrap" }, { "style", wrapStyle } },
inner);
if (const auto url = data.vurl()) {
result = tag("a", { { "href", utf(*url) } }, result);
@@ -552,7 +552,7 @@ QByteArray Parser::block(const MTPDpageBlockAudio &data) {
}
QByteArray Parser::block(const MTPDpageBlockKicker &data) {
- return tag("h6", { { "class", "iv-kicker" } }, rich(data.vtext()));
+ return tag("h6", { { "class", "kicker" } }, rich(data.vtext()));
}
QByteArray Parser::block(const MTPDpageBlockTable &data) {
@@ -599,9 +599,9 @@ QByteArray Parser::block(const MTPDpageBlockRelatedArticles &data) {
}
auto title = rich(data.vtitle());
if (!title.isEmpty()) {
- title = tag("h4", { { "class", "iv-related-title" } }, title);
+ title = tag("h4", { { "class", "related-title" } }, title);
}
- return tag("section", { { "class", "iv-related" } }, title + result);
+ return tag("section", { { "class", "related" } }, title + result);
}
QByteArray Parser::block(const MTPDpageBlockMap &data) {