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) {