mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement improved allowed reactions editing.
This commit is contained in:
parent
021e275336
commit
5e81c65ea6
9 changed files with 291 additions and 163 deletions
|
@ -1195,13 +1195,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_manage_peer_permissions" = "Permissions";
|
"lng_manage_peer_permissions" = "Permissions";
|
||||||
"lng_manage_peer_invite_links" = "Invite links";
|
"lng_manage_peer_invite_links" = "Invite links";
|
||||||
"lng_manage_peer_reactions" = "Reactions";
|
"lng_manage_peer_reactions" = "Reactions";
|
||||||
|
"lng_manage_peer_reactions_on" = "All";
|
||||||
"lng_manage_peer_reactions_off" = "Off";
|
"lng_manage_peer_reactions_off" = "Off";
|
||||||
"lng_manage_peer_requests" = "Member Requests";
|
"lng_manage_peer_requests" = "Member Requests";
|
||||||
"lng_manage_peer_requests_channel" = "Subscriber Requests";
|
"lng_manage_peer_requests_channel" = "Subscriber Requests";
|
||||||
|
|
||||||
"lng_manage_peer_reactions_enable" = "Enable Reactions";
|
"lng_manage_peer_reactions_all" = "All reactions";
|
||||||
"lng_manage_peer_reactions_about" = "Allow members to react to group messages.";
|
"lng_manage_peer_reactions_all_about" = "Members of the group can use any emoji as reactions to messages.";
|
||||||
"lng_manage_peer_reactions_about_channel" = "Allow subscribers to react to channel posts.";
|
"lng_manage_peer_reactions_all_about_channel" = "Subscribers of this channel can use any emoji as reactions to messages.";
|
||||||
|
"lng_manage_peer_reactions_some" = "Some reactions";
|
||||||
|
"lng_manage_peer_reactions_some_about" = "You can select emoji that will allow members of your group to react to messages.";
|
||||||
|
"lng_manage_peer_reactions_some_about_channel" = "You can select emoji that will allow subscribers of this channel to react to messages.";
|
||||||
|
"lng_manage_peer_reactions_none" = "No reactions";
|
||||||
|
"lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages.";
|
||||||
|
"lng_manage_peer_reactions_none_about_channel" = "Subscribers of this channel can't add any reactions to messages.";
|
||||||
|
"lng_manage_peer_reactions_some_title" = "Only allow these reactions";
|
||||||
"lng_manage_peer_reactions_available" = "Available reactions";
|
"lng_manage_peer_reactions_available" = "Available reactions";
|
||||||
|
|
||||||
"lng_manage_peer_group_type" = "Group type";
|
"lng_manage_peer_group_type" = "Group type";
|
||||||
|
|
|
@ -988,22 +988,31 @@ void Controller::fillManageSection() {
|
||||||
|
|
||||||
if (canEditReactions()) {
|
if (canEditReactions()) {
|
||||||
const auto session = &_peer->session();
|
const auto session = &_peer->session();
|
||||||
auto reactionsCount = Info::Profile::MigratedOrMeValue(
|
auto allowedReactions = Info::Profile::MigratedOrMeValue(
|
||||||
_peer
|
_peer
|
||||||
) | rpl::map(
|
) | rpl::map([=](not_null<PeerData*> peer) {
|
||||||
Info::Profile::AllowedReactionsCountValue
|
return peer->session().changes().peerFlagsValue(
|
||||||
) | rpl::flatten_latest();
|
peer,
|
||||||
auto fullCount = Info::Profile::FullReactionsCountValue(session);
|
Data::PeerUpdate::Flag::Reactions
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return Data::PeerAllowedReactions(peer);
|
||||||
|
});
|
||||||
|
}) | rpl::flatten_latest();
|
||||||
auto label = rpl::combine(
|
auto label = rpl::combine(
|
||||||
std::move(reactionsCount),
|
std::move(allowedReactions),
|
||||||
std::move(fullCount)
|
Info::Profile::FullReactionsCountValue(session)
|
||||||
) | rpl::map([=](int allowed, int total) {
|
) | rpl::map([=](const Data::AllowedReactions &allowed, int total) {
|
||||||
return allowed
|
const auto some = int(allowed.some.size());
|
||||||
? QString::number(allowed) + " / " + QString::number(total)
|
return (allowed.type != Data::AllowedReactionsType::Some)
|
||||||
|
? tr::lng_manage_peer_reactions_on(tr::now)
|
||||||
|
: some
|
||||||
|
? (QString::number(some)
|
||||||
|
+ " / "
|
||||||
|
+ QString::number(std::max(some, total)))
|
||||||
: tr::lng_manage_peer_reactions_off(tr::now);
|
: tr::lng_manage_peer_reactions_off(tr::now);
|
||||||
});
|
});
|
||||||
const auto done = [=](const std::vector<QString> &chosen, bool all) {
|
const auto done = [=](const Data::AllowedReactions &chosen) {
|
||||||
SaveAllowedReactions(_peer, chosen, all);
|
SaveAllowedReactions(_peer, chosen);
|
||||||
};
|
};
|
||||||
AddButtonWithCount(
|
AddButtonWithCount(
|
||||||
_controls.buttonsLayout,
|
_controls.buttonsLayout,
|
||||||
|
@ -1012,6 +1021,7 @@ void Controller::fillManageSection() {
|
||||||
[=] {
|
[=] {
|
||||||
_navigation->parentController()->show(Box(
|
_navigation->parentController()->show(Box(
|
||||||
EditAllowedReactionsBox,
|
EditAllowedReactionsBox,
|
||||||
|
_navigation,
|
||||||
!_peer->isBroadcast(),
|
!_peer->isBroadcast(),
|
||||||
session->data().reactions().list(
|
session->data().reactions().list(
|
||||||
Data::Reactions::Type::Active),
|
Data::Reactions::Type::Active),
|
||||||
|
|
|
@ -18,34 +18,57 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_settings.h"
|
#include "styles/style_settings.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
|
||||||
void EditAllowedReactionsBox(
|
void EditAllowedReactionsBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
bool isGroup,
|
bool isGroup,
|
||||||
const std::vector<Data::Reaction> &list,
|
const std::vector<Data::Reaction> &list,
|
||||||
const Data::AllowedReactions &allowed,
|
const Data::AllowedReactions &allowed,
|
||||||
Fn<void(const std::vector<QString> &, bool all)> callback) {
|
Fn<void(const Data::AllowedReactions &)> callback) {
|
||||||
|
using namespace Data;
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
const auto iconHeight = st::editPeerReactionsPreview;
|
const auto iconHeight = st::editPeerReactionsPreview;
|
||||||
box->setTitle(tr::lng_manage_peer_reactions());
|
box->setTitle(tr::lng_manage_peer_reactions());
|
||||||
|
|
||||||
|
enum class Option {
|
||||||
|
All,
|
||||||
|
Some,
|
||||||
|
None,
|
||||||
|
};
|
||||||
struct State {
|
struct State {
|
||||||
base::flat_map<QString, not_null<Ui::SettingsButton*>> toggles;
|
base::flat_map<ReactionId, not_null<Ui::SettingsButton*>> toggles;
|
||||||
rpl::variable<bool> anyToggled;
|
rpl::variable<Option> option;
|
||||||
rpl::event_stream<bool> forceToggleAll;
|
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>(State{
|
const auto state = box->lifetime().make_state<State>(State{
|
||||||
.anyToggled = (allowed.type != Data::AllowedReactionsType::Some),
|
.option = (allowed.type != AllowedReactionsType::Some
|
||||||
|
? Option::All
|
||||||
|
: allowed.some.empty()
|
||||||
|
? Option::None
|
||||||
|
: Option::Some),
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto collect = [=] {
|
const auto collect = [=] {
|
||||||
auto result = std::vector<QString>();
|
auto result = AllowedReactions{
|
||||||
result.reserve(state->toggles.size());
|
.type = (state->option.current() != Option::All
|
||||||
for (const auto &[emoji, button] : state->toggles) {
|
? AllowedReactionsType::Some
|
||||||
if (button->toggled()) {
|
: isGroup
|
||||||
result.push_back(emoji);
|
? AllowedReactionsType::All
|
||||||
|
: AllowedReactionsType::Default),
|
||||||
|
};
|
||||||
|
if (state->option.current() == Option::Some) {
|
||||||
|
result.some.reserve(state->toggles.size());
|
||||||
|
for (const auto &[id, button] : state->toggles) {
|
||||||
|
if (button->toggled()) {
|
||||||
|
result.some.push_back(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -53,52 +76,64 @@ void EditAllowedReactionsBox(
|
||||||
|
|
||||||
const auto container = box->verticalLayout();
|
const auto container = box->verticalLayout();
|
||||||
|
|
||||||
const auto enabled = Settings::AddButton(
|
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
|
||||||
container,
|
state->option.current());
|
||||||
tr::lng_manage_peer_reactions_enable(),
|
group->setChangedCallback([=](Option value) {
|
||||||
st::manageGroupButton.button);
|
state->option = value;
|
||||||
if (!list.empty()) {
|
});
|
||||||
AddReactionAnimatedIcon(
|
const auto addOption = [&](Option option, const QString &text) {
|
||||||
enabled,
|
container->add(
|
||||||
enabled->sizeValue(
|
object_ptr<Ui::Radioenum<Option>>(
|
||||||
) | rpl::map([=](const QSize &size) {
|
container,
|
||||||
return QPoint(
|
group,
|
||||||
st::manageGroupButton.iconPosition.x(),
|
option,
|
||||||
(size.height() - iconHeight) / 2);
|
text,
|
||||||
}),
|
st::settingsSendType),
|
||||||
iconHeight,
|
st::settingsSendTypePadding);
|
||||||
list.front(),
|
};
|
||||||
rpl::never<>(),
|
addOption(Option::All, tr::lng_manage_peer_reactions_all(tr::now));
|
||||||
rpl::never<>(),
|
addOption(Option::Some, tr::lng_manage_peer_reactions_some(tr::now));
|
||||||
&enabled->lifetime());
|
addOption(Option::None, tr::lng_manage_peer_reactions_none(tr::now));
|
||||||
}
|
|
||||||
enabled->toggleOn(state->anyToggled.value());
|
|
||||||
enabled->toggledChanges(
|
|
||||||
) | rpl::filter([=](bool value) {
|
|
||||||
return (value != state->anyToggled.current());
|
|
||||||
}) | rpl::start_to_stream(state->forceToggleAll, enabled->lifetime());
|
|
||||||
|
|
||||||
|
const auto about = [isGroup](Option option) {
|
||||||
|
switch (option) {
|
||||||
|
case Option::All: return isGroup
|
||||||
|
? tr::lng_manage_peer_reactions_all_about()
|
||||||
|
: tr::lng_manage_peer_reactions_all_about_channel();
|
||||||
|
case Option::Some: return isGroup
|
||||||
|
? tr::lng_manage_peer_reactions_some_about()
|
||||||
|
: tr::lng_manage_peer_reactions_some_about_channel();
|
||||||
|
case Option::None: return isGroup
|
||||||
|
? tr::lng_manage_peer_reactions_none_about()
|
||||||
|
: tr::lng_manage_peer_reactions_none_about_channel();
|
||||||
|
}
|
||||||
|
Unexpected("Option value in EditAllowedReactionsBox.");
|
||||||
|
};
|
||||||
Settings::AddSkip(container);
|
Settings::AddSkip(container);
|
||||||
Settings::AddDividerText(
|
Settings::AddDividerText(
|
||||||
container,
|
container,
|
||||||
(isGroup
|
state->option.value() | rpl::map(about) | rpl::flatten_latest());
|
||||||
? tr::lng_manage_peer_reactions_about
|
|
||||||
: tr::lng_manage_peer_reactions_about_channel)());
|
|
||||||
|
|
||||||
Settings::AddSkip(container);
|
const auto wrap = container->add(
|
||||||
Settings::AddSubsectionTitle(
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
container,
|
|
||||||
tr::lng_manage_peer_reactions_available());
|
|
||||||
|
|
||||||
const auto active = [&](const Data::Reaction &entry) {
|
|
||||||
return ranges::contains(allowed.some, entry.id);
|
|
||||||
};
|
|
||||||
const auto add = [&](const Data::Reaction &entry) {
|
|
||||||
if (entry.id.emoji().isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto button = Settings::AddButton(
|
|
||||||
container,
|
container,
|
||||||
|
object_ptr<Ui::VerticalLayout>(container)));
|
||||||
|
wrap->toggleOn(state->option.value() | rpl::map(_1 == Option::Some));
|
||||||
|
wrap->finishAnimating();
|
||||||
|
const auto reactions = wrap->entity();
|
||||||
|
|
||||||
|
Settings::AddSkip(reactions);
|
||||||
|
Settings::AddSubsectionTitle(
|
||||||
|
reactions,
|
||||||
|
tr::lng_manage_peer_reactions_some_title());
|
||||||
|
|
||||||
|
const auto active = [&](const ReactionId &id) {
|
||||||
|
return (allowed.type != AllowedReactionsType::Some)
|
||||||
|
|| ranges::contains(allowed.some, id);
|
||||||
|
};
|
||||||
|
const auto add = [&](const Reaction &entry) {
|
||||||
|
const auto button = Settings::AddButton(
|
||||||
|
reactions,
|
||||||
rpl::single(entry.title),
|
rpl::single(entry.title),
|
||||||
st::manageGroupButton.button);
|
st::manageGroupButton.button);
|
||||||
AddReactionAnimatedIcon(
|
AddReactionAnimatedIcon(
|
||||||
|
@ -117,29 +152,41 @@ void EditAllowedReactionsBox(
|
||||||
}) | rpl::to_empty,
|
}) | rpl::to_empty,
|
||||||
rpl::never<>(),
|
rpl::never<>(),
|
||||||
&button->lifetime());
|
&button->lifetime());
|
||||||
state->toggles.emplace(entry.id.emoji(), button);
|
state->toggles.emplace(entry.id, button);
|
||||||
button->toggleOn(rpl::single(
|
button->toggleOn(rpl::single(active(entry.id)));
|
||||||
active(entry)
|
|
||||||
) | rpl::then(
|
|
||||||
state->forceToggleAll.events()
|
|
||||||
));
|
|
||||||
button->toggledChanges(
|
|
||||||
) | rpl::start_with_next([=](bool toggled) {
|
|
||||||
if (toggled) {
|
|
||||||
state->anyToggled = true;
|
|
||||||
} else if (collect().empty()) {
|
|
||||||
state->anyToggled = false;
|
|
||||||
}
|
|
||||||
}, button->lifetime());
|
|
||||||
};
|
};
|
||||||
for (const auto &entry : list) {
|
for (const auto &entry : list) {
|
||||||
add(entry);
|
add(entry);
|
||||||
}
|
}
|
||||||
|
for (const auto &id : allowed.some) {
|
||||||
|
if (const auto customId = id.custom()) {
|
||||||
|
// Some possible forward compatibility.
|
||||||
|
const auto button = Settings::AddButton(
|
||||||
|
reactions,
|
||||||
|
rpl::single(u"Custom reaction"_q),
|
||||||
|
st::manageGroupButton.button);
|
||||||
|
AddReactionCustomIcon(
|
||||||
|
button,
|
||||||
|
button->sizeValue(
|
||||||
|
) | rpl::map([=](const QSize &size) {
|
||||||
|
return QPoint(
|
||||||
|
st::editPeerReactionsIconLeft,
|
||||||
|
(size.height() - iconHeight) / 2);
|
||||||
|
}),
|
||||||
|
iconHeight,
|
||||||
|
navigation->parentController(),
|
||||||
|
customId,
|
||||||
|
rpl::never<>(),
|
||||||
|
&button->lifetime());
|
||||||
|
state->toggles.emplace(id, button);
|
||||||
|
button->toggleOn(rpl::single(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
box->addButton(tr::lng_settings_save(), [=] {
|
box->addButton(tr::lng_settings_save(), [=] {
|
||||||
const auto ids = collect();
|
const auto result = collect();
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
callback(ids, false);
|
callback(result);
|
||||||
});
|
});
|
||||||
box->addButton(tr::lng_cancel(), [=] {
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
@ -148,16 +195,18 @@ void EditAllowedReactionsBox(
|
||||||
|
|
||||||
void SaveAllowedReactions(
|
void SaveAllowedReactions(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const std::vector<QString> &allowed,
|
const Data::AllowedReactions &allowed) {
|
||||||
bool all) {
|
auto ids = allowed.some | ranges::views::transform(
|
||||||
auto ids = allowed | ranges::views::transform([=](QString value) {
|
Data::ReactionToMTP
|
||||||
return MTP_reactionEmoji(MTP_string(value));
|
) | ranges::to<QVector<MTPReaction>>;
|
||||||
}) | ranges::to<QVector<MTPReaction>>;
|
|
||||||
|
|
||||||
const auto updated = all
|
using Type = Data::AllowedReactionsType;
|
||||||
? MTP_chatReactionsAll(MTP_flags(peer->isBroadcast()
|
const auto updated = (allowed.type != Type::Some)
|
||||||
|
? MTP_chatReactionsAll(MTP_flags((allowed.type == Type::Default)
|
||||||
? MTPDchatReactionsAll::Flag(0)
|
? MTPDchatReactionsAll::Flag(0)
|
||||||
: MTPDchatReactionsAll::Flag::f_allow_custom))
|
: MTPDchatReactionsAll::Flag::f_allow_custom))
|
||||||
|
: allowed.some.empty()
|
||||||
|
? MTP_chatReactionsNone()
|
||||||
: MTP_chatReactionsSome(MTP_vector<MTPReaction>(ids));
|
: MTP_chatReactionsSome(MTP_vector<MTPReaction>(ids));
|
||||||
peer->session().api().request(MTPmessages_SetChatAvailableReactions(
|
peer->session().api().request(MTPmessages_SetChatAvailableReactions(
|
||||||
peer->input,
|
peer->input,
|
||||||
|
|
|
@ -18,14 +18,18 @@ namespace Ui {
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class SessionNavigation;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
void EditAllowedReactionsBox(
|
void EditAllowedReactionsBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
bool isGroup,
|
bool isGroup,
|
||||||
const std::vector<Data::Reaction> &list,
|
const std::vector<Data::Reaction> &list,
|
||||||
const Data::AllowedReactions &allowed,
|
const Data::AllowedReactions &allowed,
|
||||||
Fn<void(const std::vector<QString> &, bool all)> callback);
|
Fn<void(const Data::AllowedReactions &)> callback);
|
||||||
|
|
||||||
void SaveAllowedReactions(
|
void SaveAllowedReactions(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const std::vector<QString> &allowed,
|
const Data::AllowedReactions &allowed);
|
||||||
bool all);
|
|
||||||
|
|
|
@ -251,6 +251,73 @@ void AddMessage(
|
||||||
}, widget->lifetime());
|
}, widget->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Ui::RpWidget*> AddReactionIconWrap(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
rpl::producer<QPoint> iconPositionValue,
|
||||||
|
int iconSize,
|
||||||
|
Fn<void(not_null<QWidget*>, QPainter&)> paintCallback,
|
||||||
|
rpl::producer<> &&destroys,
|
||||||
|
not_null<rpl::lifetime*> stateLifetime) {
|
||||||
|
struct State {
|
||||||
|
base::unique_qptr<Ui::RpWidget> widget;
|
||||||
|
Ui::Animations::Simple finalAnimation;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto state = stateLifetime->make_state<State>();
|
||||||
|
state->widget = base::make_unique_q<Ui::RpWidget>(parent);
|
||||||
|
|
||||||
|
const auto widget = state->widget.get();
|
||||||
|
widget->resize(iconSize, iconSize);
|
||||||
|
widget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
iconPositionValue
|
||||||
|
) | rpl::start_with_next([=](const QPoint &point) {
|
||||||
|
widget->moveToLeft(point.x(), point.y());
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
const auto update = crl::guard(widget, [=] { widget->update(); });
|
||||||
|
|
||||||
|
widget->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(widget);
|
||||||
|
|
||||||
|
if (state->finalAnimation.animating()) {
|
||||||
|
const auto progress = 1. - state->finalAnimation.value(0.);
|
||||||
|
const auto size = widget->size();
|
||||||
|
const auto scaledSize = size * progress;
|
||||||
|
const auto scaledCenter = QPoint(
|
||||||
|
(size.width() - scaledSize.width()) / 2.,
|
||||||
|
(size.height() - scaledSize.height()) / 2.);
|
||||||
|
p.setOpacity(progress);
|
||||||
|
p.translate(scaledCenter);
|
||||||
|
p.scale(progress, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
paintCallback(widget, p);
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
destroys
|
||||||
|
) | rpl::take(1) | rpl::start_with_next([=, from = 0., to = 1.] {
|
||||||
|
state->finalAnimation.start(
|
||||||
|
[=](float64 value) {
|
||||||
|
update();
|
||||||
|
if (value == to) {
|
||||||
|
stateLifetime->destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
st::defaultPopupMenu.showDuration);
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
widget->raise();
|
||||||
|
widget->show();
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void AddReactionAnimatedIcon(
|
void AddReactionAnimatedIcon(
|
||||||
|
@ -270,14 +337,8 @@ void AddReactionAnimatedIcon(
|
||||||
Entry select;
|
Entry select;
|
||||||
bool appearAnimated = false;
|
bool appearAnimated = false;
|
||||||
rpl::lifetime loadingLifetime;
|
rpl::lifetime loadingLifetime;
|
||||||
|
|
||||||
base::unique_qptr<Ui::RpWidget> widget;
|
|
||||||
|
|
||||||
Ui::Animations::Simple finalAnimation;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto state = stateLifetime->make_state<State>();
|
const auto state = stateLifetime->make_state<State>();
|
||||||
state->widget = base::make_unique_q<Ui::RpWidget>(parent);
|
|
||||||
|
|
||||||
state->appear.media = reaction.appearAnimation->createMediaView();
|
state->appear.media = reaction.appearAnimation->createMediaView();
|
||||||
state->select.media = reaction.selectAnimation->createMediaView();
|
state->select.media = reaction.selectAnimation->createMediaView();
|
||||||
|
@ -303,34 +364,7 @@ void AddReactionAnimatedIcon(
|
||||||
}
|
}
|
||||||
}, state->loadingLifetime);
|
}, state->loadingLifetime);
|
||||||
|
|
||||||
const auto widget = state->widget.get();
|
const auto paintCallback = [=](not_null<QWidget*> widget, QPainter &p) {
|
||||||
widget->resize(iconSize, iconSize);
|
|
||||||
widget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
|
|
||||||
std::move(
|
|
||||||
iconPositionValue
|
|
||||||
) | rpl::start_with_next([=](const QPoint &point) {
|
|
||||||
widget->moveToLeft(point.x(), point.y());
|
|
||||||
}, widget->lifetime());
|
|
||||||
|
|
||||||
const auto update = crl::guard(widget, [=] { widget->update(); });
|
|
||||||
|
|
||||||
widget->paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
Painter p(widget);
|
|
||||||
|
|
||||||
if (state->finalAnimation.animating()) {
|
|
||||||
const auto progress = 1. - state->finalAnimation.value(0.);
|
|
||||||
const auto size = widget->size();
|
|
||||||
const auto scaledSize = size * progress;
|
|
||||||
const auto scaledCenter = QPoint(
|
|
||||||
(size.width() - scaledSize.width()) / 2.,
|
|
||||||
(size.height() - scaledSize.height()) / 2.);
|
|
||||||
p.setOpacity(progress);
|
|
||||||
p.translate(scaledCenter);
|
|
||||||
p.scale(progress, progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto paintFrame = [&](not_null<Ui::AnimatedIcon*> animation) {
|
const auto paintFrame = [&](not_null<Ui::AnimatedIcon*> animation) {
|
||||||
const auto frame = animation->frame();
|
const auto frame = animation->frame();
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
|
@ -345,41 +379,72 @@ void AddReactionAnimatedIcon(
|
||||||
const auto appear = state->appear.icon.get();
|
const auto appear = state->appear.icon.get();
|
||||||
if (appear && !state->appearAnimated) {
|
if (appear && !state->appearAnimated) {
|
||||||
state->appearAnimated = true;
|
state->appearAnimated = true;
|
||||||
appear->animate(update);
|
appear->animate(crl::guard(widget, [=] { widget->update(); }));
|
||||||
}
|
}
|
||||||
if (appear && appear->animating()) {
|
if (appear && appear->animating()) {
|
||||||
paintFrame(appear);
|
paintFrame(appear);
|
||||||
} else if (const auto select = state->select.icon.get()) {
|
} else if (const auto select = state->select.icon.get()) {
|
||||||
paintFrame(select);
|
paintFrame(select);
|
||||||
}
|
}
|
||||||
}, widget->lifetime());
|
|
||||||
|
};
|
||||||
|
const auto widget = AddReactionIconWrap(
|
||||||
|
parent,
|
||||||
|
std::move(iconPositionValue),
|
||||||
|
iconSize,
|
||||||
|
paintCallback,
|
||||||
|
std::move(destroys),
|
||||||
|
stateLifetime);
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
selects
|
selects
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
const auto select = state->select.icon.get();
|
const auto select = state->select.icon.get();
|
||||||
if (select && !select->animating()) {
|
if (select && !select->animating()) {
|
||||||
select->animate(update);
|
select->animate(crl::guard(widget, [=] { widget->update(); }));
|
||||||
}
|
}
|
||||||
}, widget->lifetime());
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
std::move(
|
void AddReactionCustomIcon(
|
||||||
destroys
|
not_null<Ui::RpWidget*> parent,
|
||||||
) | rpl::take(1) | rpl::start_with_next([=, from = 0., to = 1.] {
|
rpl::producer<QPoint> iconPositionValue,
|
||||||
state->finalAnimation.start(
|
int iconSize,
|
||||||
[=](float64 value) {
|
not_null<Window::SessionController*> controller,
|
||||||
update();
|
DocumentId customId,
|
||||||
if (value == to) {
|
rpl::producer<> &&destroys,
|
||||||
stateLifetime->destroy();
|
not_null<rpl::lifetime*> stateLifetime) {
|
||||||
}
|
struct State {
|
||||||
},
|
std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
||||||
from,
|
Fn<void()> repaint;
|
||||||
to,
|
};
|
||||||
st::defaultPopupMenu.showDuration);
|
const auto state = stateLifetime->make_state<State>();
|
||||||
}, widget->lifetime());
|
static constexpr auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||||
|
state->custom = controller->session().data().customEmojiManager().create(
|
||||||
|
customId,
|
||||||
|
[=] { state->repaint(); },
|
||||||
|
tag);
|
||||||
|
|
||||||
widget->raise();
|
const auto paintCallback = [=](not_null<QWidget*> widget, QPainter &p) {
|
||||||
widget->show();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto size = Data::FrameSizeFromTag(tag) / ratio;
|
||||||
|
state->custom->paint(p, {
|
||||||
|
.preview = st::windowBgRipple->c,
|
||||||
|
.now = crl::now(),
|
||||||
|
.position = QPoint(
|
||||||
|
(widget->width() - size) / 2,
|
||||||
|
(widget->height() - size) / 2),
|
||||||
|
.paused = controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Layer),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const auto widget = AddReactionIconWrap(
|
||||||
|
parent,
|
||||||
|
std::move(iconPositionValue),
|
||||||
|
iconSize,
|
||||||
|
paintCallback,
|
||||||
|
std::move(destroys),
|
||||||
|
stateLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReactionsSettingsBox(
|
void ReactionsSettingsBox(
|
||||||
|
|
|
@ -28,6 +28,14 @@ void AddReactionAnimatedIcon(
|
||||||
rpl::producer<> &&selects,
|
rpl::producer<> &&selects,
|
||||||
rpl::producer<> &&destroys,
|
rpl::producer<> &&destroys,
|
||||||
not_null<rpl::lifetime*> stateLifetime);
|
not_null<rpl::lifetime*> stateLifetime);
|
||||||
|
void AddReactionCustomIcon(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
rpl::producer<QPoint> iconPositionValue,
|
||||||
|
int iconSize,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
DocumentId customId,
|
||||||
|
rpl::producer<> &&destroys,
|
||||||
|
not_null<rpl::lifetime*> stateLifetime);
|
||||||
|
|
||||||
void ReactionsSettingsBox(
|
void ReactionsSettingsBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
|
|
|
@ -140,6 +140,9 @@ PossibleItemReactionsRef LookupPossibleReactions(
|
||||||
if ((allowed.type == AllowedReactionsType::Some)
|
if ((allowed.type == AllowedReactionsType::Some)
|
||||||
&& !ranges::contains(allowed.some, id)) {
|
&& !ranges::contains(allowed.some, id)) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (id.custom()
|
||||||
|
&& allowed.type == AllowedReactionsType::Default) {
|
||||||
|
return false;
|
||||||
} else if (reaction.premium
|
} else if (reaction.premium
|
||||||
&& !session->premium()
|
&& !session->premium()
|
||||||
&& !ranges::contains(all, id, &MessageReaction::id)) {
|
&& !ranges::contains(all, id, &MessageReaction::id)) {
|
||||||
|
|
|
@ -455,23 +455,6 @@ rpl::producer<int> FullReactionsCountValue(
|
||||||
}) | rpl::distinct_until_changed();
|
}) | rpl::distinct_until_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<int> AllowedReactionsCountValue(not_null<PeerData*> peer) {
|
|
||||||
if (peer->isUser()) {
|
|
||||||
return FullReactionsCountValue(&peer->session());
|
|
||||||
}
|
|
||||||
return rpl::combine(
|
|
||||||
FullReactionsCountValue(&peer->session()),
|
|
||||||
peer->session().changes().peerFlagsValue(
|
|
||||||
peer,
|
|
||||||
UpdateFlag::Reactions)
|
|
||||||
) | rpl::map([=](int full, const auto&) {
|
|
||||||
const auto &allowed = Data::PeerAllowedReactions(peer);
|
|
||||||
return (allowed.type == Data::AllowedReactionsType::Some)
|
|
||||||
? int(allowed.some.size())
|
|
||||||
: full;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Flag, typename Peer>
|
template <typename Flag, typename Peer>
|
||||||
rpl::producer<Badge> BadgeValueFromFlags(Peer peer) {
|
rpl::producer<Badge> BadgeValueFromFlags(Peer peer) {
|
||||||
return rpl::combine(
|
return rpl::combine(
|
||||||
|
|
|
@ -89,8 +89,6 @@ rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
||||||
not_null<PeerData*> peer);
|
not_null<PeerData*> peer);
|
||||||
[[nodiscard]] rpl::producer<int> FullReactionsCountValue(
|
[[nodiscard]] rpl::producer<int> FullReactionsCountValue(
|
||||||
not_null<Main::Session*> peer);
|
not_null<Main::Session*> peer);
|
||||||
[[nodiscard]] rpl::producer<int> AllowedReactionsCountValue(
|
|
||||||
not_null<PeerData*> peer);
|
|
||||||
|
|
||||||
enum class Badge;
|
enum class Badge;
|
||||||
[[nodiscard]] rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer);
|
[[nodiscard]] rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer);
|
||||||
|
|
Loading…
Add table
Reference in a new issue