diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css
index 41a9709ca..58ac7a6d1 100644
--- a/Telegram/Resources/iv_html/page.css
+++ b/Telegram/Resources/iv_html/page.css
@@ -499,7 +499,7 @@ img,
video,
iframe {
max-width: 100%;
- max-height: 400px;
+ max-height: 480px;
vertical-align: top;
}
video {
@@ -703,24 +703,37 @@ figure.slideshow > figure figcaption a:hover {
opacity: 1;
}
+figure.collage-wrap {
+ margin: 0px 12px;
+}
+figure.collage-wrap figcaption {
+ padding: 6px 6px 0px;
+}
figure.collage {
- margin: -2px 16px;
- text-align: left;
+ overflow: hidden;
+ border-radius: 6px;
}
-figure.collage > figure {
- display: inline-block;
- vertical-align: top;
- width: calc(25% - 4px);
- margin: 2px;
- box-sizing: border-box;
+figure.collage .photo-wrap,
+figure.collage .video-wrap {
+ position: absolute;
}
-figure.collage > figure > i {
- background: no-repeat center;
+figure.collage .photo-wrap .photo {
background-size: cover;
- display: inline-block;
- vertical-align: top;
- width: 100%;
- padding-top: 100%;
+}
+figure.collage .video-wrap video {
+ object-fit: cover;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: auto;
+ height: auto;
+ min-width: 100%;
+ min-height: 100%;
+ transform: translate(-50%, -50%);
+}
+figure.collage .video-wrap .video-small,
+video[autoplay] {
+ pointer-events: none;
}
figure.table-wrap {
@@ -983,13 +996,74 @@ section.channel > a > h4 {
display: block;
margin: 0 auto;
}
-.photo-wrap {
+.photo-wrap,
+.video-wrap {
width: 100%;
- background-size: 100%;
margin: 0 auto;
+ position: relative;
+ overflow: hidden;
+}
+.photo-bg,
+.video-bg {
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ position: absolute;
+ filter: blur(16px);
+ width: 100%;
+ height: 100%;
+}
+.video-bg,
+video {
+ position: absolute;
+ top: 0px;
}
.photo {
- background-size: 100%;
+ position: relative;
+ background-size: contain;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+.photo,
+video {
+ opacity: 0;
+ transition: opacity 300ms ease-in-out;
+}
+.photo.loaded,
+video.loaded {
+ opacity: 1;
+}
+.video-play-outer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+.video-play {
+ position: relative;
+ width: 48px;
+ height: 0;
+ padding-top: 48px;
+ max-width: 48px;
+ max-height: 48px;
+ background-color: rgba(0, 0, 0, 0.34);
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+}
+.video-play::before {
+ content: '';
+ position: absolute;
+ margin: -48px -4px 0px 0px;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 10px 0 10px 16px;
+ border-color: transparent transparent transparent white;
}
.toast {
diff --git a/Telegram/Resources/iv_html/page.js b/Telegram/Resources/iv_html/page.js
index c38bd1119..181a11498 100644
--- a/Telegram/Resources/iv_html/page.js
+++ b/Telegram/Resources/iv_html/page.js
@@ -226,6 +226,36 @@ var IV = {
IV.stopRipples(e.currentTarget);
});
}
+ const photos = document.getElementsByClassName('photo');
+ for (let i = 0; i < photos.length; ++i) {
+ const photo = photos[i];
+ if (photo.classList.contains('loaded')) {
+ continue;
+ }
+
+ const url = photo.style.backgroundImage;
+ if (!url || url.length < 7) {
+ continue;
+ }
+ var img = new Image();
+ img.onload = function () {
+ photo.classList.add('loaded');
+ }
+ img.src = url.substr(5, url.length - 7);
+ if (img.complete) {
+ img.onload();
+ }
+ }
+ const videos = document.getElementsByTagName('video');
+ for (let i = 0; i < videos.length; ++i) {
+ const video = videos[i];
+ if (video.classList.contains('loaded')) {
+ continue;
+ }
+ video.addEventListener('canplay', function () {
+ video.classList.add('loaded');
+ });
+ }
IV.notify({ event: 'ready' });
},
showTooltip: function (text) {
diff --git a/Telegram/SourceFiles/iv/iv_controller.cpp b/Telegram/SourceFiles/iv/iv_controller.cpp
index 8dae7db07..dca23189c 100644
--- a/Telegram/SourceFiles/iv/iv_controller.cpp
+++ b/Telegram/SourceFiles/iv/iv_controller.cpp
@@ -489,6 +489,7 @@ 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;
+ const auto viewerPrefix = u"viewer"_q;
if (context.startsWith(channelPrefix)) {
_events.fire({
.type = Event::Type::OpenChannel,
@@ -505,6 +506,12 @@ void Controller::processLink(const QString &url, const QString &context) {
.url = url,
.context = context.mid(webpagePrefix.size()),
});
+ } else if (context.startsWith(viewerPrefix)) {
+ _events.fire({
+ .type = Event::Type::OpenMedia,
+ .url = url,
+ .context = context.mid(viewerPrefix.size()),
+ });
} else if (context.isEmpty()) {
_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 9ab5e7e05..f0b77276e 100644
--- a/Telegram/SourceFiles/iv/iv_controller.h
+++ b/Telegram/SourceFiles/iv/iv_controller.h
@@ -43,6 +43,7 @@ public:
OpenPage,
OpenLink,
OpenLinkExternal,
+ OpenMedia,
};
Type type = Type::Close;
QString url;
diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp
index 68e255925..b4539da6e 100644
--- a/Telegram/SourceFiles/iv/iv_instance.cpp
+++ b/Telegram/SourceFiles/iv/iv_instance.cpp
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "main/session/session_show.h"
#include "media/streaming/media_streaming_loader.h"
+#include "media/view/media_view_open_common.h"
#include "storage/file_download.h"
#include "storage/storage_domain.h"
#include "ui/boxes/confirm_box.h"
@@ -796,6 +797,40 @@ void Instance::show(
case Type::OpenLinkExternal:
QDesktopServices::openUrl(event.url);
break;
+ case Type::OpenMedia:
+ if (const auto window = Core::App().activeWindow()) {
+ const auto current = window->sessionController();
+ const auto controller = (current
+ && ¤t->session() == _shownSession)
+ ? current
+ : nullptr;
+ const auto item = (HistoryItem*)nullptr;
+ const auto topicRootId = MsgId(0);
+ if (event.context.startsWith("-photo")) {
+ const auto id = event.context.mid(6).toULongLong();
+ const auto photo = _shownSession->data().photo(id);
+ if (!photo->isNull()) {
+ window->openInMediaView({
+ controller,
+ photo,
+ item,
+ topicRootId
+ });
+ }
+ } else if (event.context.startsWith("-video")) {
+ const auto id = event.context.mid(6).toULongLong();
+ const auto video = _shownSession->data().document(id);
+ if (!video->isNull()) {
+ window->openInMediaView({
+ controller,
+ video,
+ item,
+ topicRootId
+ });
+ }
+ }
+ }
+ break;
case Type::OpenPage:
case Type::OpenLink:
_shownSession->api().request(MTPmessages_GetWebPage(
diff --git a/Telegram/SourceFiles/iv/iv_prepare.cpp b/Telegram/SourceFiles/iv/iv_prepare.cpp
index 4a46a7ab4..ebfbadd23 100644
--- a/Telegram/SourceFiles/iv/iv_prepare.cpp
+++ b/Telegram/SourceFiles/iv/iv_prepare.cpp
@@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "iv/iv_data.h"
#include "lang/lang_keys.h"
#include "ui/image/image_prepare.h"
+#include "ui/grouped_layout.h"
#include "styles/palette.h"
+#include "styles/style_chat.h"
#include
@@ -34,6 +36,19 @@ struct Photo {
struct Document {
uint64 id = 0;
+ int width = 0;
+ int height = 0;
+ QByteArray minithumbnail;
+};
+
+template >>
+[[nodiscard]] QByteArray Number(T value) {
+ return QByteArray::number(value);
+}
+
+template >>
+[[nodiscard]] QByteArray Percent(T value) {
+ return Number(base::SafeRound(value * 10000.) / 100.);
};
class Parser final {
@@ -50,6 +65,11 @@ private:
template
[[nodiscard]] QByteArray list(const MTPVector &data);
+ [[nodiscard]] QByteArray collage(
+ const QVector &list,
+ const std::vector &dimensions,
+ int offset = 0);
+
[[nodiscard]] QByteArray block(const MTPDpageBlockUnsupported &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockTitle &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockSubtitle &data);
@@ -64,8 +84,14 @@ private:
[[nodiscard]] QByteArray block(const MTPDpageBlockList &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockBlockquote &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockPullquote &data);
- [[nodiscard]] QByteArray block(const MTPDpageBlockPhoto &data);
- [[nodiscard]] QByteArray block(const MTPDpageBlockVideo &data);
+ [[nodiscard]] QByteArray block(
+ const MTPDpageBlockPhoto &data,
+ const Ui::GroupMediaLayout &layout = {},
+ QSize outer = {});
+ [[nodiscard]] QByteArray block(
+ const MTPDpageBlockVideo &data,
+ const Ui::GroupMediaLayout &layout = {},
+ QSize outer = {});
[[nodiscard]] QByteArray block(const MTPDpageBlockCover &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockEmbed &data);
[[nodiscard]] QByteArray block(const MTPDpageBlockEmbedPost &data);
@@ -123,6 +149,9 @@ private:
int zoom);
[[nodiscard]] QByteArray resource(QByteArray id);
+ [[nodiscard]] std::vector computeCollageDimensions(
+ const QVector &items);
+
const Options _options;
base::flat_set _resources;
@@ -209,6 +238,60 @@ QByteArray Parser::list(const MTPVector &data) {
return result.join(QByteArray());
}
+QByteArray Parser::collage(
+ const QVector &list,
+ const std::vector &dimensions,
+ int offset) {
+ Expects(list.size() == dimensions.size());
+
+ constexpr auto kPerCollage = 10;
+ const auto last = (offset + kPerCollage >= int(dimensions.size()));
+
+ auto result = QByteArray();
+ auto slice = ((offset > 0) || (dimensions.size() > kPerCollage))
+ ? (dimensions
+ | ranges::views::drop(offset)
+ | ranges::views::take(kPerCollage)
+ | ranges::to_vector)
+ : dimensions;
+ const auto layout = Ui::LayoutMediaGroup(
+ slice,
+ st::historyGroupWidthMax,
+ st::historyGroupWidthMin,
+ st::historyGroupSkip);
+ auto size = QSize();
+ for (const auto &part : layout) {
+ const auto &rect = part.geometry;
+ size = QSize(
+ std::max(size.width(), rect.x() + rect.width()),
+ std::max(size.height(), rect.y() + rect.height()));
+ }
+ for (auto i = 0, count = int(layout.size()); i != count; ++i) {
+ const auto &part = layout[i];
+ list[offset + i].match([&](const MTPDpageBlockPhoto &data) {
+ result += block(data, part, size);
+ }, [&](const MTPDpageBlockVideo &data) {
+ result += block(data, part, size);
+ }, [](const auto &) {
+ Unexpected("Block type in collage layout.");
+ });
+ }
+ const auto aspectHeight = size.height() / float64(size.width());
+ const auto aspectSkip = st::historyGroupSkip / float64(size.width());
+ auto wrapped = tag("figure", {
+ { "class", "collage" },
+ {
+ "style",
+ ("padding-top: " + Percent(aspectHeight) + "%; "
+ + "margin-bottom: " + Percent(last ? 0 : aspectSkip) + "%;")
+ },
+ }, result);
+ if (offset + kPerCollage < int(dimensions.size())) {
+ wrapped += collage(list, dimensions, offset + kPerCollage);
+ }
+ return wrapped;
+}
+
QByteArray Parser::block(const MTPDpageBlockUnsupported &data) {
return "Unsupported."_q;
}
@@ -289,40 +372,54 @@ QByteArray Parser::block(const MTPDpageBlockPullquote &data) {
rich(data.vtext()) + cite);
}
-QByteArray Parser::block(const MTPDpageBlockPhoto &data) {
+QByteArray Parser::block(
+ const MTPDpageBlockPhoto &data,
+ const Ui::GroupMediaLayout &layout,
+ QSize outer) {
+ const auto collage = !layout.geometry.isEmpty();
const auto photo = photoById(data.vphoto_id().v);
if (!photo.id) {
return "Photo not found.";
}
const auto src = photoFullUrl(photo);
auto wrapStyle = QByteArray();
- if (photo.width) {
- wrapStyle += "max-width:" + QByteArray::number(photo.width) + "px";
+ if (collage) {
+ const auto wcoef = 1. / outer.width();
+ const auto hcoef = 1. / outer.height();
+ wrapStyle += "left: " + Percent(layout.geometry.x() * wcoef) + "%; "
+ + "top: " + Percent(layout.geometry.y() * hcoef) + "%; "
+ + "width: " + Percent(layout.geometry.width() * wcoef) + "%; "
+ + "height: " + Percent(layout.geometry.height() * hcoef) + "%";
+ } else if (photo.width) {
+ wrapStyle += "max-width:" + Number(photo.width) + "px";
}
+ const auto dimension = collage
+ ? (layout.geometry.height() / float64(layout.geometry.width()))
+ : (photo.width && photo.height)
+ ? (photo.height / float64(photo.width))
+ : (3 / 4.);
+ const auto paddingTop = collage
+ ? Percent(dimension) + "%"
+ : "calc(min(480px, " + Percent(dimension) + "%))";
+ const auto style = "background-image:url('" + src + "');"
+ "padding-top: " + paddingTop + ";";
+ auto inner = tag("div", {
+ { "class", "photo" },
+ { "style", style } });
const auto minithumb = Images::ExpandInlineBytes(photo.minithumbnail);
if (!minithumb.isEmpty()) {
const auto image = Images::Read({ .content = minithumb });
- wrapStyle += ";background-image:url('data:image/jpeg;base64,"
- + minithumb.toBase64()
- + "');";
+ inner = tag("div", {
+ { "class", "photo-bg" },
+ { "style", "background-image:url('data:image/jpeg;base64,"
+ + minithumb.toBase64()
+ + "');" },
+ }) + inner;
}
- const auto dimension = (photo.width && photo.height)
- ? (photo.height / float64(photo.width))
- : (3 / 4.);
- const auto paddingTopPercent = int(base::SafeRound(dimension * 100));
- const auto style = "background-image:url('" + src + "');"
- "padding-top:" + QByteArray::number(paddingTopPercent) + "%";
- const auto inner = tag("div", {
- { "class", "photo" },
- { "style", style } });
- auto result = tag(
- "div",
- { { "class", "photo-wrap" }, { "style", wrapStyle } },
- inner);
- if (const auto url = data.vurl()) {
- result = tag("a", { { "href", utf(*url) } }, result);
- }
- auto attributes = Attributes();
+ auto attributes = Attributes{
+ { "class", "photo-wrap" },
+ { "style", wrapStyle }
+ };
if (_captionAsTitle) {
const auto caption = plain(data.vcaption().data().vtext());
const auto credit = plain(data.vcaption().data().vtext());
@@ -332,34 +429,89 @@ QByteArray Parser::block(const MTPDpageBlockPhoto &data) {
: (caption + credit);
attributes.push_back({ "title", title });
}
- } else {
+ }
+ auto result = tag("div", attributes, inner);
+
+ const auto href = data.vurl()
+ ? utf(*data.vurl())
+ : photoFullUrl(photo);
+ const auto id = Number(photo.id);
+ result = tag("a", {
+ { "href", href },
+ { "data-context", data.vurl() ? QByteArray() : "viewer-photo" + id },
+ }, result);
+ if (!_captionAsTitle) {
result += caption(data.vcaption());
}
- return tag("figure", attributes, result);
+ return result;
}
-QByteArray Parser::block(const MTPDpageBlockVideo &data) {
+QByteArray Parser::block(
+ const MTPDpageBlockVideo &data,
+ const Ui::GroupMediaLayout &layout,
+ QSize outer) {
+ const auto collage = !layout.geometry.isEmpty();
+ const auto collageSmall = collage
+ && (layout.geometry.width() < outer.width());
const auto video = documentById(data.vvideo_id().v);
if (!video.id) {
return "Video not found.";
}
const auto src = documentFullUrl(video);
auto vattributes = Attributes{};
+ if (collageSmall) {
+ vattributes.push_back({ "class", "video-small" });
+ }
if (data.is_autoplay()) {
vattributes.push_back({ "preload", "auto" });
vattributes.push_back({ "autoplay", std::nullopt });
- } else {
+ } else if (!collageSmall) {
vattributes.push_back({ "controls", std::nullopt });
}
if (data.is_loop()) {
vattributes.push_back({ "loop", std::nullopt });
}
vattributes.push_back({ "muted", std::nullopt });
- auto result = tag(
+ auto inner = tag(
"video",
vattributes,
tag("source", { { "src", src }, { "type", "video/mp4" } }));
- auto attributes = Attributes();
+ if (collageSmall) {
+ inner += tag(
+ "div",
+ { { "class", "video-play-outer" } },
+ tag("div", { { "class", "video-play" } }));
+ }
+ const auto minithumb = Images::ExpandInlineBytes(video.minithumbnail);
+ if (!minithumb.isEmpty()) {
+ const auto image = Images::Read({ .content = minithumb });
+ inner = tag("div", {
+ { "class", "video-bg" },
+ { "style", "background-image:url('data:image/jpeg;base64,"
+ + minithumb.toBase64()
+ + "');" },
+ }) + inner;
+ }
+ auto wrapStyle = QByteArray();
+ if (collage) {
+ const auto wcoef = 1. / outer.width();
+ const auto hcoef = 1. / outer.height();
+ wrapStyle += "left: " + Percent(layout.geometry.x() * wcoef) + "%; "
+ + "top: " + Percent(layout.geometry.y() * hcoef) + "%; "
+ + "width: " + Percent(layout.geometry.width() * wcoef) + "%; "
+ + "height: " + Percent(layout.geometry.height() * hcoef) + "%; ";
+ } else {
+ const auto dimension = (video.width && video.height)
+ ? (video.height / float64(video.width))
+ : (3 / 4.);
+ wrapStyle += "padding-top: calc(min(480px, "
+ + Percent(dimension)
+ + "%));";
+ }
+ auto attributes = Attributes{
+ { "class", "video-wrap" },
+ { "style", wrapStyle },
+ };
if (_captionAsTitle) {
const auto caption = plain(data.vcaption().data().vtext());
const auto credit = plain(data.vcaption().data().vtext());
@@ -369,10 +521,20 @@ QByteArray Parser::block(const MTPDpageBlockVideo &data) {
: (caption + credit);
attributes.push_back({ "title", title });
}
- } else {
+ }
+ auto result = tag("div", attributes, inner);
+ if (data.is_autoplay() || collageSmall) {
+ const auto id = Number(video.id);
+ const auto href = resource("video" + id);
+ result = tag("a", {
+ { "href", href },
+ { "data-context", "viewer-video" + id },
+ }, result);
+ }
+ if (!_captionAsTitle) {
result += caption(data.vcaption());
}
- return tag("figure", attributes, result);
+ return result;
}
QByteArray Parser::block(const MTPDpageBlockCover &data) {
@@ -394,13 +556,12 @@ QByteArray Parser::block(const MTPDpageBlockEmbed &data) {
eclass = "nowide";
} else if (data.is_full_width() || !data.vw()->v) {
width = "100%";
- height = QByteArray::number(data.vh()->v) + "px";
+ height = Number(data.vh()->v) + "px";
iframeWidth = "100%";
iframeHeight = height;
} else {
- const auto percent = data.vh()->v * 100 / data.vw()->v;
- width = QByteArray::number(data.vw()->v) + "px";
- height = QByteArray::number(percent) + "%";
+ width = Number(data.vw()->v) + "px";
+ height = Percent(data.vh()->v / float64(data.vw()->v)) + "%";
}
auto attributes = Attributes();
if (autosize) {
@@ -468,11 +629,18 @@ QByteArray Parser::block(const MTPDpageBlockEmbedPost &data) {
}
QByteArray Parser::block(const MTPDpageBlockCollage &data) {
- auto result = tag(
+ const auto &items = data.vitems().v;
+ const auto dimensions = computeCollageDimensions(items);
+ if (dimensions.empty()) {
+ return tag(
+ "figure",
+ tag("figure", list(data.vitems())) + caption(data.vcaption()));
+ }
+
+ return tag(
"figure",
- { { "class", "collage" } },
- list(data.vitems()));
- return tag("figure", result + caption(data.vcaption()));
+ { { "class", "collage-wrap" } },
+ collage(items, dimensions) + caption(data.vcaption()));
}
QByteArray Parser::block(const MTPDpageBlockSlideshow &data) {
@@ -482,7 +650,7 @@ QByteArray Parser::block(const MTPDpageBlockSlideshow &data) {
auto attributes = Attributes{
{ "type", "radio" },
{ "name", "s" },
- { "value", QByteArray::number(i) },
+ { "value", Number(i) },
{ "onchange", "return IV.slideshowSlide(this);" },
};
if (!i) {
@@ -509,7 +677,7 @@ QByteArray Parser::block(const MTPDpageBlockChannel &data) {
auto name = QByteArray();
auto username = QByteArray();
auto id = data.vchannel().match([](const auto &data) {
- return QByteArray::number(data.vid().v);
+ return Number(data.vid().v);
});
data.vchannel().match([&](const MTPDchannel &data) {
if (const auto has = data.vusername()) {
@@ -658,7 +826,7 @@ QByteArray Parser::block(const MTPDpageRelatedArticle &data) {
}
const auto webpageId = data.vwebpage_id().v;
const auto context = webpageId
- ? ("webpage" + QByteArray::number(webpageId))
+ ? ("webpage" + Number(webpageId))
: QByteArray();
return tag("a", {
{ "class", "related-link" },
@@ -690,10 +858,10 @@ QByteArray Parser::block(const MTPDpageTableCell &data) {
}
auto attributes = Attributes{ { "style", style } };
if (const auto cs = data.vcolspan()) {
- attributes.push_back({ "colspan", QByteArray::number(cs->v) });
+ attributes.push_back({ "colspan", Number(cs->v) });
}
if (const auto rs = data.vrowspan()) {
- attributes.push_back({ "rowspan", QByteArray::number(rs->v) });
+ attributes.push_back({ "rowspan", Number(rs->v) });
}
return tag(data.is_header() ? "th" : "td", attributes, text);
}
@@ -783,10 +951,10 @@ QByteArray Parser::rich(const MTPRichText &text) {
{ "src", documentFullUrl(image) },
};
if (const auto width = data.vw().v) {
- attributes.push_back({ "width", QByteArray::number(width) });
+ attributes.push_back({ "width", Number(width) });
}
if (const auto height = data.vh().v) {
- attributes.push_back({ "height", QByteArray::number(height) });
+ attributes.push_back({ "height", Number(height) });
}
return tag("img", attributes);
}, [&](const MTPDtextBold &data) {
@@ -802,7 +970,7 @@ QByteArray Parser::rich(const MTPRichText &text) {
}, [&](const MTPDtextUrl &data) {
const auto webpageId = data.vwebpage_id().v;
const auto context = webpageId
- ? ("webpage" + QByteArray::number(webpageId))
+ ? ("webpage" + Number(webpageId))
: QByteArray();
return tag("a", {
{ "href", utf(data.vurl()) },
@@ -911,6 +1079,24 @@ Document Parser::parse(const MTPDocument &document) {
};
document.match([](const MTPDdocumentEmpty &) {
}, [&](const MTPDdocument &data) {
+ for (const auto &attribute : data.vattributes().v) {
+ attribute.match([&](const MTPDdocumentAttributeImageSize &data) {
+ result.width = data.vw().v;
+ result.height = data.vh().v;
+ }, [&](const MTPDdocumentAttributeVideo &data) {
+ result.width = data.vw().v;
+ result.height = data.vh().v;
+ }, [](const auto &) {});
+ }
+ if (const auto sizes = data.vthumbs()) {
+ for (const auto &size : sizes->v) {
+ size.match([&](const MTPDphotoStrippedSize &data) {
+ result.minithumbnail = data.vbytes().v;
+ }, [&](const auto &data) {
+ });
+ }
+
+ }
});
return result;
}
@@ -938,11 +1124,11 @@ Document Parser::documentById(uint64 id) {
}
QByteArray Parser::photoFullUrl(const Photo &photo) {
- return resource("photo/" + QByteArray::number(photo.id));
+ return resource("photo/" + Number(photo.id));
}
QByteArray Parser::documentFullUrl(const Document &document) {
- return resource("document/" + QByteArray::number(document.id));
+ return resource("document/" + Number(document.id));
}
QByteArray Parser::embedUrl(const QByteArray &html) {
@@ -969,9 +1155,9 @@ QByteArray Parser::embedUrl(const QByteArray &html) {
QByteArray Parser::mapUrl(const Geo &geo, int width, int height, int zoom) {
return resource("map/"
+ GeoPointId(geo) + "&"
- + QByteArray::number(width) + ","
- + QByteArray::number(height) + "&"
- + QByteArray::number(zoom));
+ + Number(width) + ","
+ + Number(height) + "&"
+ + Number(zoom));
}
QByteArray Parser::resource(QByteArray id) {
@@ -982,6 +1168,32 @@ QByteArray Parser::resource(QByteArray id) {
return toFolder ? id : ('/' + id);
}
+std::vector Parser::computeCollageDimensions(
+ const QVector &items) {
+ auto result = std::vector(items.size());
+ if (items.size() < 2) {
+ return {};
+ }
+ for (auto i = 0, count = int(items.size()); i != count; ++i) {
+ auto size = QSize();
+ items[i].match([&](const MTPDpageBlockPhoto &data) {
+ const auto photo = photoById(data.vphoto_id().v);
+ if (photo.id && photo.width > 0 && photo.height > 0) {
+ result[i] = QSize(photo.width, photo.height);
+ }
+ }, [&](const MTPDpageBlockVideo &data) {
+ const auto document = documentById(data.vvideo_id().v);
+ if (document.id && document.width > 0 && document.height > 0) {
+ result[i] = QSize(document.width, document.height);
+ }
+ }, [](const auto &) {});
+ if (result[i].isEmpty()) {
+ return {};
+ }
+ }
+ return result;
+}
+
} // namespace
Prepared Prepare(const Source &source, const Options &options) {