Allow setting channel emoji status.
This commit is contained in:
parent
0e8058adb1
commit
941126ad69
|
@ -515,6 +515,7 @@ auto PeerPhoto::emojiList(EmojiListType type) -> EmojiListData & {
|
||||||
case EmojiListType::Profile: return _profileEmojiList;
|
case EmojiListType::Profile: return _profileEmojiList;
|
||||||
case EmojiListType::Group: return _groupEmojiList;
|
case EmojiListType::Group: return _groupEmojiList;
|
||||||
case EmojiListType::Background: return _backgroundEmojiList;
|
case EmojiListType::Background: return _backgroundEmojiList;
|
||||||
|
case EmojiListType::NoChannelStatus: return _noChannelStatusEmojiList;
|
||||||
}
|
}
|
||||||
Unexpected("Type in PeerPhoto::emojiList.");
|
Unexpected("Type in PeerPhoto::emojiList.");
|
||||||
}
|
}
|
||||||
|
@ -551,6 +552,8 @@ void PeerPhoto::requestEmojiList(EmojiListType type) {
|
||||||
? send(MTPaccount_GetDefaultProfilePhotoEmojis())
|
? send(MTPaccount_GetDefaultProfilePhotoEmojis())
|
||||||
: (type == EmojiListType::Group)
|
: (type == EmojiListType::Group)
|
||||||
? send(MTPaccount_GetDefaultGroupPhotoEmojis())
|
? send(MTPaccount_GetDefaultGroupPhotoEmojis())
|
||||||
|
: (type == EmojiListType::NoChannelStatus)
|
||||||
|
? send(MTPaccount_GetChannelRestrictedStatusEmojis())
|
||||||
: send(MTPaccount_GetDefaultBackgroundEmojis());
|
: send(MTPaccount_GetDefaultBackgroundEmojis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
Profile,
|
Profile,
|
||||||
Group,
|
Group,
|
||||||
Background,
|
Background,
|
||||||
|
NoChannelStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UserPhoto {
|
struct UserPhoto {
|
||||||
|
@ -112,6 +113,7 @@ private:
|
||||||
EmojiListData _profileEmojiList;
|
EmojiListData _profileEmojiList;
|
||||||
EmojiListData _groupEmojiList;
|
EmojiListData _groupEmojiList;
|
||||||
EmojiListData _backgroundEmojiList;
|
EmojiListData _backgroundEmojiList;
|
||||||
|
EmojiListData _noChannelStatusEmojiList;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "api/api_peer_colors.h"
|
#include "api/api_peer_colors.h"
|
||||||
|
#include "api/api_peer_photo.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "boxes/peers/replace_boost_box.h"
|
#include "boxes/peers/replace_boost_box.h"
|
||||||
#include "chat_helpers/compose/compose_show.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_channel.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/data_emoji_statuses.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
|
@ -428,11 +430,17 @@ HistoryView::Context PreviewDelegate::elementContext() {
|
||||||
return HistoryView::Context::AdminLog;
|
return HistoryView::Context::AdminLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SetValues {
|
||||||
|
uint8 colorIndex = 0;
|
||||||
|
DocumentId backgroundEmojiId = 0;
|
||||||
|
DocumentId statusId = 0;
|
||||||
|
TimeId statusUntil = 0;
|
||||||
|
bool statusChanged = false;
|
||||||
|
};
|
||||||
void Set(
|
void Set(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
uint8 colorIndex,
|
SetValues values) {
|
||||||
DocumentId backgroundEmojiId) {
|
|
||||||
const auto wasIndex = peer->colorIndex();
|
const auto wasIndex = peer->colorIndex();
|
||||||
const auto wasEmojiId = peer->backgroundEmojiId();
|
const auto wasEmojiId = peer->backgroundEmojiId();
|
||||||
|
|
||||||
|
@ -444,7 +452,7 @@ void Set(
|
||||||
peer,
|
peer,
|
||||||
UpdateFlag::Color | UpdateFlag::BackgroundEmoji);
|
UpdateFlag::Color | UpdateFlag::BackgroundEmoji);
|
||||||
};
|
};
|
||||||
setLocal(colorIndex, backgroundEmojiId);
|
setLocal(values.colorIndex, values.backgroundEmojiId);
|
||||||
|
|
||||||
const auto done = [=] {
|
const auto done = [=] {
|
||||||
show->showToast(peer->isSelf()
|
show->showToast(peer->isSelf()
|
||||||
|
@ -467,15 +475,23 @@ void Set(
|
||||||
using Flag = MTPaccount_UpdateColor::Flag;
|
using Flag = MTPaccount_UpdateColor::Flag;
|
||||||
send(MTPaccount_UpdateColor(
|
send(MTPaccount_UpdateColor(
|
||||||
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
|
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
|
||||||
MTP_int(colorIndex),
|
MTP_int(values.colorIndex),
|
||||||
MTP_long(backgroundEmojiId)));
|
MTP_long(values.backgroundEmojiId)));
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
using Flag = MTPchannels_UpdateColor::Flag;
|
using Flag = MTPchannels_UpdateColor::Flag;
|
||||||
send(MTPchannels_UpdateColor(
|
send(MTPchannels_UpdateColor(
|
||||||
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
|
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
|
||||||
channel->inputChannel,
|
channel->inputChannel,
|
||||||
MTP_int(colorIndex),
|
MTP_int(values.colorIndex),
|
||||||
MTP_long(backgroundEmojiId)));
|
MTP_long(values.backgroundEmojiId)));
|
||||||
|
|
||||||
|
if (values.statusChanged
|
||||||
|
&& (values.statusId || peer->emojiStatusId())) {
|
||||||
|
peer->owner().emojiStatuses().set(
|
||||||
|
channel,
|
||||||
|
values.statusId,
|
||||||
|
values.statusUntil);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Invalid peer type in Set(colorIndex).");
|
Unexpected("Invalid peer type in Set(colorIndex).");
|
||||||
}
|
}
|
||||||
|
@ -484,13 +500,13 @@ void Set(
|
||||||
void Apply(
|
void Apply(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
uint8 colorIndex,
|
SetValues values,
|
||||||
DocumentId backgroundEmojiId,
|
|
||||||
Fn<void()> close,
|
Fn<void()> close,
|
||||||
Fn<void()> cancel) {
|
Fn<void()> cancel) {
|
||||||
const auto session = &peer->session();
|
const auto session = &peer->session();
|
||||||
if (peer->colorIndex() == colorIndex
|
if (peer->colorIndex() == values.colorIndex
|
||||||
&& peer->backgroundEmojiId() == backgroundEmojiId) {
|
&& peer->backgroundEmojiId() == values.backgroundEmojiId
|
||||||
|
&& !values.statusChanged) {
|
||||||
close();
|
close();
|
||||||
} else if (peer->isSelf() && !session->premium()) {
|
} else if (peer->isSelf() && !session->premium()) {
|
||||||
Settings::ShowPremiumPromoToast(
|
Settings::ShowPremiumPromoToast(
|
||||||
|
@ -505,7 +521,7 @@ void Apply(
|
||||||
u"name_color"_q);
|
u"name_color"_q);
|
||||||
cancel();
|
cancel();
|
||||||
} else if (peer->isSelf()) {
|
} else if (peer->isSelf()) {
|
||||||
Set(show, peer, colorIndex, backgroundEmojiId);
|
Set(show, peer, values);
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
session->api().request(MTPpremium_GetBoostsStatus(
|
session->api().request(MTPpremium_GetBoostsStatus(
|
||||||
|
@ -518,15 +534,26 @@ void Apply(
|
||||||
const auto peerColors = &peer->session().api().peerColors();
|
const auto peerColors = &peer->session().api().peerColors();
|
||||||
const auto colorRequired = peerColors->requiredLevelFor(
|
const auto colorRequired = peerColors->requiredLevelFor(
|
||||||
peer->id,
|
peer->id,
|
||||||
colorIndex);
|
values.colorIndex);
|
||||||
const auto iconRequired = backgroundEmojiId
|
const auto iconRequired = values.backgroundEmojiId
|
||||||
? session->account().appConfig().get<int>(
|
? session->account().appConfig().get<int>(
|
||||||
"channel_bg_icon_level_min",
|
"channel_bg_icon_level_min",
|
||||||
5)
|
5)
|
||||||
: 0;
|
: 0;
|
||||||
const auto required = std::max(colorRequired, iconRequired);
|
const auto statusRequired = (values.statusChanged
|
||||||
if (data.vlevel().v >= required) {
|
&& values.statusId)
|
||||||
Set(show, peer, colorIndex, backgroundEmojiId);
|
? session->account().appConfig().get<int>(
|
||||||
|
"channel_emoji_status_level_min",
|
||||||
|
8)
|
||||||
|
: 0;
|
||||||
|
const auto required = std::max({
|
||||||
|
colorRequired,
|
||||||
|
iconRequired,
|
||||||
|
statusRequired,
|
||||||
|
});
|
||||||
|
const auto current = data.vlevel().v;
|
||||||
|
if (current >= required) {
|
||||||
|
Set(show, peer, values);
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -538,10 +565,18 @@ void Apply(
|
||||||
};
|
};
|
||||||
auto counters = ParseBoostCounters(result);
|
auto counters = ParseBoostCounters(result);
|
||||||
counters.mine = 0; // Don't show current level as just-reached.
|
counters.mine = 0; // Don't show current level as just-reached.
|
||||||
|
const auto reason = [&]() -> Ui::AskBoostReason {
|
||||||
|
if (current < statusRequired) {
|
||||||
|
return { Ui::AskBoostEmojiStatus{ statusRequired } };
|
||||||
|
} else if (current < iconRequired) {
|
||||||
|
return { Ui::AskBoostChannelColor{ iconRequired } };
|
||||||
|
}
|
||||||
|
return { Ui::AskBoostChannelColor{ colorRequired } };
|
||||||
|
}();
|
||||||
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
|
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
|
||||||
.link = qs(data.vboost_url()),
|
.link = qs(data.vboost_url()),
|
||||||
.boost = counters,
|
.boost = counters,
|
||||||
.reason = { Ui::AskBoostChannelColor{ required } },
|
.reason = reason,
|
||||||
}, openStatistics, nullptr));
|
}, openStatistics, nullptr));
|
||||||
cancel();
|
cancel();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
@ -685,15 +720,18 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
const auto right = Ui::CreateChild<Ui::RpWidget>(raw);
|
const auto right = Ui::CreateChild<Ui::RpWidget>(raw);
|
||||||
right->show();
|
right->show();
|
||||||
|
|
||||||
|
using namespace Info::Profile;
|
||||||
struct State {
|
struct State {
|
||||||
Info::Profile::EmojiStatusPanel panel;
|
EmojiStatusPanel panel;
|
||||||
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
|
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
|
||||||
DocumentId emojiId = 0;
|
DocumentId emojiId = 0;
|
||||||
uint8 index = 0;
|
uint8 index = 0;
|
||||||
};
|
};
|
||||||
const auto state = right->lifetime().make_state<State>();
|
const auto state = right->lifetime().make_state<State>();
|
||||||
state->panel.backgroundEmojiChosen(
|
state->panel.someCustomChosen(
|
||||||
) | rpl::start_with_next(emojiIdChosen, raw->lifetime());
|
) | rpl::start_with_next([=](EmojiStatusPanel::CustomChosen chosen) {
|
||||||
|
emojiIdChosen(chosen.id);
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
|
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
|
||||||
state->index = index;
|
state->index = index;
|
||||||
|
@ -761,7 +799,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
state->panel.show({
|
state->panel.show({
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.button = right,
|
.button = right,
|
||||||
.currentBackgroundEmojiId = state->emojiId,
|
.ensureAddedEmojiId = state->emojiId,
|
||||||
.customTextColor = customTextColor,
|
.customTextColor = customTextColor,
|
||||||
.backgroundEmojiMode = true,
|
.backgroundEmojiMode = true,
|
||||||
});
|
});
|
||||||
|
@ -771,6 +809,108 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiStatusButton(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
rpl::producer<DocumentId> statusIdValue,
|
||||||
|
Fn<void(DocumentId,TimeId)> statusIdChosen) {
|
||||||
|
const auto &basicSt = st::settingsButtonNoIcon;
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto added = st::normalFont->spacew;
|
||||||
|
const auto emojiSize = Data::FrameSizeFromTag({}) / ratio;
|
||||||
|
const auto noneWidth = added
|
||||||
|
+ st::normalFont->width(tr::lng_settings_color_emoji_off(tr::now));
|
||||||
|
const auto emojiWidth = added + emojiSize;
|
||||||
|
const auto rightPadding = std::max(noneWidth, emojiWidth)
|
||||||
|
+ basicSt.padding.right();
|
||||||
|
const auto st = parent->lifetime().make_state<style::SettingsButton>(
|
||||||
|
basicSt);
|
||||||
|
st->padding.setRight(rightPadding);
|
||||||
|
auto result = object_ptr<Ui::SettingsButton>(
|
||||||
|
parent,
|
||||||
|
tr::lng_edit_channel_status(),
|
||||||
|
*st);
|
||||||
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
const auto right = Ui::CreateChild<Ui::RpWidget>(raw);
|
||||||
|
right->show();
|
||||||
|
|
||||||
|
using namespace Info::Profile;
|
||||||
|
struct State {
|
||||||
|
EmojiStatusPanel panel;
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
|
||||||
|
DocumentId statusId = 0;
|
||||||
|
};
|
||||||
|
const auto state = right->lifetime().make_state<State>();
|
||||||
|
state->panel.someCustomChosen(
|
||||||
|
) | rpl::start_with_next([=](EmojiStatusPanel::CustomChosen chosen) {
|
||||||
|
statusIdChosen(chosen.id, chosen.until);
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
const auto session = &show->session();
|
||||||
|
std::move(statusIdValue) | rpl::start_with_next([=](DocumentId id) {
|
||||||
|
state->statusId = id;
|
||||||
|
state->emoji = id
|
||||||
|
? session->data().customEmojiManager().create(
|
||||||
|
id,
|
||||||
|
[=] { right->update(); })
|
||||||
|
: nullptr;
|
||||||
|
right->resize(
|
||||||
|
(id ? emojiWidth : noneWidth) + added,
|
||||||
|
right->height());
|
||||||
|
right->update();
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
raw->sizeValue(),
|
||||||
|
right->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize outer, int width) {
|
||||||
|
right->resize(width, outer.height());
|
||||||
|
const auto skip = st::settingsButton.padding.right();
|
||||||
|
right->moveToRight(skip - added, 0, outer.width());
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
right->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (state->panel.paintBadgeFrame(right)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto p = QPainter(right);
|
||||||
|
const auto height = right->height();
|
||||||
|
if (state->emoji) {
|
||||||
|
state->emoji->paint(p, {
|
||||||
|
.textColor = anim::color(
|
||||||
|
st::stickerPanPremium1,
|
||||||
|
st::stickerPanPremium2,
|
||||||
|
0.5),
|
||||||
|
.position = QPoint(added, (height - emojiSize) / 2),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const auto &font = st::normalFont;
|
||||||
|
p.setFont(font);
|
||||||
|
p.setPen(st::windowActiveTextFg);
|
||||||
|
p.drawText(
|
||||||
|
QPoint(added, (height - font->height) / 2 + font->ascent),
|
||||||
|
tr::lng_settings_color_emoji_off(tr::now));
|
||||||
|
}
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
raw->setClickedCallback([=] {
|
||||||
|
const auto controller = show->resolveWindow(
|
||||||
|
ChatHelpers::WindowUsage::PremiumPromo);
|
||||||
|
if (controller) {
|
||||||
|
state->panel.show({
|
||||||
|
.controller = controller,
|
||||||
|
.button = right,
|
||||||
|
.ensureAddedEmojiId = state->statusId,
|
||||||
|
.channelStatusMode = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EditPeerColorBox(
|
void EditPeerColorBox(
|
||||||
|
@ -785,12 +925,16 @@ void EditPeerColorBox(
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<uint8> index;
|
rpl::variable<uint8> index;
|
||||||
rpl::variable<DocumentId> emojiId;
|
rpl::variable<DocumentId> emojiId;
|
||||||
|
rpl::variable<DocumentId> statusId;
|
||||||
|
TimeId statusUntil = 0;
|
||||||
|
bool statusChanged = false;
|
||||||
bool changing = false;
|
bool changing = false;
|
||||||
bool applying = false;
|
bool applying = false;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
state->index = peer->colorIndex();
|
state->index = peer->colorIndex();
|
||||||
state->emojiId = peer->backgroundEmojiId();
|
state->emojiId = peer->backgroundEmojiId();
|
||||||
|
state->statusId = peer->emojiStatusId();
|
||||||
|
|
||||||
box->addRow(object_ptr<PreviewWrap>(
|
box->addRow(object_ptr<PreviewWrap>(
|
||||||
box,
|
box,
|
||||||
|
@ -833,6 +977,32 @@ void EditPeerColorBox(
|
||||||
? tr::lng_settings_color_emoji_about()
|
? tr::lng_settings_color_emoji_about()
|
||||||
: tr::lng_settings_color_emoji_about_channel());
|
: tr::lng_settings_color_emoji_about_channel());
|
||||||
|
|
||||||
|
if (const auto channel = peer->asChannel()) {
|
||||||
|
// Preload exceptions list.
|
||||||
|
const auto peerPhoto = &channel->session().api().peerPhoto();
|
||||||
|
[[maybe_unused]] auto list = peerPhoto->emojiListValue(
|
||||||
|
Api::PeerPhoto::EmojiListType::NoChannelStatus
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto statuses = &channel->owner().emojiStatuses();
|
||||||
|
statuses->refreshChannelDefault();
|
||||||
|
statuses->refreshChannelColored();
|
||||||
|
|
||||||
|
Ui::AddSkip(container, st::settingsColorSampleSkip);
|
||||||
|
container->add(CreateEmojiStatusButton(
|
||||||
|
container,
|
||||||
|
show,
|
||||||
|
state->statusId.value(),
|
||||||
|
[=](DocumentId id, TimeId until) {
|
||||||
|
state->statusId = id;
|
||||||
|
state->statusUntil = until;
|
||||||
|
state->statusChanged = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ui::AddSkip(container, st::settingsColorSampleSkip);
|
||||||
|
Ui::AddDividerText(container, tr::lng_edit_channel_status_about());
|
||||||
|
}
|
||||||
|
|
||||||
box->addButton(tr::lng_settings_apply(), [=] {
|
box->addButton(tr::lng_settings_apply(), [=] {
|
||||||
if (state->applying) {
|
if (state->applying) {
|
||||||
return;
|
return;
|
||||||
|
@ -840,7 +1010,13 @@ void EditPeerColorBox(
|
||||||
state->applying = true;
|
state->applying = true;
|
||||||
const auto index = state->index.current();
|
const auto index = state->index.current();
|
||||||
const auto emojiId = state->emojiId.current();
|
const auto emojiId = state->emojiId.current();
|
||||||
Apply(show, peer, index, emojiId, crl::guard(box, [=] {
|
Apply(show, peer, {
|
||||||
|
state->index.current(),
|
||||||
|
state->emojiId.current(),
|
||||||
|
state->statusId.current(),
|
||||||
|
state->statusUntil,
|
||||||
|
state->statusChanged,
|
||||||
|
}, crl::guard(box, [=] {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
}), crl::guard(box, [=] {
|
}), crl::guard(box, [=] {
|
||||||
state->applying = false;
|
state->applying = false;
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "chat_helpers/emoji_list_widget.h"
|
#include "chat_helpers/emoji_list_widget.h"
|
||||||
|
|
||||||
|
#include "api/api_peer_photo.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/tabbed_search.h"
|
#include "ui/controls/tabbed_search.h"
|
||||||
|
@ -477,10 +479,23 @@ EmojiListWidget::EmojiListWidget(
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_mode != Mode::RecentReactions && _mode != Mode::BackgroundEmoji) {
|
if (_mode != Mode::RecentReactions
|
||||||
|
&& _mode != Mode::BackgroundEmoji
|
||||||
|
&& _mode != Mode::ChannelStatus) {
|
||||||
setupSearch();
|
setupSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_mode == Mode::ChannelStatus) {
|
||||||
|
session().api().peerPhoto().emojiListValue(
|
||||||
|
Api::PeerPhoto::EmojiListType::NoChannelStatus
|
||||||
|
) | rpl::start_with_next([=](const std::vector<DocumentId> &list) {
|
||||||
|
_restrictedCustomList = { begin(list), end(list) };
|
||||||
|
if (!_custom.empty()) {
|
||||||
|
refreshCustom();
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
_customSingleSize = Data::FrameSizeFromTag(
|
_customSingleSize = Data::FrameSizeFromTag(
|
||||||
Data::CustomEmojiManager::SizeTag::Large
|
Data::CustomEmojiManager::SizeTag::Large
|
||||||
) / style::DevicePixelRatio();
|
) / style::DevicePixelRatio();
|
||||||
|
@ -1034,7 +1049,9 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
|
||||||
if (!id && _mode == Mode::EmojiStatus) {
|
if (!id && _mode == Mode::EmojiStatus) {
|
||||||
const auto star = QString::fromUtf8("\xe2\xad\x90\xef\xb8\x8f");
|
const auto star = QString::fromUtf8("\xe2\xad\x90\xef\xb8\x8f");
|
||||||
_recent.push_back({ .id = { Ui::Emoji::Find(star) } });
|
_recent.push_back({ .id = { Ui::Emoji::Find(star) } });
|
||||||
} else if (!id && _mode == Mode::BackgroundEmoji) {
|
} else if (!id
|
||||||
|
&& (_mode == Mode::BackgroundEmoji
|
||||||
|
|| _mode == Mode::ChannelStatus)) {
|
||||||
const auto fakeId = DocumentId(5246772116543512028ULL);
|
const auto fakeId = DocumentId(5246772116543512028ULL);
|
||||||
const auto no = QString::fromUtf8("\xe2\x9b\x94\xef\xb8\x8f");
|
const auto no = QString::fromUtf8("\xe2\x9b\x94\xef\xb8\x8f");
|
||||||
_recent.push_back({
|
_recent.push_back({
|
||||||
|
@ -1070,7 +1087,7 @@ base::unique_qptr<Ui::PopupMenu> EmojiListWidget::fillContextMenu(
|
||||||
: st::defaultPopupMenu));
|
: st::defaultPopupMenu));
|
||||||
if (_mode == Mode::Full) {
|
if (_mode == Mode::Full) {
|
||||||
fillRecentMenu(menu, section, index);
|
fillRecentMenu(menu, section, index);
|
||||||
} else if (_mode == Mode::EmojiStatus) {
|
} else if (_mode == Mode::EmojiStatus || _mode == Mode::ChannelStatus) {
|
||||||
fillEmojiStatusMenu(menu, section, index);
|
fillEmojiStatusMenu(menu, section, index);
|
||||||
}
|
}
|
||||||
if (menu->empty()) {
|
if (menu->empty()) {
|
||||||
|
@ -1205,7 +1222,7 @@ void EmojiListWidget::validateEmojiPaintContext(
|
||||||
auto value = Ui::Text::CustomEmojiPaintContext{
|
auto value = Ui::Text::CustomEmojiPaintContext{
|
||||||
.textColor = (_customTextColor
|
.textColor = (_customTextColor
|
||||||
? _customTextColor()
|
? _customTextColor()
|
||||||
: (_mode == Mode::EmojiStatus)
|
: (_mode == Mode::EmojiStatus || _mode == Mode::ChannelStatus)
|
||||||
? anim::color(
|
? anim::color(
|
||||||
st::stickerPanPremium1,
|
st::stickerPanPremium1,
|
||||||
st::stickerPanPremium2,
|
st::stickerPanPremium2,
|
||||||
|
@ -1402,6 +1419,10 @@ void EmojiListWidget::drawRecent(
|
||||||
_emojiPaintContext->position = position
|
_emojiPaintContext->position = position
|
||||||
+ _innerPosition
|
+ _innerPosition
|
||||||
+ _customPosition;
|
+ _customPosition;
|
||||||
|
if (_mode == Mode::ChannelStatus) {
|
||||||
|
_emojiPaintContext->internal.forceFirstFrame
|
||||||
|
= (recent.id == _recent.front().id);
|
||||||
|
}
|
||||||
custom->paint(p, *_emojiPaintContext);
|
custom->paint(p, *_emojiPaintContext);
|
||||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
||||||
if (_mode == Mode::EmojiStatus) {
|
if (_mode == Mode::EmojiStatus) {
|
||||||
|
@ -1642,6 +1663,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
Settings::ShowPremium(resolved, u"infinite_reactions"_q);
|
Settings::ShowPremium(resolved, u"infinite_reactions"_q);
|
||||||
break;
|
break;
|
||||||
case Mode::EmojiStatus:
|
case Mode::EmojiStatus:
|
||||||
|
case Mode::ChannelStatus:
|
||||||
Settings::ShowPremium(resolved, u"emoji_status"_q);
|
Settings::ShowPremium(resolved, u"emoji_status"_q);
|
||||||
break;
|
break;
|
||||||
case Mode::TopicIcon:
|
case Mode::TopicIcon:
|
||||||
|
@ -2018,8 +2040,9 @@ void EmojiListWidget::refreshCustom() {
|
||||||
auto it = sets.find(setId);
|
auto it = sets.find(setId);
|
||||||
if (it == sets.cend()
|
if (it == sets.cend()
|
||||||
|| it->second->stickers.isEmpty()
|
|| it->second->stickers.isEmpty()
|
||||||
|| (_mode == Mode::BackgroundEmoji
|
|| (_mode == Mode::BackgroundEmoji && !it->second->textColor())
|
||||||
&& !it->second->textColor())) {
|
|| (_mode == Mode::ChannelStatus
|
||||||
|
&& !it->second->channelStatus())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto canRemove = !!(it->second->flags
|
const auto canRemove = !!(it->second->flags
|
||||||
|
@ -2070,7 +2093,9 @@ void EmojiListWidget::refreshCustom() {
|
||||||
auto set = std::vector<CustomOne>();
|
auto set = std::vector<CustomOne>();
|
||||||
set.reserve(list.size());
|
set.reserve(list.size());
|
||||||
for (const auto document : list) {
|
for (const auto document : list) {
|
||||||
if (const auto sticker = document->sticker()) {
|
if (_restrictedCustomList.contains(document->id)) {
|
||||||
|
continue;
|
||||||
|
} else if (const auto sticker = document->sticker()) {
|
||||||
set.push_back({
|
set.push_back({
|
||||||
.custom = resolveCustomEmoji(document, setId),
|
.custom = resolveCustomEmoji(document, setId),
|
||||||
.document = document,
|
.document = document,
|
||||||
|
|
|
@ -71,6 +71,7 @@ enum class EmojiListMode {
|
||||||
Full,
|
Full,
|
||||||
TopicIcon,
|
TopicIcon,
|
||||||
EmojiStatus,
|
EmojiStatus,
|
||||||
|
ChannelStatus,
|
||||||
FullReactions,
|
FullReactions,
|
||||||
RecentReactions,
|
RecentReactions,
|
||||||
UserpicBuilder,
|
UserpicBuilder,
|
||||||
|
@ -384,6 +385,7 @@ private:
|
||||||
bool _grabbingChosen = false;
|
bool _grabbingChosen = false;
|
||||||
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
||||||
std::vector<CustomSet> _custom;
|
std::vector<CustomSet> _custom;
|
||||||
|
base::flat_set<DocumentId> _restrictedCustomList;
|
||||||
base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
|
base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
DocumentId,
|
DocumentId,
|
||||||
|
|
|
@ -332,6 +332,7 @@ TabbedSelector::TabbedSelector(
|
||||||
: TabbedSelector(parent, {
|
: TabbedSelector(parent, {
|
||||||
.show = std::move(show),
|
.show = std::move(show),
|
||||||
.st = ((mode == Mode::EmojiStatus
|
.st = ((mode == Mode::EmojiStatus
|
||||||
|
|| mode == Mode::ChannelStatus
|
||||||
|| mode == Mode::BackgroundEmoji
|
|| mode == Mode::BackgroundEmoji
|
||||||
|| mode == Mode::FullReactions)
|
|| mode == Mode::FullReactions)
|
||||||
? st::statusEmojiPan
|
? st::statusEmojiPan
|
||||||
|
@ -521,6 +522,8 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
|
||||||
.show = _show,
|
.show = _show,
|
||||||
.mode = (_mode == Mode::EmojiStatus
|
.mode = (_mode == Mode::EmojiStatus
|
||||||
? EmojiMode::EmojiStatus
|
? EmojiMode::EmojiStatus
|
||||||
|
: _mode == Mode::ChannelStatus
|
||||||
|
? EmojiMode::ChannelStatus
|
||||||
: _mode == Mode::BackgroundEmoji
|
: _mode == Mode::BackgroundEmoji
|
||||||
? EmojiMode::BackgroundEmoji
|
? EmojiMode::BackgroundEmoji
|
||||||
: _mode == Mode::FullReactions
|
: _mode == Mode::FullReactions
|
||||||
|
|
|
@ -81,6 +81,7 @@ enum class TabbedSelectorMode {
|
||||||
EmojiOnly,
|
EmojiOnly,
|
||||||
MediaEditor,
|
MediaEditor,
|
||||||
EmojiStatus,
|
EmojiStatus,
|
||||||
|
ChannelStatus,
|
||||||
BackgroundEmoji,
|
BackgroundEmoji,
|
||||||
FullReactions,
|
FullReactions,
|
||||||
RecentReactions,
|
RecentReactions,
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_emoji_statuses.h"
|
#include "data/data_emoji_statuses.h"
|
||||||
|
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -53,6 +54,7 @@ EmojiStatuses::EmojiStatuses(not_null<Session*> owner)
|
||||||
kRefreshDefaultListEach
|
kRefreshDefaultListEach
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
refreshDefault();
|
refreshDefault();
|
||||||
|
refreshChannelDefault();
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +76,14 @@ void EmojiStatuses::refreshColored() {
|
||||||
requestColored();
|
requestColored();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::refreshChannelDefault() {
|
||||||
|
requestChannelDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::refreshChannelColored() {
|
||||||
|
requestChannelColored();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::refreshRecentDelayed() {
|
void EmojiStatuses::refreshRecentDelayed() {
|
||||||
if (_recentRequestId || _recentRequestScheduled) {
|
if (_recentRequestId || _recentRequestScheduled) {
|
||||||
return;
|
return;
|
||||||
|
@ -91,6 +101,8 @@ const std::vector<DocumentId> &EmojiStatuses::list(Type type) const {
|
||||||
case Type::Recent: return _recent;
|
case Type::Recent: return _recent;
|
||||||
case Type::Default: return _default;
|
case Type::Default: return _default;
|
||||||
case Type::Colored: return _colored;
|
case Type::Colored: return _colored;
|
||||||
|
case Type::ChannelDefault: return _channelDefault;
|
||||||
|
case Type::ChannelColored: return _channelColored;
|
||||||
}
|
}
|
||||||
Unexpected("Type in EmojiStatuses::list.");
|
Unexpected("Type in EmojiStatuses::list.");
|
||||||
}
|
}
|
||||||
|
@ -103,6 +115,10 @@ rpl::producer<> EmojiStatuses::defaultUpdates() const {
|
||||||
return _defaultUpdated.events();
|
return _defaultUpdated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> EmojiStatuses::channelDefaultUpdates() const {
|
||||||
|
return _channelDefaultUpdated.events();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::registerAutomaticClear(
|
void EmojiStatuses::registerAutomaticClear(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
TimeId until) {
|
TimeId until) {
|
||||||
|
@ -266,7 +282,7 @@ void EmojiStatuses::requestDefault() {
|
||||||
}
|
}
|
||||||
auto &api = _owner->session().api();
|
auto &api = _owner->session().api();
|
||||||
_defaultRequestId = api.request(MTPaccount_GetDefaultEmojiStatuses(
|
_defaultRequestId = api.request(MTPaccount_GetDefaultEmojiStatuses(
|
||||||
MTP_long(_recentHash)
|
MTP_long(_defaultHash)
|
||||||
)).done([=](const MTPaccount_EmojiStatuses &result) {
|
)).done([=](const MTPaccount_EmojiStatuses &result) {
|
||||||
_defaultRequestId = 0;
|
_defaultRequestId = 0;
|
||||||
result.match([&](const MTPDaccount_emojiStatuses &data) {
|
result.match([&](const MTPDaccount_emojiStatuses &data) {
|
||||||
|
@ -299,6 +315,45 @@ void EmojiStatuses::requestColored() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::requestChannelDefault() {
|
||||||
|
if (_channelDefaultRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &api = _owner->session().api();
|
||||||
|
_channelDefaultRequestId = api.request(MTPaccount_GetDefaultEmojiStatuses(
|
||||||
|
MTP_long(_channelDefaultHash)
|
||||||
|
)).done([=](const MTPaccount_EmojiStatuses &result) {
|
||||||
|
_channelDefaultRequestId = 0;
|
||||||
|
result.match([&](const MTPDaccount_emojiStatuses &data) {
|
||||||
|
updateChannelDefault(data);
|
||||||
|
}, [&](const MTPDaccount_emojiStatusesNotModified &) {
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
_channelDefaultRequestId = 0;
|
||||||
|
_channelDefaultHash = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::requestChannelColored() {
|
||||||
|
if (_channelColoredRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &api = _owner->session().api();
|
||||||
|
_channelColoredRequestId = api.request(MTPmessages_GetStickerSet(
|
||||||
|
MTP_inputStickerSetEmojiChannelDefaultStatuses(),
|
||||||
|
MTP_int(0) // hash
|
||||||
|
)).done([=](const MTPmessages_StickerSet &result) {
|
||||||
|
_channelColoredRequestId = 0;
|
||||||
|
result.match([&](const MTPDmessages_stickerSet &data) {
|
||||||
|
updateChannelColored(data);
|
||||||
|
}, [](const MTPDmessages_stickerSetNotModified &) {
|
||||||
|
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
_channelColoredRequestId = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiStatuses::updateRecent(const MTPDaccount_emojiStatuses &data) {
|
void EmojiStatuses::updateRecent(const MTPDaccount_emojiStatuses &data) {
|
||||||
_recentHash = data.vhash().v;
|
_recentHash = data.vhash().v;
|
||||||
_recent = ListFromMTP(data);
|
_recent = ListFromMTP(data);
|
||||||
|
@ -321,27 +376,57 @@ void EmojiStatuses::updateColored(const MTPDmessages_stickerSet &data) {
|
||||||
_coloredUpdated.fire({});
|
_coloredUpdated.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiStatuses::set(DocumentId id, TimeId until) {
|
void EmojiStatuses::updateChannelDefault(
|
||||||
auto &api = _owner->session().api();
|
const MTPDaccount_emojiStatuses &data) {
|
||||||
if (_sentRequestId) {
|
_channelDefaultHash = data.vhash().v;
|
||||||
api.request(base::take(_sentRequestId)).cancel();
|
_channelDefault = ListFromMTP(data);
|
||||||
|
_channelDefaultUpdated.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::updateChannelColored(
|
||||||
|
const MTPDmessages_stickerSet &data) {
|
||||||
|
const auto &list = data.vdocuments().v;
|
||||||
|
_channelColored.clear();
|
||||||
|
_channelColored.reserve(list.size());
|
||||||
|
for (const auto &sticker : data.vdocuments().v) {
|
||||||
|
_channelColored.push_back(_owner->processDocument(sticker)->id);
|
||||||
}
|
}
|
||||||
_owner->session().user()->setEmojiStatus(id, until);
|
_channelColoredUpdated.fire({});
|
||||||
_sentRequestId = api.request(MTPaccount_UpdateEmojiStatus(
|
}
|
||||||
!id
|
|
||||||
|
void EmojiStatuses::set(DocumentId id, TimeId until) {
|
||||||
|
set(_owner->session().user(), id, until);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiStatuses::set(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
DocumentId id,
|
||||||
|
TimeId until) {
|
||||||
|
auto &api = _owner->session().api();
|
||||||
|
auto &requestId = _sentRequests[peer];
|
||||||
|
if (requestId) {
|
||||||
|
api.request(base::take(requestId)).cancel();
|
||||||
|
}
|
||||||
|
peer->setEmojiStatus(id, until);
|
||||||
|
const auto send = [&](auto &&request) {
|
||||||
|
requestId = api.request(
|
||||||
|
std::move(request)
|
||||||
|
).done([=] {
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).fail([=] {
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
const auto status = !id
|
||||||
? MTP_emojiStatusEmpty()
|
? MTP_emojiStatusEmpty()
|
||||||
: !until
|
: !until
|
||||||
? MTP_emojiStatus(MTP_long(id))
|
? MTP_emojiStatus(MTP_long(id))
|
||||||
: MTP_emojiStatusUntil(MTP_long(id), MTP_int(until))
|
: MTP_emojiStatusUntil(MTP_long(id), MTP_int(until));
|
||||||
)).done([=] {
|
if (peer->isSelf()) {
|
||||||
_sentRequestId = 0;
|
send(MTPaccount_UpdateEmojiStatus(status));
|
||||||
}).fail([=] {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
_sentRequestId = 0;
|
send(MTPchannels_UpdateEmojiStatus(channel->inputChannel, status));
|
||||||
}).send();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool EmojiStatuses::setting() const {
|
|
||||||
return _sentRequestId != 0;;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmojiStatusData ParseEmojiStatus(const MTPEmojiStatus &status) {
|
EmojiStatusData ParseEmojiStatus(const MTPEmojiStatus &status) {
|
||||||
|
|
|
@ -36,20 +36,24 @@ public:
|
||||||
void refreshRecentDelayed();
|
void refreshRecentDelayed();
|
||||||
void refreshDefault();
|
void refreshDefault();
|
||||||
void refreshColored();
|
void refreshColored();
|
||||||
|
void refreshChannelDefault();
|
||||||
|
void refreshChannelColored();
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Recent,
|
Recent,
|
||||||
Default,
|
Default,
|
||||||
Colored,
|
Colored,
|
||||||
|
ChannelDefault,
|
||||||
|
ChannelColored,
|
||||||
};
|
};
|
||||||
[[nodiscard]] const std::vector<DocumentId> &list(Type type) const;
|
[[nodiscard]] const std::vector<DocumentId> &list(Type type) const;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> recentUpdates() const;
|
[[nodiscard]] rpl::producer<> recentUpdates() const;
|
||||||
[[nodiscard]] rpl::producer<> defaultUpdates() const;
|
[[nodiscard]] rpl::producer<> defaultUpdates() const;
|
||||||
[[nodiscard]] rpl::producer<> coloredUpdates() const;
|
[[nodiscard]] rpl::producer<> channelDefaultUpdates() const;
|
||||||
|
|
||||||
void set(DocumentId id, TimeId until = 0);
|
void set(DocumentId id, TimeId until = 0);
|
||||||
[[nodiscard]] bool setting() const;
|
void set(not_null<PeerData*> peer, DocumentId id, TimeId until = 0);
|
||||||
|
|
||||||
void registerAutomaticClear(not_null<PeerData*> peer, TimeId until);
|
void registerAutomaticClear(not_null<PeerData*> peer, TimeId until);
|
||||||
|
|
||||||
|
@ -71,10 +75,14 @@ private:
|
||||||
void requestRecent();
|
void requestRecent();
|
||||||
void requestDefault();
|
void requestDefault();
|
||||||
void requestColored();
|
void requestColored();
|
||||||
|
void requestChannelDefault();
|
||||||
|
void requestChannelColored();
|
||||||
|
|
||||||
void updateRecent(const MTPDaccount_emojiStatuses &data);
|
void updateRecent(const MTPDaccount_emojiStatuses &data);
|
||||||
void updateDefault(const MTPDaccount_emojiStatuses &data);
|
void updateDefault(const MTPDaccount_emojiStatuses &data);
|
||||||
void updateColored(const MTPDmessages_stickerSet &data);
|
void updateColored(const MTPDmessages_stickerSet &data);
|
||||||
|
void updateChannelDefault(const MTPDaccount_emojiStatuses &data);
|
||||||
|
void updateChannelColored(const MTPDmessages_stickerSet &data);
|
||||||
|
|
||||||
void processClearingIn(TimeId wait);
|
void processClearingIn(TimeId wait);
|
||||||
void processClearing();
|
void processClearing();
|
||||||
|
@ -87,9 +95,13 @@ private:
|
||||||
std::vector<DocumentId> _recent;
|
std::vector<DocumentId> _recent;
|
||||||
std::vector<DocumentId> _default;
|
std::vector<DocumentId> _default;
|
||||||
std::vector<DocumentId> _colored;
|
std::vector<DocumentId> _colored;
|
||||||
|
std::vector<DocumentId> _channelDefault;
|
||||||
|
std::vector<DocumentId> _channelColored;
|
||||||
rpl::event_stream<> _recentUpdated;
|
rpl::event_stream<> _recentUpdated;
|
||||||
rpl::event_stream<> _defaultUpdated;
|
rpl::event_stream<> _defaultUpdated;
|
||||||
rpl::event_stream<> _coloredUpdated;
|
rpl::event_stream<> _coloredUpdated;
|
||||||
|
rpl::event_stream<> _channelDefaultUpdated;
|
||||||
|
rpl::event_stream<> _channelColoredUpdated;
|
||||||
|
|
||||||
mtpRequestId _recentRequestId = 0;
|
mtpRequestId _recentRequestId = 0;
|
||||||
bool _recentRequestScheduled = false;
|
bool _recentRequestScheduled = false;
|
||||||
|
@ -100,7 +112,12 @@ private:
|
||||||
|
|
||||||
mtpRequestId _coloredRequestId = 0;
|
mtpRequestId _coloredRequestId = 0;
|
||||||
|
|
||||||
mtpRequestId _sentRequestId = 0;
|
mtpRequestId _channelDefaultRequestId = 0;
|
||||||
|
uint64 _channelDefaultHash = 0;
|
||||||
|
|
||||||
|
mtpRequestId _channelColoredRequestId = 0;
|
||||||
|
|
||||||
|
base::flat_map<not_null<PeerData*>, mtpRequestId> _sentRequests;
|
||||||
|
|
||||||
base::flat_map<not_null<PeerData*>, TimeId> _clearing;
|
base::flat_map<not_null<PeerData*>, TimeId> _clearing;
|
||||||
base::Timer _clearingTimer;
|
base::Timer _clearingTimer;
|
||||||
|
|
|
@ -54,7 +54,8 @@ StickersSetFlags ParseStickersSetFlags(const MTPDstickerSet &data) {
|
||||||
| (data.is_emojis() ? Flag::Emoji : Flag())
|
| (data.is_emojis() ? Flag::Emoji : Flag())
|
||||||
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
||||||
| (data.is_videos() ? Flag::Webm : Flag())
|
| (data.is_videos() ? Flag::Webm : Flag())
|
||||||
| (data.is_text_color() ? Flag::TextColor : Flag());
|
| (data.is_text_color() ? Flag::TextColor : Flag())
|
||||||
|
| (data.is_channel_emoji_status() ? Flag::ChannelStatus : Flag());
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSet::StickersSet(
|
StickersSet::StickersSet(
|
||||||
|
@ -113,6 +114,10 @@ bool StickersSet::textColor() const {
|
||||||
return flags & StickersSetFlag::TextColor;
|
return flags & StickersSetFlag::TextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StickersSet::channelStatus() const {
|
||||||
|
return flags & StickersSetFlag::ChannelStatus;
|
||||||
|
}
|
||||||
|
|
||||||
void StickersSet::setThumbnail(const ImageWithLocation &data) {
|
void StickersSet::setThumbnail(const ImageWithLocation &data) {
|
||||||
Data::UpdateCloudFile(
|
Data::UpdateCloudFile(
|
||||||
_thumbnail,
|
_thumbnail,
|
||||||
|
|
|
@ -58,6 +58,7 @@ enum class StickersSetFlag {
|
||||||
Webm = (1 << 8),
|
Webm = (1 << 8),
|
||||||
Emoji = (1 << 9),
|
Emoji = (1 << 9),
|
||||||
TextColor = (1 << 10),
|
TextColor = (1 << 10),
|
||||||
|
ChannelStatus = (1 << 11),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
||||||
using StickersSetFlags = base::flags<StickersSetFlag>;
|
using StickersSetFlags = base::flags<StickersSetFlag>;
|
||||||
|
@ -86,6 +87,7 @@ public:
|
||||||
[[nodiscard]] StickerSetIdentifier identifier() const;
|
[[nodiscard]] StickerSetIdentifier identifier() const;
|
||||||
[[nodiscard]] StickersType type() const;
|
[[nodiscard]] StickersType type() const;
|
||||||
[[nodiscard]] bool textColor() const;
|
[[nodiscard]] bool textColor() const;
|
||||||
|
[[nodiscard]] bool channelStatus() const;
|
||||||
|
|
||||||
void setThumbnail(const ImageWithLocation &data);
|
void setThumbnail(const ImageWithLocation &data);
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ void EmojiStatusPanel::show(
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.button = button,
|
.button = button,
|
||||||
.animationSizeTag = animationSizeTag,
|
.animationSizeTag = animationSizeTag,
|
||||||
|
.ensureAddedEmojiId = controller->session().user()->emojiStatusId(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,28 +100,41 @@ void EmojiStatusPanel::show(Descriptor &&descriptor) {
|
||||||
}
|
}
|
||||||
_panelButton = button;
|
_panelButton = button;
|
||||||
_animationSizeTag = descriptor.animationSizeTag;
|
_animationSizeTag = descriptor.animationSizeTag;
|
||||||
auto list = std::vector<DocumentId>();
|
const auto feed = [=, now = descriptor.ensureAddedEmojiId](
|
||||||
|
std::vector<DocumentId> list) {
|
||||||
|
list.insert(begin(list), 0);
|
||||||
|
if (now && !ranges::contains(list, now)) {
|
||||||
|
list.push_back(now);
|
||||||
|
}
|
||||||
|
_panel->selector()->provideRecentEmoji(list);
|
||||||
|
};
|
||||||
if (descriptor.backgroundEmojiMode) {
|
if (descriptor.backgroundEmojiMode) {
|
||||||
controller->session().api().peerPhoto().emojiListValue(
|
controller->session().api().peerPhoto().emojiListValue(
|
||||||
Api::PeerPhoto::EmojiListType::Background
|
Api::PeerPhoto::EmojiListType::Background
|
||||||
) | rpl::start_with_next([=](std::vector<DocumentId> &&list) {
|
) | rpl::start_with_next([=](std::vector<DocumentId> &&list) {
|
||||||
list.insert(begin(list), 0);
|
feed(std::move(list));
|
||||||
if (const auto now = descriptor.currentBackgroundEmojiId) {
|
|
||||||
if (!ranges::contains(list, now)) {
|
|
||||||
list.push_back(now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_panel->selector()->provideRecentEmoji(list);
|
|
||||||
}, _panel->lifetime());
|
}, _panel->lifetime());
|
||||||
|
} else if (descriptor.channelStatusMode) {
|
||||||
|
const auto &statuses = controller->session().data().emojiStatuses();
|
||||||
|
const auto &other = statuses.list(Data::EmojiStatuses::Type::ChannelDefault);
|
||||||
|
auto list = statuses.list(Data::EmojiStatuses::Type::ChannelColored);
|
||||||
|
if (list.size() > kLimitFirstRow - 1) {
|
||||||
|
list.erase(begin(list) + kLimitFirstRow - 1, end(list));
|
||||||
|
}
|
||||||
|
list.reserve(list.size() + other.size() + 1);
|
||||||
|
for (const auto &id : other) {
|
||||||
|
if (!ranges::contains(list, id)) {
|
||||||
|
list.push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
feed(std::move(list));
|
||||||
} else {
|
} else {
|
||||||
const auto self = controller->session().user();
|
|
||||||
const auto &statuses = controller->session().data().emojiStatuses();
|
const auto &statuses = controller->session().data().emojiStatuses();
|
||||||
const auto &recent = statuses.list(Data::EmojiStatuses::Type::Recent);
|
const auto &recent = statuses.list(Data::EmojiStatuses::Type::Recent);
|
||||||
const auto &other = statuses.list(Data::EmojiStatuses::Type::Default);
|
const auto &other = statuses.list(Data::EmojiStatuses::Type::Default);
|
||||||
auto list = statuses.list(Data::EmojiStatuses::Type::Colored);
|
auto list = statuses.list(Data::EmojiStatuses::Type::Colored);
|
||||||
list.insert(begin(list), 0);
|
if (list.size() > kLimitFirstRow - 1) {
|
||||||
if (list.size() > kLimitFirstRow) {
|
list.erase(begin(list) + kLimitFirstRow - 1, end(list));
|
||||||
list.erase(begin(list) + kLimitFirstRow, end(list));
|
|
||||||
}
|
}
|
||||||
list.reserve(list.size() + recent.size() + other.size() + 1);
|
list.reserve(list.size() + recent.size() + other.size() + 1);
|
||||||
for (const auto &id : ranges::views::concat(recent, other)) {
|
for (const auto &id : ranges::views::concat(recent, other)) {
|
||||||
|
@ -128,15 +142,12 @@ void EmojiStatusPanel::show(Descriptor &&descriptor) {
|
||||||
list.push_back(id);
|
list.push_back(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ranges::contains(list, self->emojiStatusId())) {
|
feed(std::move(list));
|
||||||
list.push_back(self->emojiStatusId());
|
|
||||||
}
|
|
||||||
_panel->selector()->provideRecentEmoji(list);
|
|
||||||
}
|
}
|
||||||
const auto parent = _panel->parentWidget();
|
const auto parent = _panel->parentWidget();
|
||||||
const auto global = button->mapToGlobal(QPoint());
|
const auto global = button->mapToGlobal(QPoint());
|
||||||
const auto local = parent->mapFromGlobal(global);
|
const auto local = parent->mapFromGlobal(global);
|
||||||
if (descriptor.backgroundEmojiMode) {
|
if (descriptor.backgroundEmojiMode || descriptor.channelStatusMode) {
|
||||||
_panel->moveBottomRight(
|
_panel->moveBottomRight(
|
||||||
local.y() + (st::normalFont->height / 2),
|
local.y() + (st::normalFont->height / 2),
|
||||||
local.x() + button->width() * 3);
|
local.x() + button->width() * 3);
|
||||||
|
@ -175,18 +186,22 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
nullptr,
|
nullptr,
|
||||||
Descriptor{
|
Descriptor{
|
||||||
.show = controller->uiShow(),
|
.show = controller->uiShow(),
|
||||||
.st = (descriptor.backgroundEmojiMode
|
.st = ((descriptor.backgroundEmojiMode
|
||||||
|
|| descriptor.channelStatusMode)
|
||||||
? st::backgroundEmojiPan
|
? st::backgroundEmojiPan
|
||||||
: st::statusEmojiPan),
|
: st::statusEmojiPan),
|
||||||
.level = Window::GifPauseReason::Layer,
|
.level = Window::GifPauseReason::Layer,
|
||||||
.mode = (descriptor.backgroundEmojiMode
|
.mode = (descriptor.backgroundEmojiMode
|
||||||
? Mode::BackgroundEmoji
|
? Mode::BackgroundEmoji
|
||||||
|
: descriptor.channelStatusMode
|
||||||
|
? Mode::ChannelStatus
|
||||||
: Mode::EmojiStatus),
|
: Mode::EmojiStatus),
|
||||||
.customTextColor = descriptor.customTextColor,
|
.customTextColor = descriptor.customTextColor,
|
||||||
}));
|
}));
|
||||||
_customTextColor = descriptor.customTextColor;
|
_customTextColor = descriptor.customTextColor;
|
||||||
_backgroundEmojiMode = descriptor.backgroundEmojiMode;
|
_backgroundEmojiMode = descriptor.backgroundEmojiMode;
|
||||||
_panel->setDropDown(!_backgroundEmojiMode);
|
_channelStatusMode = descriptor.channelStatusMode;
|
||||||
|
_panel->setDropDown(!_backgroundEmojiMode && !_channelStatusMode);
|
||||||
_panel->setDesiredHeightValues(
|
_panel->setDesiredHeightValues(
|
||||||
1.,
|
1.,
|
||||||
st::emojiPanMinHeight / 2,
|
st::emojiPanMinHeight / 2,
|
||||||
|
@ -218,14 +233,14 @@ void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
return Chosen{ .animation = data.messageSendingFrom };
|
return Chosen{ .animation = data.messageSendingFrom };
|
||||||
});
|
});
|
||||||
|
|
||||||
if (descriptor.backgroundEmojiMode) {
|
if (descriptor.backgroundEmojiMode || descriptor.channelStatusMode) {
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
std::move(statusChosen),
|
std::move(statusChosen),
|
||||||
std::move(emojiChosen)
|
std::move(emojiChosen)
|
||||||
) | rpl::start_with_next([=](const Chosen &chosen) {
|
) | rpl::start_with_next([=](const Chosen &chosen) {
|
||||||
const auto owner = &controller->session().data();
|
const auto owner = &controller->session().data();
|
||||||
startAnimation(owner, body, chosen.id, chosen.animation);
|
startAnimation(owner, body, chosen.id, chosen.animation);
|
||||||
_backgroundEmojiChosen.fire_copy(chosen.id);
|
_someCustomChosen.fire({ chosen.id, chosen.until });
|
||||||
_panel->hideAnimated();
|
_panel->hideAnimated();
|
||||||
}, _panel->lifetime());
|
}, _panel->lifetime());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -51,15 +51,20 @@ public:
|
||||||
not_null<Window::SessionController*> controller;
|
not_null<Window::SessionController*> controller;
|
||||||
not_null<QWidget*> button;
|
not_null<QWidget*> button;
|
||||||
Data::CustomEmojiSizeTag animationSizeTag = {};
|
Data::CustomEmojiSizeTag animationSizeTag = {};
|
||||||
DocumentId currentBackgroundEmojiId = 0;
|
DocumentId ensureAddedEmojiId = 0;
|
||||||
Fn<QColor()> customTextColor;
|
Fn<QColor()> customTextColor;
|
||||||
bool backgroundEmojiMode = false;
|
bool backgroundEmojiMode = false;
|
||||||
|
bool channelStatusMode = false;
|
||||||
};
|
};
|
||||||
void show(Descriptor &&descriptor);
|
void show(Descriptor &&descriptor);
|
||||||
void repaint();
|
void repaint();
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<DocumentId> backgroundEmojiChosen() const {
|
struct CustomChosen {
|
||||||
return _backgroundEmojiChosen.events();
|
DocumentId id = 0;
|
||||||
|
TimeId until = 0;
|
||||||
|
};
|
||||||
|
[[nodiscard]] rpl::producer<CustomChosen> someCustomChosen() const {
|
||||||
|
return _someCustomChosen.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
||||||
|
@ -81,9 +86,10 @@ private:
|
||||||
Fn<bool(DocumentId)> _chooseFilter;
|
Fn<bool(DocumentId)> _chooseFilter;
|
||||||
QPointer<QWidget> _panelButton;
|
QPointer<QWidget> _panelButton;
|
||||||
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
||||||
rpl::event_stream<DocumentId> _backgroundEmojiChosen;
|
rpl::event_stream<CustomChosen> _someCustomChosen;
|
||||||
Data::CustomEmojiSizeTag _animationSizeTag = {};
|
Data::CustomEmojiSizeTag _animationSizeTag = {};
|
||||||
bool _backgroundEmojiMode = false;
|
bool _backgroundEmojiMode = false;
|
||||||
|
bool _channelStatusMode = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -526,7 +526,8 @@ bool ShowReactPremiumError(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
const Data::ReactionId &id) {
|
const Data::ReactionId &id) {
|
||||||
if (controller->session().premium()
|
if (controller->session().premium()
|
||||||
|| ranges::contains(item->chosenReactions(), id)) {
|
|| ranges::contains(item->chosenReactions(), id)
|
||||||
|
|| item->history()->peer->isBroadcast()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto &list = controller->session().data().reactions().list(
|
const auto &list = controller->session().data().reactions().list(
|
||||||
|
|
Loading…
Reference in New Issue