mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Name color changing for me / channels.
This commit is contained in:
parent
effc9873c9
commit
bcdb1bdfd2
16 changed files with 952 additions and 195 deletions
|
@ -794,6 +794,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_color_emoji_about" = "Make replies to your messages stand out by adding custom patterns to them.";
|
"lng_settings_color_emoji_about" = "Make replies to your messages stand out by adding custom patterns to them.";
|
||||||
"lng_settings_color_emoji_about_channel" = "Make replies to your channel's messages stand out by adding custom patterns to them.";
|
"lng_settings_color_emoji_about_channel" = "Make replies to your channel's messages stand out by adding custom patterns to them.";
|
||||||
"lng_settings_color_subscribe" = "Subscribe to {link} to choose a custom color for your name.";
|
"lng_settings_color_subscribe" = "Subscribe to {link} to choose a custom color for your name.";
|
||||||
|
"lng_settings_color_changed" = "Your name color has been updated!";
|
||||||
|
"lng_settings_color_changed_channel" = "Your channel color has been updated!";
|
||||||
|
|
||||||
"lng_suggest_hide_new_title" = "Hide new chats?";
|
"lng_suggest_hide_new_title" = "Hide new chats?";
|
||||||
"lng_suggest_hide_new_about" = "You are receiving lots of new chats from users who are not in your Contact List.\n\nDo you want to have such chats **automatically muted** and **archived**?";
|
"lng_suggest_hide_new_about" = "You are receiving lots of new chats from users who are not in your Contact List.\n\nDo you want to have such chats **automatically muted** and **archived**?";
|
||||||
|
|
|
@ -251,68 +251,24 @@ struct GiftCodeLink {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkLabel(
|
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkCopyIcon(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent) {
|
||||||
rpl::producer<QString> text,
|
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||||
rpl::producer<QString> link,
|
|
||||||
std::shared_ptr<Ui::Show> show) {
|
|
||||||
auto result = object_ptr<Ui::AbstractButton>(parent);
|
|
||||||
const auto raw = result.data();
|
const auto raw = result.data();
|
||||||
|
|
||||||
struct State {
|
|
||||||
State(
|
|
||||||
not_null<QWidget*> parent,
|
|
||||||
rpl::producer<QString> value,
|
|
||||||
rpl::producer<QString> link)
|
|
||||||
: text(std::move(value))
|
|
||||||
, link(std::move(link))
|
|
||||||
, label(parent, text.value(), st::giveawayGiftCodeLink)
|
|
||||||
, bg(st::roundRadiusLarge, st::windowBgOver) {
|
|
||||||
}
|
|
||||||
|
|
||||||
rpl::variable<QString> text;
|
|
||||||
rpl::variable<QString> link;
|
|
||||||
Ui::FlatLabel label;
|
|
||||||
Ui::RoundRect bg;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto state = raw->lifetime().make_state<State>(
|
|
||||||
raw,
|
|
||||||
rpl::duplicate(text),
|
|
||||||
std::move(link));
|
|
||||||
state->label.setSelectable(true);
|
|
||||||
|
|
||||||
rpl::combine(
|
|
||||||
raw->widthValue(),
|
|
||||||
std::move(text)
|
|
||||||
) | rpl::start_with_next([=](int outer, const auto&) {
|
|
||||||
const auto textWidth = state->label.textMaxWidth();
|
|
||||||
const auto skipLeft = st::giveawayGiftCodeLink.margin.left();
|
|
||||||
const auto skipRight = st::giveawayGiftCodeLinkCopyWidth;
|
|
||||||
const auto available = outer - skipRight - skipLeft;
|
|
||||||
const auto use = std::min(textWidth, available);
|
|
||||||
state->label.resizeToWidth(use);
|
|
||||||
state->label.move(outer - skipRight - use - skipLeft, 0);
|
|
||||||
}, raw->lifetime());
|
|
||||||
|
|
||||||
raw->paintRequest() | rpl::start_with_next([=] {
|
raw->paintRequest() | rpl::start_with_next([=] {
|
||||||
auto p = QPainter(raw);
|
auto p = QPainter(raw);
|
||||||
state->bg.paint(p, raw->rect());
|
|
||||||
const auto outer = raw->width();
|
|
||||||
const auto width = st::giveawayGiftCodeLinkCopyWidth;
|
|
||||||
const auto &icon = st::giveawayGiftCodeLinkCopy;
|
const auto &icon = st::giveawayGiftCodeLinkCopy;
|
||||||
const auto left = outer - width + (width - icon.width()) / 2;
|
const auto left = (raw->width() - icon.width()) / 2;
|
||||||
const auto top = (raw->height() - icon.height()) / 2;
|
const auto top = (raw->height() - icon.height()) / 2;
|
||||||
icon.paint(p, left, top, raw->width());
|
icon.paint(p, left, top, raw->width());
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
state->label.setAttribute(Qt::WA_TransparentForMouseEvents);
|
raw->resize(
|
||||||
|
st::giveawayGiftCodeLinkCopyWidth,
|
||||||
|
st::giveawayGiftCodeLinkHeight);
|
||||||
|
|
||||||
raw->resize(raw->width(), st::giveawayGiftCodeLinkHeight);
|
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
raw->setClickedCallback([=] {
|
|
||||||
QGuiApplication::clipboard()->setText(state->link.current());
|
|
||||||
show->showToast(tr::lng_username_copied(tr::now));
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -502,11 +458,12 @@ void GiftCodeBox(
|
||||||
|
|
||||||
const auto link = MakeGiftCodeLink(&controller->session(), slug);
|
const auto link = MakeGiftCodeLink(&controller->session(), slug);
|
||||||
box->addRow(
|
box->addRow(
|
||||||
MakeLinkLabel(
|
Ui::MakeLinkLabel(
|
||||||
box,
|
box,
|
||||||
rpl::single(link.text),
|
rpl::single(link.text),
|
||||||
rpl::single(link.link),
|
rpl::single(link.link),
|
||||||
box->uiShow()),
|
box->uiShow(),
|
||||||
|
MakeLinkCopyIcon(box)),
|
||||||
st::giveawayGiftCodeLinkMargin);
|
st::giveawayGiftCodeLinkMargin);
|
||||||
|
|
||||||
auto table = box->addRow(
|
auto table = box->addRow(
|
||||||
|
|
|
@ -7,14 +7,38 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/peers/edit_peer_color_box.h"
|
#include "boxes/peers/edit_peer_color_box.h"
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_web_page.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "info/boosts/info_boosts_widget.h"
|
||||||
|
#include "info/info_memento.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
#include "settings/settings_premium.h"
|
||||||
|
#include "ui/boxes/boost_box.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/chat/chat_theme.h"
|
||||||
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "window/themes/window_theme.h"
|
||||||
|
#include "window/section_widget.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
#include "styles/style_settings.h"
|
#include "styles/style_settings.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
@ -23,32 +47,97 @@ namespace {
|
||||||
|
|
||||||
using namespace Settings;
|
using namespace Settings;
|
||||||
|
|
||||||
class ColorSample final : public Ui::RpWidget {
|
constexpr auto kFakeChannelId = ChannelId(0xFFFFFFF000ULL);
|
||||||
|
constexpr auto kFakeWebPageId = WebPageId(0xFFFFFFFF00000000ULL);
|
||||||
|
constexpr auto kSelectAnimationDuration = crl::time(150);
|
||||||
|
|
||||||
|
class ColorSample final : public Ui::AbstractButton {
|
||||||
public:
|
public:
|
||||||
ColorSample(
|
ColorSample(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
std::shared_ptr<Ui::ChatStyle> st,
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
rpl::producer<uint8> colorIndex,
|
rpl::producer<uint8> colorIndex,
|
||||||
const QString &name);
|
const QString &name);
|
||||||
|
ColorSample(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
uint8 colorIndex,
|
||||||
|
bool selected);
|
||||||
|
|
||||||
|
[[nodiscard]] uint8 index() const;
|
||||||
int naturalWidth() const override;
|
int naturalWidth() const override;
|
||||||
|
|
||||||
|
void setSelected(bool selected);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
std::shared_ptr<Ui::ChatStyle> _st;
|
std::shared_ptr<Ui::ChatStyle> _style;
|
||||||
Ui::Text::String _name;
|
Ui::Text::String _name;
|
||||||
uint8 _index = 0;
|
uint8 _index = 0;
|
||||||
|
Ui::Animations::Simple _selectAnimation;
|
||||||
|
bool _selected = false;
|
||||||
|
bool _simple = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PreviewDelegate final : public HistoryView::DefaultElementDelegate {
|
||||||
|
public:
|
||||||
|
PreviewDelegate(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Ui::ChatStyle*> st,
|
||||||
|
Fn<void()> update);
|
||||||
|
|
||||||
|
bool elementAnimationsPaused() override;
|
||||||
|
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||||
|
HistoryView::Context elementContext() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<QWidget*> _parent;
|
||||||
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PreviewWrap final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
PreviewWrap(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
std::shared_ptr<Ui::ChatTheme> theme,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
rpl::producer<uint8> colorIndexValue);
|
||||||
|
~PreviewWrap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Element = HistoryView::Element;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
void initElements();
|
||||||
|
|
||||||
|
const not_null<Ui::GenericBox*> _box;
|
||||||
|
const not_null<PeerData*> _peer;
|
||||||
|
const not_null<ChannelData*> _fake;
|
||||||
|
const not_null<History*> _history;
|
||||||
|
const not_null<WebPageData*> _webpage;
|
||||||
|
const std::shared_ptr<Ui::ChatTheme> _theme;
|
||||||
|
const std::shared_ptr<Ui::ChatStyle> _style;
|
||||||
|
const std::unique_ptr<PreviewDelegate> _delegate;
|
||||||
|
const not_null<HistoryItem*> _replyToItem;
|
||||||
|
const not_null<HistoryItem*> _replyItem;
|
||||||
|
std::unique_ptr<Element> _element;
|
||||||
|
Ui::PeerUserpicView _userpic;
|
||||||
|
QPoint _position;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ColorSample::ColorSample(
|
ColorSample::ColorSample(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
std::shared_ptr<Ui::ChatStyle> st,
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
rpl::producer<uint8> colorIndex,
|
rpl::producer<uint8> colorIndex,
|
||||||
const QString &name)
|
const QString &name)
|
||||||
: RpWidget(parent)
|
: AbstractButton(parent)
|
||||||
, _st(st)
|
, _style(style)
|
||||||
, _name(st::semiboldTextStyle, name) {
|
, _name(st::semiboldTextStyle, name) {
|
||||||
std::move(
|
std::move(
|
||||||
colorIndex
|
colorIndex
|
||||||
|
@ -58,11 +147,35 @@ ColorSample::ColorSample(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorSample::ColorSample(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
uint8 colorIndex,
|
||||||
|
bool selected)
|
||||||
|
: AbstractButton(parent)
|
||||||
|
, _style(style)
|
||||||
|
, _index(colorIndex)
|
||||||
|
, _selected(selected)
|
||||||
|
, _simple(true) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorSample::setSelected(bool selected) {
|
||||||
|
if (_selected == selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_selected = selected;
|
||||||
|
_selectAnimation.start(
|
||||||
|
[=] { update(); },
|
||||||
|
_selected ? 0. : 1.,
|
||||||
|
_selected ? 1. : 0.,
|
||||||
|
kSelectAnimationDuration);
|
||||||
|
}
|
||||||
|
|
||||||
void ColorSample::paintEvent(QPaintEvent *e) {
|
void ColorSample::paintEvent(QPaintEvent *e) {
|
||||||
auto p = Painter(this);
|
auto p = Painter(this);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
const auto colors = _st->coloredValues(false, _index);
|
const auto colors = _style->coloredValues(false, _index);
|
||||||
if (!colors.outlines[1].alpha()) {
|
if (!_simple && !colors.outlines[1].alpha()) {
|
||||||
const auto radius = height() / 2;
|
const auto radius = height() / 2;
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setBrush(colors.bg);
|
p.setBrush(colors.bg);
|
||||||
|
@ -81,32 +194,52 @@ void ColorSample::paintEvent(QPaintEvent *e) {
|
||||||
1,
|
1,
|
||||||
style::al_top);
|
style::al_top);
|
||||||
} else {
|
} else {
|
||||||
const auto size = width();
|
const auto size = float64(width());
|
||||||
const auto half = size / 2.;
|
const auto half = size / 2.;
|
||||||
|
const auto full = QRectF(-half, -half, size, size);
|
||||||
p.translate(size / 2., size / 2.);
|
p.translate(size / 2., size / 2.);
|
||||||
p.rotate(-45.);
|
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setClipRect(-size, -size, 3 * size, size);
|
if (colors.outlines[1].alpha()) {
|
||||||
|
p.rotate(-45.);
|
||||||
|
p.setClipRect(-size, 0, 3 * size, size);
|
||||||
|
p.setBrush(colors.outlines[1]);
|
||||||
|
p.drawEllipse(full);
|
||||||
|
p.setClipRect(-size, -size, 3 * size, size);
|
||||||
|
}
|
||||||
p.setBrush(colors.outlines[0]);
|
p.setBrush(colors.outlines[0]);
|
||||||
p.drawEllipse(-half, -half, size, size);
|
p.drawEllipse(full);
|
||||||
p.setClipRect(-size, 0, 3 * size, size);
|
p.setClipping(false);
|
||||||
p.setBrush(colors.outlines[1]);
|
|
||||||
p.drawEllipse(-half, -half, size, size);
|
|
||||||
if (colors.outlines[2].alpha()) {
|
if (colors.outlines[2].alpha()) {
|
||||||
const auto center = st::settingsColorSampleCenter;
|
const auto multiplier = size / st::settingsColorSampleSize;
|
||||||
const auto radius = st::settingsColorSampleCenterRadius;
|
const auto center = st::settingsColorSampleCenter * multiplier;
|
||||||
p.setClipping(false);
|
const auto radius = st::settingsColorSampleCenterRadius
|
||||||
|
* multiplier;
|
||||||
p.setBrush(colors.outlines[2]);
|
p.setBrush(colors.outlines[2]);
|
||||||
p.drawRoundedRect(
|
p.drawRoundedRect(
|
||||||
QRectF(-center / 2., -center / 2., center, center),
|
QRectF(-center / 2., -center / 2., center, center),
|
||||||
radius,
|
radius,
|
||||||
radius);
|
radius);
|
||||||
}
|
}
|
||||||
|
const auto selected = _selectAnimation.value(_selected ? 1. : 0.);
|
||||||
|
if (selected > 0) {
|
||||||
|
const auto line = st::settingsColorRadioStroke * 1.;
|
||||||
|
const auto thickness = selected * line;
|
||||||
|
auto pen = st::boxBg->p;
|
||||||
|
pen.setWidthF(thickness);
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
p.setPen(pen);
|
||||||
|
const auto skip = 1.5 * line;
|
||||||
|
p.drawEllipse(full.marginsRemoved({ skip, skip, skip, skip }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8 ColorSample::index() const {
|
||||||
|
return _index;
|
||||||
|
}
|
||||||
|
|
||||||
int ColorSample::naturalWidth() const {
|
int ColorSample::naturalWidth() const {
|
||||||
if (_st->colorPatternIndex(_index)) {
|
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
|
||||||
return st::settingsColorSampleSize;
|
return st::settingsColorSampleSize;
|
||||||
}
|
}
|
||||||
const auto padding = st::settingsColorSamplePadding;
|
const auto padding = st::settingsColorSamplePadding;
|
||||||
|
@ -115,14 +248,463 @@ int ColorSample::naturalWidth() const {
|
||||||
padding.top() + st::semiboldFont->height + padding.bottom());
|
padding.top() + st::semiboldFont->height + padding.bottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreviewWrap::PreviewWrap(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
std::shared_ptr<Ui::ChatTheme> theme,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
rpl::producer<uint8> colorIndexValue)
|
||||||
|
: RpWidget(box)
|
||||||
|
, _box(box)
|
||||||
|
, _peer(peer)
|
||||||
|
, _fake(_peer->owner().channel(kFakeChannelId))
|
||||||
|
, _history(_fake->owner().history(_fake))
|
||||||
|
, _webpage(_peer->owner().webpage(
|
||||||
|
kFakeWebPageId,
|
||||||
|
WebPageType::Article,
|
||||||
|
u"internal:peer-color-webpage-preview"_q,
|
||||||
|
u"internal:peer-color-webpage-preview"_q,
|
||||||
|
tr::lng_settings_color_link_name(tr::now),
|
||||||
|
tr::lng_settings_color_link_title(tr::now),
|
||||||
|
{ tr::lng_settings_color_link_description(tr::now) },
|
||||||
|
nullptr, // photo
|
||||||
|
nullptr, // document
|
||||||
|
WebPageCollage(),
|
||||||
|
0, // duration
|
||||||
|
QString(), // author
|
||||||
|
false, // hasLargeMedia
|
||||||
|
0)) // pendingTill
|
||||||
|
, _theme(theme)
|
||||||
|
, _style(style)
|
||||||
|
, _delegate(std::make_unique<PreviewDelegate>(box, _style.get(), [=] {
|
||||||
|
update();
|
||||||
|
}))
|
||||||
|
, _replyToItem(_history->addNewLocalMessage(
|
||||||
|
_history->nextNonHistoryEntryId(),
|
||||||
|
(MessageFlag::FakeHistoryItem
|
||||||
|
| MessageFlag::HasFromId
|
||||||
|
| MessageFlag::Post),
|
||||||
|
UserId(), // via
|
||||||
|
FullReplyTo(),
|
||||||
|
base::unixtime::now(), // date
|
||||||
|
_fake->id,
|
||||||
|
QString(), // postAuthor
|
||||||
|
TextWithEntities{ _peer->isSelf()
|
||||||
|
? tr::lng_settings_color_reply(tr::now)
|
||||||
|
: tr::lng_settings_color_reply_channel(tr::now),
|
||||||
|
},
|
||||||
|
MTP_messageMediaEmpty(),
|
||||||
|
HistoryMessageMarkupData(),
|
||||||
|
uint64(0)))
|
||||||
|
, _replyItem(_history->addNewLocalMessage(
|
||||||
|
_history->nextNonHistoryEntryId(),
|
||||||
|
(MessageFlag::FakeHistoryItem
|
||||||
|
| MessageFlag::HasFromId
|
||||||
|
| MessageFlag::HasReplyInfo
|
||||||
|
| MessageFlag::Post),
|
||||||
|
UserId(), // via
|
||||||
|
FullReplyTo{ .messageId = _replyToItem->fullId() },
|
||||||
|
base::unixtime::now(), // date
|
||||||
|
_fake->id,
|
||||||
|
QString(), // postAuthor
|
||||||
|
TextWithEntities{ _peer->isSelf()
|
||||||
|
? tr::lng_settings_color_text(tr::now)
|
||||||
|
: tr::lng_settings_color_text_channel(tr::now),
|
||||||
|
},
|
||||||
|
MTP_messageMediaWebPage(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_webPagePending(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_long(_webpage->id),
|
||||||
|
MTPstring(),
|
||||||
|
MTP_int(0))),
|
||||||
|
HistoryMessageMarkupData(),
|
||||||
|
uint64(0)))
|
||||||
|
, _element(_replyItem->createView(_delegate.get()))
|
||||||
|
, _position(0, st::msgMargin.bottom()) {
|
||||||
|
_style->apply(_theme.get());
|
||||||
|
|
||||||
|
_fake->setName(peer->name(), QString());
|
||||||
|
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
|
||||||
|
_fake->changeColorIndex(index);
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
const auto session = &_history->session();
|
||||||
|
session->data().viewRepaintRequest(
|
||||||
|
) | rpl::start_with_next([=](not_null<const Element*> view) {
|
||||||
|
if (view == _element.get()) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
initElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewWrap::~PreviewWrap() {
|
||||||
|
_element = nullptr;
|
||||||
|
_replyItem->destroy();
|
||||||
|
_replyToItem->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
|
auto p = Painter(this);
|
||||||
|
const auto clip = e->rect();
|
||||||
|
|
||||||
|
p.setClipRect(clip);
|
||||||
|
Window::SectionWidget::PaintBackground(
|
||||||
|
p,
|
||||||
|
_theme.get(),
|
||||||
|
QSize(_box->width(), _box->window()->height()),
|
||||||
|
clip);
|
||||||
|
|
||||||
|
auto context = _theme->preparePaintContext(
|
||||||
|
_style.get(),
|
||||||
|
rect(),
|
||||||
|
clip,
|
||||||
|
!window()->isActiveWindow());
|
||||||
|
|
||||||
|
p.translate(_position);
|
||||||
|
_element->draw(p, context);
|
||||||
|
|
||||||
|
if (_element->displayFromPhoto()) {
|
||||||
|
auto userpicMinBottomSkip = st::historyPaddingBottom
|
||||||
|
+ st::msgMargin.bottom();
|
||||||
|
auto userpicBottom = height()
|
||||||
|
- _element->marginBottom()
|
||||||
|
- _element->marginTop();
|
||||||
|
const auto item = _element->data();
|
||||||
|
const auto userpicTop = userpicBottom - st::msgPhotoSize;
|
||||||
|
_peer->paintUserpicLeft(
|
||||||
|
p,
|
||||||
|
_userpic,
|
||||||
|
st::historyPhotoLeft,
|
||||||
|
userpicTop,
|
||||||
|
width(),
|
||||||
|
st::msgPhotoSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::initElements() {
|
||||||
|
_element->initDimensions();
|
||||||
|
|
||||||
|
widthValue(
|
||||||
|
) | rpl::filter([=](int width) {
|
||||||
|
return width > st::msgMinWidth;
|
||||||
|
}) | rpl::start_with_next([=](int width) {
|
||||||
|
const auto height = _position.y()
|
||||||
|
+ _element->resizeGetHeight(width)
|
||||||
|
+ st::msgMargin.top();
|
||||||
|
resize(width, height);
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewDelegate::PreviewDelegate(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Ui::ChatStyle*> st,
|
||||||
|
Fn<void()> update)
|
||||||
|
: _parent(parent)
|
||||||
|
, _pathGradient(HistoryView::MakePathShiftGradient(st, update)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreviewDelegate::elementAnimationsPaused() {
|
||||||
|
return _parent->window()->isActiveWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PreviewDelegate::elementPathShiftGradient()
|
||||||
|
-> not_null<Ui::PathShiftGradient*> {
|
||||||
|
return _pathGradient.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryView::Context PreviewDelegate::elementContext() {
|
||||||
|
return HistoryView::Context::AdminLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
uint8 colorIndex) {
|
||||||
|
const auto was = peer->colorIndex();
|
||||||
|
peer->changeColorIndex(colorIndex);
|
||||||
|
peer->session().changes().peerUpdated(
|
||||||
|
peer,
|
||||||
|
Data::PeerUpdate::Flag::Color);
|
||||||
|
const auto done = [=] {
|
||||||
|
show->showToast(peer->isSelf()
|
||||||
|
? tr::lng_settings_color_changed(tr::now)
|
||||||
|
: tr::lng_settings_color_changed_channel(tr::now));
|
||||||
|
};
|
||||||
|
const auto fail = [=](const MTP::Error &error) {
|
||||||
|
peer->changeColorIndex(was);
|
||||||
|
peer->session().changes().peerUpdated(
|
||||||
|
peer,
|
||||||
|
Data::PeerUpdate::Flag::Color);
|
||||||
|
show->showToast(error.type());
|
||||||
|
};
|
||||||
|
const auto send = [&](auto &&request) {
|
||||||
|
peer->session().api().request(
|
||||||
|
std::move(request)
|
||||||
|
).done(done).fail(fail).send();
|
||||||
|
};
|
||||||
|
if (peer->isSelf()) {
|
||||||
|
send(MTPaccount_UpdateColor(
|
||||||
|
MTP_flags(
|
||||||
|
MTPaccount_UpdateColor::Flag::f_background_emoji_id),
|
||||||
|
MTP_int(colorIndex),
|
||||||
|
MTP_long(peer->backgroundEmojiId())));
|
||||||
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
|
send(MTPchannels_UpdateColor(
|
||||||
|
MTP_flags(
|
||||||
|
MTPchannels_UpdateColor::Flag::f_background_emoji_id),
|
||||||
|
channel->inputChannel,
|
||||||
|
MTP_int(colorIndex),
|
||||||
|
MTP_long(peer->backgroundEmojiId())));
|
||||||
|
} else {
|
||||||
|
Unexpected("Invalid peer type in Set(colorIndex).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Apply(
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
uint8 colorIndex,
|
||||||
|
Fn<void()> close,
|
||||||
|
Fn<void()> cancel) {
|
||||||
|
const auto session = &peer->session();
|
||||||
|
if (peer->colorIndex() == colorIndex) {
|
||||||
|
close();
|
||||||
|
} else if (peer->isSelf() && !session->premium()) {
|
||||||
|
Settings::ShowPremiumPromoToast(
|
||||||
|
show,
|
||||||
|
tr::lng_settings_color_subscribe(
|
||||||
|
tr::now,
|
||||||
|
lt_link,
|
||||||
|
Ui::Text::Link(
|
||||||
|
Ui::Text::Bold(
|
||||||
|
tr::lng_send_as_premium_required_link(tr::now))),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
u"name_color"_q);
|
||||||
|
cancel();
|
||||||
|
} else if (peer->isSelf()) {
|
||||||
|
Set(show, peer, colorIndex);
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
session->api().request(MTPpremium_GetBoostsStatus(
|
||||||
|
peer->input
|
||||||
|
)).done([=](const MTPpremium_BoostsStatus &result) {
|
||||||
|
const auto &data = result.data();
|
||||||
|
const auto required = session->account().appConfig().get<int>(
|
||||||
|
"channel_color_level_min",
|
||||||
|
5);
|
||||||
|
if (data.vlevel().v >= required) {
|
||||||
|
Set(show, peer, colorIndex);
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto next = data.vnext_level_boosts().value_or_empty();
|
||||||
|
const auto openStatistics = [=] {
|
||||||
|
if (const auto controller = show->resolveWindow(
|
||||||
|
ChatHelpers::WindowUsage::PremiumPromo)) {
|
||||||
|
controller->showSection(Info::Boosts::Make(peer));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
|
||||||
|
.link = qs(data.vboost_url()),
|
||||||
|
.boost = {
|
||||||
|
.level = data.vlevel().v,
|
||||||
|
.boosts = data.vboosts().v,
|
||||||
|
.thisLevelBoosts = data.vcurrent_level_boosts().v,
|
||||||
|
.nextLevelBoosts = next,
|
||||||
|
},
|
||||||
|
.requiredLevel = required,
|
||||||
|
}, openStatistics, nullptr));
|
||||||
|
cancel();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
show->showToast(error.type());
|
||||||
|
cancel();
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorSelector final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
ColorSelector(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
rpl::producer<std::vector<uint8>> indices,
|
||||||
|
uint8 index,
|
||||||
|
Fn<void(uint8)> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillFrom(std::vector<uint8> indices);
|
||||||
|
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
const std::shared_ptr<Ui::ChatStyle> _style;
|
||||||
|
std::vector<std::unique_ptr<ColorSample>> _samples;
|
||||||
|
const Fn<void(uint8)> _callback;
|
||||||
|
uint8 _index = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ColorSelector::ColorSelector(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
rpl::producer<std::vector<uint8>> indices,
|
||||||
|
uint8 index,
|
||||||
|
Fn<void(uint8)> callback)
|
||||||
|
: RpWidget(box)
|
||||||
|
, _style(style)
|
||||||
|
, _callback(std::move(callback))
|
||||||
|
, _index(index) {
|
||||||
|
std::move(
|
||||||
|
indices
|
||||||
|
) | rpl::start_with_next([=](std::vector<uint8> indices) {
|
||||||
|
fillFrom(std::move(indices));
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorSelector::fillFrom(std::vector<uint8> indices) {
|
||||||
|
auto samples = std::vector<std::unique_ptr<ColorSample>>();
|
||||||
|
const auto add = [&](uint8 index) {
|
||||||
|
auto i = ranges::find(_samples, index, &ColorSample::index);
|
||||||
|
if (i != end(_samples)) {
|
||||||
|
samples.push_back(std::move(*i));
|
||||||
|
_samples.erase(i);
|
||||||
|
} else {
|
||||||
|
samples.push_back(std::make_unique<ColorSample>(
|
||||||
|
this,
|
||||||
|
_style,
|
||||||
|
index,
|
||||||
|
index == _index));
|
||||||
|
samples.back()->show();
|
||||||
|
samples.back()->setClickedCallback([=] {
|
||||||
|
if (_index != index) {
|
||||||
|
_callback(index);
|
||||||
|
|
||||||
|
ranges::find(
|
||||||
|
_samples,
|
||||||
|
_index,
|
||||||
|
&ColorSample::index
|
||||||
|
)->get()->setSelected(false);
|
||||||
|
_index = index;
|
||||||
|
ranges::find(
|
||||||
|
_samples,
|
||||||
|
_index,
|
||||||
|
&ColorSample::index
|
||||||
|
)->get()->setSelected(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const auto index : indices) {
|
||||||
|
add(index);
|
||||||
|
}
|
||||||
|
if (!ranges::contains(indices, _index)) {
|
||||||
|
add(_index);
|
||||||
|
}
|
||||||
|
_samples = std::move(samples);
|
||||||
|
if (width() > 0) {
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
|
if (newWidth <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const auto count = int(_samples.size());
|
||||||
|
const auto columns = Ui::kSimpleColorIndexCount;
|
||||||
|
const auto rows = (count + columns - 1) / columns;
|
||||||
|
const auto skip = st::settingsColorRadioSkip;
|
||||||
|
const auto size = (newWidth - skip * (columns - 1)) / float64(columns);
|
||||||
|
const auto isize = int(base::SafeRound(size));
|
||||||
|
auto top = 0;
|
||||||
|
auto left = 0.;
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
const auto row = i / columns;
|
||||||
|
const auto column = i % columns;
|
||||||
|
_samples[i]->resize(isize, isize);
|
||||||
|
_samples[i]->move(int(base::SafeRound(left)), top);
|
||||||
|
left += size + skip;
|
||||||
|
if (!((i + 1) % columns)) {
|
||||||
|
top += isize + skip;
|
||||||
|
left = 0.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (top - skip) + ((count % columns) ? (isize + skip) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EditPeerColorBox(
|
void EditPeerColorBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
std::shared_ptr<Ui::ChatStyle> st) {
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
std::shared_ptr<Ui::ChatTheme> theme) {
|
||||||
|
box->setTitle(tr::lng_settings_color_title());
|
||||||
|
box->setWidth(st::boxWideWidth);
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
rpl::variable<uint8> index;
|
||||||
|
bool changing = false;
|
||||||
|
bool applying = false;
|
||||||
|
};
|
||||||
|
const auto state = box->lifetime().make_state<State>();
|
||||||
|
state->index = peer->colorIndex();
|
||||||
|
|
||||||
|
box->addRow(object_ptr<PreviewWrap>(
|
||||||
|
box,
|
||||||
|
style,
|
||||||
|
theme,
|
||||||
|
peer,
|
||||||
|
state->index.value()
|
||||||
|
), {});
|
||||||
|
|
||||||
|
const auto appConfig = &peer->session().account().appConfig();
|
||||||
|
auto indices = rpl::single(
|
||||||
|
rpl::empty
|
||||||
|
) | rpl::then(
|
||||||
|
appConfig->refreshed()
|
||||||
|
) | rpl::map([=] {
|
||||||
|
const auto list = appConfig->get<std::vector<int>>(
|
||||||
|
"peer_colors_available",
|
||||||
|
{ 0, 1, 2, 3, 4, 5, 6 });
|
||||||
|
return list | ranges::views::transform([](int i) {
|
||||||
|
return uint8(i);
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
});
|
||||||
|
const auto margin = st::settingsColorRadioMargin;
|
||||||
|
const auto skip = st::settingsColorRadioSkip;
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<ColorSelector>(
|
||||||
|
box,
|
||||||
|
style,
|
||||||
|
std::move(indices),
|
||||||
|
state->index.current(),
|
||||||
|
[=](uint8 index) { state->index = index; }),
|
||||||
|
{ margin, skip, margin, skip });
|
||||||
|
|
||||||
|
const auto container = box->verticalLayout();
|
||||||
|
AddDividerText(container, peer->isSelf()
|
||||||
|
? tr::lng_settings_color_about()
|
||||||
|
: tr::lng_settings_color_about_channel());
|
||||||
|
|
||||||
|
box->addButton(tr::lng_settings_apply(), [=] {
|
||||||
|
if (state->applying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->applying = true;
|
||||||
|
Apply(show, peer, state->index.current(), crl::guard(box, [=] {
|
||||||
|
box->closeBox();
|
||||||
|
}), crl::guard(box, [=] {
|
||||||
|
state->applying = false;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddPeerColorButton(
|
void AddPeerColorButton(
|
||||||
|
@ -144,11 +726,16 @@ void AddPeerColorButton(
|
||||||
return peer->colorIndex();
|
return peer->colorIndex();
|
||||||
});
|
});
|
||||||
const auto name = peer->shortName();
|
const auto name = peer->shortName();
|
||||||
const auto st = std::make_shared<Ui::ChatStyle>(
|
|
||||||
|
const auto style = std::make_shared<Ui::ChatStyle>(
|
||||||
peer->session().colorIndicesValue());
|
peer->session().colorIndicesValue());
|
||||||
|
const auto theme = std::shared_ptr<Ui::ChatTheme>(
|
||||||
|
Window::Theme::DefaultChatThemeOn(button->lifetime()));
|
||||||
|
style->apply(theme.get());
|
||||||
|
|
||||||
const auto sample = Ui::CreateChild<ColorSample>(
|
const auto sample = Ui::CreateChild<ColorSample>(
|
||||||
button.get(),
|
button.get(),
|
||||||
st,
|
style,
|
||||||
rpl::duplicate(colorIndexValue),
|
rpl::duplicate(colorIndexValue),
|
||||||
name);
|
name);
|
||||||
sample->show();
|
sample->show();
|
||||||
|
@ -167,7 +754,7 @@ void AddPeerColorButton(
|
||||||
- (st::settingsColorButton.padding.right() - sampleSize)
|
- (st::settingsColorButton.padding.right() - sampleSize)
|
||||||
- st::settingsButton.style.font->width(button)
|
- st::settingsButton.style.font->width(button)
|
||||||
- st::settingsButtonRightSkip;
|
- st::settingsButtonRightSkip;
|
||||||
if (st->colorPatternIndex(colorIndex)) {
|
if (style->colorPatternIndex(colorIndex)) {
|
||||||
sample->resize(sampleSize, sampleSize);
|
sample->resize(sampleSize, sampleSize);
|
||||||
} else {
|
} else {
|
||||||
const auto padding = st::settingsColorSamplePadding;
|
const auto padding = st::settingsColorSamplePadding;
|
||||||
|
@ -188,7 +775,7 @@ void AddPeerColorButton(
|
||||||
const auto right = st::settingsColorButton.padding.right()
|
const auto right = st::settingsColorButton.padding.right()
|
||||||
- st::settingsColorSampleSkip
|
- st::settingsColorSampleSkip
|
||||||
- st::settingsColorSampleSize
|
- st::settingsColorSampleSize
|
||||||
- (st->colorPatternIndex(colorIndex)
|
- (style->colorPatternIndex(colorIndex)
|
||||||
? 0
|
? 0
|
||||||
: st::settingsColorSamplePadding.right());
|
: st::settingsColorSamplePadding.right());
|
||||||
sample->move(
|
sample->move(
|
||||||
|
@ -197,4 +784,8 @@ void AddPeerColorButton(
|
||||||
}, sample->lifetime());
|
}, sample->lifetime());
|
||||||
|
|
||||||
sample->setAttribute(Qt::WA_TransparentForMouseEvents);
|
sample->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
show->show(Box(EditPeerColorBox, show, peer, style, theme));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Show;
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
class ChatStyle;
|
class ChatStyle;
|
||||||
|
class ChatTheme;
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
@ -21,7 +22,8 @@ void EditPeerColorBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
std::shared_ptr<Ui::ChatStyle> st = nullptr);
|
std::shared_ptr<Ui::ChatStyle> style = nullptr,
|
||||||
|
std::shared_ptr<Ui::ChatTheme> theme = nullptr);
|
||||||
|
|
||||||
void AddPeerColorButton(
|
void AddPeerColorButton(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
|
|
@ -838,6 +838,8 @@ void HistoryMessageReply::paint(
|
||||||
? resolvedMessage->displayFrom()
|
? resolvedMessage->displayFrom()
|
||||||
: resolvedStory
|
: resolvedStory
|
||||||
? resolvedStory->peer().get()
|
? resolvedStory->peer().get()
|
||||||
|
: _externalSender
|
||||||
|
? _externalSender
|
||||||
: nullptr;
|
: nullptr;
|
||||||
const auto backgroundEmojiId = colorPeer
|
const auto backgroundEmojiId = colorPeer
|
||||||
? colorPeer->backgroundEmojiId()
|
? colorPeer->backgroundEmojiId()
|
||||||
|
|
|
@ -70,39 +70,6 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<Ui::ChatTheme> DefaultThemeOn(
|
|
||||||
rpl::lifetime &lifetime) {
|
|
||||||
auto result = std::make_unique<Ui::ChatTheme>();
|
|
||||||
|
|
||||||
using namespace Window::Theme;
|
|
||||||
const auto push = [=, raw = result.get()] {
|
|
||||||
const auto background = Background();
|
|
||||||
const auto &paper = background->paper();
|
|
||||||
raw->setBackground({
|
|
||||||
.prepared = background->prepared(),
|
|
||||||
.preparedForTiled = background->preparedForTiled(),
|
|
||||||
.gradientForFill = background->gradientForFill(),
|
|
||||||
.colorForFill = background->colorForFill(),
|
|
||||||
.colors = paper.backgroundColors(),
|
|
||||||
.patternOpacity = paper.patternOpacity(),
|
|
||||||
.gradientRotation = paper.gradientRotation(),
|
|
||||||
.isPattern = paper.isPattern(),
|
|
||||||
.tile = background->tile(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
push();
|
|
||||||
Background()->updates(
|
|
||||||
) | rpl::start_with_next([=](const BackgroundUpdate &update) {
|
|
||||||
if (update.type == BackgroundUpdate::Type::New
|
|
||||||
|| update.type == BackgroundUpdate::Type::Changed) {
|
|
||||||
push();
|
|
||||||
}
|
|
||||||
}, lifetime);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] TextWithEntities HighlightParsedLinks(
|
[[nodiscard]] TextWithEntities HighlightParsedLinks(
|
||||||
TextWithEntities text,
|
TextWithEntities text,
|
||||||
const std::vector<MessageLinkRange> &links) {
|
const std::vector<MessageLinkRange> &links) {
|
||||||
|
@ -124,7 +91,6 @@ private:
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PreviewWrap final : public Ui::RpWidget {
|
class PreviewWrap final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
PreviewWrap(
|
PreviewWrap(
|
||||||
|
@ -195,7 +161,7 @@ PreviewWrap::PreviewWrap(
|
||||||
: RpWidget(box)
|
: RpWidget(box)
|
||||||
, _box(box)
|
, _box(box)
|
||||||
, _history(history)
|
, _history(history)
|
||||||
, _theme(DefaultThemeOn(lifetime()))
|
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||||
, _style(std::make_unique<Ui::ChatStyle>(
|
, _style(std::make_unique<Ui::ChatStyle>(
|
||||||
history->session().colorIndicesValue()))
|
history->session().colorIndicesValue()))
|
||||||
, _delegate(std::make_unique<PreviewDelegate>(
|
, _delegate(std::make_unique<PreviewDelegate>(
|
||||||
|
@ -376,7 +342,6 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto p = Painter(this);
|
auto p = Painter(this);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
|
|
||||||
auto context = _theme->preparePaintContext(
|
auto context = _theme->preparePaintContext(
|
||||||
_style.get(),
|
_style.get(),
|
||||||
|
|
|
@ -210,16 +210,14 @@ void InnerWidget::fill() {
|
||||||
fakeShowed->events(),
|
fakeShowed->events(),
|
||||||
rpl::single(status.overview.isBoosted),
|
rpl::single(status.overview.isBoosted),
|
||||||
dividerContent.data(),
|
dividerContent.data(),
|
||||||
Ui::BoostBoxData{
|
Ui::BoostCounters{
|
||||||
.boost = Ui::BoostCounters{
|
.level = status.overview.level,
|
||||||
.level = status.overview.level,
|
.boosts = status.overview.boostCount,
|
||||||
.boosts = status.overview.boostCount,
|
.thisLevelBoosts
|
||||||
.thisLevelBoosts
|
= status.overview.currentLevelBoostCount,
|
||||||
= status.overview.currentLevelBoostCount,
|
.nextLevelBoosts
|
||||||
.nextLevelBoosts
|
= status.overview.nextLevelBoostCount,
|
||||||
= status.overview.nextLevelBoostCount,
|
.mine = status.overview.isBoosted,
|
||||||
.mine = status.overview.isBoosted,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
st::statisticsLimitsLinePadding);
|
st::statisticsLimitsLinePadding);
|
||||||
inner->add(object_ptr<Ui::DividerLabel>(
|
inner->add(object_ptr<Ui::DividerLabel>(
|
||||||
|
|
|
@ -567,10 +567,13 @@ filterLinkChatsList: PeerList(peerListBox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsColorSampleSize: 20px;
|
settingsColorSampleSize: 20px;
|
||||||
settingsColorSampleCenter: 8px;
|
settingsColorSampleCenter: 6px;
|
||||||
settingsColorSampleCenterRadius: 2px;
|
settingsColorSampleCenterRadius: 2px;
|
||||||
settingsColorSamplePadding: margins(8px, 2px, 8px, 2px);
|
settingsColorSamplePadding: margins(8px, 2px, 8px, 2px);
|
||||||
settingsColorSampleSkip: 6px;
|
settingsColorSampleSkip: 6px;
|
||||||
settingsColorButton: SettingsButton(settingsButton) {
|
settingsColorButton: SettingsButton(settingsButton) {
|
||||||
padding: margins(60px, 10px, 48px, 10px);
|
padding: margins(60px, 10px, 48px, 10px);
|
||||||
}
|
}
|
||||||
|
settingsColorRadioMargin: 17px;
|
||||||
|
settingsColorRadioSkip: 13px;
|
||||||
|
settingsColorRadioStroke: 2px;
|
||||||
|
|
|
@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/widgets/checkbox.h" // Ui::RadiobuttonGroup.
|
#include "ui/widgets/checkbox.h" // Ui::RadiobuttonGroup.
|
||||||
#include "ui/widgets/gradient_round_button.h"
|
#include "ui/widgets/gradient_round_button.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
@ -1466,6 +1467,36 @@ QString LookupPremiumRef(PremiumPreview section) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowPremiumPromoToast(
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
TextWithEntities textWithLink,
|
||||||
|
const QString &ref) {
|
||||||
|
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
|
||||||
|
const auto toast = std::make_shared<WeakToast>();
|
||||||
|
(*toast) = show->showToast({
|
||||||
|
.text = std::move(textWithLink),
|
||||||
|
.st = &st::defaultMultilineToast,
|
||||||
|
.duration = Ui::Toast::kDefaultDuration * 2,
|
||||||
|
.multiline = true,
|
||||||
|
.filter = crl::guard(&show->session(), [=](
|
||||||
|
const ClickHandlerPtr &,
|
||||||
|
Qt::MouseButton button) {
|
||||||
|
if (button == Qt::LeftButton) {
|
||||||
|
if (const auto strong = toast->get()) {
|
||||||
|
strong->hideAnimated();
|
||||||
|
(*toast) = nullptr;
|
||||||
|
if (const auto controller = show->resolveWindow(
|
||||||
|
ChatHelpers::WindowUsage::PremiumPromo)) {
|
||||||
|
Settings::ShowPremium(controller, ref);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
not_null<Ui::GradientButton*> CreateSubscribeButton(
|
not_null<Ui::GradientButton*> CreateSubscribeButton(
|
||||||
SubscribeButtonArgs &&args) {
|
SubscribeButtonArgs &&args) {
|
||||||
Expects(args.show || args.controller);
|
Expects(args.show || args.controller);
|
||||||
|
|
|
@ -51,6 +51,11 @@ void StartPremiumPayment(
|
||||||
|
|
||||||
[[nodiscard]] QString LookupPremiumRef(PremiumPreview section);
|
[[nodiscard]] QString LookupPremiumRef(PremiumPreview section);
|
||||||
|
|
||||||
|
void ShowPremiumPromoToast(
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
TextWithEntities textWithLink,
|
||||||
|
const QString &ref);
|
||||||
|
|
||||||
struct SubscribeButtonArgs final {
|
struct SubscribeButtonArgs final {
|
||||||
Window::SessionController *controller = nullptr;
|
Window::SessionController *controller = nullptr;
|
||||||
not_null<Ui::RpWidget*> parent;
|
not_null<Ui::RpWidget*> parent;
|
||||||
|
|
|
@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_premium.h"
|
#include "styles/style_premium.h"
|
||||||
|
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
void StartFireworks(not_null<QWidget*> parent) {
|
void StartFireworks(not_null<QWidget*> parent) {
|
||||||
|
@ -57,7 +59,7 @@ void BoostBox(
|
||||||
BoxShowFinishes(box),
|
BoxShowFinishes(box),
|
||||||
state->you.value(),
|
state->you.value(),
|
||||||
box->verticalLayout(),
|
box->verticalLayout(),
|
||||||
data,
|
data.boost,
|
||||||
st::boxRowPadding);
|
st::boxRowPadding);
|
||||||
|
|
||||||
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
||||||
|
@ -170,31 +172,188 @@ void BoostBox(
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> MakeLinkLabel(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<QString> text,
|
||||||
|
rpl::producer<QString> link,
|
||||||
|
std::shared_ptr<Ui::Show> show,
|
||||||
|
object_ptr<Ui::RpWidget> right) {
|
||||||
|
auto result = object_ptr<Ui::AbstractButton>(parent);
|
||||||
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
const auto rawRight = right.release();
|
||||||
|
if (rawRight) {
|
||||||
|
rawRight->setParent(raw);
|
||||||
|
rawRight->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
State(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<QString> value,
|
||||||
|
rpl::producer<QString> link)
|
||||||
|
: text(std::move(value))
|
||||||
|
, link(std::move(link))
|
||||||
|
, label(parent, text.value(), st::giveawayGiftCodeLink)
|
||||||
|
, bg(st::roundRadiusLarge, st::windowBgOver) {
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::variable<QString> text;
|
||||||
|
rpl::variable<QString> link;
|
||||||
|
Ui::FlatLabel label;
|
||||||
|
Ui::RoundRect bg;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto state = raw->lifetime().make_state<State>(
|
||||||
|
raw,
|
||||||
|
rpl::duplicate(text),
|
||||||
|
std::move(link));
|
||||||
|
state->label.setSelectable(true);
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
raw->widthValue(),
|
||||||
|
std::move(text)
|
||||||
|
) | rpl::start_with_next([=](int outer, const auto&) {
|
||||||
|
const auto textWidth = state->label.textMaxWidth();
|
||||||
|
const auto skipLeft = st::giveawayGiftCodeLink.margin.left();
|
||||||
|
const auto skipRight = rawRight
|
||||||
|
? rawRight->width()
|
||||||
|
: st::giveawayGiftCodeLink.margin.right();
|
||||||
|
const auto available = outer - skipRight - skipLeft;
|
||||||
|
const auto use = std::min(textWidth, available);
|
||||||
|
state->label.resizeToWidth(use);
|
||||||
|
const auto forCenter = (outer - use) / 2;
|
||||||
|
const auto x = (forCenter < skipLeft)
|
||||||
|
? skipLeft
|
||||||
|
: (forCenter > outer - skipRight - use)
|
||||||
|
? (outer - skipRight - use)
|
||||||
|
: forCenter;
|
||||||
|
state->label.moveToLeft(x, st::giveawayGiftCodeLink.margin.top());
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
raw->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(raw);
|
||||||
|
state->bg.paint(p, raw->rect());
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
state->label.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
raw->resize(raw->width(), st::giveawayGiftCodeLinkHeight);
|
||||||
|
if (rawRight) {
|
||||||
|
raw->widthValue() | rpl::start_with_next([=](int width) {
|
||||||
|
rawRight->move(width - rawRight->width(), 0);
|
||||||
|
}, raw->lifetime());
|
||||||
|
}
|
||||||
|
raw->setClickedCallback([=] {
|
||||||
|
QGuiApplication::clipboard()->setText(state->link.current());
|
||||||
|
show->showToast(tr::lng_username_copied(tr::now));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AskBoostBox(
|
||||||
|
not_null<GenericBox*> box,
|
||||||
|
AskBoostBoxData data,
|
||||||
|
Fn<void()> openStatistics,
|
||||||
|
Fn<void()> startGiveaway) {
|
||||||
|
box->setWidth(st::boxWideWidth);
|
||||||
|
box->setStyle(st::boostBox);
|
||||||
|
|
||||||
|
const auto full = !data.boost.nextLevelBoosts;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
rpl::variable<bool> you = false;
|
||||||
|
bool submitted = false;
|
||||||
|
};
|
||||||
|
const auto state = box->lifetime().make_state<State>(State{
|
||||||
|
.you = data.boost.mine,
|
||||||
|
});
|
||||||
|
|
||||||
|
FillBoostLimit(
|
||||||
|
BoxShowFinishes(box),
|
||||||
|
state->you.value(),
|
||||||
|
box->verticalLayout(),
|
||||||
|
data.boost,
|
||||||
|
st::boxRowPadding);
|
||||||
|
|
||||||
|
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
||||||
|
|
||||||
|
auto title = tr::lng_boost_channel_title_color();
|
||||||
|
auto text = rpl::combine(
|
||||||
|
tr::lng_boost_channel_needs_level_color(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(data.requiredLevel)),
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
|
tr::lng_boost_channel_ask(Ui::Text::RichLangValue)
|
||||||
|
) | rpl::map([](TextWithEntities &&text, TextWithEntities &&ask) {
|
||||||
|
return text.append(u"\n\n"_q).append(std::move(ask));
|
||||||
|
});
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
box,
|
||||||
|
std::move(title),
|
||||||
|
st::boostTitle),
|
||||||
|
st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0));
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
box,
|
||||||
|
std::move(text),
|
||||||
|
st::boostText),
|
||||||
|
(st::boxRowPadding
|
||||||
|
+ QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
|
||||||
|
|
||||||
|
auto stats = object_ptr<Ui::IconButton>(box, st::boostLinkStatsButton);
|
||||||
|
stats->setClickedCallback(openStatistics);
|
||||||
|
box->addRow(MakeLinkLabel(
|
||||||
|
box,
|
||||||
|
rpl::single(data.link),
|
||||||
|
rpl::single(data.link),
|
||||||
|
box->uiShow(),
|
||||||
|
std::move(stats)));
|
||||||
|
|
||||||
|
auto submit = tr::lng_boost_channel_ask_button();
|
||||||
|
const auto button = box->addButton(rpl::duplicate(submit), [=] {
|
||||||
|
QGuiApplication::clipboard()->setText(data.link);
|
||||||
|
box->uiShow()->showToast(tr::lng_username_copied(tr::now));
|
||||||
|
});
|
||||||
|
rpl::combine(
|
||||||
|
std::move(submit),
|
||||||
|
box->widthValue()
|
||||||
|
) | rpl::start_with_next([=](const QString &, int width) {
|
||||||
|
const auto &padding = st::boostBox.buttonPadding;
|
||||||
|
button->resizeToWidth(width
|
||||||
|
- padding.left()
|
||||||
|
- padding.right());
|
||||||
|
button->moveToLeft(padding.left(), button->y());
|
||||||
|
}, button->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
void FillBoostLimit(
|
void FillBoostLimit(
|
||||||
rpl::producer<> showFinished,
|
rpl::producer<> showFinished,
|
||||||
rpl::producer<bool> you,
|
rpl::producer<bool> you,
|
||||||
not_null<VerticalLayout*> container,
|
not_null<VerticalLayout*> container,
|
||||||
BoostBoxData data,
|
BoostCounters data,
|
||||||
style::margins limitLinePadding) {
|
style::margins limitLinePadding) {
|
||||||
const auto full = !data.boost.nextLevelBoosts;
|
const auto full = !data.nextLevelBoosts;
|
||||||
|
|
||||||
if (data.boost.mine && data.boost.boosts > 0) {
|
if (data.mine && data.boosts > 0) {
|
||||||
--data.boost.boosts;
|
--data.boosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (full) {
|
if (full) {
|
||||||
data.boost.nextLevelBoosts = data.boost.boosts
|
data.nextLevelBoosts = data.boosts
|
||||||
+ (data.boost.mine ? 1 : 0);
|
+ (data.mine ? 1 : 0);
|
||||||
data.boost.thisLevelBoosts = 0;
|
data.thisLevelBoosts = 0;
|
||||||
if (data.boost.level > 0) {
|
if (data.level > 0) {
|
||||||
--data.boost.level;
|
--data.level;
|
||||||
}
|
}
|
||||||
} else if (data.boost.mine
|
} else if (data.mine
|
||||||
&& data.boost.level > 0
|
&& data.level > 0
|
||||||
&& data.boost.boosts < data.boost.thisLevelBoosts) {
|
&& data.boosts < data.thisLevelBoosts) {
|
||||||
--data.boost.level;
|
--data.level;
|
||||||
data.boost.nextLevelBoosts = data.boost.thisLevelBoosts;
|
data.nextLevelBoosts = data.thisLevelBoosts;
|
||||||
data.boost.thisLevelBoosts = 0;
|
data.thisLevelBoosts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto addSkip = [&](int skip) {
|
const auto addSkip = [&](int skip) {
|
||||||
|
@ -205,18 +364,18 @@ void FillBoostLimit(
|
||||||
|
|
||||||
const auto levelWidth = [&](int add) {
|
const auto levelWidth = [&](int add) {
|
||||||
return st::normalFont->width(
|
return st::normalFont->width(
|
||||||
tr::lng_boost_level(tr::now, lt_count, data.boost.level + add));
|
tr::lng_boost_level(tr::now, lt_count, data.level + add));
|
||||||
};
|
};
|
||||||
const auto paddings = 2 * st::premiumLineTextSkip;
|
const auto paddings = 2 * st::premiumLineTextSkip;
|
||||||
const auto labelLeftWidth = paddings + levelWidth(0);
|
const auto labelLeftWidth = paddings + levelWidth(0);
|
||||||
const auto labelRightWidth = paddings + levelWidth(1);
|
const auto labelRightWidth = paddings + levelWidth(1);
|
||||||
const auto ratio = [=](int boosts) {
|
const auto ratio = [=](int boosts) {
|
||||||
const auto min = std::min(
|
const auto min = std::min(
|
||||||
data.boost.boosts,
|
data.boosts,
|
||||||
data.boost.thisLevelBoosts);
|
data.thisLevelBoosts);
|
||||||
const auto max = std::max({
|
const auto max = std::max({
|
||||||
data.boost.boosts,
|
data.boosts,
|
||||||
data.boost.nextLevelBoosts,
|
data.nextLevelBoosts,
|
||||||
1,
|
1,
|
||||||
});
|
});
|
||||||
Assert(boosts >= min && boosts <= max);
|
Assert(boosts >= min && boosts <= max);
|
||||||
|
@ -239,12 +398,12 @@ void FillBoostLimit(
|
||||||
return (first + (index - 1) * other) / available;
|
return (first + (index - 1) * other) / available;
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto min = std::min(data.boost.boosts, data.boost.thisLevelBoosts);
|
const auto min = std::min(data.boosts, data.thisLevelBoosts);
|
||||||
const auto now = data.boost.boosts;
|
const auto now = data.boosts;
|
||||||
const auto max = (data.boost.nextLevelBoosts > min)
|
const auto max = (data.nextLevelBoosts > min)
|
||||||
? (data.boost.nextLevelBoosts)
|
? (data.nextLevelBoosts)
|
||||||
: (data.boost.boosts > 0)
|
: (data.boosts > 0)
|
||||||
? data.boost.boosts
|
? data.boosts
|
||||||
: 1;
|
: 1;
|
||||||
auto bubbleRowState = (
|
auto bubbleRowState = (
|
||||||
std::move(you)
|
std::move(you)
|
||||||
|
@ -280,8 +439,8 @@ void FillBoostLimit(
|
||||||
container,
|
container,
|
||||||
st::boostLimits,
|
st::boostLimits,
|
||||||
Premium::LimitRowLabels{
|
Premium::LimitRowLabels{
|
||||||
.leftLabel = level(data.boost.level),
|
.leftLabel = level(data.level),
|
||||||
.rightLabel = level(data.boost.level + 1),
|
.rightLabel = level(data.level + 1),
|
||||||
.dynamic = true,
|
.dynamic = true,
|
||||||
},
|
},
|
||||||
std::move(ratioValue),
|
std::move(ratioValue),
|
||||||
|
|
|
@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
void StartFireworks(not_null<QWidget*> parent);
|
void StartFireworks(not_null<QWidget*> parent);
|
||||||
|
|
||||||
|
class Show;
|
||||||
|
class RpWidget;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
|
|
||||||
|
@ -32,11 +36,30 @@ void BoostBox(
|
||||||
BoostBoxData data,
|
BoostBoxData data,
|
||||||
Fn<void(Fn<void(bool)>)> boost);
|
Fn<void(Fn<void(bool)>)> boost);
|
||||||
|
|
||||||
|
struct AskBoostBoxData {
|
||||||
|
QString link;
|
||||||
|
BoostCounters boost;
|
||||||
|
int requiredLevel = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AskBoostBox(
|
||||||
|
not_null<GenericBox*> box,
|
||||||
|
AskBoostBoxData data,
|
||||||
|
Fn<void()> openStatistics,
|
||||||
|
Fn<void()> startGiveaway);
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<RpWidget> MakeLinkLabel(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<QString> text,
|
||||||
|
rpl::producer<QString> link,
|
||||||
|
std::shared_ptr<Show> show,
|
||||||
|
object_ptr<RpWidget> right);
|
||||||
|
|
||||||
void FillBoostLimit(
|
void FillBoostLimit(
|
||||||
rpl::producer<> showFinished,
|
rpl::producer<> showFinished,
|
||||||
rpl::producer<bool> you,
|
rpl::producer<bool> you,
|
||||||
not_null<VerticalLayout*> container,
|
not_null<VerticalLayout*> container,
|
||||||
BoostBoxData data,
|
BoostCounters data,
|
||||||
style::margins limitLinePadding);
|
style::margins limitLinePadding);
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "ui/controls/send_as_button.h"
|
#include "ui/controls/send_as_button.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -168,39 +167,6 @@ rpl::producer<not_null<PeerData*>> ListController::clicked() const {
|
||||||
return _clicked.events();
|
return _clicked.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowPremiumPromoToast(not_null<Window::SessionController*> controller) {
|
|
||||||
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
|
|
||||||
const auto toast = std::make_shared<WeakToast>();
|
|
||||||
|
|
||||||
auto link = Ui::Text::Link(
|
|
||||||
tr::lng_send_as_premium_required_link(tr::now));
|
|
||||||
link.entities.push_back(
|
|
||||||
EntityInText(EntityType::Semibold, 0, link.text.size()));
|
|
||||||
(*toast) = controller->showToast({
|
|
||||||
.text = tr::lng_send_as_premium_required(
|
|
||||||
tr::now,
|
|
||||||
lt_link,
|
|
||||||
link,
|
|
||||||
Ui::Text::WithEntities),
|
|
||||||
.st = &st::defaultMultilineToast,
|
|
||||||
.duration = Ui::Toast::kDefaultDuration * 2,
|
|
||||||
.multiline = true,
|
|
||||||
.filter = crl::guard(&controller->session(), [=](
|
|
||||||
const ClickHandlerPtr &,
|
|
||||||
Qt::MouseButton button) {
|
|
||||||
if (button == Qt::LeftButton) {
|
|
||||||
if (const auto strong = toast->get()) {
|
|
||||||
strong->hideAnimated();
|
|
||||||
(*toast) = nullptr;
|
|
||||||
Settings::ShowPremium(controller, "send_as");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ChooseSendAsBox(
|
void ChooseSendAsBox(
|
||||||
|
@ -272,7 +238,17 @@ void SetupSendAsButton(
|
||||||
if (i != end(list)
|
if (i != end(list)
|
||||||
&& i->premiumRequired
|
&& i->premiumRequired
|
||||||
&& !sendAs->session().premium()) {
|
&& !sendAs->session().premium()) {
|
||||||
ShowPremiumPromoToast(window);
|
Settings::ShowPremiumPromoToast(
|
||||||
|
window->uiShow(),
|
||||||
|
tr::lng_send_as_premium_required(
|
||||||
|
tr::now,
|
||||||
|
lt_link,
|
||||||
|
Ui::Text::Link(
|
||||||
|
Ui::Text::Bold(
|
||||||
|
tr::lng_send_as_premium_required_link(
|
||||||
|
tr::now))),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
u"send_as"_q);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session->sendAsPeers().saveChosen(peer, sendAs);
|
session->sendAsPeers().saveChosen(peer, sendAs);
|
||||||
|
|
|
@ -288,6 +288,14 @@ giveawayGiftCodeLinkHeight: 42px;
|
||||||
giveawayGiftCodeLinkCopyWidth: 40px;
|
giveawayGiftCodeLinkCopyWidth: 40px;
|
||||||
giveawayGiftCodeLinkMargin: margins(24px, 8px, 24px, 12px);
|
giveawayGiftCodeLinkMargin: margins(24px, 8px, 24px, 12px);
|
||||||
|
|
||||||
|
boostLinkStatsButton: IconButton(defaultIconButton) {
|
||||||
|
width: giveawayGiftCodeLinkCopyWidth;
|
||||||
|
height: giveawayGiftCodeLinkHeight;
|
||||||
|
icon: icon{{ "menu/stats", menuIconColor }};
|
||||||
|
iconOver: icon{{ "menu/stats", menuIconColor }};
|
||||||
|
ripple: emptyRippleAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
giveawayGiftCodeTable: Table(defaultTable) {
|
giveawayGiftCodeTable: Table(defaultTable) {
|
||||||
labelMinWidth: 91px;
|
labelMinWidth: 91px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1591,5 +1591,36 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) {
|
||||||
QByteArray());
|
QByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::ChatTheme> DefaultChatThemeOn(rpl::lifetime &lifetime) {
|
||||||
|
auto result = std::make_unique<Ui::ChatTheme>();
|
||||||
|
|
||||||
|
const auto push = [=, raw = result.get()] {
|
||||||
|
const auto background = Background();
|
||||||
|
const auto &paper = background->paper();
|
||||||
|
raw->setBackground({
|
||||||
|
.prepared = background->prepared(),
|
||||||
|
.preparedForTiled = background->preparedForTiled(),
|
||||||
|
.gradientForFill = background->gradientForFill(),
|
||||||
|
.colorForFill = background->colorForFill(),
|
||||||
|
.colors = paper.backgroundColors(),
|
||||||
|
.patternOpacity = paper.patternOpacity(),
|
||||||
|
.gradientRotation = paper.gradientRotation(),
|
||||||
|
.isPattern = paper.isPattern(),
|
||||||
|
.tile = background->tile(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
push();
|
||||||
|
Background()->updates(
|
||||||
|
) | rpl::start_with_next([=](const BackgroundUpdate &update) {
|
||||||
|
if (update.type == BackgroundUpdate::Type::New
|
||||||
|
|| update.type == BackgroundUpdate::Type::Changed) {
|
||||||
|
push();
|
||||||
|
}
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Theme
|
} // namespace Theme
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Controller;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
struct ChatThemeBackground;
|
struct ChatThemeBackground;
|
||||||
|
class ChatTheme;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Webview {
|
namespace Webview {
|
||||||
|
@ -309,5 +310,8 @@ bool ReadPaletteValues(
|
||||||
|
|
||||||
[[nodiscard]] Webview::ThemeParams WebViewParams();
|
[[nodiscard]] Webview::ThemeParams WebViewParams();
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<Ui::ChatTheme> DefaultChatThemeOn(
|
||||||
|
rpl::lifetime &lifetime);
|
||||||
|
|
||||||
} // namespace Theme
|
} // namespace Theme
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
Loading…
Add table
Reference in a new issue