mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support complex history and anchors.
This commit is contained in:
parent
fae10cfa6b
commit
8b62c37c34
7 changed files with 369 additions and 68 deletions
|
@ -128,9 +128,33 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
|
|||
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 {
|
||||
padding-bottom: 12px;
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
max-width: 732px;
|
||||
margin: 0 auto;
|
||||
|
@ -150,6 +174,9 @@ article h2 {
|
|||
margin: -6px 18px 12px;
|
||||
color: var(--td-window-sub-text-fg);
|
||||
}
|
||||
article h5 {
|
||||
margin: 21px 18px 12px;
|
||||
}
|
||||
article address {
|
||||
font-size: 15px;
|
||||
color: var(--td-window-sub-text-fg);
|
||||
|
|
|
@ -35,20 +35,31 @@ var IV = {
|
|||
context: context,
|
||||
});
|
||||
} else if (target.hash.length < 2) {
|
||||
IV.hash = '';
|
||||
IV.scrollTo(0);
|
||||
IV.jumpToHash('');
|
||||
} else {
|
||||
const name = 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);
|
||||
}
|
||||
IV.jumpToHash(target.hash.substr(1));
|
||||
}
|
||||
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) {
|
||||
const keyW = (e.key === 'w')
|
||||
|| (e.code === 'KeyW')
|
||||
|
@ -210,7 +221,7 @@ var IV = {
|
|||
}
|
||||
},
|
||||
init: function () {
|
||||
IV.hash = window.location.hash.substr(1);
|
||||
window.history.replaceState(IV.computeCurrentState(), '');
|
||||
|
||||
const buttons = document.getElementsByClassName('fixed_button');
|
||||
for (let i = 0; i < buttons.length; ++i) {
|
||||
|
@ -228,6 +239,10 @@ var IV = {
|
|||
IV.stopRipples(e.currentTarget);
|
||||
});
|
||||
}
|
||||
IV.initMedia();
|
||||
IV.notify({ event: 'ready' });
|
||||
},
|
||||
initMedia: function () {
|
||||
const photos = document.getElementsByClassName('photo');
|
||||
for (let i = 0; i < photos.length; ++i) {
|
||||
const photo = photos[i];
|
||||
|
@ -258,7 +273,6 @@ var IV = {
|
|||
video.classList.add('loaded');
|
||||
});
|
||||
}
|
||||
IV.notify({ event: 'ready' });
|
||||
},
|
||||
showTooltip: function (text) {
|
||||
var toast = document.createElement('div');
|
||||
|
@ -272,14 +286,163 @@ var IV = {
|
|||
document.body.removeChild(toast);
|
||||
}, 3000);
|
||||
},
|
||||
scrollTo: function (y) {
|
||||
scrollTo: function (y, instant) {
|
||||
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) {
|
||||
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;
|
||||
|
@ -288,3 +451,9 @@ document.onmouseenter = IV.frameMouseEnter;
|
|||
document.onmouseup = IV.frameMouseUp;
|
||||
document.onscroll = IV.frameScrolled;
|
||||
window.onmessage = IV.postMessageHandler;
|
||||
|
||||
window.addEventListener('popstate', function (e) {
|
||||
if (e.state) {
|
||||
IV.showDOM(e.state.index, e.state.hash, e.state.scroll);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtGui/QPainter>
|
||||
#include <charconv>
|
||||
|
||||
namespace Iv {
|
||||
namespace {
|
||||
|
@ -130,9 +131,7 @@ namespace {
|
|||
.replace('\'', "\\\'");
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray WrapPage(
|
||||
const Prepared &page,
|
||||
const QByteArray &initScript) {
|
||||
[[nodiscard]] QByteArray WrapPage(const Prepared &page) {
|
||||
#ifdef Q_OS_MAC
|
||||
const auto classAttribute = ""_q;
|
||||
#else // Q_OS_MAC
|
||||
|
@ -143,7 +142,7 @@ namespace {
|
|||
+ (page.hasCode ? "IV.initPreBlocks();" : "")
|
||||
+ (page.hasEmbeds ? "IV.initEmbedBlocks();" : "")
|
||||
+ "IV.init();"
|
||||
+ initScript;
|
||||
+ page.script;
|
||||
|
||||
const auto contentAttributes = page.rtl
|
||||
? " 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>
|
||||
</svg>
|
||||
</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>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -199,28 +200,29 @@ Controller::Controller()
|
|||
_webview->eval("IV.updateStyles('" + str + "');");
|
||||
}
|
||||
}) {
|
||||
createWindow();
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
_window->hide();
|
||||
_ready = false;
|
||||
_webview = nullptr;
|
||||
_title = nullptr;
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
bool Controller::showFast(const QString &url, const QString &hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Controller::show(
|
||||
const QString &dataPath,
|
||||
Prepared page,
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) {
|
||||
createWindow();
|
||||
const auto js = fillInChannelValuesScript(std::move(inChannelValues));
|
||||
|
||||
page.script = fillInChannelValuesScript(std::move(inChannelValues));
|
||||
_titleText.setText(st::ivTitle.style, page.title);
|
||||
InvokeQueued(_container, [=, page = std::move(page)]() mutable {
|
||||
showInWindow(dataPath, std::move(page), js);
|
||||
if (!_webview) {
|
||||
return;
|
||||
}
|
||||
showInWindow(dataPath, std::move(page));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -228,13 +230,15 @@ QByteArray Controller::fillInChannelValuesScript(
|
|||
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) {
|
||||
auto result = QByteArray();
|
||||
for (auto &[id, in] : inChannelValues) {
|
||||
std::move(in) | rpl::start_with_next([=](bool in) {
|
||||
if (_ready) {
|
||||
_webview->eval(toggleInChannelScript(id, in));
|
||||
} else {
|
||||
_inChannelChanged[id] = in;
|
||||
}
|
||||
}, _lifetime);
|
||||
if (_inChannelSubscribed.emplace(id).second) {
|
||||
std::move(in) | rpl::start_with_next([=](bool in) {
|
||||
if (_ready) {
|
||||
_webview->eval(toggleInChannelScript(id, in));
|
||||
} else {
|
||||
_inChannelChanged[id] = in;
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
}
|
||||
for (const auto &[id, in] : base::take(_inChannelChanged)) {
|
||||
result += toggleInChannelScript(id, in);
|
||||
|
@ -342,14 +346,10 @@ void Controller::createWindow() {
|
|||
window->show();
|
||||
}
|
||||
|
||||
void Controller::showInWindow(
|
||||
const QString &dataPath,
|
||||
Prepared page,
|
||||
const QByteArray &initScript) {
|
||||
Expects(_container != nullptr);
|
||||
void Controller::createWebview(const QString &dataPath) {
|
||||
Expects(!_webview);
|
||||
|
||||
const auto window = _window.get();
|
||||
_url = page.url;
|
||||
_webview = std::make_unique<Webview::Window>(
|
||||
_container,
|
||||
Webview::WindowConfig{
|
||||
|
@ -362,10 +362,7 @@ void Controller::showInWindow(
|
|||
_ready = false;
|
||||
_webview = nullptr;
|
||||
});
|
||||
if (!raw->widget()) {
|
||||
_events.fire({ Event::Type::Close });
|
||||
return;
|
||||
}
|
||||
|
||||
window->events(
|
||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close) {
|
||||
|
@ -411,11 +408,18 @@ void Controller::showInWindow(
|
|||
for (const auto &[id, in] : base::take(_inChannelChanged)) {
|
||||
script += toggleInChannelScript(id, in);
|
||||
}
|
||||
if (_navigateToIndexWhenReady >= 0) {
|
||||
script += navigateScript(
|
||||
std::exchange(_navigateToIndexWhenReady, -1),
|
||||
base::take(_navigateToHashWhenReady));
|
||||
}
|
||||
if (!script.isEmpty()) {
|
||||
_webview->eval(script);
|
||||
}
|
||||
} 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;
|
||||
};
|
||||
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) {
|
||||
_subscribedToColors = true;
|
||||
|
||||
|
@ -444,7 +448,33 @@ void Controller::showInWindow(
|
|||
_updateStyles.call();
|
||||
}, _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 js = !css && id.ends_with(".js");
|
||||
|
@ -464,12 +494,48 @@ void Controller::showInWindow(
|
|||
});
|
||||
|
||||
raw->init(R"()");
|
||||
}
|
||||
|
||||
auto id = u"iv/page.html"_q;
|
||||
if (!page.hash.isEmpty()) {
|
||||
id += '#' + page.hash;
|
||||
void Controller::showInWindow(const QString &dataPath, Prepared page) {
|
||||
Expects(_container != nullptr);
|
||||
|
||||
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) {
|
||||
|
@ -537,8 +603,8 @@ void Controller::minimize() {
|
|||
}
|
||||
}
|
||||
|
||||
void Controller::menu(const QString &hash) {
|
||||
if (!_webview || _menu) {
|
||||
void Controller::menu(int index, const QString &hash) {
|
||||
if (!_webview || _menu || index < 0 || index > _pages.size()) {
|
||||
return;
|
||||
}
|
||||
_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(), [=] {
|
||||
_events.fire({ .type = Event::Type::OpenLinkExternal, .url = url });
|
||||
});
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
QString context;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool showFast(const QString &url, const QString &hash);
|
||||
void show(
|
||||
const QString &dataPath,
|
||||
Prepared page,
|
||||
|
@ -70,12 +71,12 @@ public:
|
|||
|
||||
private:
|
||||
void createWindow();
|
||||
void createWebview(const QString &dataPath);
|
||||
[[nodiscard]] QByteArray navigateScript(int index, const QString &hash);
|
||||
|
||||
void updateTitleGeometry();
|
||||
void paintTitle(Painter &p, QRect clip);
|
||||
void showInWindow(
|
||||
const QString &dataPath,
|
||||
Prepared page,
|
||||
const QByteArray &initScript);
|
||||
void showInWindow(const QString &dataPath, Prepared page);
|
||||
[[nodiscard]] QByteArray fillInChannelValuesScript(
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues);
|
||||
[[nodiscard]] QByteArray toggleInChannelScript(
|
||||
|
@ -85,7 +86,7 @@ private:
|
|||
void processKey(const QString &key, const QString &modifier);
|
||||
void processLink(const QString &url, const QString &context);
|
||||
|
||||
void menu(const QString &hash);
|
||||
void menu(int index, const QString &hash);
|
||||
void escape();
|
||||
void close();
|
||||
void quit();
|
||||
|
@ -101,11 +102,16 @@ private:
|
|||
rpl::event_stream<Webview::DataRequest> _dataRequests;
|
||||
rpl::event_stream<Event> _events;
|
||||
base::flat_map<QByteArray, bool> _inChannelChanged;
|
||||
base::flat_set<QByteArray> _inChannelSubscribed;
|
||||
SingleQueuedInvokation _updateStyles;
|
||||
QString _url;
|
||||
bool _subscribedToColors = false;
|
||||
bool _ready = false;
|
||||
|
||||
std::vector<Prepared> _pages;
|
||||
base::flat_map<QString, int> _indices;
|
||||
QString _navigateToHashWhenReady;
|
||||
int _navigateToIndexWhenReady = -1;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ struct Options {
|
|||
struct Prepared {
|
||||
QString title;
|
||||
QByteArray content;
|
||||
QByteArray script;
|
||||
QString url;
|
||||
QString hash;
|
||||
std::vector<QByteArray> resources;
|
||||
|
|
|
@ -81,6 +81,8 @@ public:
|
|||
[[nodiscard]] bool activeFor(not_null<Main::Session*> session) const;
|
||||
[[nodiscard]] bool active() const;
|
||||
|
||||
void moveTo(not_null<Data*> data, QString hash);
|
||||
|
||||
void showJoinedTooltip();
|
||||
void minimize();
|
||||
|
||||
|
@ -115,6 +117,9 @@ private:
|
|||
std::vector<Webview::DataRequest> requests;
|
||||
};
|
||||
|
||||
void prepare(not_null<Data*> data, const QString &hash);
|
||||
void createController();
|
||||
|
||||
void showLocal(Prepared result);
|
||||
void showWindowed(Prepared result);
|
||||
|
||||
|
@ -163,6 +168,8 @@ private:
|
|||
base::flat_map<DocumentId, FileLoad> _files;
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> _inChannelValues;
|
||||
|
||||
bool _preparing = false;
|
||||
|
||||
QString _localBase;
|
||||
base::flat_map<QByteArray, QByteArray> _embeds;
|
||||
base::flat_map<QString, MapPreview> _maps;
|
||||
|
@ -181,15 +188,24 @@ Shown::Shown(
|
|||
not_null<Data*> data,
|
||||
QString hash)
|
||||
: _session(&show->session())
|
||||
, _show(show)
|
||||
, _id(data->id()) {
|
||||
, _show(show) {
|
||||
prepare(data, hash);
|
||||
}
|
||||
|
||||
void Shown::prepare(not_null<Data*> data, const QString &hash) {
|
||||
const auto weak = base::make_weak(this);
|
||||
|
||||
_preparing = true;
|
||||
const auto id = _id = data->id();
|
||||
const auto base = /*local ? LookupLocalPath(show) : */QString();
|
||||
data->prepare({ .saveToFolder = base }, [=](Prepared result) {
|
||||
result.hash = hash;
|
||||
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);
|
||||
fillChannelJoinedValues(result);
|
||||
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->events(
|
||||
|
@ -436,6 +454,12 @@ void Shown::showWindowed(Prepared result) {
|
|||
sendEmbed(id.mid(5).toUtf8(), std::move(request));
|
||||
}
|
||||
}, _controller->lifetime());
|
||||
}
|
||||
|
||||
void Shown::showWindowed(Prepared result) {
|
||||
if (!_controller) {
|
||||
createController();
|
||||
}
|
||||
|
||||
const auto domain = &_session->domain();
|
||||
_controller->show(
|
||||
|
@ -753,6 +777,12 @@ bool Shown::active() const {
|
|||
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() {
|
||||
if (_controller) {
|
||||
_controller->showJoinedTooltip();
|
||||
|
@ -774,7 +804,8 @@ void Instance::show(
|
|||
not_null<Data*> data,
|
||||
QString hash) {
|
||||
const auto session = &show->session();
|
||||
if (_shown && _shown->showing(session, data)) {
|
||||
if (_shown && _shownSession == session) {
|
||||
_shown->moveTo(data, hash);
|
||||
return;
|
||||
}
|
||||
_shown = std::make_unique<Shown>(show, data, hash);
|
||||
|
|
|
@ -744,7 +744,7 @@ QByteArray Parser::block(const MTPDpageBlockAudio &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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue