mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 13:47:05 +02:00
New top bar, sharing, internal IV links style.
This commit is contained in:
parent
315859bf7b
commit
e1b55b560a
11 changed files with 502 additions and 292 deletions
|
@ -82,43 +82,24 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
#top_menu circle {
|
||||
fill: var(--td-history-to-down-fg);
|
||||
#top_shadow {
|
||||
z-index: 999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
background-color: var(--td-shadow-fg)
|
||||
}
|
||||
#top_menu:hover circle {
|
||||
fill: var(--td-history-to-down-fg-over);
|
||||
}
|
||||
#top_menu {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
#top_back path,
|
||||
#top_back line,
|
||||
#bottom_up path {
|
||||
stroke: var(--td-history-to-down-fg);
|
||||
}
|
||||
#top_back path,
|
||||
#top_back line {
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
#bottom_up path {
|
||||
stroke-width: 1.4;
|
||||
}
|
||||
#top_back:hover path,
|
||||
#top_back:hover line,
|
||||
#bottom_up:hover path {
|
||||
stroke: var(--td-history-to-down-fg-over);
|
||||
}
|
||||
#top_back {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
transition: left 200ms linear;
|
||||
}
|
||||
#top_back.hidden {
|
||||
left: -36px;
|
||||
}
|
||||
#bottom_up {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
|
@ -210,6 +191,14 @@ article a[href] {
|
|||
color: var(--td-window-active-text-fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
article a.internal-iv-link {
|
||||
border-radius: 3px;
|
||||
margin: 0px -3px;
|
||||
padding: 0px 3px;
|
||||
position: relative;
|
||||
background: var(--td-light-button-bg-over);
|
||||
color: var(--td-light-button-fg);
|
||||
}
|
||||
article span.reference {
|
||||
border: dotted var(--td-window-sub-text-fg);
|
||||
border-width: 1px 1px 1px 2px;
|
||||
|
|
|
@ -206,9 +206,6 @@ var IV = {
|
|||
},
|
||||
stopRipples: function (button) {
|
||||
const id = button.id ? button.id : button;
|
||||
if (IV.frozenRipple === id) {
|
||||
return;
|
||||
}
|
||||
button = document.getElementById(id);
|
||||
const ripples = button.getElementsByClassName('ripple');
|
||||
for (var i = 0; i < ripples.length; ++i) {
|
||||
|
@ -218,15 +215,6 @@ var IV = {
|
|||
}
|
||||
}
|
||||
},
|
||||
clearFrozenRipple: function () {
|
||||
if (IV.frozenRipple) {
|
||||
const button = document.getElementById(IV.frozenRipple);
|
||||
IV.frozenRipple = null;
|
||||
if (button) {
|
||||
IV.stopRipples(button);
|
||||
}
|
||||
}
|
||||
},
|
||||
init: function () {
|
||||
IV.platform = window.navigator.platform.toLowerCase();
|
||||
IV.mac = IV.platform.startsWith('mac');
|
||||
|
@ -310,12 +298,6 @@ var IV = {
|
|||
behavior: instant ? 'instant' : 'smooth'
|
||||
});
|
||||
},
|
||||
menu: function (button) {
|
||||
IV.frozenRipple = button.id;
|
||||
const state = this.computeCurrentState();
|
||||
IV.notify({ event: 'menu', index: state.index, hash: state.hash });
|
||||
},
|
||||
|
||||
computeCurrentState: function () {
|
||||
var now = IV.findPageScroll();
|
||||
return {
|
||||
|
@ -490,13 +472,13 @@ var IV = {
|
|||
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.notify({
|
||||
event: 'location_change',
|
||||
index: IV.index,
|
||||
position: IV.position,
|
||||
hash: IV.computeCurrentState().hash,
|
||||
});
|
||||
if (IV.cache[index].contentUpdated) {
|
||||
IV.cache[index].contentUpdated = false;
|
||||
IV.applyUpdatedContent(index);
|
||||
|
|
|
@ -21,25 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace {
|
||||
|
||||
QString SiteNameFromUrl(const QString &url) {
|
||||
const auto u = QUrl(url);
|
||||
QString pretty = u.isValid() ? u.toDisplayString() : url;
|
||||
const auto m = QRegularExpression(u"^[a-zA-Z0-9]+://"_q).match(pretty);
|
||||
if (m.hasMatch()) pretty = pretty.mid(m.capturedLength());
|
||||
int32 slash = pretty.indexOf('/');
|
||||
if (slash > 0) pretty = pretty.mid(0, slash);
|
||||
QStringList components = pretty.split('.', Qt::SkipEmptyParts);
|
||||
if (components.size() >= 2) {
|
||||
components = components.mid(components.size() - 2);
|
||||
return components.at(0).at(0).toUpper()
|
||||
+ components.at(0).mid(1)
|
||||
+ '.'
|
||||
+ components.at(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
WebPageCollage ExtractCollage(
|
||||
[[nodiscard]] WebPageCollage ExtractCollage(
|
||||
not_null<Data::Session*> owner,
|
||||
const QVector<MTPPageBlock> &items,
|
||||
const QVector<MTPPhoto> &photos,
|
||||
|
@ -256,7 +238,7 @@ bool WebPageData::applyChanges(
|
|||
} else if (!newDescription.text.isEmpty()
|
||||
&& viewTitleText.isEmpty()
|
||||
&& !resultUrl.isEmpty()) {
|
||||
return SiteNameFromUrl(resultUrl);
|
||||
return Iv::SiteNameFromUrl(resultUrl);
|
||||
}
|
||||
return QString();
|
||||
}();
|
||||
|
|
|
@ -8,94 +8,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
using "ui/basic.style";
|
||||
using "ui/widgets/widgets.style";
|
||||
|
||||
ivTitleHeight: 24px;
|
||||
ivTitleIconShift: point(0px, 0px);
|
||||
ivTitleButton: IconButton(windowTitleButton) {
|
||||
height: ivTitleHeight;
|
||||
iconPosition: ivTitleIconShift;
|
||||
}
|
||||
ivTitleButtonClose: IconButton(windowTitleButtonClose) {
|
||||
height: ivTitleHeight;
|
||||
iconPosition: ivTitleIconShift;
|
||||
}
|
||||
ivMenuToggle: IconButton(defaultIconButton) {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
ivTitleButtonSize: size(windowTitleButtonWidth, ivTitleHeight);
|
||||
ivTitle: WindowTitle(defaultWindowTitle) {
|
||||
height: ivTitleHeight;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: font(semibold 12px);
|
||||
icon: icon {{ "title_menu_dots", menuIconColor }};
|
||||
iconOver: icon {{ "title_menu_dots", menuIconColor }};
|
||||
|
||||
rippleAreaPosition: point(6px, 6px);
|
||||
rippleAreaSize: 36px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
shadow: false;
|
||||
minimize: IconButton(ivTitleButton) {
|
||||
icon: icon {
|
||||
{ ivTitleButtonSize, titleButtonBg },
|
||||
{ "title_button_minimize", titleButtonFg, ivTitleIconShift },
|
||||
};
|
||||
iconOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgOver },
|
||||
{ "title_button_minimize", titleButtonFgOver, ivTitleIconShift },
|
||||
};
|
||||
}
|
||||
minimizeIconActive: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActive },
|
||||
{ "title_button_minimize", titleButtonFgActive, ivTitleIconShift },
|
||||
};
|
||||
minimizeIconActiveOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||
{ "title_button_minimize", titleButtonFgActiveOver, ivTitleIconShift },
|
||||
};
|
||||
maximize: IconButton(windowTitleButton) {
|
||||
icon: icon {
|
||||
{ ivTitleButtonSize, titleButtonBg },
|
||||
{ "title_button_maximize", titleButtonFg, ivTitleIconShift },
|
||||
};
|
||||
iconOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgOver },
|
||||
{ "title_button_maximize", titleButtonFgOver, ivTitleIconShift },
|
||||
};
|
||||
}
|
||||
maximizeIconActive: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActive },
|
||||
{ "title_button_maximize", titleButtonFgActive, ivTitleIconShift },
|
||||
};
|
||||
maximizeIconActiveOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||
{ "title_button_maximize", titleButtonFgActiveOver, ivTitleIconShift },
|
||||
};
|
||||
restoreIcon: icon {
|
||||
{ ivTitleButtonSize, titleButtonBg },
|
||||
{ "title_button_restore", titleButtonFg, ivTitleIconShift },
|
||||
};
|
||||
restoreIconOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgOver },
|
||||
{ "title_button_restore", titleButtonFgOver, ivTitleIconShift },
|
||||
};
|
||||
restoreIconActive: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActive },
|
||||
{ "title_button_restore", titleButtonFgActive, ivTitleIconShift },
|
||||
};
|
||||
restoreIconActiveOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||
{ "title_button_restore", titleButtonFgActiveOver, ivTitleIconShift },
|
||||
};
|
||||
close: IconButton(windowTitleButtonClose) {
|
||||
icon: icon {
|
||||
{ ivTitleButtonSize, titleButtonCloseBg },
|
||||
{ "title_button_close", titleButtonCloseFg, ivTitleIconShift },
|
||||
};
|
||||
iconOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonCloseBgOver },
|
||||
{ "title_button_close", titleButtonCloseFgOver, ivTitleIconShift },
|
||||
};
|
||||
}
|
||||
closeIconActive: icon {
|
||||
{ ivTitleButtonSize, titleButtonCloseBgActive },
|
||||
{ "title_button_close", titleButtonCloseFgActive, ivTitleIconShift },
|
||||
};
|
||||
closeIconActiveOver: icon {
|
||||
{ ivTitleButtonSize, titleButtonCloseBgActiveOver },
|
||||
{ "title_button_close", titleButtonCloseFgActiveOver, ivTitleIconShift },
|
||||
};
|
||||
}
|
||||
ivTitleExpandedHeight: 76px;
|
||||
ivMenuPosition: point(-8px, 36px);
|
||||
ivMenuPosition: point(-2px, 40px);
|
||||
ivBack: IconButton(ivMenuToggle) {
|
||||
width: 60px;
|
||||
icon: icon {{ "box_button_back", menuIconColor }};
|
||||
iconOver: icon {{ "box_button_back", menuIconColor }};
|
||||
rippleAreaPosition: point(12px, 6px);
|
||||
}
|
||||
ivSubtitleFont: font(16px semibold);
|
||||
ivSubtitle: FlatLabel(defaultFlatLabel) {
|
||||
textFg: boxTitleFg;
|
||||
maxHeight: 26px;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: ivSubtitleFont;
|
||||
}
|
||||
}
|
||||
ivSubtitleHeight: 48px;
|
||||
ivSubtitleTop: 12px;
|
||||
ivSubtitleLeft: 22px;
|
||||
ivSubtitleSkip: 0px;
|
||||
|
|
|
@ -13,8 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "iv/iv_data.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/platform/ui_platform_window_title.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/painter.h"
|
||||
#include "webview/webview_data_stream_memory.h"
|
||||
|
@ -35,11 +38,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QWindow>
|
||||
#include <charconv>
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
|
||||
namespace Iv {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QByteArray ComputeStyles() {
|
||||
static const auto map = base::flat_map<QByteArray, const style::color*>{
|
||||
{ "shadow-fg", &st::shadowFg },
|
||||
{ "scroll-bg", &st::scrollBg },
|
||||
{ "scroll-bg-over", &st::scrollBgOver },
|
||||
{ "scroll-bar-bg", &st::scrollBarBg },
|
||||
|
@ -54,6 +60,8 @@ namespace {
|
|||
{ "window-shadow-fg", &st::windowShadowFg },
|
||||
{ "box-divider-bg", &st::boxDividerBg },
|
||||
{ "box-divider-fg", &st::boxDividerFg },
|
||||
{ "light-button-fg", &st::lightButtonFg },
|
||||
{ "light-button-bg-over", &st::lightButtonBgOver },
|
||||
{ "menu-icon-fg", &st::menuIconFg },
|
||||
{ "menu-icon-fg-over", &st::menuIconFgOver },
|
||||
{ "menu-bg", &st::menuBg },
|
||||
|
@ -164,19 +172,7 @@ namespace {
|
|||
<link rel="stylesheet" href="/iv/page.css" />
|
||||
</head>
|
||||
<body>
|
||||
<button class="fixed_button hidden" id="top_back" onclick="IV.back();">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="5.37464142" y1="12" x2="18.5" y2="12"></line>
|
||||
<path d="M11.5,18.3 L5.27277119,12.0707223 C5.23375754,12.0316493 5.23375754,11.9683507 5.27277119,11.9292777 L11.5,5.7 L11.5,5.7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="fixed_button" id="top_menu" onclick="IV.menu(this);">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="17.4" r="1.7"></circle>
|
||||
<circle cx="12" cy="12" r="1.7"></circle>
|
||||
<circle cx="12" cy="6.6" r="1.7"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="top_shadow"></div>
|
||||
<button class="fixed_button hidden" id="bottom_up" onclick="IV.scrollTo(0);">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<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>
|
||||
|
@ -198,26 +194,93 @@ namespace {
|
|||
|
||||
} // namespace
|
||||
|
||||
Controller::Controller()
|
||||
Controller::Controller(Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox)
|
||||
: _updateStyles([=] {
|
||||
const auto str = EscapeForScriptString(ComputeStyles());
|
||||
if (_webview) {
|
||||
_webview->eval("IV.updateStyles('" + str + "');");
|
||||
}
|
||||
}) {
|
||||
})
|
||||
, _showShareBox(std::move(showShareBox)) {
|
||||
createWindow();
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
destroyShareMenu();
|
||||
if (_window) {
|
||||
_window->hide();
|
||||
}
|
||||
_ready = false;
|
||||
_webview = nullptr;
|
||||
_title = nullptr;
|
||||
_back.destroy();
|
||||
_menu = nullptr;
|
||||
_menuToggle.destroy();
|
||||
_subtitle = nullptr;
|
||||
_subtitleWrap = nullptr;
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
void Controller::updateTitleGeometry(int newWidth) const {
|
||||
_subtitleWrap->setGeometry(
|
||||
0,
|
||||
st::windowTitleHeight,
|
||||
newWidth,
|
||||
st::ivSubtitleHeight);
|
||||
_subtitleWrap->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
QPainter(_subtitleWrap.get()).fillRect(clip, st::windowBg);
|
||||
}, _subtitleWrap->lifetime());
|
||||
|
||||
const auto progress = _subtitleLeft.value(_back->toggled() ? 1. : 0.);
|
||||
const auto left = anim::interpolate(
|
||||
st::ivSubtitleLeft,
|
||||
_back->width() + st::ivSubtitleSkip,
|
||||
progress);
|
||||
_subtitle->resizeToWidth(newWidth - left - _menuToggle->width());
|
||||
_subtitle->moveToLeft(left, st::ivSubtitleTop);
|
||||
|
||||
_back->moveToLeft(0, 0);
|
||||
_menuToggle->moveToRight(0, 0);
|
||||
}
|
||||
|
||||
void Controller::initControls() {
|
||||
_subtitleWrap = std::make_unique<Ui::RpWidget>(_window.get());
|
||||
_subtitle = std::make_unique<Ui::FlatLabel>(
|
||||
_subtitleWrap.get(),
|
||||
_index.value() | rpl::filter(
|
||||
rpl::mappers::_1 >= 0
|
||||
) | rpl::map([=](int index) {
|
||||
return _pages[index].name;
|
||||
}),
|
||||
st::ivSubtitle);
|
||||
_subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
_menuToggle.create(_subtitleWrap.get(), st::ivMenuToggle);
|
||||
_menuToggle->setClickedCallback([=] { showMenu(); });
|
||||
|
||||
_back.create(
|
||||
_subtitleWrap.get(),
|
||||
object_ptr<Ui::IconButton>(_subtitleWrap.get(), st::ivBack));
|
||||
_back->entity()->setClickedCallback([=] {
|
||||
if (_webview) {
|
||||
_webview->eval("IV.back();");
|
||||
} else {
|
||||
_back->hide(anim::type::normal);
|
||||
}
|
||||
});
|
||||
|
||||
_back->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
_subtitleLeft.start(
|
||||
[=] { updateTitleGeometry(_window->width()); },
|
||||
toggled ? 0. : 1.,
|
||||
toggled ? 1. : 0.,
|
||||
st::fadeWrapDuration);
|
||||
}, _back->lifetime());
|
||||
_back->hide(anim::type::instant);
|
||||
|
||||
_subtitleLeft.stop();
|
||||
}
|
||||
|
||||
bool Controller::showFast(const QString &url, const QString &hash) {
|
||||
return false;
|
||||
}
|
||||
|
@ -227,8 +290,6 @@ void Controller::show(
|
|||
Prepared page,
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues) {
|
||||
page.script = fillInChannelValuesScript(std::move(inChannelValues));
|
||||
_titleText.setText(st::ivTitle.style, page.title);
|
||||
_title->update();
|
||||
InvokeQueued(_container, [=, page = std::move(page)]() mutable {
|
||||
showInWindow(dataPath, std::move(page));
|
||||
});
|
||||
|
@ -277,35 +338,8 @@ QByteArray Controller::toggleInChannelScript(
|
|||
return "IV.toggleChannelJoined('" + id + "', " + value + ");";
|
||||
}
|
||||
|
||||
void Controller::updateTitleGeometry() {
|
||||
_title->setGeometry(0, 0, _window->width(), st::ivTitle.height);
|
||||
}
|
||||
|
||||
void Controller::paintTitle(Painter &p, QRect clip) {
|
||||
const auto active = _window->isActiveWindow();
|
||||
const auto full = _title->width();
|
||||
p.setPen(active ? st::ivTitle.fgActive : st::ivTitle.fg);
|
||||
const auto available = QRect(
|
||||
_titleLeftSkip,
|
||||
0,
|
||||
full - _titleLeftSkip - _titleRightSkip,
|
||||
_title->height());
|
||||
const auto use = std::min(available.width(), _titleText.maxWidth());
|
||||
const auto center = full
|
||||
- 2 * std::max(_titleLeftSkip, _titleRightSkip);
|
||||
const auto left = (use <= center)
|
||||
? ((full - use) / 2)
|
||||
: (use < available.width() && _titleLeftSkip < _titleRightSkip)
|
||||
? (available.x() + available.width() - use)
|
||||
: available.x();
|
||||
const auto titleTextHeight = st::ivTitle.style.font->height;
|
||||
const auto top = (st::ivTitle.height - titleTextHeight) / 2;
|
||||
_titleText.drawLeftElided(p, left, top, available.width(), full);
|
||||
}
|
||||
|
||||
void Controller::createWindow() {
|
||||
_window = std::make_unique<Ui::RpWindow>();
|
||||
_window->setTitleStyle(st::ivTitle);
|
||||
const auto window = _window.get();
|
||||
|
||||
base::qt_signal_producer(
|
||||
|
@ -314,61 +348,25 @@ void Controller::createWindow() {
|
|||
) | rpl::filter([=] {
|
||||
return _webview && window->window()->windowHandle()->isActive();
|
||||
}) | rpl::start_with_next([=] {
|
||||
_webview->focus();
|
||||
setInnerFocus();
|
||||
}, window->lifetime());
|
||||
|
||||
_title = std::make_unique<Ui::RpWidget>(window);
|
||||
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_title->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = Painter(_title.get());
|
||||
paintTitle(p, clip);
|
||||
}, _title->lifetime());
|
||||
window->widthValue() | rpl::start_with_next([=] {
|
||||
updateTitleGeometry();
|
||||
}, _title->lifetime());
|
||||
initControls();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
_titleLeftSkip = 8 + 12 + 8 + 12 + 8 + 12 + 8;
|
||||
_titleRightSkip = st::ivTitle.style.font->spacew;
|
||||
#else // Q_OS_MAC
|
||||
using namespace Ui::Platform;
|
||||
TitleControlsLayoutValue(
|
||||
) | rpl::start_with_next([=](TitleControls::Layout layout) {
|
||||
const auto accumulate = [](const auto &list) {
|
||||
auto result = 0;
|
||||
for (const auto control : list) {
|
||||
switch (control) {
|
||||
case TitleControl::Close:
|
||||
result += st::ivTitle.close.width;
|
||||
break;
|
||||
case TitleControl::Minimize:
|
||||
result += st::ivTitle.minimize.width;
|
||||
break;
|
||||
case TitleControl::Maximize:
|
||||
result += st::ivTitle.maximize.width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto space = st::ivTitle.style.font->spacew;
|
||||
_titleLeftSkip = accumulate(layout.left) + space;
|
||||
_titleRightSkip = accumulate(layout.right) + space;
|
||||
_title->update();
|
||||
}, _title->lifetime());
|
||||
#endif // Q_OS_MAC
|
||||
window->widthValue() | rpl::start_with_next([=](int width) {
|
||||
updateTitleGeometry(width);
|
||||
}, _subtitle->lifetime());
|
||||
|
||||
window->setGeometry({ 200, 200, 600, 800 });
|
||||
window->setMinimumSize({ st::windowMinWidth, st::windowMinHeight });
|
||||
|
||||
_container = Ui::CreateChild<Ui::RpWidget>(window->body().get());
|
||||
_container = Ui::CreateChild<Ui::RpWidget>(window->window());
|
||||
rpl::combine(
|
||||
window->body()->sizeValue(),
|
||||
_title->heightValue()
|
||||
window->sizeValue(),
|
||||
_subtitleWrap->heightValue()
|
||||
) | rpl::start_with_next([=](QSize size, int title) {
|
||||
title -= window->body()->y();
|
||||
_container->setGeometry(QRect(QPoint(), size).marginsRemoved(
|
||||
{ 0, title, 0, 0 }));
|
||||
{ 0, title + st::windowTitleHeight, 0, 0 }));
|
||||
}, _container->lifetime());
|
||||
|
||||
_container->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
|
@ -452,10 +450,12 @@ void Controller::createWebview(const QString &dataPath) {
|
|||
if (!script.isEmpty()) {
|
||||
_webview->eval(script);
|
||||
}
|
||||
} else if (event == u"menu"_q) {
|
||||
menu(
|
||||
object.value("index").toInt(),
|
||||
object.value("hash").toString());
|
||||
} else if (event == u"location_change"_q) {
|
||||
_index = object.value("index").toInt();
|
||||
_hash = object.value("hash").toString();
|
||||
_back->toggle(
|
||||
(object.value("position").toInt() > 0),
|
||||
anim::type::normal);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -543,37 +543,47 @@ void Controller::showInWindow(const QString &dataPath, Prepared page) {
|
|||
Expects(_container != nullptr);
|
||||
|
||||
const auto url = page.url;
|
||||
const auto hash = page.hash;
|
||||
_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;
|
||||
}
|
||||
const auto index = i->second;
|
||||
_index = index;
|
||||
if (!_webview) {
|
||||
createWebview(dataPath);
|
||||
if (_webview && _webview->widget()) {
|
||||
auto id = u"iv/page%1.html"_q.arg(index);
|
||||
if (!hash.isEmpty()) {
|
||||
id += '#' + hash;
|
||||
if (!_hash.isEmpty()) {
|
||||
id += '#' + _hash;
|
||||
}
|
||||
_webview->navigateToData(id);
|
||||
_webview->focus();
|
||||
activate();
|
||||
} else {
|
||||
_events.fire({ Event::Type::Close });
|
||||
}
|
||||
} else if (_ready) {
|
||||
_webview->eval(navigateScript(index, hash));
|
||||
_window->raise();
|
||||
_window->activateWindow();
|
||||
_window->setFocus();
|
||||
_webview->focus();
|
||||
_webview->eval(navigateScript(index, _hash));
|
||||
activate();
|
||||
} else {
|
||||
_navigateToIndexWhenReady = index;
|
||||
_navigateToHashWhenReady = hash;
|
||||
_window->raise();
|
||||
_window->activateWindow();
|
||||
_window->setFocus();
|
||||
_navigateToHashWhenReady = _hash;
|
||||
activate();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::activate() {
|
||||
_window->raise();
|
||||
_window->activateWindow();
|
||||
_window->setFocus();
|
||||
setInnerFocus();
|
||||
}
|
||||
|
||||
void Controller::setInnerFocus() {
|
||||
if (const auto onstack = _shareFocus) {
|
||||
onstack();
|
||||
} else if (_webview) {
|
||||
_webview->focus();
|
||||
}
|
||||
}
|
||||
|
@ -657,8 +667,17 @@ void Controller::minimize() {
|
|||
}
|
||||
}
|
||||
|
||||
void Controller::menu(int index, const QString &hash) {
|
||||
if (!_webview || _menu || index < 0 || index > _pages.size()) {
|
||||
QString Controller::composeCurrentUrl() const {
|
||||
const auto index = _index.current();
|
||||
Assert(index >= 0 && index < _pages.size());
|
||||
|
||||
return _pages[index].url
|
||||
+ (_hash.isEmpty() ? u""_q : ('#' + _hash));
|
||||
}
|
||||
|
||||
void Controller::showMenu() {
|
||||
const auto index = _index.current();
|
||||
if (_menu || index < 0 || index > _pages.size()) {
|
||||
return;
|
||||
}
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
|
@ -666,14 +685,15 @@ void Controller::menu(int index, const QString &hash) {
|
|||
st::popupMenuWithIcons);
|
||||
_menu->setDestroyedCallback(crl::guard(_window.get(), [
|
||||
this,
|
||||
weakButton = Ui::MakeWeak(_menuToggle.data()),
|
||||
menu = _menu.get()] {
|
||||
if (_webview) {
|
||||
_webview->eval("IV.clearFrozenRipple();");
|
||||
if (_menu == menu && weakButton) {
|
||||
weakButton->setForceRippled(false);
|
||||
}
|
||||
}));
|
||||
_menuToggle->setForceRippled(true);
|
||||
|
||||
const auto url = _pages[index].url
|
||||
+ (hash.isEmpty() ? u""_q : ('#' + hash));
|
||||
const auto url = composeCurrentUrl();
|
||||
const auto openInBrowser = crl::guard(_window.get(), [=] {
|
||||
_events.fire({ .type = Event::Type::OpenLinkExternal, .url = url });
|
||||
});
|
||||
|
@ -683,6 +703,7 @@ void Controller::menu(int index, const QString &hash) {
|
|||
&st::menuIconIpAddress);
|
||||
|
||||
_menu->addAction(tr::lng_iv_share(tr::now), [=] {
|
||||
showShareMenu();
|
||||
}, &st::menuIconShare);
|
||||
|
||||
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
|
||||
|
@ -691,7 +712,11 @@ void Controller::menu(int index, const QString &hash) {
|
|||
}
|
||||
|
||||
void Controller::escape() {
|
||||
close();
|
||||
if (const auto onstack = _shareHide) {
|
||||
onstack();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::close() {
|
||||
|
@ -706,4 +731,56 @@ rpl::lifetime &Controller::lifetime() {
|
|||
return _lifetime;
|
||||
}
|
||||
|
||||
void Controller::destroyShareMenu() {
|
||||
_shareHide = nullptr;
|
||||
if (_shareFocus) {
|
||||
_shareFocus = nullptr;
|
||||
setInnerFocus();
|
||||
}
|
||||
if (_shareWrap) {
|
||||
_shareWrap->windowHandle()->setParent(nullptr);
|
||||
_shareWrap = nullptr;
|
||||
_shareContainer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::showShareMenu() {
|
||||
const auto index = _index.current();
|
||||
if (_shareWrap || index < 0 || index > _pages.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_shareWrap = std::make_unique<Ui::RpWidget>(nullptr);
|
||||
const auto margins = QMargins(0, st::windowTitleHeight, 0, 0);
|
||||
_shareWrap->setGeometry(_window->geometry().marginsRemoved(margins));
|
||||
_shareWrap->setWindowFlag(Qt::FramelessWindowHint);
|
||||
_shareWrap->setAttribute(Qt::WA_TranslucentBackground);
|
||||
_shareWrap->setAttribute(Qt::WA_NoSystemBackground);
|
||||
_shareWrap->createWinId();
|
||||
|
||||
_shareContainer.reset(QWidget::createWindowContainer(
|
||||
_shareWrap->windowHandle(),
|
||||
_window.get(),
|
||||
Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint));
|
||||
_window->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
_shareContainer->setGeometry(QRect(QPoint(), size).marginsRemoved(
|
||||
margins));
|
||||
}, _shareWrap->lifetime());
|
||||
|
||||
auto result = _showShareBox({
|
||||
.parent = _shareWrap.get(),
|
||||
.url = composeCurrentUrl(),
|
||||
});
|
||||
_shareFocus = result.focus;
|
||||
_shareHide = result.hide;
|
||||
|
||||
std::move(result.destroyRequests) | rpl::start_with_next([=] {
|
||||
destroyShareMenu();
|
||||
}, _shareWrap->lifetime());
|
||||
|
||||
Ui::ForceFullRepaintSync(_shareWrap.get());
|
||||
_shareContainer->show();
|
||||
activate();
|
||||
}
|
||||
|
||||
} // namespace Iv
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "base/invoke_queued.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/text/text.h"
|
||||
|
@ -23,15 +24,29 @@ namespace Ui {
|
|||
class RpWidget;
|
||||
class RpWindow;
|
||||
class PopupMenu;
|
||||
class FlatLabel;
|
||||
class IconButton;
|
||||
template <typename Widget>
|
||||
class FadeWrapScaled;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Iv {
|
||||
|
||||
struct Prepared;
|
||||
|
||||
struct ShareBoxResult {
|
||||
Fn<void()> focus;
|
||||
Fn<void()> hide;
|
||||
rpl::producer<> destroyRequests;
|
||||
};
|
||||
struct ShareBoxDescriptor {
|
||||
not_null<Ui::RpWidget*> parent;
|
||||
QString url;
|
||||
};
|
||||
|
||||
class Controller final {
|
||||
public:
|
||||
Controller();
|
||||
explicit Controller(Fn<ShareBoxResult(ShareBoxDescriptor)> showShareBox);
|
||||
~Controller();
|
||||
|
||||
struct Event {
|
||||
|
@ -77,8 +92,6 @@ private:
|
|||
[[nodiscard]] QByteArray navigateScript(int index, const QString &hash);
|
||||
[[nodiscard]] QByteArray reloadScript(int index);
|
||||
|
||||
void updateTitleGeometry();
|
||||
void paintTitle(Painter &p, QRect clip);
|
||||
void showInWindow(const QString &dataPath, Prepared page);
|
||||
[[nodiscard]] QByteArray fillInChannelValuesScript(
|
||||
base::flat_map<QByteArray, rpl::producer<bool>> inChannelValues);
|
||||
|
@ -89,17 +102,28 @@ private:
|
|||
void processKey(const QString &key, const QString &modifier);
|
||||
void processLink(const QString &url, const QString &context);
|
||||
|
||||
void menu(int index, const QString &hash);
|
||||
void initControls();
|
||||
void updateTitleGeometry(int newWidth) const;
|
||||
|
||||
void activate();
|
||||
void setInnerFocus();
|
||||
void showMenu();
|
||||
void escape();
|
||||
void close();
|
||||
void quit();
|
||||
|
||||
[[nodiscard]] QString composeCurrentUrl() const;
|
||||
void showShareMenu();
|
||||
void destroyShareMenu();
|
||||
|
||||
std::unique_ptr<Ui::RpWindow> _window;
|
||||
std::unique_ptr<Ui::RpWidget> _title;
|
||||
std::unique_ptr<Ui::RpWidget> _subtitleWrap;
|
||||
rpl::variable<QString> _subtitleText;
|
||||
std::unique_ptr<Ui::FlatLabel> _subtitle;
|
||||
Ui::Animations::Simple _subtitleLeft;
|
||||
object_ptr<Ui::IconButton> _menuToggle = { nullptr };
|
||||
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _back = { nullptr };
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
Ui::Text::String _titleText;
|
||||
int _titleLeftSkip = 0;
|
||||
int _titleRightSkip = 0;
|
||||
Ui::RpWidget *_container = nullptr;
|
||||
std::unique_ptr<Webview::Window> _webview;
|
||||
rpl::event_stream<Webview::DataRequest> _dataRequests;
|
||||
|
@ -111,6 +135,15 @@ private:
|
|||
bool _subscribedToColors = false;
|
||||
bool _ready = false;
|
||||
|
||||
rpl::variable<int> _index = -1;
|
||||
QString _hash;
|
||||
|
||||
Fn<ShareBoxResult(ShareBoxDescriptor)> _showShareBox;
|
||||
std::unique_ptr<Ui::RpWidget> _shareWrap;
|
||||
std::unique_ptr<QWidget> _shareContainer;
|
||||
Fn<void()> _shareFocus;
|
||||
Fn<void()> _shareHide;
|
||||
|
||||
std::vector<Prepared> _pages;
|
||||
base::flat_map<QString, int> _indices;
|
||||
QString _navigateToHashWhenReady;
|
||||
|
|
|
@ -9,6 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "iv/iv_prepare.h"
|
||||
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
namespace Iv {
|
||||
|
||||
QByteArray GeoPointId(Geo point) {
|
||||
|
@ -45,9 +48,9 @@ Data::Data(const MTPDwebPage &webpage, const MTPPage &page)
|
|||
.webpageDocument = (webpage.vdocument()
|
||||
? *webpage.vdocument()
|
||||
: std::optional<MTPDocument>()),
|
||||
.title = (webpage.vtitle()
|
||||
? qs(*webpage.vtitle())
|
||||
: qs(webpage.vauthor().value_or_empty()))
|
||||
.name = (webpage.vsite_name()
|
||||
? qs(*webpage.vsite_name())
|
||||
: SiteNameFromUrl(qs(webpage.vurl())))
|
||||
})) {
|
||||
}
|
||||
|
||||
|
@ -67,4 +70,22 @@ void Data::prepare(const Options &options, Fn<void(Prepared)> done) const {
|
|||
});
|
||||
}
|
||||
|
||||
QString SiteNameFromUrl(const QString &url) {
|
||||
const auto u = QUrl(url);
|
||||
QString pretty = u.isValid() ? u.toDisplayString() : url;
|
||||
const auto m = QRegularExpression(u"^[a-zA-Z0-9]+://"_q).match(pretty);
|
||||
if (m.hasMatch()) pretty = pretty.mid(m.capturedLength());
|
||||
int32 slash = pretty.indexOf('/');
|
||||
if (slash > 0) pretty = pretty.mid(0, slash);
|
||||
QStringList components = pretty.split('.', Qt::SkipEmptyParts);
|
||||
if (components.size() >= 2) {
|
||||
components = components.mid(components.size() - 2);
|
||||
return components.at(0).at(0).toUpper()
|
||||
+ components.at(0).mid(1)
|
||||
+ '.'
|
||||
+ components.at(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace Iv
|
||||
|
|
|
@ -16,7 +16,8 @@ struct Options {
|
|||
};
|
||||
|
||||
struct Prepared {
|
||||
QString title;
|
||||
QString name;
|
||||
//QString title;
|
||||
QByteArray content;
|
||||
QByteArray script;
|
||||
QString url;
|
||||
|
@ -53,4 +54,6 @@ private:
|
|||
|
||||
};
|
||||
|
||||
[[nodiscard]] QString SiteNameFromUrl(const QString &url);
|
||||
|
||||
} // namespace Iv
|
||||
|
|
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "iv/iv_instance.h"
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
#include "apiwrap.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "core/application.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/shortcuts.h"
|
||||
|
@ -20,10 +20,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_file_origin.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "iv/iv_controller.h"
|
||||
#include "iv/iv_data.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lottie/lottie_common.h" // Lottie::ReadContent.
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
|
@ -34,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/file_download.h"
|
||||
#include "storage/storage_domain.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/layers/layer_widget.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "webview/webview_data_stream_memory.h"
|
||||
#include "webview/webview_interface.h"
|
||||
|
@ -42,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/window_session_controller_link_info.h"
|
||||
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace Iv {
|
||||
namespace {
|
||||
|
@ -124,6 +131,7 @@ private:
|
|||
|
||||
void showLocal(Prepared result);
|
||||
void showWindowed(Prepared result);
|
||||
[[nodiscard]] ShareBoxResult shareBox(ShareBoxDescriptor &&descriptor);
|
||||
|
||||
// Local.
|
||||
void showProgress(int index);
|
||||
|
@ -445,10 +453,185 @@ void Shown::writeEmbed(QString id, QString hash) {
|
|||
}
|
||||
}
|
||||
|
||||
ShareBoxResult Shown::shareBox(ShareBoxDescriptor &&descriptor) {
|
||||
class Show final : public Ui::Show {
|
||||
public:
|
||||
Show(QPointer<QWidget> parent, Fn<Ui::LayerStackWidget*()> lookup)
|
||||
: _parent(parent)
|
||||
, _lookup(lookup) {
|
||||
}
|
||||
void showOrHideBoxOrLayer(
|
||||
std::variant<
|
||||
v::null_t,
|
||||
object_ptr<Ui::BoxContent>,
|
||||
std::unique_ptr<Ui::LayerWidget>> &&layer,
|
||||
Ui::LayerOptions options,
|
||||
anim::type animated) const {
|
||||
using UniqueLayer = std::unique_ptr<Ui::LayerWidget>;
|
||||
using ObjectBox = object_ptr<Ui::BoxContent>;
|
||||
const auto stack = _lookup();
|
||||
if (!stack) {
|
||||
return;
|
||||
} else if (auto layerWidget = std::get_if<UniqueLayer>(&layer)) {
|
||||
stack->showLayer(std::move(*layerWidget), options, animated);
|
||||
} else if (auto box = std::get_if<ObjectBox>(&layer)) {
|
||||
stack->showBox(std::move(*box), options, animated);
|
||||
} else {
|
||||
stack->hideAll(animated);
|
||||
}
|
||||
}
|
||||
not_null<QWidget*> toastParent() const {
|
||||
return _parent.data();
|
||||
}
|
||||
bool valid() const override {
|
||||
return _lookup() != nullptr;
|
||||
}
|
||||
operator bool() const override {
|
||||
return valid();
|
||||
}
|
||||
|
||||
private:
|
||||
const QPointer<QWidget> _parent;
|
||||
const Fn<Ui::LayerStackWidget*()> _lookup;
|
||||
|
||||
};
|
||||
|
||||
const auto url = descriptor.url;
|
||||
const auto wrap = descriptor.parent;
|
||||
|
||||
struct State {
|
||||
Ui::LayerStackWidget *stack = nullptr;
|
||||
rpl::event_stream<> destroyRequests;
|
||||
};
|
||||
const auto state = wrap->lifetime().make_state<State>();
|
||||
|
||||
const auto weak = QPointer<Ui::RpWidget>(wrap);
|
||||
const auto lookup = crl::guard(weak, [state] { return state->stack; });
|
||||
const auto layer = Ui::CreateChild<Ui::LayerStackWidget>(
|
||||
wrap.get(),
|
||||
[=] { return std::make_shared<Show>(weak.data(), lookup); });
|
||||
state->stack = layer;
|
||||
const auto show = layer->showFactory()();
|
||||
|
||||
layer->setHideByBackgroundClick(false);
|
||||
layer->move(0, 0);
|
||||
wrap->sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
layer->resize(size);
|
||||
}, layer->lifetime());
|
||||
layer->hideFinishEvents(
|
||||
) | rpl::filter([=] {
|
||||
return !!lookup(); // Last hide finish is sent from destructor.
|
||||
}) | rpl::start_with_next([=] {
|
||||
state->destroyRequests.fire({});
|
||||
}, wrap->lifetime());
|
||||
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto sending = std::make_shared<bool>();
|
||||
auto copyCallback = [=] {
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
show->showToast(tr::lng_background_link_copied(tr::now));
|
||||
};
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<::Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
::Data::ForwardOptions) {
|
||||
if (*sending || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto error = [&] {
|
||||
for (const auto thread : result) {
|
||||
const auto error = GetErrorTextForSending(
|
||||
thread,
|
||||
{ .text = &comment });
|
||||
if (!error.isEmpty()) {
|
||||
return std::make_pair(error, thread);
|
||||
}
|
||||
}
|
||||
return std::make_pair(QString(), result.front());
|
||||
}();
|
||||
if (!error.first.isEmpty()) {
|
||||
auto text = TextWithEntities();
|
||||
if (result.size() > 1) {
|
||||
text.append(
|
||||
Ui::Text::Bold(error.second->chatListName())
|
||||
).append("\n\n");
|
||||
}
|
||||
text.append(error.first);
|
||||
if (const auto weak = *box) {
|
||||
weak->getDelegate()->show(Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.inform = true,
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
*sending = true;
|
||||
if (!comment.text.isEmpty()) {
|
||||
comment.text = url + "\n" + comment.text;
|
||||
const auto add = url.size() + 1;
|
||||
for (auto &tag : comment.tags) {
|
||||
tag.offset += add;
|
||||
}
|
||||
} else {
|
||||
comment.text = url;
|
||||
}
|
||||
auto &api = _session->api();
|
||||
for (const auto thread : result) {
|
||||
auto message = Api::MessageToSend(
|
||||
Api::SendAction(thread, options));
|
||||
message.textWithTags = comment;
|
||||
message.action.clearDraft = false;
|
||||
api.sendMessage(std::move(message));
|
||||
}
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
show->showToast(tr::lng_share_done(tr::now));
|
||||
};
|
||||
auto filterCallback = [](not_null<::Data::Thread*> thread) {
|
||||
if (const auto user = thread->peer()->asUser()) {
|
||||
if (user->canSendIgnoreRequirePremium()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ::Data::CanSend(thread, ChatRestriction::SendOther);
|
||||
};
|
||||
const auto focus = crl::guard(layer, [=] {
|
||||
if (!layer->window()->isActiveWindow()) {
|
||||
layer->window()->activateWindow();
|
||||
layer->window()->setFocus();
|
||||
}
|
||||
layer->setInnerFocus();
|
||||
});
|
||||
auto result = ShareBoxResult{
|
||||
.focus = focus,
|
||||
.hide = [=] { show->hideLayer(); },
|
||||
.destroyRequests = state->destroyRequests.events(),
|
||||
};
|
||||
*box = show->show(
|
||||
Box<ShareBox>(ShareBox::Descriptor{
|
||||
.session = _session,
|
||||
.copyCallback = std::move(copyCallback),
|
||||
.submitCallback = std::move(submitCallback),
|
||||
.filterCallback = std::move(filterCallback),
|
||||
.premiumRequiredError = SharePremiumRequiredError(),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther,
|
||||
anim::type::normal);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Shown::createController() {
|
||||
Expects(!_controller);
|
||||
|
||||
_controller = std::make_unique<Controller>();
|
||||
const auto showShareBox = [=](ShareBoxDescriptor &&descriptor) {
|
||||
return shareBox(std::move(descriptor));
|
||||
};
|
||||
_controller = std::make_unique<Controller>(std::move(showShareBox));
|
||||
|
||||
_controller->events(
|
||||
) | rpl::start_to_stream(_events, _controller->lifetime());
|
||||
|
@ -836,9 +1019,7 @@ void Instance::show(
|
|||
const auto session = &show->session();
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (data->partial()) {
|
||||
base::call_delayed(10000, [=] {
|
||||
requestFull(session, data->id());
|
||||
});
|
||||
requestFull(session, data->id());
|
||||
}
|
||||
});
|
||||
if (_shown && _shownSession == session) {
|
||||
|
|
|
@ -204,7 +204,7 @@ private:
|
|||
Parser::Parser(const Source &source, const Options &options)
|
||||
: _options(options) {
|
||||
process(source);
|
||||
_result.title = source.title;
|
||||
_result.name = source.name;
|
||||
_result.rtl = source.page.data().is_rtl();
|
||||
_result.content = list(source.page.data().vblocks());
|
||||
}
|
||||
|
@ -998,6 +998,7 @@ QByteArray Parser::rich(const MTPRichText &text) {
|
|||
: QByteArray();
|
||||
return tag("a", {
|
||||
{ "href", utf(data.vurl()) },
|
||||
{ "class", webpageId ? "internal-iv-link" : "" },
|
||||
{ "data-context", context },
|
||||
}, rich(data.vtext()));
|
||||
}, [&](const MTPDtextEmail &data) {
|
||||
|
|
|
@ -16,7 +16,7 @@ struct Source {
|
|||
MTPPage page;
|
||||
std::optional<MTPPhoto> webpagePhoto;
|
||||
std::optional<MTPDocument> webpageDocument;
|
||||
QString title;
|
||||
QString name;
|
||||
};
|
||||
|
||||
[[nodiscard]] Prepared Prepare(
|
||||
|
|
Loading…
Add table
Reference in a new issue