diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 65bdb834d..2b765f110 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3584,6 +3584,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_channel_title" = "Edit channel"; "lng_edit_bot_title" = "Edit bot"; "lng_edit_sign_messages" = "Sign messages"; +"lng_edit_sign_messages_about" = "Add names of admins to the messages they post."; +"lng_edit_sign_profiles" = "Show authors' profiles"; +"lng_edit_sign_profiles_about" = "Add names and photos of admins to the messages they post, linking to their profiles."; "lng_edit_group" = "Edit group"; "lng_edit_channel_color" = "Change name color"; "lng_edit_channel_level_min" = "Level 1+"; diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index c05a42e95..fb096e626 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -41,14 +41,14 @@ void InnerFillMessagePostFlags( const SendOptions &options, not_null peer, MessageFlags &flags) { - const auto anonymousPost = peer->amAnonymous(); if (ShouldSendSilent(peer, options)) { flags |= MessageFlag::Silent; } - if (!anonymousPost || options.sendAs) { + if (!peer->amAnonymous()) { flags |= MessageFlag::HasFromId; - return; - } else if (peer->asMegagroup()) { + } + const auto channel = peer->asBroadcast(); + if (!channel) { return; } flags |= MessageFlag::Post; @@ -57,7 +57,7 @@ void InnerFillMessagePostFlags( return; } flags |= MessageFlag::HasViews; - if (peer->asChannel()->addsSignature()) { + if (channel->addsSignature()) { flags |= MessageFlag::HasPostAuthor; } } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 0f0fa264b..5220341a3 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3282,9 +3282,9 @@ void ApiWrap::forwardMessages( if (!action.options.scheduled && !action.options.shortcutId) { histories.readInbox(history); } + const auto sendAs = action.options.sendAs; const auto anonymousPost = peer->amAnonymous(); const auto silentPost = ShouldSendSilent(peer, action.options); - const auto sendAs = action.options.sendAs; using SendFlag = MTPmessages_ForwardMessages::Flag; auto flags = MessageFlags(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 86dd68802..4af6d5fbd 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1057,19 +1057,50 @@ void Controller::fillSignaturesButton() { return; } - AddButtonWithText( + const auto signs = AddButtonWithText( _controls.buttonsLayout, tr::lng_edit_sign_messages(), rpl::single(QString()), [] {}, { &st::menuIconSigned } - )->toggleOn(rpl::single(channel->addsSignature()) - )->toggledValue( + )->toggleOn(rpl::single(channel->addsSignature())); + + const auto profiles = _controls.buttonsLayout->add( + object_ptr>( + _controls.buttonsLayout, + EditPeerInfoBox::CreateButton( + _controls.buttonsLayout, + tr::lng_edit_sign_profiles(), + rpl::single(QString()), + [] {}, + st::manageGroupTopButtonWithText, + { &st::menuIconSigned }))); + profiles->toggleOn(signs->toggledValue()); + profiles->finishAnimating(); + + profiles->entity()->toggleOn(rpl::single( + channel->addsSignature() && channel->signatureProfiles() + ))->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + _signatureProfilesSavedValue = toggled; + }, profiles->entity()->lifetime()); + + signs->toggledValue( ) | rpl::start_with_next([=](bool toggled) { _signaturesSavedValue = toggled; + if (!toggled) { + _signatureProfilesSavedValue = false; + } }, _controls.buttonsLayout->lifetime()); - _signatureProfilesSavedValue = channel->signatureProfiles(); + Ui::AddSkip(_controls.buttonsLayout); + Ui::AddDividerText( + _controls.buttonsLayout, + rpl::conditional( + signs->toggledValue(), + tr::lng_edit_sign_profiles_about(Ui::Text::WithEntities), + tr::lng_edit_sign_messages_about(Ui::Text::WithEntities))); + Ui::AddSkip(_controls.buttonsLayout); } void Controller::fillHistoryVisibilityButton() { @@ -1227,11 +1258,9 @@ void Controller::fillManageSection() { } if (canEditSignatures) { fillSignaturesButton(); - } - if (canEditPreHistoryHidden + } else if (canEditPreHistoryHidden || canEditForum || canEditColorIndex - || canEditSignatures //|| canEditInviteLinks || canViewOrEditLinkedChat || canEditType) { diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 7c2846d5d..39e41a753 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -184,7 +184,11 @@ void ChannelData::setFlags(ChannelDataFlags which) { }); } } - if (diff & (Flag::Forum | Flag::CallNotEmpty | Flag::SimilarExpanded)) { + if (diff & (Flag::Forum + | Flag::CallNotEmpty + | Flag::SimilarExpanded + | Flag::Signatures + | Flag::SignatureProfiles)) { if (const auto history = this->owner().historyLoaded(this)) { if (diff & Flag::CallNotEmpty) { history->updateChatListEntry(); @@ -203,6 +207,12 @@ void ChannelData::setFlags(ChannelDataFlags which) { history->owner().requestItemResize(item); } } + if (diff & Flag::SignatureProfiles) { + history->forceFullResize(); + } + if (diff & (Flag::Signatures | Flag::SignatureProfiles)) { + session().changes().peerUpdated(this, UpdateFlag::Rights); + } } } if (const auto raw = taken.get()) { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 68ef86684..08aaca699 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -1268,9 +1268,12 @@ Data::RestrictionCheckResult PeerData::amRestricted( } bool PeerData::amAnonymous() const { - return isBroadcast() - || (isChannel() - && (asChannel()->adminRights() & ChatAdminRight::Anonymous)); + if (const auto channel = asChannel()) { + return channel->isBroadcast() + ? !channel->signatureProfiles() + : (channel->adminRights() & ChatAdminRight::Anonymous); + } + return false; } bool PeerData::canRevokeFullHistory() const { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 515a6556f..932844085 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -313,19 +313,20 @@ enum class MessageFlag : uint64 { // If not set then we need to refresh _displayFrom value. DisplayFromChecked = (1ULL << 40), + DisplayFromProfiles = (1ULL << 41), - ShowSimilarChannels = (1ULL << 41), + ShowSimilarChannels = (1ULL << 42), - Sponsored = (1ULL << 42), + Sponsored = (1ULL << 43), - ReactionsAreTags = (1ULL << 43), + ReactionsAreTags = (1ULL << 44), - ShortcutMessage = (1ULL << 44), + ShortcutMessage = (1ULL << 45), - EffectWatched = (1ULL << 45), + EffectWatched = (1ULL << 46), - SensitiveContent = (1ULL << 46), - AllowSensitive = (1ULL << 47), + SensitiveContent = (1ULL << 47), + AllowSensitive = (1ULL << 48), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index b5b36e80d..57409c365 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -730,8 +730,8 @@ bool HistoryInner::canHaveFromUserpics() const { && !_peer->isRepliesChat() && !_isChatWide) { return false; - } else if (_peer->isChannel() && !_peer->isMegagroup()) { - return false; + } else if (const auto channel = _peer->asBroadcast()) { + return channel->signatureProfiles(); } return true; } diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 4f0b5fa8a..18e803aeb 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1232,10 +1232,19 @@ PeerData *HistoryItem::computeDisplayFrom() const { } PeerData *HistoryItem::displayFrom() const { - if (!(_flags & MessageFlag::DisplayFromChecked)) { - _flags |= MessageFlag::DisplayFromChecked; - _displayFrom = computeDisplayFrom(); + if (_flags & MessageFlag::DisplayFromChecked) { + const auto showing = isPostShowingAuthor(); + const auto flag = (_flags & MessageFlag::DisplayFromProfiles); + if (showing && !flag) { + _flags |= MessageFlag::DisplayFromProfiles; + } else if (!showing && flag) { + _flags &= ~MessageFlag::DisplayFromProfiles; + } else { + return _displayFrom; + } } + _flags |= MessageFlag::DisplayFromChecked; + _displayFrom = computeDisplayFrom(); return _displayFrom; } @@ -2148,7 +2157,7 @@ QString HistoryItem::notificationHeader() const { return QString(); } else if (out() && isFromScheduled() && !_history->peer->isSelf()) { return tr::lng_from_you(tr::now); - } else if (!_history->peer->isUser() && !isPost()) { + } else if (!_history->peer->isUser() && !isPostHidingAuthor()) { return from()->name(); } return QString(); @@ -2746,7 +2755,9 @@ bool HistoryItem::inThread(MsgId rootId) const { } not_null HistoryItem::author() const { - return (isPost() && !isSponsored()) ? _history->peer : from(); + return (isPostHidingAuthor() && !isSponsored()) + ? _history->peer + : from(); } TimeId HistoryItem::originalDate() const { @@ -3092,6 +3103,23 @@ bool HistoryItem::isUploading() const { return _media && _media->uploading(); } +bool HistoryItem::hasRealFromId() const { + return !isPost() || (_flags & MessageFlag::HasFromId); +} + +bool HistoryItem::isPostHidingAuthor() const { + if (!isPost()) { + return false; + } else if (const auto channel = _history->peer->asBroadcast()) { + return !channel->signatureProfiles(); + } + return false; // Should not happen, I guess. +} + +bool HistoryItem::isPostShowingAuthor() const { + return isPost() && !isPostHidingAuthor(); +} + bool HistoryItem::isRegular() const { return isHistoryEntry() && !isLocal(); } @@ -3464,7 +3492,7 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { return {}; }; const auto sender = [&]() -> std::optional { - if (options.hideSender || isPost() || isEmpty()) { + if (options.hideSender || isPostHidingAuthor() || isEmpty()) { return {}; } else if (!_history->peer->isUser()) { if (const auto from = displayFrom()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index a11c6ce97..6b10b6534 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -327,6 +327,9 @@ public: [[nodiscard]] bool showSimilarChannels() const { return _flags & MessageFlag::ShowSimilarChannels; } + [[nodiscard]] bool hasRealFromId() const; + [[nodiscard]] bool isPostHidingAuthor() const; + [[nodiscard]] bool isPostShowingAuthor() const; [[nodiscard]] bool isRegular() const; [[nodiscard]] bool isUploading() const; void sendFailed(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 86012f331..2b3696f35 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -822,6 +822,14 @@ HistoryWidget::HistoryWidget( updateStickersByEmoji(); updateFieldPlaceholder(); _preview->checkNow(false); + + const auto was = (_sendAs != nullptr); + refreshSendAsToggle(); + if (was != (_sendAs != nullptr)) { + updateControlsVisibility(); + updateControlsGeometry(); + orderWidgets(); + } } if (flags & PeerUpdateFlag::Migration) { handlePeerMigration(); diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index 42ea80dce..6784c7eca 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/click_handler_types.h" #include "main/main_session.h" #include "lottie/lottie_icon.h" +#include "data/data_channel.h" #include "data/data_session.h" #include "data/data_message_reactions.h" #include "window/window_session_controller.h" @@ -570,10 +571,14 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null message) { if (message->context() == Context::ShortcutMessages) { result.flags |= Flag::Shortcut; } - if (const auto msgsigned = item->Get()) { - if (!msgsigned->isAnonymousRank) { - result.author = msgsigned->author; - } + if (!item->isPost() + || !item->hasRealFromId() + || !item->history()->peer->asChannel()->signatureProfiles()) { + if (const auto msgsigned = item->Get()) { + if (!msgsigned->isAnonymousRank) { + result.author = msgsigned->author; + } + } } if (message->displayedEditDate()) { result.flags |= Flag::Edited; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 50f5b7454..cb27621ba 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -1087,7 +1087,7 @@ bool Element::computeIsAttachToPrevious(not_null previous) { const auto item = view->data(); return !item->isService() && !item->isEmpty() - && !item->isPost() + && !item->isPostHidingAuthor() && (!item->history()->peer->isMegagroup() || !view->hasOutLayout() || !item->from()->isChannel()); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 50ee782d8..345ecf954 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -408,6 +408,7 @@ Message::Message( Element *replacing) : Element(delegate, data, replacing, Flag(0)) , _hideReply(delegate->elementHideReply(this)) +, _postShowingAuthor(data->isPostShowingAuthor() ? 1 : 0) , _bottomInfo( &data->history()->owner().reactions(), BottomInfoDataFromMessage(this)) { @@ -2272,10 +2273,11 @@ bool Message::hasFromPhoto() const { case Context::SavedSublist: case Context::ScheduledTopic: { const auto item = data(); - if (item->isPost()) { + if (item->isPostHidingAuthor()) { return false; - } - if (item->isEmpty() + } else if (item->isPost()) { + return true; + } else if (item->isEmpty() || (context() == Context::Replies && item->isDiscussionPost())) { return false; } else if (delegate()->elementIsChatWide()) { @@ -4244,6 +4246,14 @@ int Message::resizeContentGetHeight(int newWidth) { const auto mediaDisplayed = media ? media->isDisplayed() : false; const auto bubble = drawBubble(); + const auto postShowingAuthor = item->isPostShowingAuthor() ? 1 : 0; + if (_postShowingAuthor != postShowingAuthor) { + _postShowingAuthor = postShowingAuthor; + _bottomInfo.update(BottomInfoDataFromMessage(this), newWidth); + _fromNameVersion = -1; + previousInBlocksChanged(); + } + item->resolveDependent(); // This code duplicates countGeometry() but also resizes media. diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 48a45bb03..6b8c28d48 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -315,10 +315,11 @@ private: mutable std::unique_ptr _fromNameStatus; Ui::Text::String _rightBadge; mutable int _fromNameVersion = 0; - uint32 _bubbleWidthLimit : 29 = 0; + uint32 _bubbleWidthLimit : 28 = 0; uint32 _invertMedia : 1 = 0; uint32 _hideReply : 1 = 0; uint32 _rightBadgeHasBoosts : 1 = 0; + uint32 _postShowingAuthor : 1 = 0; BottomInfo _bottomInfo; diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index 17065d7ef..ab75de103 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -43,11 +43,16 @@ SendAsPeers::SendAsPeers(not_null session) bool SendAsPeers::shouldChoose(not_null peer) { refresh(peer); - return Data::CanSendAnything(peer, false) && (list(peer).size() > 1); + const auto channel = peer->asBroadcast(); + return Data::CanSendAnything(peer, false) + && (list(peer).size() > 1) + && (!channel + || channel->addsSignature() + || channel->signatureProfiles()); } void SendAsPeers::refresh(not_null peer, bool force) { - if (!peer->isMegagroup()) { + if (!peer->isChannel()) { return; } const auto now = crl::now(); @@ -117,7 +122,7 @@ not_null SendAsPeers::ResolveChosen( ? i->peer : !list.empty() ? list.front().peer - : (peer->isMegagroup() && peer->amAnonymous()) + : peer->amAnonymous() ? peer : peer->session().user(); } diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index 0856b044a..d7c7f08ab 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -263,9 +263,9 @@ void SetupSendAsButton( auto userpic = current->value( ) | rpl::filter([=](PeerData *peer) { - return peer && peer->isMegagroup(); + return peer && peer->isChannel(); }) | rpl::map([=](not_null peer) { - const auto channel = peer->asMegagroup(); + const auto channel = peer->asChannel(); auto updates = rpl::single( rpl::empty