Support complex history and anchors.

This commit is contained in:
John Preston 2023-12-07 14:37:58 +04:00
parent fae10cfa6b
commit 8b62c37c34
7 changed files with 369 additions and 68 deletions

View file

@ -128,9 +128,33 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
bottom: -36px; bottom: -36px;
} }
.page-scroll {
position: absolute;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
.page-slide {
position: relative;
width: 100%;
margin-left: 0%;
transition: margin 240ms ease-in-out;
}
.hidden-left,
.hidden-right {
pointer-events: none;
}
.hidden-left .page-slide {
margin-left: -100%;
}
.hidden-right .page-slide {
margin-left: 100%;
}
article { article {
padding-bottom: 12px; padding-bottom: 12px;
overflow: hidden; overflow-y: hidden;
overflow-x: auto;
white-space: pre-wrap; white-space: pre-wrap;
max-width: 732px; max-width: 732px;
margin: 0 auto; margin: 0 auto;
@ -150,6 +174,9 @@ article h2 {
margin: -6px 18px 12px; margin: -6px 18px 12px;
color: var(--td-window-sub-text-fg); color: var(--td-window-sub-text-fg);
} }
article h5 {
margin: 21px 18px 12px;
}
article address { article address {
font-size: 15px; font-size: 15px;
color: var(--td-window-sub-text-fg); color: var(--td-window-sub-text-fg);

View file

@ -35,20 +35,31 @@ var IV = {
context: context, context: context,
}); });
} else if (target.hash.length < 2) { } else if (target.hash.length < 2) {
IV.hash = ''; IV.jumpToHash('');
IV.scrollTo(0);
} else { } else {
const name = target.hash.substr(1); IV.jumpToHash(target.hash.substr(1));
IV.hash = name;
const element = document.getElementsByName(name)[0];
if (element) {
const y = element.getBoundingClientRect().y;
IV.scrollTo(y + document.documentElement.scrollTop);
}
} }
e.preventDefault(); e.preventDefault();
}, },
jumpToHash: function (hash, instant) {
var current = IV.computeCurrentState();
current.hash = hash;
window.history.replaceState(current, '');
if (hash == '') {
IV.scrollTo(0, instant);
return;
}
var element = document.getElementsByName(hash)[0];
if (element) {
var y = 0;
while (element && !element.classList.contains('page-scroll')) {
y += element.offsetTop;
element = element.offsetParent;
}
IV.scrollTo(y, instant);
}
},
frameKeyDown: function (e) { frameKeyDown: function (e) {
const keyW = (e.key === 'w') const keyW = (e.key === 'w')
|| (e.code === 'KeyW') || (e.code === 'KeyW')
@ -210,7 +221,7 @@ var IV = {
} }
}, },
init: function () { init: function () {
IV.hash = window.location.hash.substr(1); window.history.replaceState(IV.computeCurrentState(), '');
const buttons = document.getElementsByClassName('fixed_button'); const buttons = document.getElementsByClassName('fixed_button');
for (let i = 0; i < buttons.length; ++i) { for (let i = 0; i < buttons.length; ++i) {
@ -228,6 +239,10 @@ var IV = {
IV.stopRipples(e.currentTarget); IV.stopRipples(e.currentTarget);
}); });
} }
IV.initMedia();
IV.notify({ event: 'ready' });
},
initMedia: function () {
const photos = document.getElementsByClassName('photo'); const photos = document.getElementsByClassName('photo');
for (let i = 0; i < photos.length; ++i) { for (let i = 0; i < photos.length; ++i) {
const photo = photos[i]; const photo = photos[i];
@ -258,7 +273,6 @@ var IV = {
video.classList.add('loaded'); video.classList.add('loaded');
}); });
} }
IV.notify({ event: 'ready' });
}, },
showTooltip: function (text) { showTooltip: function (text) {
var toast = document.createElement('div'); var toast = document.createElement('div');
@ -272,14 +286,163 @@ var IV = {
document.body.removeChild(toast); document.body.removeChild(toast);
}, 3000); }, 3000);
}, },
scrollTo: function (y) { scrollTo: function (y, instant) {
document.getElementById('bottom_up').classList.add('hidden'); document.getElementById('bottom_up').classList.add('hidden');
window.scrollTo({ top: y || 0, behavior: 'smooth' }); IV.findPageScroll().scrollTo({
top: y || 0,
behavior: instant ? 'instant' : 'smooth'
});
}, },
menu: function (button) { menu: function (button) {
IV.frozenRipple = button.id; IV.frozenRipple = button.id;
IV.notify({ event: 'menu', hash: IV.hash }); const state = this.computeCurrentState();
} IV.notify({ event: 'menu', index: state.index, hash: state.hash });
},
computeCurrentState: function () {
var now = IV.findPageScroll();
return {
position: IV.position,
index: IV.index,
hash: ((!window.history.state
|| window.history.state.hash === undefined)
? window.location.hash.substr(1)
: window.history.state.hash),
scroll: now ? now.scrollTop : 0
};
},
navigateTo: function (index, hash) {
if (!index && !IV.index) {
IV.navigateToDOM(IV.index, hash);
return;
}
IV.pending = [index, hash];
if (!IV.cache[index]) {
IV.cache[index] = { loading: true };
let xhr = new XMLHttpRequest();
xhr.onload = function () {
IV.cache[index].loading = false;
IV.cache[index].content = xhr.responseText;
if (IV.pending && IV.pending[0] == index) {
IV.navigateToLoaded(index, IV.pending[1]);
}
}
xhr.open('GET', 'page' + index + '.json');
xhr.send();
} else if (IV.cache[index].dom) {
IV.navigateToDOM(index, hash);
} else if (IV.cache[index].content) {
IV.navigateToLoaded(index, hash);
}
},
navigateToLoaded: function (index, hash) {
if (IV.cache[index].dom) {
IV.navigateToDOM(index, hash);
} else {
var data = JSON.parse(IV.cache[index].content);
var el = document.createElement('div');
el.className = 'page-scroll';
el.innerHTML = '<div class="page-slide"><article>'
+ data.html
+ '</article></div>';
IV.cache[index].dom = el;
IV.navigateToDOM(index, hash);
eval(data.js);
}
},
navigateToDOM: function (index, hash) {
IV.pending = null;
if (IV.index == index) {
IV.jumpToHash(hash);
return;
}
window.history.replaceState(IV.computeCurrentState(), '');
IV.position = IV.position + 1;
window.history.pushState(
{ position: IV.position, index: index, hash: hash },
'',
'page' + index + '.html' + (hash.length ? '#' + hash : ''));
IV.showDOM(index, hash);
},
findPageScroll: function () {
var all = document.getElementsByClassName('page-scroll');
for (i = 0; i < all.length; ++i) {
if (!all[i].classList.contains('hidden-left')
&& !all[i].classList.contains('hidden-right')) {
return all[i];
}
}
return null;
},
showDOM: function (index, hash, scroll) {
IV.pending = null;
if (IV.index != index) {
var initial = !window.history.state
|| window.history.state.position === undefined;
var back = initial
|| IV.position > window.history.state.position;
IV.position = initial ? 0 : window.history.state.position;
var now = IV.cache[index].dom;
var was = IV.findPageScroll();
if (!IV.cache[IV.index]) {
IV.cache[IV.index] = {};
}
IV.cache[IV.index].dom = was;
was.parentNode.appendChild(now);
if (scroll !== undefined) {
now.scrollTop = scroll;
}
now.classList.add(back ? 'hidden-left' : 'hidden-right');
now.classList.remove(back ? 'hidden-right' : 'hidden-left');
now.firstChild.getAnimations().forEach(
(animation) => animation.finish());
if (!was.listening) {
was.listening = true;
was.firstChild.addEventListener('transitionend', function (e) {
if (was.classList.contains('hidden-left')
|| was.classList.contains('hidden-right')) {
if (was.parentNode) {
was.parentNode.removeChild(was);
}
}
});
}
was.classList.add(back ? 'hidden-right' : 'hidden-left');
now.classList.remove(back ? 'hidden-left' : 'hidden-right');
var topBack = document.getElementById('top_back');
if (!IV.position) {
topBack.classList.add('hidden');
} else {
topBack.classList.remove('hidden');
}
IV.index = index;
IV.initMedia();
if (scroll === undefined) {
IV.jumpToHash(hash, true);
}
} else if (scroll !== undefined) {
IV.scrollTo(scroll);
} else {
IV.jumpToHash(hash);
}
},
back: function () {
window.history.back();
},
cache: {},
index: 0,
position: 0
}; };
document.onclick = IV.frameClickHandler; document.onclick = IV.frameClickHandler;
@ -288,3 +451,9 @@ document.onmouseenter = IV.frameMouseEnter;
document.onmouseup = IV.frameMouseUp; document.onmouseup = IV.frameMouseUp;
document.onscroll = IV.frameScrolled; document.onscroll = IV.frameScrolled;
window.onmessage = IV.postMessageHandler; window.onmessage = IV.postMessageHandler;
window.addEventListener('popstate', function (e) {
if (e.state) {
IV.showDOM(e.state.index, e.state.hash, e.state.scroll);
}
});

View file

@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QJsonValue> #include <QtCore/QJsonValue>
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <charconv>
namespace Iv { namespace Iv {
namespace { namespace {
@ -130,9 +131,7 @@ namespace {
.replace('\'', "\\\'"); .replace('\'', "\\\'");
} }
[[nodiscard]] QByteArray WrapPage( [[nodiscard]] QByteArray WrapPage(const Prepared &page) {
const Prepared &page,
const QByteArray &initScript) {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
const auto classAttribute = ""_q; const auto classAttribute = ""_q;
#else // Q_OS_MAC #else // Q_OS_MAC
@ -143,7 +142,7 @@ namespace {
+ (page.hasCode ? "IV.initPreBlocks();" : "") + (page.hasCode ? "IV.initPreBlocks();" : "")
+ (page.hasEmbeds ? "IV.initEmbedBlocks();" : "") + (page.hasEmbeds ? "IV.initEmbedBlocks();" : "")
+ "IV.init();" + "IV.init();"
+ initScript; + page.script;
const auto contentAttributes = page.rtl const auto contentAttributes = page.rtl
? " dir=\"rtl\" class=\"rtl\""_q ? " dir=\"rtl\" class=\"rtl\""_q
@ -183,7 +182,9 @@ namespace {
<path d="M14.9972363,18 L9.13865768,12.1414214 C9.06055283,12.0633165 9.06055283,11.9366835 9.13865768,11.8585786 L14.9972363,6 L14.9972363,6" transform="translate(11.997236, 12.000000) scale(-1, -1) rotate(-90.000000) translate(-11.997236, -12.000000) "></path> <path d="M14.9972363,18 L9.13865768,12.1414214 C9.06055283,12.0633165 9.06055283,11.9366835 9.13865768,11.8585786 L14.9972363,6 L14.9972363,6" transform="translate(11.997236, 12.000000) scale(-1, -1) rotate(-90.000000) translate(-11.997236, -12.000000) "></path>
</svg> </svg>
</button> </button>
<article)"_q + contentAttributes + ">"_q + page.content + R"(</article> <div class="page-scroll"><div class="page-slide">
<article)"_q + contentAttributes + ">"_q + page.content + R"(</article>
</div></div>
<script>)"_q + js + R"(</script> <script>)"_q + js + R"(</script>
</body> </body>
</html> </html>
@ -199,28 +200,29 @@ Controller::Controller()
_webview->eval("IV.updateStyles('" + str + "');"); _webview->eval("IV.updateStyles('" + str + "');");
} }
}) { }) {
createWindow();
} }
Controller::~Controller() { Controller::~Controller() {
_window->hide();
_ready = false; _ready = false;
_webview = nullptr; _webview = nullptr;
_title = nullptr; _title = nullptr;
_window = nullptr; _window = nullptr;
} }
bool Controller::showFast(const QString &url, const QString &hash) {
return false;
}
void Controller::show( void Controller::show(
const QString &dataPath, const QString &dataPath,
Prepared page, Prepared page,
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) { base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) {
createWindow(); page.script = fillInChannelValuesScript(std::move(inChannelValues));
const auto js = fillInChannelValuesScript(std::move(inChannelValues));
_titleText.setText(st::ivTitle.style, page.title); _titleText.setText(st::ivTitle.style, page.title);
InvokeQueued(_container, [=, page = std::move(page)]() mutable { InvokeQueued(_container, [=, page = std::move(page)]() mutable {
showInWindow(dataPath, std::move(page), js); showInWindow(dataPath, std::move(page));
if (!_webview) {
return;
}
}); });
} }
@ -228,13 +230,15 @@ QByteArray Controller::fillInChannelValuesScript(
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) { base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) {
auto result = QByteArray(); auto result = QByteArray();
for (auto &[id, in] : inChannelValues) { for (auto &[id, in] : inChannelValues) {
std::move(in) | rpl::start_with_next([=](bool in) { if (_inChannelSubscribed.emplace(id).second) {
if (_ready) { std::move(in) | rpl::start_with_next([=](bool in) {
_webview->eval(toggleInChannelScript(id, in)); if (_ready) {
} else { _webview->eval(toggleInChannelScript(id, in));
_inChannelChanged[id] = in; } else {
} _inChannelChanged[id] = in;
}, _lifetime); }
}, _lifetime);
}
} }
for (const auto &[id, in] : base::take(_inChannelChanged)) { for (const auto &[id, in] : base::take(_inChannelChanged)) {
result += toggleInChannelScript(id, in); result += toggleInChannelScript(id, in);
@ -342,14 +346,10 @@ void Controller::createWindow() {
window->show(); window->show();
} }
void Controller::showInWindow( void Controller::createWebview(const QString &dataPath) {
const QString &dataPath, Expects(!_webview);
Prepared page,
const QByteArray &initScript) {
Expects(_container != nullptr);
const auto window = _window.get(); const auto window = _window.get();
_url = page.url;
_webview = std::make_unique<Webview::Window>( _webview = std::make_unique<Webview::Window>(
_container, _container,
Webview::WindowConfig{ Webview::WindowConfig{
@ -362,10 +362,7 @@ void Controller::showInWindow(
_ready = false; _ready = false;
_webview = nullptr; _webview = nullptr;
}); });
if (!raw->widget()) {
_events.fire({ Event::Type::Close });
return;
}
window->events( window->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) { ) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close) { if (e->type() == QEvent::Close) {
@ -411,11 +408,18 @@ void Controller::showInWindow(
for (const auto &[id, in] : base::take(_inChannelChanged)) { for (const auto &[id, in] : base::take(_inChannelChanged)) {
script += toggleInChannelScript(id, in); script += toggleInChannelScript(id, in);
} }
if (_navigateToIndexWhenReady >= 0) {
script += navigateScript(
std::exchange(_navigateToIndexWhenReady, -1),
base::take(_navigateToHashWhenReady));
}
if (!script.isEmpty()) { if (!script.isEmpty()) {
_webview->eval(script); _webview->eval(script);
} }
} else if (event == u"menu"_q) { } else if (event == u"menu"_q) {
menu(object.value("hash").toString()); menu(
object.value("index").toInt(),
object.value("hash").toString());
} }
}); });
}); });
@ -433,7 +437,7 @@ void Controller::showInWindow(
return Webview::DataResult::Done; return Webview::DataResult::Done;
}; };
const auto id = std::string_view(request.id).substr(3); const auto id = std::string_view(request.id).substr(3);
if (id == "page.html") { if (id.starts_with("page") && id.ends_with(".html")) {
if (!_subscribedToColors) { if (!_subscribedToColors) {
_subscribedToColors = true; _subscribedToColors = true;
@ -444,7 +448,33 @@ void Controller::showInWindow(
_updateStyles.call(); _updateStyles.call();
}, _webview->lifetime()); }, _webview->lifetime());
} }
return finishWith(WrapPage(page, initScript), "text/html"); auto index = 0;
const auto result = std::from_chars(
id.data() + 4,
id.data() + id.size() - 5,
index);
if (result.ec != std::errc()
|| index < 0
|| index >= _pages.size()) {
return Webview::DataResult::Failed;
}
return finishWith(WrapPage(_pages[index]), "text/html");
} else if (id.starts_with("page") && id.ends_with(".json")) {
auto index = 0;
const auto result = std::from_chars(
id.data() + 4,
id.data() + id.size() - 5,
index);
if (result.ec != std::errc()
|| index < 0
|| index >= _pages.size()) {
return Webview::DataResult::Failed;
}
auto &page = _pages[index];
return finishWith(QJsonDocument(QJsonObject{
{ "html", QJsonValue(QString::fromUtf8(page.content)) },
{ "js", QJsonValue(QString::fromUtf8(page.script)) },
}).toJson(QJsonDocument::Compact), "application/json");
} }
const auto css = id.ends_with(".css"); const auto css = id.ends_with(".css");
const auto js = !css && id.ends_with(".js"); const auto js = !css && id.ends_with(".js");
@ -464,12 +494,48 @@ void Controller::showInWindow(
}); });
raw->init(R"()"); raw->init(R"()");
}
auto id = u"iv/page.html"_q; void Controller::showInWindow(const QString &dataPath, Prepared page) {
if (!page.hash.isEmpty()) { Expects(_container != nullptr);
id += '#' + page.hash;
const auto url = page.url;
const auto hash = page.hash;
auto i = _indices.find(url);
if (i == end(_indices)) {
_pages.push_back(std::move(page));
i = _indices.emplace(url, int(_pages.size() - 1)).first;
} }
raw->navigateToData(id); const auto index = i->second;
if (!_webview) {
createWebview(dataPath);
if (_webview && _webview->widget()) {
auto id = u"iv/page%1.html"_q.arg(index);
if (!hash.isEmpty()) {
id += '#' + hash;
}
_webview->navigateToData(id);
} else {
_events.fire({ Event::Type::Close });
}
} else if (_ready) {
_webview->eval(navigateScript(index, hash));
_window->activateWindow();
_window->setFocus();
} else {
_navigateToIndexWhenReady = index;
_navigateToHashWhenReady = hash;
_window->activateWindow();
_window->setFocus();
}
}
QByteArray Controller::navigateScript(int index, const QString &hash) {
return "IV.navigateTo("
+ QByteArray::number(index)
+ ", '"
+ EscapeForScriptString(hash.toUtf8())
+ "');";
} }
void Controller::processKey(const QString &key, const QString &modifier) { void Controller::processKey(const QString &key, const QString &modifier) {
@ -537,8 +603,8 @@ void Controller::minimize() {
} }
} }
void Controller::menu(const QString &hash) { void Controller::menu(int index, const QString &hash) {
if (!_webview || _menu) { if (!_webview || _menu || index < 0 || index > _pages.size()) {
return; return;
} }
_menu = base::make_unique_q<Ui::PopupMenu>( _menu = base::make_unique_q<Ui::PopupMenu>(
@ -552,7 +618,8 @@ void Controller::menu(const QString &hash) {
} }
})); }));
const auto url = _url + (hash.isEmpty() ? u""_q : ('#' + hash)); const auto url = _pages[index].url
+ (hash.isEmpty() ? u""_q : ('#' + hash));
const auto openInBrowser = crl::guard(_window.get(), [=] { const auto openInBrowser = crl::guard(_window.get(), [=] {
_events.fire({ .type = Event::Type::OpenLinkExternal, .url = url }); _events.fire({ .type = Event::Type::OpenLinkExternal, .url = url });
}); });

View file

@ -50,6 +50,7 @@ public:
QString context; QString context;
}; };
[[nodiscard]] bool showFast(const QString &url, const QString &hash);
void show( void show(
const QString &dataPath, const QString &dataPath,
Prepared page, Prepared page,
@ -70,12 +71,12 @@ public:
private: private:
void createWindow(); void createWindow();
void createWebview(const QString &dataPath);
[[nodiscard]] QByteArray navigateScript(int index, const QString &hash);
void updateTitleGeometry(); void updateTitleGeometry();
void paintTitle(Painter &p, QRect clip); void paintTitle(Painter &p, QRect clip);
void showInWindow( void showInWindow(const QString &dataPath, Prepared page);
const QString &dataPath,
Prepared page,
const QByteArray &initScript);
[[nodiscard]] QByteArray fillInChannelValuesScript( [[nodiscard]] QByteArray fillInChannelValuesScript(
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues); base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues);
[[nodiscard]] QByteArray toggleInChannelScript( [[nodiscard]] QByteArray toggleInChannelScript(
@ -85,7 +86,7 @@ private:
void processKey(const QString &key, const QString &modifier); void processKey(const QString &key, const QString &modifier);
void processLink(const QString &url, const QString &context); void processLink(const QString &url, const QString &context);
void menu(const QString &hash); void menu(int index, const QString &hash);
void escape(); void escape();
void close(); void close();
void quit(); void quit();
@ -101,11 +102,16 @@ private:
rpl::event_stream<Webview::DataRequest> _dataRequests; rpl::event_stream<Webview::DataRequest> _dataRequests;
rpl::event_stream<Event> _events; rpl::event_stream<Event> _events;
base::flat_map<QByteArray, bool> _inChannelChanged; base::flat_map<QByteArray, bool> _inChannelChanged;
base::flat_set<QByteArray> _inChannelSubscribed;
SingleQueuedInvokation _updateStyles; SingleQueuedInvokation _updateStyles;
QString _url;
bool _subscribedToColors = false; bool _subscribedToColors = false;
bool _ready = false; bool _ready = false;
std::vector<Prepared> _pages;
base::flat_map<QString, int> _indices;
QString _navigateToHashWhenReady;
int _navigateToIndexWhenReady = -1;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };

View file

@ -18,6 +18,7 @@ struct Options {
struct Prepared { struct Prepared {
QString title; QString title;
QByteArray content; QByteArray content;
QByteArray script;
QString url; QString url;
QString hash; QString hash;
std::vector<QByteArray> resources; std::vector<QByteArray> resources;

View file

@ -81,6 +81,8 @@ public:
[[nodiscard]] bool activeFor(not_null<Main::Session*> session) const; [[nodiscard]] bool activeFor(not_null<Main::Session*> session) const;
[[nodiscard]] bool active() const; [[nodiscard]] bool active() const;
void moveTo(not_null<Data*> data, QString hash);
void showJoinedTooltip(); void showJoinedTooltip();
void minimize(); void minimize();
@ -115,6 +117,9 @@ private:
std::vector<Webview::DataRequest> requests; std::vector<Webview::DataRequest> requests;
}; };
void prepare(not_null<Data*> data, const QString &hash);
void createController();
void showLocal(Prepared result); void showLocal(Prepared result);
void showWindowed(Prepared result); void showWindowed(Prepared result);
@ -163,6 +168,8 @@ private:
base::flat_map<DocumentId, FileLoad> _files; base::flat_map<DocumentId, FileLoad> _files;
base::flat_map<QByteArray, rpl::producer<bool>> _inChannelValues; base::flat_map<QByteArray, rpl::producer<bool>> _inChannelValues;
bool _preparing = false;
QString _localBase; QString _localBase;
base::flat_map<QByteArray, QByteArray> _embeds; base::flat_map<QByteArray, QByteArray> _embeds;
base::flat_map<QString, MapPreview> _maps; base::flat_map<QString, MapPreview> _maps;
@ -181,15 +188,24 @@ Shown::Shown(
not_null<Data*> data, not_null<Data*> data,
QString hash) QString hash)
: _session(&show->session()) : _session(&show->session())
, _show(show) , _show(show) {
, _id(data->id()) { prepare(data, hash);
}
void Shown::prepare(not_null<Data*> data, const QString &hash) {
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
_preparing = true;
const auto id = _id = data->id();
const auto base = /*local ? LookupLocalPath(show) : */QString(); const auto base = /*local ? LookupLocalPath(show) : */QString();
data->prepare({ .saveToFolder = base }, [=](Prepared result) { data->prepare({ .saveToFolder = base }, [=](Prepared result) {
result.hash = hash; result.hash = hash;
crl::on_main(weak, [=, result = std::move(result)]() mutable { crl::on_main(weak, [=, result = std::move(result)]() mutable {
result.url = _id; result.url = id;
if (_id != id || !_preparing) {
return;
}
_preparing = false;
_embeds = std::move(result.embeds); _embeds = std::move(result.embeds);
fillChannelJoinedValues(result); fillChannelJoinedValues(result);
if (!base.isEmpty()) { if (!base.isEmpty()) {
@ -416,7 +432,9 @@ void Shown::writeEmbed(QString id, QString hash) {
} }
} }
void Shown::showWindowed(Prepared result) { void Shown::createController() {
Expects(!_controller);
_controller = std::make_unique<Controller>(); _controller = std::make_unique<Controller>();
_controller->events( _controller->events(
@ -436,6 +454,12 @@ void Shown::showWindowed(Prepared result) {
sendEmbed(id.mid(5).toUtf8(), std::move(request)); sendEmbed(id.mid(5).toUtf8(), std::move(request));
} }
}, _controller->lifetime()); }, _controller->lifetime());
}
void Shown::showWindowed(Prepared result) {
if (!_controller) {
createController();
}
const auto domain = &_session->domain(); const auto domain = &_session->domain();
_controller->show( _controller->show(
@ -753,6 +777,12 @@ bool Shown::active() const {
return _controller && _controller->active(); return _controller && _controller->active();
} }
void Shown::moveTo(not_null<Data*> data, QString hash) {
if (!_controller || !_controller->showFast(data->id(), hash)) {
prepare(data, hash);
}
}
void Shown::showJoinedTooltip() { void Shown::showJoinedTooltip() {
if (_controller) { if (_controller) {
_controller->showJoinedTooltip(); _controller->showJoinedTooltip();
@ -774,7 +804,8 @@ void Instance::show(
not_null<Data*> data, not_null<Data*> data,
QString hash) { QString hash) {
const auto session = &show->session(); const auto session = &show->session();
if (_shown && _shown->showing(session, data)) { if (_shown && _shownSession == session) {
_shown->moveTo(data, hash);
return; return;
} }
_shown = std::make_unique<Shown>(show, data, hash); _shown = std::make_unique<Shown>(show, data, hash);

View file

@ -744,7 +744,7 @@ QByteArray Parser::block(const MTPDpageBlockAudio &data) {
} }
QByteArray Parser::block(const MTPDpageBlockKicker &data) { QByteArray Parser::block(const MTPDpageBlockKicker &data) {
return tag("h6", { { "class", "kicker" } }, rich(data.vtext())); return tag("h5", { { "class", "kicker" } }, rich(data.vtext()));
} }
QByteArray Parser::block(const MTPDpageBlockTable &data) { QByteArray Parser::block(const MTPDpageBlockTable &data) {