diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 7cbaec14f9..447c414292 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -66,6 +66,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "data/data_photo.h" #include "data/data_photo_media.h" +#include "data/data_peer_values.h" +#include "data/data_chat.h" #include "data/data_user.h" #include "data/data_file_click_handler.h" #include "data/data_file_origin.h" @@ -258,12 +260,61 @@ HistoryInner::HistoryInner( ) | rpl::start_with_next([=](int d) { _scroll->scrollToY(_scroll->scrollTop() + d); }, _scroll->lifetime()); + + setupSharingDisallowed(); } Main::Session &HistoryInner::session() const { return _controller->session(); } +void HistoryInner::setupSharingDisallowed() { + Expects(_peer != nullptr); + + if (_peer->isUser()) { + _sharingDisallowed = false; + return; + } + const auto chat = _peer->asChat(); + const auto channel = _peer->asChannel(); + _sharingDisallowed = chat + ? Data::PeerFlagValue(chat, ChatDataFlag::NoForwards) + : Data::PeerFlagValue(channel, ChannelDataFlag::NoForwards); + + auto rights = chat + ? chat->adminRightsValue() + : channel->adminRightsValue(); + auto canDelete = std::move( + rights + ) | rpl::map([=] { + return chat + ? chat->canDeleteMessages() + : channel->canDeleteMessages(); + }); + rpl::combine( + _sharingDisallowed.value(), + std::move(canDelete) + ) | rpl::filter([=](bool disallowed, bool canDelete) { + return hasSelectRestriction() && !getSelectedItems().empty(); + }) | rpl::start_with_next([=] { + _widget->clearSelected(); + if (_mouseAction == MouseAction::PrepareSelect) { + mouseActionCancel(); + } + }, lifetime()); +} + +bool HistoryInner::hasSelectRestriction() const { + if (!_sharingDisallowed.current()) { + return false; + } else if (const auto chat = _peer->asChat()) { + return !chat->canDeleteMessages(); + } else if (const auto channel = _peer->asChannel()) { + return !channel->canDeleteMessages(); + } + return true; +} + void HistoryInner::messagesReceived( PeerData *peer, const QVector &messages) { @@ -1216,12 +1267,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but _selected.emplace(_mouseActionItem, selStatus); _mouseAction = MouseAction::Selecting; repaintItem(_mouseActionItem); - } else { + } else if (!hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } } - } else if (!_pressWasInactive) { + } else if (!_pressWasInactive && !hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; // start items select } } @@ -1250,7 +1301,8 @@ std::unique_ptr HistoryInner::prepareDrag() { } const auto pressedHandler = ClickHandler::getPressed(); - if (dynamic_cast(pressedHandler.get())) { + if (dynamic_cast(pressedHandler.get()) + || !_peer->allowsForwarding()) { return nullptr; } @@ -1281,7 +1333,6 @@ std::unique_ptr HistoryInner::prepareDrag() { } } } - auto urls = QList(); const auto selectedText = [&] { if (uponSelected) { @@ -1737,6 +1788,29 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } }; + const auto addSelectMessageAction = [&]( + not_null item, + bool asGroup = true) { + if (item->isRegular() + && !item->isService() + && !hasSelectRestriction()) { + const auto itemId = item->fullId(); + _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { + if (const auto item = session->data().message(itemId)) { + if (const auto view = item->mainView()) { + if (asGroup) { + changeSelectionAsGroup(&_selected, item, SelectAction::Select); + } else { + changeSelection(&_selected, item, SelectAction::Select); + } + repaintItem(item); + _widget->updateTopBarSelection(); + } + } + }); + } + }; + if (hasWhoReadItem) { const auto participantChosen = [=](uint64 id) { controller->showPeerInfo(PeerId(id)); @@ -1808,17 +1882,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } } - if (item->isRegular() && !item->isService()) { - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelection(&_selected, item, SelectAction::Select); - repaintItem(item); - _widget->updateTopBarSelection(); - } - } - }); - } + addSelectMessageAction(item, false); if (isUponSelected != -2 && blockSender) { _menu->addAction(tr::lng_profile_block_user(tr::now), [=] { blockSenderItem(itemId); @@ -1959,37 +2023,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } } - if (item->isRegular() && !item->isService()) { - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelectionAsGroup(&_selected, item, SelectAction::Select); - repaintItem(view); - _widget->updateTopBarSelection(); - } - } - }); - } + addSelectMessageAction(item); if (isUponSelected != -2 && canBlockSender) { _menu->addAction(tr::lng_profile_block_user(tr::now), [=] { blockSenderAsGroup(itemId); }); } - } else { - if (App::mousedItem() - && App::mousedItem()->data()->isRegular() - && !App::mousedItem()->data()->isService()) { - const auto itemId = App::mousedItem()->data()->fullId(); - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelectionAsGroup(&_selected, item, SelectAction::Select); - repaintItem(item); - _widget->updateTopBarSelection(); - } - } - }); - } + } else if (App::mousedItem()) { + addSelectMessageAction(App::mousedItem()->data()); } } @@ -2001,8 +2042,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } +bool HistoryInner::hasCopyRestriction() const { + return !_peer->allowsForwarding(); +} + bool HistoryInner::showCopyRestriction() { - if (_peer->allowsForwarding()) { + if (!hasCopyRestriction()) { return false; } Ui::ShowMultilineToast({ @@ -2817,18 +2862,6 @@ MessageIdsList HistoryInner::getSelectedItems() const { return result; } -void HistoryInner::selectItem(not_null item) { - if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { - _selected.clear(); - } else if (_selected.size() == MaxSelectedItems - && _selected.find(item) == _selected.cend()) { - return; - } - _selected.emplace(item, FullSelection); - _widget->updateTopBarSelection(); - _widget->update(); -} - void HistoryInner::onTouchSelect() { _touchSelect = true; mouseActionStart(_touchPos, Qt::LeftButton); @@ -3115,6 +3148,9 @@ void HistoryInner::mouseActionUpdate() { void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting) { if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) { return; + } else if (dragSelFrom && hasSelectRestriction()) { + updateDragSelection(nullptr, nullptr, false); + return; } _dragSelFrom = dragSelFrom; _dragSelTo = dragSelTo; @@ -3248,7 +3284,9 @@ void HistoryInner::notifyMigrateUpdated() { } void HistoryInner::applyDragSelection() { - applyDragSelection(&_selected); + if (!hasSelectRestriction()) { + applyDragSelection(&_selected); + } } bool HistoryInner::isSelected( diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 6252a3b0fc..c7e28ca60d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -85,7 +85,6 @@ public: HistoryView::TopBarWidget::SelectedState getSelectionState() const; void clearSelected(bool onlyTextSelection = false); MessageIdsList getSelectedItems() const; - void selectItem(not_null item); bool inSelectionMode() const; bool elementIntersectsRange( not_null view, @@ -342,7 +341,11 @@ private: void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); + + void setupSharingDisallowed(); + [[nodiscard]] bool hasCopyRestriction() const; bool showCopyRestriction(); + [[nodiscard]] bool hasSelectRestriction() const; // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; @@ -416,6 +419,8 @@ private: Ui::SelectScrollManager _selectScroll; + rpl::variable _sharingDisallowed = false; + Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 217d479447..ce8b19d4c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -811,7 +811,10 @@ bool AddSelectMessageAction( const auto item = request.item; if (request.overSelection && !request.selectedItems.empty()) { return false; - } else if (!item || item->isLocal() || item->isService()) { + } else if (!item + || item->isLocal() + || item->isService() + || list->hasSelectRestriction()) { return false; } const auto owner = &item->history()->owner(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 74f2766bfb..7ea987b297 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1076,7 +1076,9 @@ void ListWidget::cancelSelection() { } void ListWidget::selectItem(not_null item) { - if (const auto view = viewForItem(item)) { + if (hasSelectRestriction()) { + return; + } else if (const auto view = viewForItem(item)) { clearTextSelection(); changeSelection( _selected, @@ -1087,7 +1089,9 @@ void ListWidget::selectItem(not_null item) { } void ListWidget::selectItemAsGroup(not_null item) { - if (const auto view = viewForItem(item)) { + if (hasSelectRestriction()) { + return; + } else if (const auto view = viewForItem(item)) { clearTextSelection(); changeSelectionAsGroup( _selected, @@ -1165,8 +1169,6 @@ bool ListWidget::isEmpty() const { && (_itemsHeight + _itemsRevealHeight == 0); } - - bool ListWidget::hasCopyRestriction() const { return _delegate->listCopyRestrictionType() != CopyRestrictionType::None; } @@ -1184,6 +1186,11 @@ bool ListWidget::showCopyRestriction() { return true; } +bool ListWidget::hasSelectRestriction() const { + return _delegate->listSelectRestrictionType() + != CopyRestrictionType::None; +} + int ListWidget::itemMinimalHeight() const { return st::msgMarginTopAttached + st::msgPhotoSize @@ -1751,7 +1758,9 @@ void ListWidget::paintEvent(QPaintEvent *e) { } void ListWidget::applyDragSelection() { - applyDragSelection(_selected); + if (!hasSelectRestriction()) { + applyDragSelection(_selected); + } clearDragSelection(); pushSelectedItems(); } @@ -2080,7 +2089,9 @@ void ListWidget::leaveEventHook(QEvent *e) { } void ListWidget::updateDragSelection() { - if (!_overState.itemId || !_pressState.itemId) { + if (!_overState.itemId + || !_pressState.itemId + || hasSelectRestriction()) { clearDragSelection(); return; } else if (_items.empty() || !_overElement || !_selectEnabled) { @@ -2281,7 +2292,7 @@ void ListWidget::mouseActionStart( } else if (hasSelectedItems()) { if (overSelectedItems()) { _mouseAction = MouseAction::PrepareDrag; - } else if (!_pressWasInactive) { + } else if (!_pressWasInactive && !hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } @@ -2326,7 +2337,7 @@ void ListWidget::mouseActionStart( _mouseTextSymbol, _mouseTextSymbol)); _mouseAction = MouseAction::Selecting; - } else { + } else if (!hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } @@ -2653,7 +2664,8 @@ std::unique_ptr ListWidget::prepareDrag() { return nullptr; } auto pressedHandler = ClickHandler::getPressed(); - if (dynamic_cast(pressedHandler.get())) { + if (dynamic_cast(pressedHandler.get()) + || hasCopyRestriction()) { return nullptr; } @@ -3081,7 +3093,6 @@ void ConfirmSendNowSelectedItems(not_null widget) { clearSelection); } - CopyRestrictionType CopyRestrictionTypeFor( not_null peer) { return peer->allowsForwarding() @@ -3091,4 +3102,18 @@ CopyRestrictionType CopyRestrictionTypeFor( : CopyRestrictionType::Group; } +CopyRestrictionType SelectRestrictionTypeFor( + not_null peer) { + if (const auto chat = peer->asChat()) { + return chat->canDeleteMessages() + ? CopyRestrictionType::None + : CopyRestrictionTypeFor(peer); + } else if (const auto channel = peer->asChannel()) { + return channel->canDeleteMessages() + ? CopyRestrictionType::None + : CopyRestrictionTypeFor(peer); + } + return CopyRestrictionType::None; +} + } // 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 b0aa959cfc..2ced1f8b92 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -101,6 +101,7 @@ public: virtual void listHandleViaClick(not_null bot) = 0; virtual not_null listChatTheme() = 0; virtual CopyRestrictionType listCopyRestrictionType() = 0; + virtual CopyRestrictionType listSelectRestrictionType() = 0; }; @@ -213,6 +214,7 @@ public: [[nodiscard]] bool hasCopyRestriction() const; [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] bool hasSelectRestriction() const; // AbstractTooltipShower interface QString tooltipText() const override; @@ -616,5 +618,7 @@ void ConfirmSendNowSelectedItems(not_null widget); [[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( not_null peer); +[[nodiscard]] CopyRestrictionType SelectRestrictionTypeFor( + 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 8bae691010..cd07eafad4 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -680,6 +680,10 @@ CopyRestrictionType PinnedWidget::listCopyRestrictionType() { return CopyRestrictionTypeFor(_history->peer); } +CopyRestrictionType PinnedWidget::listSelectRestrictionType() { + return SelectRestrictionTypeFor(_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 562df6734e..e36afffeee 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -104,6 +104,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() 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 0131c24c59..7071d6f247 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1931,6 +1931,10 @@ CopyRestrictionType RepliesWidget::listCopyRestrictionType() { return CopyRestrictionTypeFor(_history->peer); } +CopyRestrictionType RepliesWidget::listSelectRestrictionType() { + return SelectRestrictionTypeFor(_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 072141ac7a..c7a83473eb 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -139,6 +139,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() 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 e08733df8e..1cc373f0e5 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1235,6 +1235,10 @@ CopyRestrictionType ScheduledWidget::listCopyRestrictionType() { return CopyRestrictionType::None; } +CopyRestrictionType ScheduledWidget::listSelectRestrictionType() { + 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 982b738c6d..47a805c40d 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -120,6 +120,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override;