diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 876cd811ba..13baa9b6f1 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -141,7 +141,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_error_pinned_max#other" = "Sorry, you can pin no more than {count} chats to the top."; "lng_error_public_groups_denied" = "Unfortunately, you were banned from participating in public groups.\n{more_info}"; "lng_error_cant_edit_admin" = "Sorry, you can't edit permissions for this admin."; -"lng_error_cant_add_member" = "Sorry, you can't add the bot to this group."; +"lng_error_cant_add_member" = "Sorry, you can't add the bot to this group. Ask a group admin to do it."; "lng_edit_deleted" = "This message was deleted"; "lng_edit_too_long" = "Your message text is too long"; @@ -1060,8 +1060,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_forward_cant" = "Sorry, no way to forward here :("; "lng_forward_confirm" = "Forward to {recipient}?"; "lng_forward_share_contact" = "Share contact to {recipient}?"; +"lng_forward_share_cant" = "Sorry, no way to share contact here :("; "lng_forward_send_file_confirm" = "Send «{name}» to {recipient}?"; "lng_forward_send_files_confirm" = "Send selected files to {recipient}?"; +"lng_forward_send_files_cant" = "Sorry, no way to send media here :("; "lng_forward_send" = "Send"; "lng_forward_messages#one" = "{count} forwarded message"; "lng_forward_messages#other" = "{count} forwarded messages"; @@ -1275,6 +1277,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_rights_chat_banned_forever" = "Forever"; "lng_rights_chat_banned_block" = "Block and remove from group"; +"lng_restricted_send_message" = "The admins of this group restricted you from writing here."; +"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here."; +"lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here."; +"lng_restricted_send_gifs" = "The admins of this group restricted you from posting GIFs here."; +"lng_restricted_send_inline" = "The admins of this group restricted you from posting inline content here."; + // Not used "lng_topbar_info" = "Info"; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 6dbf75d99e..d41fcaec49 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -884,6 +884,25 @@ void shareGameScoreFromItem(HistoryItem *item) { if (!data->requests.empty()) { return; // Share clicked already. } + if (result.empty()) { + return; + } + + auto restrictedEverywhere = true; + auto restrictedSomewhere = false; + for_const (auto peer, result) { + if (auto megagroup = peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_games()) { + restrictedSomewhere = true; + continue; + } + } + restrictedEverywhere = false; + } + if (restrictedEverywhere) { + Ui::show(Box(lang(lng_restricted_send_inline)), KeepOtherLayers); + return; + } auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) { if (auto main = App::main()) { @@ -901,6 +920,12 @@ void shareGameScoreFromItem(HistoryItem *item) { if (auto main = App::main()) { if (auto item = App::histItemById(data->msgId)) { for_const (auto peer, result) { + if (auto megagroup = peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_games()) { + continue; + } + } + MTPVector random = MTP_vector(1, rand_value()); auto request = MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input); auto callback = doneCallback; diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 14baaa7618..bd02381322 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -28,6 +28,11 @@ switchPmButton: RoundButton(defaultBoxButton) { height: 34px; textTop: 7px; } +stickersRestrictedLabel: FlatLabel(defaultFlatLabel) { + width: 320px; + align: align(center); + textFg: noContactsColor; +} stickersTrendingHeader: 45px; stickersTrendingSkip: 15px; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 284ddc8ca5..7dfdf7611b 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -26,12 +26,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "chat_helpers/stickers.h" #include "styles/style_chat_helpers.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" #include "ui/widgets/shadow.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/scroll_area.h" #include "storage/localstorage.h" #include "lang/lang_keys.h" #include "mainwindow.h" +#include "observer_peer.h" namespace ChatHelpers { namespace { @@ -46,7 +48,7 @@ public: LeftToRight, RightToLeft, }; - void setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner); + void setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner, bool wasSectionIcons); void start(); void paintFrame(QPainter &p, float64 dt, float64 opacity); @@ -72,10 +74,11 @@ private: int _painterInnerRight = 0; int _frameIntsPerLineAdd = 0; + bool _wasSectionIcons = false; }; -void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner) { +void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner, bool wasSectionIcons) { Expects(!started()); _direction = direction; _leftImage = QPixmap::fromImage(std::move(left).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly); @@ -109,6 +112,8 @@ void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage _painterInnerWidth = _innerWidth / cIntRetinaFactor(); _painterInnerHeight = _innerHeight / cIntRetinaFactor(); _painterCategoriesTop = _painterInnerBottom - st::emojiCategory.height; + + _wasSectionIcons = wasSectionIcons; } void TabbedSelector::SlideAnimation::start() { @@ -166,7 +171,7 @@ void TabbedSelector::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 Painter p(&_frame); p.setOpacity(opacity); p.fillRect(_painterInnerLeft, _painterInnerTop, _painterInnerWidth, _painterCategoriesTop - _painterInnerTop, st::emojiPanBg); - p.fillRect(_painterInnerLeft, _painterCategoriesTop, _painterInnerWidth, _painterInnerBottom - _painterCategoriesTop, st::emojiPanCategories); + p.fillRect(_painterInnerLeft, _painterCategoriesTop, _painterInnerWidth, _painterInnerBottom - _painterCategoriesTop, _wasSectionIcons ? st::emojiPanCategories : st::emojiPanBg); p.setCompositionMode(QPainter::CompositionMode_SourceOver); if (leftTo > _innerLeft) { p.setOpacity(opacity * leftAlpha); @@ -337,6 +342,12 @@ TabbedSelector::TabbedSelector(QWidget *parent, gsl::not_nullraise(); _tabsSlider->raise(); + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { + if (update.peer == _currentPeer) { + checkRestrictedPeer(); + } + })); + // setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_OpaquePaintEvent, false); showAll(); @@ -354,6 +365,9 @@ void TabbedSelector::resizeEvent(QResizeEvent *e) { _scroll->resize(_scroll->width(), contentHeight); } _bottomShadow->setGeometry(_tabsSlider->x(), _scroll->y() + _scroll->height() - st::lineWidth, _tabsSlider->width(), st::lineWidth); + if (_restrictedLabel) { + _restrictedLabel->move((width() - _restrictedLabel->width()), (height() / 3 - _restrictedLabel->height() / 2)); + } _footerTop = height() - st::emojiCategory.height; for (auto &tab : _tabs) { @@ -394,8 +408,7 @@ void TabbedSelector::paintSlideFrame(Painter &p, TimeMs ms) { } void TabbedSelector::paintContent(Painter &p) { - auto showSectionIcons = (_currentTabType != SelectorTab::Gifs); - auto &bottomBg = showSectionIcons ? st::emojiPanCategories : st::emojiPanBg; + auto &bottomBg = hasSectionIcons() ? st::emojiPanCategories : st::emojiPanBg; if (_roundRadius > 0) { auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius); App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); @@ -410,8 +423,12 @@ void TabbedSelector::paintContent(Painter &p) { auto sidesTop = marginTop(); auto sidesHeight = height() - sidesTop - marginBottom(); - p.fillRect(myrtlrect(width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg); - p.fillRect(myrtlrect(0, sidesTop, st::buttonRadius, sidesHeight), st::emojiPanBg); + if (_restrictedLabel) { + p.fillRect(0, sidesTop, width(), sidesHeight, st::emojiPanBg); + } else { + p.fillRect(myrtlrect(width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg); + p.fillRect(myrtlrect(0, sidesTop, st::buttonRadius, sidesHeight), st::emojiPanBg); + } } int TabbedSelector::marginTop() const { @@ -504,15 +521,56 @@ void TabbedSelector::stickersInstalled(uint64 setId) { stickers()->showStickerSet(setId); } -void TabbedSelector::setInlineQueryPeer(PeerData *peer) { +void TabbedSelector::setCurrentPeer(PeerData *peer) { gifs()->setInlineQueryPeer(peer); + _currentPeer = peer; + checkRestrictedPeer(); +} + +void TabbedSelector::checkRestrictedPeer() { + if (auto megagroup = _currentPeer ? _currentPeer->asMegagroup() : nullptr) { + auto restricted = (_currentTabType == SelectorTab::Stickers) ? megagroup->restrictedRights().is_send_stickers() : + (_currentTabType == SelectorTab::Gifs) ? megagroup->restrictedRights().is_send_gifs() : false; + if (restricted) { + if (!_restrictedLabel) { + auto text = (_currentTabType == SelectorTab::Stickers) ? lang(lng_restricted_send_stickers) : + (_currentTabType == SelectorTab::Gifs) ? lang(lng_restricted_send_gifs) : false; + _restrictedLabel.create(this, text, Ui::FlatLabel::InitType::Simple, st::stickersRestrictedLabel); + _restrictedLabel->show(); + _restrictedLabel->move((width() - _restrictedLabel->width()), (height() / 3 - _restrictedLabel->height() / 2)); + currentTab()->footer()->hide(); + _scroll->hide(); + _bottomShadow->hide(); + update(); + } + return; + } + } + if (_restrictedLabel) { + _restrictedLabel.destroy(); + if (!_a_slide.animating()) { + currentTab()->footer()->show(); + _scroll->show(); + _bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs); + update(); + } + } +} + +bool TabbedSelector::isRestrictedView() { + checkRestrictedPeer(); + return (_restrictedLabel != nullptr); } void TabbedSelector::showAll() { - currentTab()->footer()->show(); - _scroll->show(); + if (isRestrictedView()) { + _restrictedLabel->show(); + } else { + currentTab()->footer()->show(); + _scroll->show(); + _bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs); + } _topShadow->show(); - _bottomShadow->setVisible(_currentTabType == SelectorTab::Gifs); _tabsSlider->show(); } @@ -551,6 +609,10 @@ void TabbedSelector::createTabsSlider() { _topShadow->setGeometry(_tabsSlider->x(), _tabsSlider->bottomNoMargins() - st::lineWidth, _tabsSlider->width(), st::lineWidth); } +bool TabbedSelector::hasSectionIcons() const { + return (_currentTabType != SelectorTab::Gifs) && !_restrictedLabel; +} + void TabbedSelector::switchTab() { auto tab = _tabsSlider->activeSection(); t_assert(tab >= 0 && tab < Tab::kCount); @@ -559,6 +621,7 @@ void TabbedSelector::switchTab() { return; } + auto wasSectionIcons = hasSectionIcons(); auto wasTab = _currentTabType; currentTab()->saveScrollTop(); @@ -573,6 +636,9 @@ void TabbedSelector::switchTab() { currentTab()->returnWidget(std::move(widget)); _currentTabType = newTabType; + _restrictedLabel.destroy(); + checkRestrictedPeer(); + currentTab()->widget()->refreshRecent(); currentTab()->widget()->preloadImages(); setWidgetToScrollArea(); @@ -585,7 +651,7 @@ void TabbedSelector::switchTab() { } _slideAnimation = std::make_unique(); auto slidingRect = QRect(_tabsSlider->x() * cIntRetinaFactor(), _scroll->y() * cIntRetinaFactor(), _tabsSlider->width() * cIntRetinaFactor(), (height() - _scroll->y()) * cIntRetinaFactor()); - _slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect); + _slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect, wasSectionIcons); auto corners = App::cornersMask(ImageRoundRadius::Small); _slideAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); _slideAnimation->start(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 8f84389acc..d308893c43 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -33,6 +33,7 @@ namespace Ui { class PlainShadow; class ScrollArea; class SettingsSlider; +class FlatLabel; } // namesapce Ui namespace Window { @@ -51,7 +52,7 @@ class EmojiListWidget; class StickersListWidget; class GifsListWidget; -class TabbedSelector : public TWidget { +class TabbedSelector : public TWidget, private base::Subscriber { Q_OBJECT public: @@ -60,7 +61,7 @@ public: void setRoundRadius(int radius); void refreshStickers(); void stickersInstalled(uint64 setId); - void setInlineQueryPeer(PeerData *peer); + void setCurrentPeer(PeerData *peer); void hideFinished(); void showStarted(); @@ -150,6 +151,9 @@ private: void paintSlideFrame(Painter &p, TimeMs ms); void paintContent(Painter &p); + void checkRestrictedPeer(); + bool isRestrictedView(); + QImage grabForAnimation(); void scrollToY(int y); @@ -157,6 +161,7 @@ private: void showAll(); void hideForSliding(); + bool hasSectionIcons() const; void setWidgetToScrollArea(); void createTabsSlider(); void switchTab(); @@ -178,6 +183,7 @@ private: int _roundRadius = 0; int _footerTop = 0; + PeerData *_currentPeer = nullptr; class SlideAnimation; std::unique_ptr _slideAnimation; @@ -187,6 +193,7 @@ private: object_ptr _topShadow; object_ptr _bottomShadow; object_ptr _scroll; + object_ptr _restrictedLabel = { nullptr }; std::array _tabs; SelectorTab _currentTabType = SelectorTab::Emoji; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 73029162c1..144a3ed424 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -2947,10 +2947,9 @@ void DialogsWidget::updateDragInScroll(bool inScroll) { void DialogsWidget::dropEvent(QDropEvent *e) { _chooseByDragTimer.stop(); if (_scroll->geometry().contains(e->pos())) { - PeerData *p = _inner->updateFromParentDrag(mapToGlobal(e->pos())); - if (p) { + if (auto peer = _inner->updateFromParentDrag(mapToGlobal(e->pos()))) { e->acceptProposedAction(); - App::main()->onFilesOrForwardDrop(p->id, e->mimeData()); + App::main()->onFilesOrForwardDrop(peer->id, e->mimeData()); } } } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 71f9397420..fc910dc5e0 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -85,6 +85,8 @@ constexpr auto kTabbedSelectorToggleTooltipCount = 3; constexpr auto kScrollToVoiceAfterScrolledMs = 1000; constexpr auto kSkipRepaintWhileScrollMs = 100; constexpr auto kShowMembersDropdownTimeoutMs = 300; +constexpr auto kDisplayEditTimeWarningMs = 300 * 1000; +constexpr auto kFullDayInMs = 86400 * 1000; ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() { return [](ChannelData *channel, MsgId msgId) { @@ -363,8 +365,16 @@ bool HistoryHider::offerPeer(PeerId peer) { } _offered = App::peer(peer); auto phrase = QString(); - QString recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB'; + auto recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB'; if (_sharedContact) { + if (!_offered->canWrite()) { + Ui::show(Box(lang(lng_forward_share_cant))); + _offered = nullptr; + _toText.setText(st::boxLabelStyle, QString()); + _toTextWidth = 0; + resizeEvent(nullptr); + return false; + } phrase = lng_forward_share_contact(lt_recipient, recipient); } else if (_sendPath) { auto toId = _offered->id; @@ -650,6 +660,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to); } }); + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { + if (update.peer == _peer) { + onPreviewCheck(); + } + })); orderWidgets(); } @@ -1879,7 +1894,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _peer = App::peer(peerId); _channel = peerToChannel(_peer->id); _canSendMessages = canSendMessages(_peer); - _tabbedSelector->setInlineQueryPeer(_peer); + _tabbedSelector->setCurrentPeer(_peer); } updateTopBarSelection(); @@ -2143,6 +2158,13 @@ bool HistoryWidget::canWriteMessage() const { return true; } +bool HistoryWidget::isRestrictedWrite() const { + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + return megagroup->restrictedRights().is_send_messages(); + } + return false; +} + void HistoryWidget::updateControlsVisibility() { if (!_a_show.animating()) { _topShadow->setVisible(_peer != nullptr); @@ -3198,7 +3220,13 @@ void HistoryWidget::step_recording(float64 ms, bool timer) { } void HistoryWidget::chooseAttach() { - if (!_history) return; + if (!_peer || !_peer->canWrite()) return; + if (auto megagroup = _peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_media()) { + Ui::show(Box(lang(lng_restricted_send_media))); + return; + } + } auto filter = FileDialog::AllFilesFilter() + qsl(";;Image files (*") + cImgExtensions().join(qsl(" *")) + qsl(")"); @@ -3297,6 +3325,13 @@ void HistoryWidget::recordStartCallback() { if (!Media::Capture::instance()->available()) { return; } + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_send_media()) { + Ui::show(Box(lang(lng_restricted_send_media))); + return; + } + } + emit Media::Capture::instance()->start(); _recording = _inField = true; @@ -4307,6 +4342,12 @@ bool HistoryWidget::confirmSendingFiles(const QStringList &files, CompressConfir } bool HistoryWidget::confirmSendingFiles(const SendingFilesLists &lists, CompressConfirm compressed, const QString *addedComment) { + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_send_media()) { + Ui::show(Box(lang(lng_restricted_send_media))); + return false; + } + } return validateSendingFiles(lists, [this, &lists, compressed, addedComment](const QStringList &files) { auto insertTextOnCancel = QString(); auto sendCallback = [this](const QStringList &files, const QImage &image, std::unique_ptr information, bool compressed, const QString &caption, MsgId replyTo) { @@ -4955,6 +4996,8 @@ void HistoryWidget::updateHistoryGeometry(bool initial, bool loadedDown, const S } else { if (_canSendMessages) { newScrollHeight -= (_field->height() + 2 * st::historySendPadding); + } else if (isRestrictedWrite()) { + newScrollHeight -= _unblock->height(); } if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { newScrollHeight -= st::historyReplyHeight; @@ -5268,16 +5311,34 @@ void HistoryWidget::onFieldTabbed() { } bool HistoryWidget::onStickerSend(DocumentData *sticker) { + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_send_stickers()) { + Ui::show(Box(lang(lng_restricted_send_stickers)), KeepOtherLayers); + return false; + } + } return sendExistingDocument(sticker, QString()); } void HistoryWidget::onPhotoSend(PhotoData *photo) { + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_send_media()) { + Ui::show(Box(lang(lng_restricted_send_media)), KeepOtherLayers); + return; + } + } sendExistingPhoto(photo, QString()); } void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot) { if (!_history || !result || !canSendMessages(_peer)) return; + auto errorText = result->getErrorOnSend(_history); + if (!errorText.isEmpty()) { + Ui::show(Box(errorText)); + return; + } + App::main()->readServerHistory(_history); fastShowAtEnd(_history); @@ -5867,8 +5928,7 @@ void HistoryWidget::onStickerPackInfo() { } void HistoryWidget::previewCancel() { - MTP::cancel(_previewRequest); - _previewRequest = 0; + MTP::cancel(base::take(_previewRequest)); _previewData = nullptr; _previewLinks.clear(); updatePreview(); @@ -5884,11 +5944,25 @@ void HistoryWidget::onPreviewParse() { } void HistoryWidget::onPreviewCheck() { - if (_previewCancelled) return; - QStringList linksList = _field->linksList(); - QString newLinks = linksList.join(' '); + auto previewRestricted = [this] { + if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_embed_links()) { + return true; + } + } + return false; + }; + if (_previewCancelled || previewRestricted()) { + MTP::cancel(base::take(_previewRequest)); + _previewData = nullptr; + _previewLinks.clear(); + update(); + return; + } + auto linksList = _field->linksList(); + auto newLinks = linksList.join(' '); if (newLinks != _previewLinks) { - MTP::cancel(_previewRequest); + MTP::cancel(base::take(_previewRequest)); _previewLinks = newLinks; if (_previewLinks.isEmpty()) { if (_previewData && _previewData->pendingTill >= 0) previewCancel(); @@ -6397,12 +6471,14 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { } } -namespace { +void HistoryWidget::drawRestrictedWrite(Painter &p) { + auto rect = myrtlrect(0, height() - _unblock->height(), _chatWidth, _unblock->height()); + p.fillRect(rect, st::historyReplyBg); -constexpr int DisplayEditTimeWarningMs = 300 * 1000; -constexpr int FullDayInMs = 86400 * 1000; - -} // namespace + p.setFont(st::normalFont); + p.setPen(st::windowSubTextFg); + p.drawText(rect.marginsRemoved(QMargins(st::historySendPadding, 0, st::historySendPadding, 0)), lang(lng_restricted_send_message), style::al_center); +} void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int top) const { if (!rect.intersects(myrtlrect(left, top, _chatWidth - left, st::normalFont->height))) { @@ -6421,8 +6497,8 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int auto editTimeLeft = (Global::EditTimeLimit() * 1000LL) - timeSinceMessage; if (editTimeLeft < 2) { editTimeLeftText = qsl("0:00"); - } else if (editTimeLeft > DisplayEditTimeWarningMs) { - updateIn = static_cast(qMin(editTimeLeft - DisplayEditTimeWarningMs, qint64(FullDayInMs))); + } else if (editTimeLeft > kDisplayEditTimeWarningMs) { + updateIn = static_cast(qMin(editTimeLeft - kDisplayEditTimeWarningMs, qint64(kFullDayInMs))); } else { updateIn = static_cast(editTimeLeft % 1000); if (!updateIn) { @@ -6583,6 +6659,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { if (!_send->isHidden() && _recording) { drawRecording(p, _send->recordActiveRatio()); } + } else if (isRestrictedWrite()) { + drawRestrictedWrite(p); } if (_pinnedBar && !_pinnedBar->cancel->isHidden()) { drawPinnedBar(p); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index b751a9427a..2d0100cfaa 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -523,6 +523,7 @@ private: bool historyHasNotFreezedUnreadBar(History *history) const; bool canWriteMessage() const; + bool isRestrictedWrite() const; void orderWidgets(); void clearInlineBot(); @@ -577,6 +578,7 @@ private: void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const; void drawRecording(Painter &p, float64 recordActive); void drawPinnedBar(Painter &p); + void drawRestrictedWrite(Painter &p); void updateMouseTracking(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 0448f84429..3a2babc85c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -290,6 +290,10 @@ void Result::addToHistory(History *history, MTPDmessage::Flags flags, MsgId msgI sendData->addToHistory(this, history, flags, msgId, fromId, mtpDate, viaBotId, replyToId, markup); } +QString Result::getErrorOnSend(History *history) const { + return sendData->getErrorOnSend(this, history); +} + bool Result::getLocationCoords(LocationCoords *outLocation) const { return sendData->getLocationCoords(outLocation); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index 9274fd741a..6db867ed25 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -68,6 +68,7 @@ public: bool hasThumbDisplay() const; void addToHistory(History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId) const; + QString getErrorOnSend(History *history) const; // interface for Layout:: usage bool getLocationCoords(LocationCoords *outLocation) const; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 6702ae4f22..80349a402f 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "inline_bots/inline_bot_result.h" #include "storage/localstorage.h" +#include "lang/lang_keys.h" namespace InlineBots { namespace internal { @@ -44,6 +45,15 @@ UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { history->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(msgId), MTP_int(fromId), peerToMTP(history->peer->id), MTPnullFwdHeader, MTP_int(viaBotId), MTP_int(replyToId), mtpDate, fields.text, fields.media, markup, fields.entities, MTP_int(1), MTPint()), NewMessageUnread); } +QString SendDataCommon::getErrorOnSend(const Result *owner, History *history) const { + if (auto megagroup = history->peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_messages()) { + return lang(lng_restricted_send_message); + } + } + return QString(); +} + SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const { SentMTPMessageFields result; result.text = MTP_string(_message); @@ -83,17 +93,48 @@ UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { history->addNewPhoto(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _photo, _caption, markup); } +QString SendPhoto::getErrorOnSend(const Result *owner, History *history) const { + if (auto megagroup = history->peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_media()) { + return lang(lng_restricted_send_media); + } + } + return QString(); +} + void SendFile::addToHistory(const Result *owner, History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { history->addNewDocument(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _document, _caption, markup); } +QString SendFile::getErrorOnSend(const Result *owner, History *history) const { + if (auto megagroup = history->peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_media()) { + return lang(lng_restricted_send_media); + } else if (megagroup->restrictedRights().is_send_stickers() && (_document->sticker() != nullptr)) { + return lang(lng_restricted_send_stickers); + } else if (megagroup->restrictedRights().is_send_gifs() && _document->isAnimation() && !_document->isRoundVideo()) { + return lang(lng_restricted_send_gifs); + } + } + return QString(); +} + void SendGame::addToHistory(const Result *owner, History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const { history->addNewGame(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _game, markup); } +QString SendGame::getErrorOnSend(const Result *owner, History *history) const { + if (auto megagroup = history->peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_games()) { + return lang(lng_restricted_send_inline); + } + } + return QString(); +} + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h index feb9c2dd41..6906a615d9 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -46,6 +46,7 @@ public: virtual void addToHistory(const Result *owner, History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const = 0; + virtual QString getErrorOnSend(const Result *owner, History *history) const = 0; virtual bool hasLocationCoords() const { return false; @@ -74,6 +75,8 @@ public: MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override; + QString getErrorOnSend(const Result *owner, History *history) const override; + }; // Plain text message. @@ -193,6 +196,8 @@ public: MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override; + QString getErrorOnSend(const Result *owner, History *history) const override; + private: PhotoData *_photo; QString _caption; @@ -215,6 +220,8 @@ public: MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override; + QString getErrorOnSend(const Result *owner, History *history) const override; + private: DocumentData *_document; QString _caption; @@ -236,6 +243,8 @@ public: MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override; + QString getErrorOnSend(const Result *owner, History *history) const override; + private: GameData *_game; diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index a45a75cda2..b3d182d0ce 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -36,6 +36,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "window/window_controller.h" #include "ui/widgets/scroll_area.h" +#include "ui/widgets/labels.h" +#include "observer_peer.h" namespace InlineBots { namespace Layout { @@ -67,6 +69,15 @@ Inner::Inner(QWidget *parent, gsl::not_null controller) : T update(); } }); + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { + if (update.peer == _inlineQueryPeer) { + auto isRestricted = (_restrictedLabel != nullptr); + if (isRestricted != isRestrictedView()) { + auto h = countHeight(); + if (h != height()) resize(width(), h); + } + } + })); } void Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { @@ -77,7 +88,39 @@ void Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { } } +void Inner::checkRestrictedPeer() { + if (auto megagroup = _inlineQueryPeer ? _inlineQueryPeer->asMegagroup() : nullptr) { + if (megagroup->restrictedRights().is_send_inline()) { + if (!_restrictedLabel) { + _restrictedLabel.create(this, lang(lng_restricted_send_inline), Ui::FlatLabel::InitType::Simple, st::stickersRestrictedLabel); + _restrictedLabel->show(); + _restrictedLabel->move(st::inlineResultsLeft - st::buttonRadius, st::stickerPanPadding); + if (_switchPmButton) { + _switchPmButton->hide(); + } + update(); + } + return; + } + } + if (_restrictedLabel) { + _restrictedLabel.destroy(); + if (_switchPmButton) { + _switchPmButton->show(); + } + update(); + } +} + +bool Inner::isRestrictedView() { + checkRestrictedPeer(); + return (_restrictedLabel != nullptr); +} + int Inner::countHeight() { + if (isRestrictedView()) { + return st::stickerPanPadding + _restrictedLabel->height() + st::stickerPanPadding; + } auto result = st::stickerPanPadding; if (_switchPmButton) { result += _switchPmButton->height() + st::inlineResultsSkip; @@ -102,6 +145,9 @@ void Inner::paintEvent(QPaintEvent *e) { } void Inner::paintInlineItems(Painter &p, const QRect &r) { + if (_restrictedLabel) { + return; + } if (_rows.isEmpty() && !_switchPmButton) { p.setFont(st::normalFont); p.setPen(st::noContactsColor); @@ -278,7 +324,7 @@ bool Inner::inlineRowFinalize(Row &row, int32 &sumWidth, bool force) { } void Inner::inlineBotChanged() { - refreshInlineRows(nullptr, nullptr, true); + refreshInlineRows(nullptr, nullptr, nullptr, true); } void Inner::clearInlineRows(bool resultsDeleted) { @@ -392,12 +438,16 @@ void Inner::refreshSwitchPmButton(const CacheEntry *entry) { _switchPmStartToken = entry->switchPmStartToken; auto buttonTop = st::stickerPanPadding; _switchPmButton->move(st::inlineResultsLeft - st::buttonRadius, buttonTop); + if (isRestrictedView()) { + _switchPmButton->hide(); + } } update(); } -int Inner::refreshInlineRows(UserData *bot, const CacheEntry *entry, bool resultsDeleted) { +int Inner::refreshInlineRows(PeerData *queryPeer, UserData *bot, const CacheEntry *entry, bool resultsDeleted) { _inlineBot = bot; + _inlineQueryPeer = queryPeer; refreshSwitchPmButton(entry); auto clearResults = [this, entry]() { if (!entry) { @@ -1072,7 +1122,7 @@ bool Widget::refreshInlineRows(int *added) { _inlineNextOffset = it->second->nextOffset; } if (!entry) prepareCache(); - auto result = _inner->refreshInlineRows(_inlineBot, entry, false); + auto result = _inner->refreshInlineRows(_inlineQueryPeer, _inlineBot, entry, false); if (added) *added = result; return (entry != nullptr); } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.h b/Telegram/SourceFiles/inline_bots/inline_results_widget.h index 921b102511..09d55173ce 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.h +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.h @@ -31,6 +31,7 @@ class ScrollArea; class IconButton; class LinkButton; class RoundButton; +class FlatLabel; class RippleAnimation; } // namesapce Ui @@ -68,7 +69,7 @@ public: void clearSelection(); - int refreshInlineRows(UserData *bot, const CacheEntry *results, bool resultsDeleted); + int refreshInlineRows(PeerData *queryPeer, UserData *bot, const CacheEntry *results, bool resultsDeleted); void inlineBotChanged(); void hideInlineRowsPanel(); void clearInlineRowsPanel(); @@ -110,6 +111,8 @@ private: static constexpr bool kRefreshIconsNoAnimation = false; void updateSelected(); + void checkRestrictedPeer(); + bool isRestrictedView(); void paintInlineItems(Painter &p, const QRect &r); @@ -120,7 +123,8 @@ private: int _visibleTop = 0; int _visibleBottom = 0; - UserData *_inlineBot; + UserData *_inlineBot = nullptr; + PeerData *_inlineQueryPeer = nullptr; TimeMs _lastScrolled = 0; QTimer _updateInlineItems; bool _inlineWithThumb = false; @@ -128,6 +132,8 @@ private: object_ptr _switchPmButton = { nullptr }; QString _switchPmStartToken; + object_ptr _restrictedLabel = { nullptr }; + struct Row { int height = 0; QVector items; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 7d684a0a8f..b9df471574 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -77,7 +77,7 @@ namespace { constexpr auto kSaveFloatPlayerPositionTimeoutMs = TimeMs(1000); -MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) { +MTPMessagesFilter TypeToMediaFilter(MediaOverviewType &type) { switch (type) { case OverviewPhotos: return MTP_inputMessagesFilterPhotos(); case OverviewVideos: return MTP_inputMessagesFilterVideo(); @@ -92,6 +92,64 @@ MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) { } } +bool HasMediaItems(const SelectedItemSet &items) { + for_const (auto item, items) { + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypePhoto: + case MediaTypeVideo: + case MediaTypeFile: + case MediaTypeMusicFile: + case MediaTypeVoiceFile: return true; + case MediaTypeGif: return media->getDocument()->isRoundVideo(); + } + } + } + return false; +} + +bool HasStickerItems(const SelectedItemSet &items) { + for_const (auto item, items) { + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypeSticker: return true; + } + } + } + return false; +} + +bool HasGifItems(const SelectedItemSet &items) { + for_const (auto item, items) { + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypeGif: return !media->getDocument()->isRoundVideo(); + } + } + } + return false; +} + +bool HasGameItems(const SelectedItemSet &items) { + for_const (auto item, items) { + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypeGame: return true; + } + } + } + return false; +} + +bool HasInlineItems(const SelectedItemSet &items) { + for_const (auto item, items) { + if (item->viaBot()) { + return true; + } + } + return false; +} + } // namespace StackItemSection::StackItemSection(std::unique_ptr &&memento) : StackItem(nullptr) @@ -505,22 +563,33 @@ void MainWidget::finishFloatPlayerDrag(gsl::not_null instance, bool clos } } -bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { - PeerData *p = App::peer(peer); - if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && !p->asChat()->canWrite()) || (p->isUser() && p->asUser()->isInaccessible())) { - Ui::show(Box(lang(lng_forward_cant))); +bool MainWidget::onForward(const PeerId &peerId, ForwardWhatMessages what) { + Expects(peerId != 0); + auto peer = App::peer(peerId); + auto finishWithError = [this, what](const QString &error) { + Ui::show(Box(error)); + if (what == ForwardPressedMessage || what == ForwardPressedLinkMessage) { + // We've already released the mouse button, so the forwarding is cancelled. + if (_hider) { + _hider->startHide(); + noHider(_hider); + } + } return false; + }; + if (!peer->canWrite()) { + return finishWithError(lang(lng_forward_cant)); } - _history->cancelReply(); - _toForward.clear(); + + auto toForward = SelectedItemSet(); if (what == ForwardSelectedMessages) { if (_overview) { - _overview->fillSelectedItems(_toForward, false); + _overview->fillSelectedItems(toForward, false); } else { - _history->fillSelectedItems(_toForward, false); + _history->fillSelectedItems(toForward, false); } } else { - HistoryItem *item = 0; + auto item = (HistoryItem*)nullptr; if (what == ForwardContextMessage) { item = App::contextItem(); } else if (what == ForwardPressedMessage) { @@ -529,9 +598,25 @@ bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { item = App::pressedLinkItem(); } if (item && item->toHistoryMessage() && item->id > 0) { - _toForward.insert(item->id, item); + toForward.insert(item->id, item); } } + if (auto megagroup = peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_media() && HasMediaItems(toForward)) { + return finishWithError(lang(lng_restricted_send_media)); + } else if (megagroup->restrictedRights().is_send_stickers() && HasStickerItems(toForward)) { + return finishWithError(lang(lng_restricted_send_stickers)); + } else if (megagroup->restrictedRights().is_send_gifs() && HasGifItems(toForward)) { + return finishWithError(lang(lng_restricted_send_gifs)); + } else if (megagroup->restrictedRights().is_send_games() && HasGameItems(toForward)) { + return finishWithError(lang(lng_restricted_send_inline)); + } else if (megagroup->restrictedRights().is_send_inline() && HasInlineItems(toForward)) { + return finishWithError(lang(lng_restricted_send_inline)); + } + } + + _toForward = toForward; + _history->cancelReply(); updateForwardingItemRemovedSubscription(); updateForwardingTexts(); Ui::showPeerHistory(peer, ShowAtUnreadMsgId); @@ -777,23 +862,40 @@ void MainWidget::onUpdateMuted() { App::updateMuted(); } -void MainWidget::onShareContact(const PeerId &peer, UserData *contact) { - _history->onShareContact(peer, contact); +void MainWidget::onShareContact(const PeerId &peerId, UserData *contact) { + _history->onShareContact(peerId, contact); } -bool MainWidget::onSendPaths(const PeerId &peer) { +bool MainWidget::onSendPaths(const PeerId &peerId) { + Expects(peerId != 0); + auto peer = App::peer(peerId); + if (!peer->canWrite()) { + Ui::show(Box(lang(lng_forward_send_files_cant))); + return false; + } else if (auto megagroup = peer->asMegagroup()) { + if (megagroup->restrictedRights().is_send_media()) { + Ui::show(Box(lang(lng_restricted_send_media))); + return false; + } + } Ui::showPeerHistory(peer, ShowAtTheEndMsgId); return _history->confirmSendingFiles(cSendPaths()); } -void MainWidget::onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data) { +void MainWidget::onFilesOrForwardDrop(const PeerId &peerId, const QMimeData *data) { + Expects(peerId != 0); if (data->hasFormat(qsl("application/x-td-forward-selected"))) { - onForward(peer, ForwardSelectedMessages); + onForward(peerId, ForwardSelectedMessages); } else if (data->hasFormat(qsl("application/x-td-forward-pressed-link"))) { - onForward(peer, ForwardPressedLinkMessage); + onForward(peerId, ForwardPressedLinkMessage); } else if (data->hasFormat(qsl("application/x-td-forward-pressed"))) { - onForward(peer, ForwardPressedMessage); + onForward(peerId, ForwardPressedMessage); } else { + auto peer = App::peer(peerId); + if (!peer->canWrite()) { + Ui::show(Box(lang(lng_forward_send_files_cant))); + return; + } Ui::showPeerHistory(peer, ShowAtTheEndMsgId); _history->confirmSendingFiles(data); } @@ -1622,7 +1724,7 @@ void MainWidget::searchMessages(const QString &query, PeerData *inPeer) { } bool MainWidget::preloadOverview(PeerData *peer, MediaOverviewType type) { - auto filter = typeToMediaFilter(type); + auto filter = TypeToMediaFilter(type); if (filter.type() == mtpc_inputMessagesFilterEmpty) { return false; } @@ -1695,7 +1797,7 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many auto minId = history->overviewMinId(type); auto limit = (many || history->overview[type].size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; - auto filter = typeToMediaFilter(type); + auto filter = TypeToMediaFilter(type); if (filter.type() == mtpc_inputMessagesFilterEmpty) { return; }