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