mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +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_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_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_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(
|
||||
not_null<QWidget*> parent,
|
||||
rpl::producer<QString> text,
|
||||
rpl::producer<QString> link,
|
||||
std::shared_ptr<Ui::Show> show) {
|
||||
auto result = object_ptr<Ui::AbstractButton>(parent);
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkCopyIcon(
|
||||
not_null<QWidget*> parent) {
|
||||
auto result = object_ptr<Ui::RpWidget>(parent);
|
||||
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([=] {
|
||||
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 left = outer - width + (width - icon.width()) / 2;
|
||||
const auto left = (raw->width() - icon.width()) / 2;
|
||||
const auto top = (raw->height() - icon.height()) / 2;
|
||||
icon.paint(p, left, top, raw->width());
|
||||
}, raw->lifetime());
|
||||
|
||||
state->label.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
raw->resize(
|
||||
st::giveawayGiftCodeLinkCopyWidth,
|
||||
st::giveawayGiftCodeLinkHeight);
|
||||
|
||||
raw->resize(raw->width(), st::giveawayGiftCodeLinkHeight);
|
||||
raw->setClickedCallback([=] {
|
||||
QGuiApplication::clipboard()->setText(state->link.current());
|
||||
show->showToast(tr::lng_username_copied(tr::now));
|
||||
});
|
||||
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -502,11 +458,12 @@ void GiftCodeBox(
|
|||
|
||||
const auto link = MakeGiftCodeLink(&controller->session(), slug);
|
||||
box->addRow(
|
||||
MakeLinkLabel(
|
||||
Ui::MakeLinkLabel(
|
||||
box,
|
||||
rpl::single(link.text),
|
||||
rpl::single(link.link),
|
||||
box->uiShow()),
|
||||
box->uiShow(),
|
||||
MakeLinkCopyIcon(box)),
|
||||
st::giveawayGiftCodeLinkMargin);
|
||||
|
||||
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 "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.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 "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.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_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/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_settings.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
@ -23,32 +47,97 @@ namespace {
|
|||
|
||||
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:
|
||||
ColorSample(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<Ui::ChatStyle> st,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<uint8> colorIndex,
|
||||
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;
|
||||
|
||||
void setSelected(bool selected);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
std::shared_ptr<Ui::ChatStyle> _st;
|
||||
std::shared_ptr<Ui::ChatStyle> _style;
|
||||
Ui::Text::String _name;
|
||||
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(
|
||||
not_null<QWidget*> parent,
|
||||
std::shared_ptr<Ui::ChatStyle> st,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<uint8> colorIndex,
|
||||
const QString &name)
|
||||
: RpWidget(parent)
|
||||
, _st(st)
|
||||
: AbstractButton(parent)
|
||||
, _style(style)
|
||||
, _name(st::semiboldTextStyle, name) {
|
||||
std::move(
|
||||
colorIndex
|
||||
|
@ -58,11 +147,35 @@ ColorSample::ColorSample(
|
|||
}, 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) {
|
||||
auto p = Painter(this);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto colors = _st->coloredValues(false, _index);
|
||||
if (!colors.outlines[1].alpha()) {
|
||||
const auto colors = _style->coloredValues(false, _index);
|
||||
if (!_simple && !colors.outlines[1].alpha()) {
|
||||
const auto radius = height() / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(colors.bg);
|
||||
|
@ -81,32 +194,52 @@ void ColorSample::paintEvent(QPaintEvent *e) {
|
|||
1,
|
||||
style::al_top);
|
||||
} else {
|
||||
const auto size = width();
|
||||
const auto size = float64(width());
|
||||
const auto half = size / 2.;
|
||||
const auto full = QRectF(-half, -half, size, size);
|
||||
p.translate(size / 2., size / 2.);
|
||||
p.rotate(-45.);
|
||||
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.drawEllipse(-half, -half, size, size);
|
||||
p.setClipRect(-size, 0, 3 * size, size);
|
||||
p.setBrush(colors.outlines[1]);
|
||||
p.drawEllipse(-half, -half, size, size);
|
||||
p.drawEllipse(full);
|
||||
p.setClipping(false);
|
||||
if (colors.outlines[2].alpha()) {
|
||||
const auto center = st::settingsColorSampleCenter;
|
||||
const auto radius = st::settingsColorSampleCenterRadius;
|
||||
p.setClipping(false);
|
||||
const auto multiplier = size / st::settingsColorSampleSize;
|
||||
const auto center = st::settingsColorSampleCenter * multiplier;
|
||||
const auto radius = st::settingsColorSampleCenterRadius
|
||||
* multiplier;
|
||||
p.setBrush(colors.outlines[2]);
|
||||
p.drawRoundedRect(
|
||||
QRectF(-center / 2., -center / 2., center, center),
|
||||
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 {
|
||||
if (_st->colorPatternIndex(_index)) {
|
||||
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
|
||||
return st::settingsColorSampleSize;
|
||||
}
|
||||
const auto padding = st::settingsColorSamplePadding;
|
||||
|
@ -115,14 +248,463 @@ int ColorSample::naturalWidth() const {
|
|||
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
|
||||
|
||||
void EditPeerColorBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::ChatStyle> st) {
|
||||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
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(
|
||||
|
@ -144,11 +726,16 @@ void AddPeerColorButton(
|
|||
return peer->colorIndex();
|
||||
});
|
||||
const auto name = peer->shortName();
|
||||
const auto st = std::make_shared<Ui::ChatStyle>(
|
||||
|
||||
const auto style = std::make_shared<Ui::ChatStyle>(
|
||||
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>(
|
||||
button.get(),
|
||||
st,
|
||||
style,
|
||||
rpl::duplicate(colorIndexValue),
|
||||
name);
|
||||
sample->show();
|
||||
|
@ -167,7 +754,7 @@ void AddPeerColorButton(
|
|||
- (st::settingsColorButton.padding.right() - sampleSize)
|
||||
- st::settingsButton.style.font->width(button)
|
||||
- st::settingsButtonRightSkip;
|
||||
if (st->colorPatternIndex(colorIndex)) {
|
||||
if (style->colorPatternIndex(colorIndex)) {
|
||||
sample->resize(sampleSize, sampleSize);
|
||||
} else {
|
||||
const auto padding = st::settingsColorSamplePadding;
|
||||
|
@ -188,7 +775,7 @@ void AddPeerColorButton(
|
|||
const auto right = st::settingsColorButton.padding.right()
|
||||
- st::settingsColorSampleSkip
|
||||
- st::settingsColorSampleSize
|
||||
- (st->colorPatternIndex(colorIndex)
|
||||
- (style->colorPatternIndex(colorIndex)
|
||||
? 0
|
||||
: st::settingsColorSamplePadding.right());
|
||||
sample->move(
|
||||
|
@ -197,4 +784,8 @@ void AddPeerColorButton(
|
|||
}, sample->lifetime());
|
||||
|
||||
sample->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
show->show(Box(EditPeerColorBox, show, peer, style, theme));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ class Show;
|
|||
namespace Ui {
|
||||
class GenericBox;
|
||||
class ChatStyle;
|
||||
class ChatTheme;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
|
@ -21,7 +22,8 @@ void EditPeerColorBox(
|
|||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
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(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
|
|
|
@ -838,6 +838,8 @@ void HistoryMessageReply::paint(
|
|||
? resolvedMessage->displayFrom()
|
||||
: resolvedStory
|
||||
? resolvedStory->peer().get()
|
||||
: _externalSender
|
||||
? _externalSender
|
||||
: nullptr;
|
||||
const auto backgroundEmojiId = colorPeer
|
||||
? 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(
|
||||
TextWithEntities text,
|
||||
const std::vector<MessageLinkRange> &links) {
|
||||
|
@ -124,7 +91,6 @@ private:
|
|||
return text;
|
||||
}
|
||||
|
||||
|
||||
class PreviewWrap final : public Ui::RpWidget {
|
||||
public:
|
||||
PreviewWrap(
|
||||
|
@ -195,7 +161,7 @@ PreviewWrap::PreviewWrap(
|
|||
: RpWidget(box)
|
||||
, _box(box)
|
||||
, _history(history)
|
||||
, _theme(DefaultThemeOn(lifetime()))
|
||||
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||
, _style(std::make_unique<Ui::ChatStyle>(
|
||||
history->session().colorIndicesValue()))
|
||||
, _delegate(std::make_unique<PreviewDelegate>(
|
||||
|
@ -376,7 +342,6 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
|
||||
auto p = Painter(this);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
|
||||
auto context = _theme->preparePaintContext(
|
||||
_style.get(),
|
||||
|
|
|
@ -210,16 +210,14 @@ void InnerWidget::fill() {
|
|||
fakeShowed->events(),
|
||||
rpl::single(status.overview.isBoosted),
|
||||
dividerContent.data(),
|
||||
Ui::BoostBoxData{
|
||||
.boost = Ui::BoostCounters{
|
||||
.level = status.overview.level,
|
||||
.boosts = status.overview.boostCount,
|
||||
.thisLevelBoosts
|
||||
= status.overview.currentLevelBoostCount,
|
||||
.nextLevelBoosts
|
||||
= status.overview.nextLevelBoostCount,
|
||||
.mine = status.overview.isBoosted,
|
||||
}
|
||||
Ui::BoostCounters{
|
||||
.level = status.overview.level,
|
||||
.boosts = status.overview.boostCount,
|
||||
.thisLevelBoosts
|
||||
= status.overview.currentLevelBoostCount,
|
||||
.nextLevelBoosts
|
||||
= status.overview.nextLevelBoostCount,
|
||||
.mine = status.overview.isBoosted,
|
||||
},
|
||||
st::statisticsLimitsLinePadding);
|
||||
inner->add(object_ptr<Ui::DividerLabel>(
|
||||
|
|
|
@ -567,10 +567,13 @@ filterLinkChatsList: PeerList(peerListBox) {
|
|||
}
|
||||
|
||||
settingsColorSampleSize: 20px;
|
||||
settingsColorSampleCenter: 8px;
|
||||
settingsColorSampleCenter: 6px;
|
||||
settingsColorSampleCenterRadius: 2px;
|
||||
settingsColorSamplePadding: margins(8px, 2px, 8px, 2px);
|
||||
settingsColorSampleSkip: 6px;
|
||||
settingsColorButton: SettingsButton(settingsButton) {
|
||||
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/text_utilities.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/checkbox.h" // Ui::RadiobuttonGroup.
|
||||
#include "ui/widgets/gradient_round_button.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
|
@ -1466,6 +1467,36 @@ QString LookupPremiumRef(PremiumPreview section) {
|
|||
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(
|
||||
SubscribeButtonArgs &&args) {
|
||||
Expects(args.show || args.controller);
|
||||
|
|
|
@ -51,6 +51,11 @@ void StartPremiumPayment(
|
|||
|
||||
[[nodiscard]] QString LookupPremiumRef(PremiumPreview section);
|
||||
|
||||
void ShowPremiumPromoToast(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
TextWithEntities textWithLink,
|
||||
const QString &ref);
|
||||
|
||||
struct SubscribeButtonArgs final {
|
||||
Window::SessionController *controller = nullptr;
|
||||
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_premium.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void StartFireworks(not_null<QWidget*> parent) {
|
||||
|
@ -57,7 +59,7 @@ void BoostBox(
|
|||
BoxShowFinishes(box),
|
||||
state->you.value(),
|
||||
box->verticalLayout(),
|
||||
data,
|
||||
data.boost,
|
||||
st::boxRowPadding);
|
||||
|
||||
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
||||
|
@ -170,31 +172,188 @@ void BoostBox(
|
|||
}, 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(
|
||||
rpl::producer<> showFinished,
|
||||
rpl::producer<bool> you,
|
||||
not_null<VerticalLayout*> container,
|
||||
BoostBoxData data,
|
||||
BoostCounters data,
|
||||
style::margins limitLinePadding) {
|
||||
const auto full = !data.boost.nextLevelBoosts;
|
||||
const auto full = !data.nextLevelBoosts;
|
||||
|
||||
if (data.boost.mine && data.boost.boosts > 0) {
|
||||
--data.boost.boosts;
|
||||
if (data.mine && data.boosts > 0) {
|
||||
--data.boosts;
|
||||
}
|
||||
|
||||
if (full) {
|
||||
data.boost.nextLevelBoosts = data.boost.boosts
|
||||
+ (data.boost.mine ? 1 : 0);
|
||||
data.boost.thisLevelBoosts = 0;
|
||||
if (data.boost.level > 0) {
|
||||
--data.boost.level;
|
||||
data.nextLevelBoosts = data.boosts
|
||||
+ (data.mine ? 1 : 0);
|
||||
data.thisLevelBoosts = 0;
|
||||
if (data.level > 0) {
|
||||
--data.level;
|
||||
}
|
||||
} else if (data.boost.mine
|
||||
&& data.boost.level > 0
|
||||
&& data.boost.boosts < data.boost.thisLevelBoosts) {
|
||||
--data.boost.level;
|
||||
data.boost.nextLevelBoosts = data.boost.thisLevelBoosts;
|
||||
data.boost.thisLevelBoosts = 0;
|
||||
} else if (data.mine
|
||||
&& data.level > 0
|
||||
&& data.boosts < data.thisLevelBoosts) {
|
||||
--data.level;
|
||||
data.nextLevelBoosts = data.thisLevelBoosts;
|
||||
data.thisLevelBoosts = 0;
|
||||
}
|
||||
|
||||
const auto addSkip = [&](int skip) {
|
||||
|
@ -205,18 +364,18 @@ void FillBoostLimit(
|
|||
|
||||
const auto levelWidth = [&](int add) {
|
||||
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 labelLeftWidth = paddings + levelWidth(0);
|
||||
const auto labelRightWidth = paddings + levelWidth(1);
|
||||
const auto ratio = [=](int boosts) {
|
||||
const auto min = std::min(
|
||||
data.boost.boosts,
|
||||
data.boost.thisLevelBoosts);
|
||||
data.boosts,
|
||||
data.thisLevelBoosts);
|
||||
const auto max = std::max({
|
||||
data.boost.boosts,
|
||||
data.boost.nextLevelBoosts,
|
||||
data.boosts,
|
||||
data.nextLevelBoosts,
|
||||
1,
|
||||
});
|
||||
Assert(boosts >= min && boosts <= max);
|
||||
|
@ -239,12 +398,12 @@ void FillBoostLimit(
|
|||
return (first + (index - 1) * other) / available;
|
||||
};
|
||||
|
||||
const auto min = std::min(data.boost.boosts, data.boost.thisLevelBoosts);
|
||||
const auto now = data.boost.boosts;
|
||||
const auto max = (data.boost.nextLevelBoosts > min)
|
||||
? (data.boost.nextLevelBoosts)
|
||||
: (data.boost.boosts > 0)
|
||||
? data.boost.boosts
|
||||
const auto min = std::min(data.boosts, data.thisLevelBoosts);
|
||||
const auto now = data.boosts;
|
||||
const auto max = (data.nextLevelBoosts > min)
|
||||
? (data.nextLevelBoosts)
|
||||
: (data.boosts > 0)
|
||||
? data.boosts
|
||||
: 1;
|
||||
auto bubbleRowState = (
|
||||
std::move(you)
|
||||
|
@ -280,8 +439,8 @@ void FillBoostLimit(
|
|||
container,
|
||||
st::boostLimits,
|
||||
Premium::LimitRowLabels{
|
||||
.leftLabel = level(data.boost.level),
|
||||
.rightLabel = level(data.boost.level + 1),
|
||||
.leftLabel = level(data.level),
|
||||
.rightLabel = level(data.level + 1),
|
||||
.dynamic = true,
|
||||
},
|
||||
std::move(ratioValue),
|
||||
|
|
|
@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void StartFireworks(not_null<QWidget*> parent);
|
||||
|
||||
class Show;
|
||||
class RpWidget;
|
||||
class GenericBox;
|
||||
class VerticalLayout;
|
||||
|
||||
|
@ -32,11 +36,30 @@ void BoostBox(
|
|||
BoostBoxData data,
|
||||
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(
|
||||
rpl::producer<> showFinished,
|
||||
rpl::producer<bool> you,
|
||||
not_null<VerticalLayout*> container,
|
||||
BoostBoxData data,
|
||||
BoostCounters data,
|
||||
style::margins limitLinePadding);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "ui/controls/send_as_button.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -168,39 +167,6 @@ rpl::producer<not_null<PeerData*>> ListController::clicked() const {
|
|||
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
|
||||
|
||||
void ChooseSendAsBox(
|
||||
|
@ -272,7 +238,17 @@ void SetupSendAsButton(
|
|||
if (i != end(list)
|
||||
&& i->premiumRequired
|
||||
&& !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;
|
||||
}
|
||||
session->sendAsPeers().saveChosen(peer, sendAs);
|
||||
|
|
|
@ -288,6 +288,14 @@ giveawayGiftCodeLinkHeight: 42px;
|
|||
giveawayGiftCodeLinkCopyWidth: 40px;
|
||||
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) {
|
||||
labelMinWidth: 91px;
|
||||
}
|
||||
|
|
|
@ -1591,5 +1591,36 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) {
|
|||
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 Window
|
||||
|
|
|
@ -28,6 +28,7 @@ class Controller;
|
|||
|
||||
namespace Ui {
|
||||
struct ChatThemeBackground;
|
||||
class ChatTheme;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Webview {
|
||||
|
@ -309,5 +310,8 @@ bool ReadPaletteValues(
|
|||
|
||||
[[nodiscard]] Webview::ThemeParams WebViewParams();
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Ui::ChatTheme> DefaultChatThemeOn(
|
||||
rpl::lifetime &lifetime);
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
||||
|
|
Loading…
Add table
Reference in a new issue