From 487e8a9009155a379cc2d7366fd86ea2e0f3ef24 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 20:13:59 +0400 Subject: [PATCH] Disable copy to clipboard if noforwards. --- Telegram/Resources/langs/lang.strings | 6 ++- .../history/history_inner_widget.cpp | 51 +++++++++++++------ .../history/history_inner_widget.h | 1 + .../view/history_view_context_menu.cpp | 46 ++++++++++------- .../history/view/history_view_list_widget.cpp | 39 ++++++++++++-- .../history/view/history_view_list_widget.h | 24 ++++++--- .../view/history_view_pinned_section.cpp | 4 ++ .../view/history_view_pinned_section.h | 1 + .../view/history_view_replies_section.cpp | 4 ++ .../view/history_view_replies_section.h | 1 + .../view/history_view_scheduled_section.cpp | 4 ++ .../view/history_view_scheduled_section.h | 1 + .../media/view/media_view_overlay_widget.cpp | 27 +++++++++- .../media/view/media_view_overlay_widget.h | 3 ++ 14 files changed, 166 insertions(+), 46 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2462ecb6b5..0569a664c7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -148,8 +148,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group."; "lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel."; "lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted."; -"lng_error_noforwards_group" = "Sorry, forwarding is disabled from this group."; -"lng_error_noforwards_channel" = "Sorry, forwarding is disabled from this channel."; +"lng_error_noforwards_group" = "Sorry, forwarding from this group is disabled by admins."; +"lng_error_noforwards_channel" = "Sorry, forwarding from this channel is disabled by admins."; +"lng_error_nocopy_group" = "Sorry, copying from this group is disabled by admins."; +"lng_error_nocopy_channel" = "Sorry, copying from this channel is disabled by admins."; "lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?"; "lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?"; "lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?"; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 0d1cf828a4..7cbaec14f9 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/chat_style.h" #include "ui/widgets/popup_menu.h" #include "ui/image/image.h" -#include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/effects/path_shift_gradient.h" #include "ui/text/text_options.h" #include "ui/boxes/report_box.h" @@ -1494,7 +1494,8 @@ void HistoryInner::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && !_selected.empty() - && _selected.cbegin()->second != FullSelection) { + && _selected.cbegin()->second != FullSelection + && _peer->allowsForwarding()) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { TextUtilities::SetClipboardText( @@ -1673,7 +1674,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }; const auto addPhotoActions = [&](not_null photo) { const auto media = photo->activeMediaView(); - if (!photo->isNull() && media && media->loaded()) { + if (!photo->isNull() && media && media->loaded() && _peer->allowsForwarding()) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); })); @@ -1753,7 +1754,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (lnkPhoto || lnkDocument) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); - if (isUponSelected > 0) { + if (isUponSelected > 0 && _peer->allowsForwarding()) { _menu->addAction( (isUponSelected > 1 ? tr::lng_context_copy_selected_items(tr::now) @@ -1844,11 +1845,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto view = item ? item->mainView() : nullptr; if (isUponSelected > 0) { - _menu->addAction( - ((isUponSelected > 1) - ? tr::lng_context_copy_selected_items(tr::now) - : tr::lng_context_copy_selected(tr::now)), - [=] { copySelectedText(); }); + if (_peer->allowsForwarding()) { + _menu->addAction( + ((isUponSelected > 1) + ? tr::lng_context_copy_selected_items(tr::now) + : tr::lng_context_copy_selected(tr::now)), + [=] { copySelectedText(); }); + } addItemActions(item, item); } else { addItemActions(item, albumPartItem); @@ -1892,6 +1895,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (!item->isService() && view && !link + && _peer->allowsForwarding() && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(tr::lng_context_copy_text(tr::now), [=] { copyContextText(itemId); @@ -1997,8 +2001,22 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } +bool HistoryInner::showCopyRestriction() { + if (_peer->allowsForwarding()) { + return false; + } + Ui::ShowMultilineToast({ + .text = { _peer->isBroadcast() + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + void HistoryInner::copySelectedText() { - TextUtilities::SetClipboardText(getSelectedText()); + if (!showCopyRestriction()) { + TextUtilities::SetClipboardText(getSelectedText()); + } } void HistoryInner::savePhotoToFile(not_null photo) { @@ -2027,10 +2045,10 @@ void HistoryInner::copyContextImage(not_null photo) { const auto media = photo->activeMediaView(); if (photo->isNull() || !media || !media->loaded()) { return; + } else if (!showCopyRestriction()) { + const auto image = media->image(Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } - - const auto image = media->image(Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); } void HistoryInner::showStickerPackInfo(not_null document) { @@ -2078,7 +2096,9 @@ void HistoryInner::saveContextGif(FullMsgId itemId) { } void HistoryInner::copyContextText(FullMsgId itemId) { - if (const auto item = session().data().message(itemId)) { + if (showCopyRestriction()) { + return; + } else if (const auto item = session().data().message(itemId)) { if (const auto group = session().data().groups().find(item)) { TextUtilities::SetClipboardText(HistoryGroupText(group)); } else { @@ -2174,7 +2194,8 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { copySelectedText(); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E - && e->modifiers().testFlag(Qt::ControlModifier)) { + && e->modifiers().testFlag(Qt::ControlModifier) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index d55f5308ea..6252a3b0fc 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -342,6 +342,7 @@ private: void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); + bool showCopyRestriction(); // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index daadcbe0b8..217d479447 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -129,15 +129,19 @@ void AddPhotoActions( not_null menu, not_null photo, not_null list) { - menu->addAction( - tr::lng_context_save_image(tr::now), - App::LambdaDelayed( - st::defaultDropdownMenu.menu.ripple.hideDuration, - &photo->session(), - [=] { SavePhotoToFile(photo); })); - menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - CopyImage(photo); - }); + if (!list->hasCopyRestriction()) { + menu->addAction( + tr::lng_context_save_image(tr::now), + App::LambdaDelayed( + st::defaultDropdownMenu.menu.ripple.hideDuration, + &photo->session(), + [=] { SavePhotoToFile(photo); })); + menu->addAction(tr::lng_context_copy_image(tr::now), [=] { + if (!list->showCopyRestriction()) { + CopyImage(photo); + } + }); + } if (photo->hasAttachedStickers()) { const auto controller = list->controller(); auto callback = [=] { @@ -903,12 +907,14 @@ base::unique_qptr FillContextMenu( const auto hasSelection = !request.selectedItems.empty() || !request.selectedText.empty(); - if (request.overSelection) { + if (request.overSelection && !list->hasCopyRestriction()) { const auto text = request.selectedItems.empty() ? tr::lng_context_copy_selected(tr::now) : tr::lng_context_copy_selected_items(tr::now); result->addAction(text, [=] { - TextUtilities::SetClipboardText(list->getSelectedText()); + if (!list->showCopyRestriction()) { + TextUtilities::SetClipboardText(list->getSelectedText()); + } }); } @@ -930,17 +936,21 @@ base::unique_qptr FillContextMenu( view->data()->fullId(), list); } - if (!link && (view->hasVisibleText() || mediaHasTextForCopy)) { + if (!link + && (view->hasVisibleText() || mediaHasTextForCopy) + && !list->hasCopyRestriction()) { const auto asGroup = (request.pointState != PointState::GroupPart); result->addAction(tr::lng_context_copy_text(tr::now), [=] { - if (const auto item = owner->message(itemId)) { - if (asGroup) { - if (const auto group = owner->groups().find(item)) { - TextUtilities::SetClipboardText(HistoryGroupText(group)); - return; + if (!list->showCopyRestriction()) { + if (const auto item = owner->message(itemId)) { + if (asGroup) { + if (const auto group = owner->groups().find(item)) { + TextUtilities::SetClipboardText(HistoryGroupText(group)); + return; + } } + TextUtilities::SetClipboardText(HistoryItemText(item)); } - TextUtilities::SetClipboardText(HistoryItemText(item)); } }); } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 43d615f776..74f2766bfb 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/widgets/popup_menu.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/inactive_press.h" #include "ui/effects/path_shift_gradient.h" #include "ui/chat/chat_theme.h" @@ -1164,6 +1165,25 @@ bool ListWidget::isEmpty() const { && (_itemsHeight + _itemsRevealHeight == 0); } + + +bool ListWidget::hasCopyRestriction() const { + return _delegate->listCopyRestrictionType() != CopyRestrictionType::None; +} + +bool ListWidget::showCopyRestriction() { + const auto type = _delegate->listCopyRestrictionType(); + if (type == CopyRestrictionType::None) { + return false; + } + Ui::ShowMultilineToast({ + .text = { (type == CopyRestrictionType::Channel) + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + int ListWidget::itemMinimalHeight() const { return st::msgMarginTopAttached + st::msgPhotoSize @@ -1892,11 +1912,13 @@ void ListWidget::keyPressEvent(QKeyEvent *e) { _delegate->listCancelRequest(); } } else if (e == QKeySequence::Copy - && (hasSelectedText() || hasSelectedItems())) { + && (hasSelectedText() || hasSelectedItems()) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E - && e->modifiers().testFlag(Qt::ControlModifier)) { + && e->modifiers().testFlag(Qt::ControlModifier) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { @@ -2417,7 +2439,8 @@ void ListWidget::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && _selectedTextItem - && _selectedTextRange.from != _selectedTextRange.to) { + && _selectedTextRange.from != _selectedTextRange.to + && !hasCopyRestriction()) { if (const auto view = viewForItem(_selectedTextItem)) { TextUtilities::SetClipboardText( view->selectedText(_selectedTextRange), @@ -3058,4 +3081,14 @@ void ConfirmSendNowSelectedItems(not_null widget) { clearSelection); } + +CopyRestrictionType CopyRestrictionTypeFor( + not_null peer) { + return peer->allowsForwarding() + ? CopyRestrictionType::None + : peer->isBroadcast() + ? CopyRestrictionType::Channel + : CopyRestrictionType::Group; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 7d52f1f062..b0aa959cfc 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -41,6 +41,12 @@ enum class CursorState : char; enum class PointState : char; enum class Context : char; +enum class CopyRestrictionType : char { + None, + Group, + Channel, +}; + struct SelectedItem { explicit SelectedItem(FullMsgId msgId) : msgId(msgId) { } @@ -94,6 +100,7 @@ public: const FullMsgId &context) = 0; virtual void listHandleViaClick(not_null bot) = 0; virtual not_null listChatTheme() = 0; + virtual CopyRestrictionType listCopyRestrictionType() = 0; }; @@ -101,7 +108,6 @@ struct SelectionData { bool canDelete = false; bool canForward = false; bool canSendNow = false; - }; using SelectedMap = base::flat_map< @@ -199,11 +205,14 @@ public: void selectItem(not_null item); void selectItemAsGroup(not_null item); - bool loadedAtTopKnown() const; - bool loadedAtTop() const; - bool loadedAtBottomKnown() const; - bool loadedAtBottom() const; - bool isEmpty() const; + [[nodiscard]] bool loadedAtTopKnown() const; + [[nodiscard]] bool loadedAtTop() const; + [[nodiscard]] bool loadedAtBottomKnown() const; + [[nodiscard]] bool loadedAtBottom() const; + [[nodiscard]] bool isEmpty() const; + + [[nodiscard]] bool hasCopyRestriction() const; + [[nodiscard]] bool showCopyRestriction(); // AbstractTooltipShower interface QString tooltipText() const override; @@ -605,4 +614,7 @@ void ConfirmDeleteSelectedItems(not_null widget); void ConfirmForwardSelectedItems(not_null widget); void ConfirmSendNowSelectedItems(not_null widget); +[[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( + not_null peer); + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index db237777b8..8bae691010 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -676,6 +676,10 @@ not_null PinnedWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType PinnedWidget::listCopyRestrictionType() { + return CopyRestrictionTypeFor(_history->peer); +} + void PinnedWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index 27c81678ba..562df6734e 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -103,6 +103,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index a549414879..0131c24c59 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1927,6 +1927,10 @@ not_null RepliesWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType RepliesWidget::listCopyRestrictionType() { + return CopyRestrictionTypeFor(_history->peer); +} + void RepliesWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 14d6847f5a..072141ac7a 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -138,6 +138,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 48ac4f7cad..e08733df8e 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1231,6 +1231,10 @@ not_null ScheduledWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType ScheduledWidget::listCopyRestrictionType() { + return CopyRestrictionType::None; +} + void ScheduledWidget::confirmSendNowSelected() { ConfirmSendNowSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 6bdd38cf45..982b738c6d 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -119,6 +119,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 6618b4cc86..8b1763fc86 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/platform/ui_platform_utility.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/text/format_values.h" #include "ui/item_text_options.h" #include "ui/ui_utility.h" @@ -554,6 +555,23 @@ QSize OverlayWidget::flipSizeByRotation(QSize size) const { return FlipSizeByRotation(size, _rotation); } +bool OverlayWidget::hasCopyRestriction() const { + return _history && !_history->peer->allowsForwarding(); +} + +bool OverlayWidget::showCopyRestriction() { + if (!hasCopyRestriction()) { + return false; + } + Ui::ShowMultilineToast({ + .parentOverride = _widget, + .text = { _history->peer->isBroadcast() + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + bool OverlayWidget::videoShown() const { return _streamed && !_streamed->instance.info().video.cover.isNull(); } @@ -891,8 +909,10 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { : tr::lng_context_show_in_folder(tr::now); addAction(text, [=] { showInFolder(); }); } - if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { - addAction(tr::lng_mediaview_copy(tr::now), [=] { copyMedia(); }); + if (!hasCopyRestriction()) { + if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { + addAction(tr::lng_mediaview_copy(tr::now), [=] { copyMedia(); }); + } } if ((_photo && _photo->hasAttachedStickers()) || (_document && _document->hasAttachedStickers())) { @@ -1756,6 +1776,9 @@ void OverlayWidget::showMediaOverview() { } void OverlayWidget::copyMedia() { + if (showCopyRestriction()) { + return; + } _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); if (_document) { QGuiApplication::clipboard()->setImage(transformedShownContent()); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index dbd63bdf9f..d6d779ebc6 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -386,6 +386,9 @@ private: void validatePhotoImage(Image *image, bool blurred); void validatePhotoCurrentImage(); + [[nodiscard]] bool hasCopyRestriction() const; + [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] QSize flipSizeByRotation(QSize size) const; void applyVideoSize();