diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 678446bce..d5bae2a8b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -887,6 +887,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_faved_stickers" = "Favorite stickers"; "lng_faved_stickers_add" = "Add to Favorites"; "lng_faved_stickers_remove" = "Remove from Favorites"; +"lng_group_stickers" = "Group stickers"; +"lng_group_stickers_description" = "You can choose sticker set which will be available for every member while in the group chat."; +"lng_group_stickers_add" = "Choose sticker set"; "lng_switch_stickers" = "Stickers"; "lng_switch_emoji" = "Emoji"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 02511c0bf..3e596128f 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -313,6 +313,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt auto canViewAdmins = channel->canViewAdmins(); auto canViewMembers = channel->canViewMembers(); + auto canEditStickers = channel->canEditStickers(); channel->flagsFull = f.vflags.v; if (auto photo = App::feedPhoto(f.vchat_photo)) { @@ -382,11 +383,25 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt } else { channel->mgInfo->pinnedMsgId = 0; } + + auto stickersChanged = (canEditStickers != channel->canEditStickers()); + auto stickerSet = (f.has_stickerset() ? &f.vstickerset.c_stickerSet() : nullptr); + auto newSetId = (stickerSet ? stickerSet->vid.v : 0); + auto oldSetId = (channel->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) ? channel->mgInfo->stickerSet.c_inputStickerSetID().vid.v : 0; + if (oldSetId != newSetId) { + channel->mgInfo->stickerSet = stickerSet ? MTP_inputStickerSetID(stickerSet->vid, stickerSet->vaccess_hash) : MTP_inputStickerSetEmpty(); + stickersChanged = true; + } + if (stickersChanged) { + Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelStickersChanged); + } } channel->fullUpdated(); if (canViewAdmins != channel->canViewAdmins() - || canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged); + || canViewMembers != channel->canViewMembers()) { + Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged); + } notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings); } @@ -1260,101 +1275,7 @@ PeerData *ApiWrap::notifySettingReceived(MTPInputNotifyPeer notifyPeer, const MT void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { _stickerSetRequests.remove(setId); - - if (result.type() != mtpc_messages_stickerSet) return; - auto &d(result.c_messages_stickerSet()); - - if (d.vset.type() != mtpc_stickerSet) return; - auto &s(d.vset.c_stickerSet()); - - auto &sets = Global::RefStickerSets(); - auto it = sets.find(setId); - if (it == sets.cend()) return; - - it->access = s.vaccess_hash.v; - it->hash = s.vhash.v; - it->shortName = qs(s.vshort_name); - it->title = stickerSetTitle(s); - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special); - it->flags = s.vflags.v | clientFlags; - it->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded; - - auto &d_docs = d.vdocuments.v; - auto custom = sets.find(Stickers::CustomSetId); - - StickerPack pack; - pack.reserve(d_docs.size()); - for (int32 i = 0, l = d_docs.size(); i != l; ++i) { - DocumentData *doc = App::feedDocument(d_docs.at(i)); - if (!doc || !doc->sticker()) continue; - - pack.push_back(doc); - if (custom != sets.cend()) { - int32 index = custom->stickers.indexOf(doc); - if (index >= 0) { - custom->stickers.removeAt(index); - } - } - } - if (custom != sets.cend() && custom->stickers.isEmpty()) { - sets.erase(custom); - custom = sets.end(); - } - - auto writeRecent = false; - auto &recent = cGetRecentStickers(); - for (auto i = recent.begin(); i != recent.cend();) { - if (it->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - - if (pack.isEmpty()) { - int removeIndex = Global::StickerSetsOrder().indexOf(setId); - if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); - sets.erase(it); - } else { - it->stickers = pack; - it->emoji.clear(); - auto &v = d.vpacks.v; - for (auto i = 0, l = v.size(); i != l; ++i) { - if (v[i].type() != mtpc_stickerPack) continue; - - auto &pack = v[i].c_stickerPack(); - if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { - emoji = emoji->original(); - auto &stickers = pack.vdocuments.v; - - StickerPack p; - p.reserve(stickers.size()); - for (auto j = 0, c = stickers.size(); j != c; ++j) { - auto doc = App::document(stickers[j].v); - if (!doc || !doc->sticker()) continue; - - p.push_back(doc); - } - it->emoji.insert(emoji, p); - } - } - } - - if (writeRecent) { - Local::writeUserSettings(); - } - - if (it->flags & MTPDstickerSet::Flag::f_installed) { - if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { - Local::writeInstalledStickers(); - } - } - if (it->flags & MTPDstickerSet_ClientFlag::f_featured) { - Local::writeFeaturedStickers(); - } - - if (App::main()) emit App::main()->stickersUpdated(); + Stickers::FeedSetFull(result); } void ApiWrap::requestWebPageDelayed(WebPageData *page) { diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index c280e4347..55ae70721 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -312,7 +312,7 @@ void Application::startApplication() { } void Application::createMessenger() { - t_assert(!App::quitting()); + Expects(!App::quitting()); _messengerInstance = std::make_unique(); } diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 248158de9..51085d425 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -208,6 +208,11 @@ stickerIconMove: 400; stickerPreviewDuration: 150; stickerPreviewMin: 0.1; +stickerGroupCategorySize: 28px; +stickerGroupCategoryAbout: defaultTextStyle; +stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px); +stickerGroupCategoryAdd: stickersTrendingAdd; + stickersToastMaxWidth: 340px; stickersToastPadding: margins(16px, 13px, 16px, 12px); diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index 62ea4b6b3..4104fd244 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -651,6 +651,132 @@ std::vector> GetEmojiListFromSet(gsl::not_nullaccess = set.vaccess_hash.v; + it->title = title; + it->shortName = qs(set.vshort_name); + flags = it->flags; + auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special); + it->flags = set.vflags.v | clientFlags; + if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) { + it->count = set.vcount.v; + it->hash = set.vhash.v; + it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set + } + } + auto changedFlags = (flags ^ it->flags); + if (changedFlags & MTPDstickerSet::Flag::f_archived) { + auto index = Global::ArchivedStickerSetsOrder().indexOf(it->id); + if (it->flags & MTPDstickerSet::Flag::f_archived) { + if (index < 0) { + Global::RefArchivedStickerSetsOrder().push_front(it->id); + } + } else if (index >= 0) { + Global::RefArchivedStickerSetsOrder().removeAt(index); + } + } + return &it.value(); +} + +Set *FeedSetFull(const MTPmessages_StickerSet &data) { + Expects(data.type() == mtpc_messages_stickerSet); + Expects(data.c_messages_stickerSet().vset.type() == mtpc_stickerSet); + auto &d = data.c_messages_stickerSet(); + auto set = FeedSet(d.vset.c_stickerSet()); + + set->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded; + + auto &sets = Global::RefStickerSets(); + auto &d_docs = d.vdocuments.v; + auto custom = sets.find(Stickers::CustomSetId); + + auto pack = StickerPack(); + pack.reserve(d_docs.size()); + for (auto i = 0, l = d_docs.size(); i != l; ++i) { + auto doc = App::feedDocument(d_docs.at(i)); + if (!doc || !doc->sticker()) continue; + + pack.push_back(doc); + if (custom != sets.cend()) { + auto index = custom->stickers.indexOf(doc); + if (index >= 0) { + custom->stickers.removeAt(index); + } + } + } + if (custom != sets.cend() && custom->stickers.isEmpty()) { + sets.erase(custom); + custom = sets.end(); + } + + auto writeRecent = false; + auto &recent = cGetRecentStickers(); + for (auto i = recent.begin(); i != recent.cend();) { + if (set->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + + if (pack.isEmpty()) { + int removeIndex = Global::StickerSetsOrder().indexOf(set->id); + if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); + sets.remove(set->id); + set = nullptr; + } else { + set->stickers = pack; + set->emoji.clear(); + auto &v = d.vpacks.v; + for (auto i = 0, l = v.size(); i != l; ++i) { + if (v[i].type() != mtpc_stickerPack) continue; + + auto &pack = v[i].c_stickerPack(); + if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { + emoji = emoji->original(); + auto &stickers = pack.vdocuments.v; + + StickerPack p; + p.reserve(stickers.size()); + for (auto j = 0, c = stickers.size(); j != c; ++j) { + auto doc = App::document(stickers[j].v); + if (!doc || !doc->sticker()) continue; + + p.push_back(doc); + } + set->emoji.insert(emoji, p); + } + } + } + + if (writeRecent) { + Local::writeUserSettings(); + } + + if (set) { + if (set->flags & MTPDstickerSet::Flag::f_installed) { + if (!(set->flags & MTPDstickerSet::Flag::f_archived)) { + Local::writeInstalledStickers(); + } + } + if (set->flags & MTPDstickerSet_ClientFlag::f_featured) { + Local::writeFeaturedStickers(); + } + } + + if (App::main()) emit App::main()->stickersUpdated(); + + return set; +} + namespace internal { FeaturedReader::FeaturedReader(QObject *parent) : QObject(parent) diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index 3d02e8926..a4d01ea43 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -42,6 +42,9 @@ void GifsReceived(const QVector &items, int32 hash); StickerPack GetListByEmoji(gsl::not_null emoji); std::vector> GetEmojiListFromSet(gsl::not_null document); +Set *FeedSet(const MTPDstickerSet &data); +Set *FeedSetFull(const MTPmessages_StickerSet &data); + namespace internal { class FeaturedReader : public QObject, private MTP::Sender { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 49bb727a2..d2011cf93 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -33,6 +33,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/sticker_set_box.h" #include "boxes/stickers_box.h" #include "boxes/confirm_box.h" +#include "auth_session.h" +#include "observer_peer.h" +#include "apiwrap.h" namespace ChatHelpers { namespace { @@ -49,6 +52,7 @@ struct StickerIcon { } uint64 setId = 0; DocumentData *sticker = nullptr; + ChannelData *megagroup = nullptr; int pixw = 0; int pixh = 0; @@ -207,6 +211,8 @@ void StickersListWidget::Footer::paintEvent(QPaintEvent *e) { auto pix = icon.sticker->thumb->pix(icon.pixw, icon.pixh); p.drawPixmapLeft(x + (st::emojiCategory.width - icon.pixw) / 2, _iconsTop + (st::emojiCategory.height - icon.pixh) / 2, width(), pix); + } else if (icon.megagroup) { + icon.megagroup->paintUserpicLeft(p, x + (st::emojiCategory.width - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiCategory.height - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); } else { auto getSpecialSetIcon = [](uint64 setId) { if (setId == Stickers::FeaturedSetId) { @@ -432,7 +438,8 @@ StickersListWidget::StickersListWidget(QWidget *parent, gsl::not_nullwidth(_addText)) -, _settings(this, lang(lng_stickers_you_have)) { +, _settings(this, lang(lng_stickers_you_have)) +, _megagroupSetAbout(st::emojiPanWidth - st::emojiScroll.width - st::emojiPanHeaderLeft) { resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight()); setMouseTracking(true); @@ -447,6 +454,11 @@ StickersListWidget::StickersListWidget(QWidget *parent, gsl::not_null StickersListWidget::createFooter() { @@ -504,9 +516,14 @@ bool StickersListWidget::enumerateSections(Callback callback) const { auto &set = _mySets[i]; info.section = i; info.count = set.pack.size(); - info.rowsCount = (info.count / kStickersPanelPerRow) + ((info.count % kStickersPanelPerRow) ? 1 : 0); info.rowsTop = info.top + (setHasTitle(set) ? st::emojiPanHeader : st::stickerPanPadding); - info.rowsBottom = info.rowsTop + info.rowsCount * st::stickerPanSize.height(); + if (set.id == Stickers::MegagroupEmptySetId) { + info.rowsCount = 0; + info.rowsBottom = info.rowsTop + _megagroupSetButtonRect.y() + _megagroupSetButtonRect.height() + st::stickerGroupCategoryAddMargin.bottom(); + } else { + info.rowsCount = (info.count / kStickersPanelPerRow) + ((info.count % kStickersPanelPerRow) ? 1 : 0); + info.rowsBottom = info.rowsTop + info.rowsCount * st::stickerPanSize.height(); + } if (!callback(info)) { return false; } @@ -744,6 +761,10 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, info.top + st::emojiPanHeaderTop, width(), titleText, titleWidth); } if (clip.top() + clip.height() > info.rowsTop) { + if (set.id == Stickers::MegagroupEmptySetId) { + auto buttonSelected = (base::get_if(&_selected) != nullptr); + paintMegagroupEmptySet(p, info.rowsTop, buttonSelected, ms); + } auto special = (set.flags & MTPDstickerSet::Flag::f_official) != 0; auto fromRow = floorclamp(clip.y() - info.rowsTop, st::stickerPanSize.height(), 0, info.rowsCount); auto toRow = ceilclamp(clip.y() + clip.height() - info.rowsTop, st::stickerPanSize.height(), 0, info.rowsCount); @@ -762,6 +783,29 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { }); } +int StickersListWidget::megagroupSetInfoLeft() const { + return st::emojiPanHeaderLeft - st::buttonRadius; +} + +void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected, TimeMs ms) { + auto infoLeft = megagroupSetInfoLeft(); + _megagroupSetAbout.drawLeft(p, infoLeft, y, width() - infoLeft, width()); + + auto &textBg = buttonSelected ? st::stickerGroupCategoryAdd.textBgOver : st::stickerGroupCategoryAdd.textBg; + + auto button = _megagroupSetButtonRect.translated(0, y); + App::roundRect(p, myrtlrect(button), textBg, ImageRoundRadius::Small); + if (_megagroupSetButtonRipple) { + _megagroupSetButtonRipple->paint(p, button.x(), button.y(), width(), ms); + if (_megagroupSetButtonRipple->empty()) { + _megagroupSetButtonRipple.reset(); + } + } + p.setFont(st::stickerGroupCategoryAdd.font); + p.setPen(buttonSelected ? st::stickerGroupCategoryAdd.textFgOver : st::stickerGroupCategoryAdd.textFg); + p.drawTextLeft(button.x() - (st::stickerGroupCategoryAdd.width / 2), button.y() + st::stickerGroupCategoryAdd.textTop, width(), _megagroupSetButtonText, _megagroupSetButtonTextWidth); +} + void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected) { auto sticker = set.pack[index]; if (!sticker->sticker()) return; @@ -825,8 +869,16 @@ bool StickersListWidget::hasRemoveButton(int index) const { if (index < 0 || index >= _mySets.size()) { return false; } - auto flags = _mySets[index].flags; - return !(flags & MTPDstickerSet_ClientFlag::f_special); + auto &set = _mySets[index]; + auto flags = set.flags; + if (!(flags & MTPDstickerSet_ClientFlag::f_special)) { + return true; + } + if (set.megagroupSet) { + t_assert(_megagroupSet != nullptr); + return (set.id == Stickers::MegagroupEmptySetId) ? (index + 1 != _mySets.size()) : _megagroupSet->canEditStickers(); + } + return false; } QRect StickersListWidget::removeButtonRect(int index) const { @@ -857,6 +909,10 @@ void StickersListWidget::setPressed(OverState newPressed) { if (set.ripple) { set.ripple->lastStop(); } + } else if (base::get_if(&_pressed)) { + if (_megagroupSetButtonRipple) { + _megagroupSetButtonRipple->lastStop(); + } } _pressed = newPressed; if (auto button = base::get_if(&_pressed)) { @@ -867,9 +923,32 @@ void StickersListWidget::setPressed(OverState newPressed) { set.ripple = createButtonRipple(button->section); } set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section)); + } else if (base::get_if(&_pressed)) { + if (!_megagroupSetButtonRipple) { + auto maskSize = _megagroupSetButtonRect.size(); + auto mask = Ui::RippleAnimation::roundRectMask(maskSize, st::buttonRadius); + _megagroupSetButtonRipple = std::make_unique(st::stickerGroupCategoryAdd.ripple, std::move(mask), [this] { + rtlupdate(megagroupSetButtonRectFinal()); + }); + } + _megagroupSetButtonRipple->add(mapFromGlobal(QCursor::pos()) - myrtlrect(megagroupSetButtonRectFinal()).topLeft()); } } +QRect StickersListWidget::megagroupSetButtonRectFinal() const { + auto result = QRect(); + if (_section == Section::Stickers) { + enumerateSections([this, &result](const SectionInfo &info) { + if (_mySets[info.section].id == Stickers::MegagroupEmptySetId) { + result = _megagroupSetButtonRect.translated(0, info.rowsTop); + return false; + } + return true; + }); + } + return result; +} + QSharedPointer StickersListWidget::createButtonRipple(int section) { if (_section == Section::Featured) { auto maskSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height); @@ -937,6 +1016,8 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { } else { removeSet(sets[button->section].id); } + } else if (base::get_if(&pressed)) { + Ui::show(Box("TODO")); } } } @@ -1047,6 +1128,7 @@ void StickersListWidget::refreshStickers() { refreshRecentStickers(false); refreshFavedStickers(); + refreshMegagroupStickers(); for_const (auto setId, Global::StickerSetsOrder()) { appendSet(_mySets, setId, AppendSkip::Archived); } @@ -1203,6 +1285,32 @@ void StickersListWidget::refreshFavedStickers() { _mySets.push_back(Set(Stickers::FavedSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_faved_stickers), it->stickers.size() * 2, it->stickers)); } +void StickersListWidget::refreshMegagroupStickers() { + if (!_megagroupSet) { + return; + } + if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetEmpty) { + if (_megagroupSet->canEditStickers()) { + _mySets.push_back(Set(Stickers::MegagroupEmptySetId, qFlags(MTPDstickerSet_ClientFlag::f_special), lang(lng_group_stickers), 0)); + _mySets.back().megagroupSet = true; + } + return; + } + if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) { + auto &set = _megagroupSet->mgInfo->stickerSet.c_inputStickerSetID(); + appendSet(_mySets, set.vid.v); + if (_mySets.back().id == set.vid.v) { + _mySets.back().megagroupSet = true; + return; + } + } + request(MTPmessages_GetStickerSet(_megagroupSet->mgInfo->stickerSet)).done([this](const MTPmessages_StickerSet &result) { + if (auto set = Stickers::FeedSetFull(result)) { + refreshStickers(); + } + }); +} + void StickersListWidget::fillIcons(QList &icons) { icons.clear(); icons.reserve(_mySets.size() + 1); @@ -1220,6 +1328,11 @@ void StickersListWidget::fillIcons(QList &icons) { icons.push_back(StickerIcon(Stickers::FavedSetId)); } for (auto l = _mySets.size(); i != l; ++i) { + if (_mySets[i].megagroupSet) { + icons.push_back(StickerIcon(Stickers::MegagroupEmptySetId)); + icons.back().megagroup = _megagroupSet; + continue; + } auto s = _mySets[i].pack[0]; auto availw = st::emojiCategory.width - 2 * st::stickerIconPadding, availh = st::emojiCategory.height - 2 * st::stickerIconPadding; auto thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; @@ -1288,26 +1401,32 @@ void StickersListWidget::updateSelected() { if (p.y() >= info.top && p.y() < info.rowsTop) { if (hasRemoveButton(section) && myrtlrect(removeButtonRect(section)).contains(p.x(), p.y())) { newSelected = OverButton { section }; - } else { + } else if (!(sets[section].flags & MTPDstickerSet_ClientFlag::f_special)) { newSelected = OverSet { section }; } } else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom && sx >= 0) { auto yOffset = p.y() - info.rowsTop; auto &set = sets[section]; - auto special = ((set.flags & MTPDstickerSet::Flag::f_official) != 0); - auto rowIndex = qFloor(yOffset / st::stickerPanSize.height()); - auto columnIndex = qFloor(sx / st::stickerPanSize.width()); - auto index = rowIndex * kStickersPanelPerRow + columnIndex; - if (index >= 0 && index < set.pack.size()) { - auto overDelete = false; - if (stickerHasDeleteButton(set, index)) { - auto inx = sx - (columnIndex * st::stickerPanSize.width()); - auto iny = yOffset - (rowIndex * st::stickerPanSize.height()); - if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.width() && iny < st::stickerPanDelete.height()) { - overDelete = true; - } + if (set.id == Stickers::MegagroupEmptySetId) { + if (_megagroupSetButtonRect.contains(stickersLeft() + sx, yOffset)) { + newSelected = OverGroupAdd {}; + } + } else { + auto special = ((set.flags & MTPDstickerSet::Flag::f_official) != 0); + auto rowIndex = qFloor(yOffset / st::stickerPanSize.height()); + auto columnIndex = qFloor(sx / st::stickerPanSize.width()); + auto index = rowIndex * kStickersPanelPerRow + columnIndex; + if (index >= 0 && index < set.pack.size()) { + auto overDelete = false; + if (stickerHasDeleteButton(set, index)) { + auto inx = sx - (columnIndex * st::stickerPanSize.width()); + auto iny = yOffset - (rowIndex * st::stickerPanSize.height()); + if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.width() && iny < st::stickerPanDelete.height()) { + overDelete = true; + } + } + newSelected = OverSticker { section, index, overDelete }; } - newSelected = OverSticker { section, index, overDelete }; } } } @@ -1419,6 +1538,31 @@ void StickersListWidget::showStickerSet(uint64 setId) { update(); } +void StickersListWidget::refreshMegagroupSetGeometry() { + auto left = megagroupSetInfoLeft(); + auto availableWidth = (width() - left); + auto top = _megagroupSetAbout.countHeight(availableWidth) + st::stickerGroupCategoryAddMargin.top(); + _megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.font->width(_megagroupSetButtonText); + auto buttonWidth = _megagroupSetButtonTextWidth - st::stickerGroupCategoryAdd.width; + _megagroupSetButtonRect = QRect(left, top, buttonWidth, st::stickerGroupCategoryAdd.height); +} + +void StickersListWidget::showMegagroupSet(ChannelData *megagroup) { + Expects(!megagroup || megagroup->isMegagroup()); + if (_megagroupSet != megagroup) { + _megagroupSet = megagroup; + + if (_megagroupSetAbout.isEmpty()) { + _megagroupSetAbout.setText(st::stickerGroupCategoryAbout, lang(lng_group_stickers_description)); + _megagroupSetButtonText = lang(lng_group_stickers_add).toUpper(); + refreshMegagroupSetGeometry(); + } + _megagroupSetButtonRipple.reset(); + + refreshStickers(); + } +} + void StickersListWidget::displaySet(quint64 setId) { auto &sets = Global::StickerSets(); auto it = sets.constFind(setId); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index f353ae56d..b37e6eb10 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -47,6 +47,7 @@ public: object_ptr createFooter() override; void showStickerSet(uint64 setId); + void showMegagroupSet(ChannelData *megagroup); void refreshStickers(); @@ -104,6 +105,8 @@ private: struct OverButton { int section; }; + struct OverGroupAdd { + }; friend inline bool operator==(OverSticker a, OverSticker b) { return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete); } @@ -113,7 +116,10 @@ private: friend inline bool operator==(OverButton a, OverButton b) { return (a.section == b.section); } - using OverState = base::optional_variant; + friend inline bool operator==(OverGroupAdd a, OverGroupAdd b) { + return true; + } + using OverState = base::optional_variant; struct SectionInfo { int section = 0; @@ -128,6 +134,7 @@ private: Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), pack(pack) { } uint64 id; + bool megagroupSet = false; MTPDstickerSet::Flags flags; QString title; StickerPack pack; @@ -148,6 +155,7 @@ private: bool stickerHasDeleteButton(const Set &set, int index) const; void refreshRecentStickers(bool resize = true); void refreshFavedStickers(); + void refreshMegagroupStickers(); void updateSelected(); void setSelected(OverState newSelected); @@ -173,6 +181,7 @@ private: void paintFeaturedStickers(Painter &p, QRect clip); void paintStickers(Painter &p, QRect clip); + void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected, TimeMs ms); void paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected); int stickersRight() const; @@ -180,12 +189,16 @@ private: QRect featuredAddRect(int index) const; bool hasRemoveButton(int index) const; QRect removeButtonRect(int index) const; + int megagroupSetInfoLeft() const; + void refreshMegagroupSetGeometry(); + QRect megagroupSetButtonRectFinal() const; enum class AppendSkip { + None, Archived, Installed, }; - void appendSet(Sets &to, uint64 setId, AppendSkip skip); + void appendSet(Sets &to, uint64 setId, AppendSkip skip = AppendSkip::None); void selectEmoji(EmojiPtr emoji); int stickersLeft() const; @@ -194,6 +207,7 @@ private: void removeRecentSticker(int section, int index); void removeFavedSticker(int section, int index); + ChannelData *_megagroupSet = nullptr; Sets _mySets; Sets _featuredSets; OrderedSet _installedLocallySets; @@ -210,6 +224,12 @@ private: OverState _pressed = nullptr; QPoint _lastMousePosition; + Text _megagroupSetAbout; + QString _megagroupSetButtonText; + int _megagroupSetButtonTextWidth = 0; + QRect _megagroupSetButtonRect; + std::unique_ptr _megagroupSetButtonRipple; + QString _addText; int _addWidth; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 17587b557..946095529 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -519,6 +519,10 @@ void TabbedSelector::stickersInstalled(uint64 setId) { stickers()->showStickerSet(setId); } +void TabbedSelector::showMegagroupSet(ChannelData *megagroup) { + stickers()->showMegagroupSet(megagroup); +} + void TabbedSelector::setCurrentPeer(PeerData *peer) { gifs()->setInlineQueryPeer(peer); _currentPeer = peer; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index b6815fcc1..d773eacec 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -61,6 +61,7 @@ public: void setRoundRadius(int radius); void refreshStickers(); void stickersInstalled(uint64 setId); + void showMegagroupSet(ChannelData *megagroup); void setCurrentPeer(PeerData *peer); void hideFinished(); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 286d879a9..b470bba1d 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -535,45 +535,6 @@ DefineVar(Sandbox, ProxyData, PreLaunchProxy); } // namespace Sandbox -namespace Stickers { - -Set *FeedSet(const MTPDstickerSet &set) { - MTPDstickerSet::Flags flags = 0; - - auto &sets = Global::RefStickerSets(); - auto it = sets.find(set.vid.v); - auto title = stickerSetTitle(set); - if (it == sets.cend()) { - it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded)); - } else { - it->access = set.vaccess_hash.v; - it->title = title; - it->shortName = qs(set.vshort_name); - flags = it->flags; - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special); - it->flags = set.vflags.v | clientFlags; - if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) { - it->count = set.vcount.v; - it->hash = set.vhash.v; - it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set - } - } - auto changedFlags = (flags ^ it->flags); - if (changedFlags & MTPDstickerSet::Flag::f_archived) { - auto index = Global::ArchivedStickerSetsOrder().indexOf(it->id); - if (it->flags & MTPDstickerSet::Flag::f_archived) { - if (index < 0) { - Global::RefArchivedStickerSetsOrder().push_front(it->id); - } - } else if (index >= 0) { - Global::RefArchivedStickerSetsOrder().removeAt(index); - } - } - return &it.value(); -} - -} // namespace Stickers - namespace Global { namespace internal { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index e673a8dee..0017bc1f5 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -251,6 +251,7 @@ constexpr auto NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, s constexpr auto CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers constexpr auto FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL; // for emoji/stickers panel, should not appear in Sets constexpr auto FavedSetId = 0xFFFFFFFFFFFFFFFAULL; // for cloud-stored faved stickers +constexpr auto MegagroupEmptySetId = 0xFFFFFFFFFFFFFFEFULL; // for setting up megagroup sticker set struct Set { Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags) : id(id) @@ -278,8 +279,6 @@ inline MTPInputStickerSet inputSetId(const Set &set) { return MTP_inputStickerSetShortName(MTP_string(set.shortName)); } -Set *FeedSet(const MTPDstickerSet &set); - } // namespace Stickers namespace Global { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index b1ef3d3e5..d59189c8f 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1772,6 +1772,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re applyDraft(false); _send->finishAnimation(); + _tabbedSelector->showMegagroupSet(_peer->asMegagroup()); + updateControlsGeometry(); if (!_previewCancelled) { onPreviewParse(); @@ -1786,6 +1788,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re unreadCountChanged(_history); // set _historyDown badge. } else { clearFieldText(); + _tabbedSelector->showMegagroupSet(nullptr); doneShow(); } updateForwarding(); diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index 8fc257118..a2c127ab5 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -33,7 +33,7 @@ struct PeerUpdate { } PeerData *peer; - enum class Flag { + enum class Flag : uint32 { None = 0x00000000U, // Common flags @@ -50,7 +50,7 @@ struct PeerUpdate { InviteLinkChanged = 0x00000100U, MembersChanged = 0x00000200U, AdminsChanged = 0x00000400U, - BannedUsersChanged = 0x00000800U, + BannedUsersChanged = 0x00000800U, // For users UserCanShareContact = 0x00010000U, @@ -69,6 +69,7 @@ struct PeerUpdate { // For channels ChannelAmIn = 0x00010000U, ChannelRightsChanged = 0x00020000U, + ChannelStickersChanged = 0x00040000U, }; Q_DECLARE_FLAGS(Flags, Flag); Flags flags = 0; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index c01a8e167..0420035ce 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -750,6 +750,7 @@ struct MegagroupInfo { int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other MsgId pinnedMsgId = 0; bool joinedMessageFound = false; + MTPInputStickerSet stickerSet = MTP_inputStickerSetEmpty(); enum LastParticipantsStatus { LastParticipantsUpToDate = 0x00, @@ -760,6 +761,7 @@ struct MegagroupInfo { int lastParticipantsCount = 0; ChatData *migrateFromPtr = nullptr; + }; class ChannelData : public PeerData { @@ -922,6 +924,9 @@ public: bool canEditUsername() const { return amCreator() && (flagsFull & MTPDchannelFull::Flag::f_can_set_username); } + bool canEditStickers() const { + return (flagsFull & MTPDchannelFull::Flag::f_can_set_stickers); + } bool canDelete() const { constexpr auto kDeleteChannelMembersLimit = 1000; return amCreator() && (membersCount() <= kDeleteChannelMembersLimit);