Support group-like channels.

This commit is contained in:
John Preston 2024-08-13 13:51:11 +02:00
parent 24a7e48b75
commit 9ea495f59d
17 changed files with 152 additions and 46 deletions

View file

@ -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+";

View file

@ -41,14 +41,14 @@ void InnerFillMessagePostFlags(
const SendOptions &options,
not_null<PeerData*> 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;
}
}

View file

@ -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();

View file

@ -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<Ui::SlideWrap<Ui::SettingsButton>>(
_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) {

View file

@ -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()) {

View file

@ -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 {

View file

@ -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<MessageFlag>;

View file

@ -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;
}

View file

@ -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<PeerData*> 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<QString> {
if (options.hideSender || isPost() || isEmpty()) {
if (options.hideSender || isPostHidingAuthor() || isEmpty()) {
return {};
} else if (!_history->peer->isUser()) {
if (const auto from = displayFrom()) {

View file

@ -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();

View file

@ -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();

View file

@ -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*> message) {
if (message->context() == Context::ShortcutMessages) {
result.flags |= Flag::Shortcut;
}
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
if (!msgsigned->isAnonymousRank) {
result.author = msgsigned->author;
}
if (!item->isPost()
|| !item->hasRealFromId()
|| !item->history()->peer->asChannel()->signatureProfiles()) {
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
if (!msgsigned->isAnonymousRank) {
result.author = msgsigned->author;
}
}
}
if (message->displayedEditDate()) {
result.flags |= Flag::Edited;

View file

@ -1087,7 +1087,7 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
const auto item = view->data();
return !item->isService()
&& !item->isEmpty()
&& !item->isPost()
&& !item->isPostHidingAuthor()
&& (!item->history()->peer->isMegagroup()
|| !view->hasOutLayout()
|| !item->from()->isChannel());

View file

@ -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.

View file

@ -315,10 +315,11 @@ private:
mutable std::unique_ptr<FromNameStatus> _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;

View file

@ -43,11 +43,16 @@ SendAsPeers::SendAsPeers(not_null<Session*> session)
bool SendAsPeers::shouldChoose(not_null<PeerData*> 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<PeerData*> peer, bool force) {
if (!peer->isMegagroup()) {
if (!peer->isChannel()) {
return;
}
const auto now = crl::now();
@ -117,7 +122,7 @@ not_null<PeerData*> SendAsPeers::ResolveChosen(
? i->peer
: !list.empty()
? list.front().peer
: (peer->isMegagroup() && peer->amAnonymous())
: peer->amAnonymous()
? peer
: peer->session().user();
}

View file

@ -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<PeerData*> peer) {
const auto channel = peer->asMegagroup();
const auto channel = peer->asChannel();
auto updates = rpl::single(
rpl::empty