mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Load non-streamable documents.
This commit is contained in:
parent
5c428ca502
commit
c46f34c677
6 changed files with 135 additions and 38 deletions
|
@ -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%;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,11 @@ public:
|
|||
Quit,
|
||||
OpenChannel,
|
||||
JoinChannel,
|
||||
OpenPage,
|
||||
OpenLink,
|
||||
};
|
||||
Type type = Type::Close;
|
||||
QString url;
|
||||
QString context;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<bool> loaded;
|
||||
int64 offset = 0;
|
||||
};
|
||||
struct FileLoad {
|
||||
struct FileStream {
|
||||
not_null<DocumentData*> document;
|
||||
std::unique_ptr<Media::Streaming::Loader> loader;
|
||||
std::vector<PartRequest> requests;
|
||||
std::string mime;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
struct FileLoad {
|
||||
std::shared_ptr<::Data::DocumentMedia> media;
|
||||
std::vector<Webview::DataRequest> 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<Main::SessionShow> _show;
|
||||
QString _id;
|
||||
std::unique_ptr<Controller> _controller;
|
||||
base::flat_map<DocumentId, FileStream> _streams;
|
||||
base::flat_map<DocumentId, FileLoad> _files;
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> _inChannelValues;
|
||||
|
||||
|
@ -157,6 +168,7 @@ private:
|
|||
|
||||
rpl::event_stream<Controller::Event> _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<DocumentData*> document) {
|
||||
return !document->loading();
|
||||
}) | rpl::start_with_next([=](not_null<DocumentData*> 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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue