Support topic closing.

This commit is contained in:
John Preston 2022-10-18 19:39:41 +04:00
parent fe41fbd7e9
commit ad2f9438a2
9 changed files with 132 additions and 17 deletions

View file

@ -3517,10 +3517,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forum_topic_new" = "New Topic"; "lng_forum_topic_new" = "New Topic";
"lng_forum_topic_edit" = "Edit Topic"; "lng_forum_topic_edit" = "Edit Topic";
"lng_forum_topic_title" = "Topic Title"; "lng_forum_topic_title" = "Topic Title";
"lng_forum_topic_closed" = "This topic is now closed.";
"lng_forum_topics_switch" = "Topics"; "lng_forum_topics_switch" = "Topics";
"lng_forum_no_topics" = "No topics currently created in this forum."; "lng_forum_no_topics" = "No topics currently created in this forum.";
"lng_forum_create_topic" = "Create topic"; "lng_forum_create_topic" = "Create topic";
"lng_forum_discard_sure" = "Discard sure?"; "lng_forum_discard_sure" = "Are you sure you want to discard this topic?";
"lng_forum_view_as_messages" = "View as Messages"; "lng_forum_view_as_messages" = "View as Messages";
// Wnd specific // Wnd specific

View file

@ -154,6 +154,7 @@ struct TopicUpdate {
IconId = (1U << 6), IconId = (1U << 6),
ColorId = (1U << 7), ColorId = (1U << 7),
CloudDraft = (1U << 8), CloudDraft = (1U << 8),
Closed = (1U << 9),
LastUsedBit = (1U << 8), LastUsedBit = (1U << 8),
}; };

View file

@ -198,6 +198,14 @@ MsgId ForumTopic::rootId() const {
return _rootId; return _rootId;
} }
bool ForumTopic::canEdit() const {
return (_flags & Flag::My) || channel()->canEditTopics();
}
bool ForumTopic::canToggleClosed() const {
return !creating() && canEdit();
}
bool ForumTopic::creating() const { bool ForumTopic::creating() const {
return _forum->creating(_rootId); return _forum->creating(_rootId);
} }
@ -231,13 +239,11 @@ void ForumTopic::applyTopic(const MTPDforumTopic &data) {
applyColorId(data.vicon_color().v); applyColorId(data.vicon_color().v);
const auto pinned = _list->pinned(); const auto pinned = _list->pinned();
#if 0 // #TODO forum pinned
if (data.is_pinned()) { if (data.is_pinned()) {
pinned->addPinned(Dialogs::Key(this)); pinned->addPinned(Dialogs::Key(this));
} else { } else {
pinned->setPinned(Dialogs::Key(this), false); pinned->setPinned(Dialogs::Key(this), false);
} }
#endif
owner().notifySettings().apply(this, data.vnotify_settings()); owner().notifySettings().apply(this, data.vnotify_settings());
@ -249,19 +255,60 @@ void ForumTopic::applyTopic(const MTPDforumTopic &data) {
_rootId, _rootId,
draft->c_draftMessage()); draft->c_draftMessage());
} }
if (data.is_my()) {
_flags |= Flag::My;
} else {
_flags &= ~Flag::My;
}
setClosed(data.is_closed());
_replies->setInboxReadTill( _replies->setInboxReadTill(
data.vread_inbox_max_id().v, data.vread_inbox_max_id().v,
data.vunread_count().v); data.vunread_count().v);
_replies->setOutboxReadTill(data.vread_outbox_max_id().v); _replies->setOutboxReadTill(data.vread_outbox_max_id().v);
applyTopicTopMessage(data.vtop_message().v); applyTopicTopMessage(data.vtop_message().v);
#if 0 // #TODO forum unread mark
setUnreadMark(data.is_unread_mark());
#endif
unreadMentions().setCount(data.vunread_mentions_count().v); unreadMentions().setCount(data.vunread_mentions_count().v);
unreadReactions().setCount(data.vunread_reactions_count().v); unreadReactions().setCount(data.vunread_reactions_count().v);
} }
bool ForumTopic::closed() const {
return _flags & Flag::Closed;
}
void ForumTopic::setClosed(bool closed) {
if (this->closed() == closed) {
return;
} else if (closed) {
_flags |= Flag::Closed;
} else {
_flags &= ~Flag::Closed;
}
session().changes().topicUpdated(this, UpdateFlag::Closed);
}
void ForumTopic::setClosedAndSave(bool closed) {
setClosed(closed);
const auto api = &session().api();
const auto weak = base::make_weak(this);
api->request(MTPchannels_EditForumTopic(
MTP_flags(MTPchannels_EditForumTopic::Flag::f_closed),
channel()->inputChannel,
MTP_int(_rootId),
MTPstring(), // title
MTPlong(), // icon_emoji_id
MTP_bool(closed)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).fail([=](const MTP::Error &error) {
if (error.type() != u"TOPIC_NOT_MODIFIED") {
if (const auto topic = weak.get()) {
topic->forum()->requestTopic(topic->rootId());
}
}
}).send();
}
void ForumTopic::indexTitleParts() { void ForumTopic::indexTitleParts() {
_titleWords.clear(); _titleWords.clear();
_titleFirstLetters.clear(); _titleFirstLetters.clear();

View file

@ -61,6 +61,13 @@ public:
[[nodiscard]] rpl::producer<> destroyed() const; [[nodiscard]] rpl::producer<> destroyed() const;
[[nodiscard]] MsgId rootId() const; [[nodiscard]] MsgId rootId() const;
[[nodiscard]] bool canEdit() const;
[[nodiscard]] bool canToggleClosed() const;
[[nodiscard]] bool closed() const;
void setClosed(bool closed);
void setClosedAndSave(bool closed);
[[nodiscard]] bool creating() const; [[nodiscard]] bool creating() const;
void discard(); void discard();
@ -128,6 +135,13 @@ public:
->not_null<HistoryView::SendActionPainter*> override; ->not_null<HistoryView::SendActionPainter*> override;
private: private:
enum class Flag : uchar {
Closed = (1 << 0),
My = (1 << 1),
};
friend inline constexpr bool is_flag_type(Flag) { return true; }
using Flags = base::flags<Flag>;
void indexTitleParts(); void indexTitleParts();
void validateDefaultIcon() const; void validateDefaultIcon() const;
void applyTopicTopMessage(MsgId topMessageId); void applyTopicTopMessage(MsgId topMessageId);
@ -158,6 +172,7 @@ private:
base::flat_set<QChar> _titleFirstLetters; base::flat_set<QChar> _titleFirstLetters;
int _titleVersion = 0; int _titleVersion = 0;
int32 _colorId = 0; int32 _colorId = 0;
Flags _flags;
std::unique_ptr<Ui::Text::CustomEmoji> _icon; std::unique_ptr<Ui::Text::CustomEmoji> _icon;
mutable QImage _defaultIcon; // on-demand mutable QImage _defaultIcon; // on-demand

View file

@ -534,6 +534,15 @@ bool PeerData::canCreateTopics() const {
return isForum() && canPinMessages(); return isForum() && canPinMessages();
} }
bool PeerData::canEditTopics() const {
if (const auto channel = asChannel()) {
return channel->isForum()
&& (channel->amCreator()
|| (channel->adminRights() & ChatAdminRight::PinMessages));
}
return false;
}
bool PeerData::canEditMessagesIndefinitely() const { bool PeerData::canEditMessagesIndefinitely() const {
if (const auto user = asUser()) { if (const auto user = asUser()) {
return user->isSelf(); return user->isSelf();

View file

@ -340,6 +340,7 @@ public:
[[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const; [[nodiscard]] bool canEditMessagesIndefinitely() const;
[[nodiscard]] bool canCreateTopics() const; [[nodiscard]] bool canCreateTopics() const;
[[nodiscard]] bool canEditTopics() const;
[[nodiscard]] bool canExportChatHistory() const; [[nodiscard]] bool canExportChatHistory() const;
// Returns true if about text was changed. // Returns true if about text was changed.

View file

@ -638,7 +638,6 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) { auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) {
auto result = PreparedText{}; auto result = PreparedText{};
// #TODO lang-forum
result.text = { tr::lng_action_topic_created(tr::now) }; result.text = { tr::lng_action_topic_created(tr::now) };
return result; return result;
}; };

View file

@ -568,19 +568,34 @@ void RepliesWidget::setupComposeControls() {
std::move(hasSendingMessage), std::move(hasSendingMessage),
_1 && _2); _1 && _2);
auto topicWriteRestrictions = rpl::single(
) | rpl::then(session().changes().topicUpdates(
Data::TopicUpdate::Flag::Closed
) | rpl::filter([=](const Data::TopicUpdate &update) {
return (update.topic->history() == _history)
&& (update.topic->rootId() == _rootId);
}) | rpl::to_empty) | rpl::map([=] {
const auto topic = _topic
? _topic
: _history->peer->forumTopicFor(_rootId);
return (topic->canToggleClosed() || !topic->closed())
? std::optional<QString>()
: tr::lng_forum_topic_closed(tr::now);
});
auto writeRestriction = rpl::combine( auto writeRestriction = rpl::combine(
session().changes().peerFlagsValue( session().changes().peerFlagsValue(
_history->peer, _history->peer,
Data::PeerUpdate::Flag::Rights), Data::PeerUpdate::Flag::Rights),
Data::CanWriteValue(_history->peer) Data::CanWriteValue(_history->peer),
) | rpl::map([=] { std::move(topicWriteRestrictions)
) | rpl::map([=](auto, auto, std::optional<QString> topicRestriction) {
const auto restriction = Data::RestrictionError( const auto restriction = Data::RestrictionError(
_history->peer, _history->peer,
ChatRestriction::SendMessages); ChatRestriction::SendMessages);
return restriction return restriction
? restriction ? restriction
: _history->peer->canWrite() : _history->peer->canWrite()
? std::optional<QString>() ? std::move(topicRestriction)
: tr::lng_group_not_accessible(tr::now); : tr::lng_group_not_accessible(tr::now);
}); });
@ -1465,7 +1480,7 @@ bool RepliesWidget::preventsClose(Fn<void()> &&continueCallback) const {
} }
}; };
controller()->show(Ui::MakeConfirmBox({ controller()->show(Ui::MakeConfirmBox({
.text = rpl::single(u"Sure discard?"_q), // #TODO lang-forum .text = tr::lng_forum_discard_sure(tr::now),
.confirmed = std::move(sure), .confirmed = std::move(sure),
.confirmText = tr::lng_record_lock_discard(), .confirmText = tr::lng_record_lock_discard(),
.confirmStyle = &st::attentionBoxButton, .confirmStyle = &st::attentionBoxButton,

View file

@ -226,6 +226,7 @@ private:
void addThemeEdit(); void addThemeEdit();
void addBlockUser(); void addBlockUser();
void addViewDiscussion(); void addViewDiscussion();
void addToggleTopicClosed();
void addExportChat(); void addExportChat();
void addReport(); void addReport();
void addNewContact(); void addNewContact();
@ -239,6 +240,7 @@ private:
void addCreateTopic(); void addCreateTopic();
void addViewAsMessages(); void addViewAsMessages();
void addSearchTopics(); void addSearchTopics();
void addDeleteTopic();
not_null<SessionController*> _controller; not_null<SessionController*> _controller;
Dialogs::EntryState _request; Dialogs::EntryState _request;
@ -386,6 +388,19 @@ void Filler::addHidePromotion() {
}, &st::menuIconRemove); }, &st::menuIconRemove);
} }
void Filler::addToggleTopicClosed() {
if (!_topic || !_topic->canToggleClosed()) {
return;
}
const auto closed = _topic->closed();
const auto weak = base::make_weak(_topic);
_addAction(closed ? u"Reopen"_q : u"Close"_q, [=] {
if (const auto topic = weak.get()) {
topic->setClosedAndSave(!closed);
}
}, closed ? &st::menuIconRestartBot : &st::menuIconBlock);
}
void Filler::addTogglePin() { void Filler::addTogglePin() {
if (!_peer || _topic) { if (!_peer || _topic) {
// #TODO forum pinned // #TODO forum pinned
@ -802,13 +817,19 @@ void Filler::addDeleteContact() {
}); });
} }
void Filler::addManageTopic() { void Filler::addDeleteTopic() {
const auto topic = _thread->asTopic(); if (!_topic/* || !_topic->canDelete()*/) {
if (!topic) {
return; return;
} }
const auto history = topic->history(); }
const auto rootId = topic->rootId();
void Filler::addManageTopic() {
if (!_topic || !_topic->canEdit()) {
return;
}
const auto history = _topic->history();
const auto rootId = _topic->rootId();
const auto navigation = _controller; const auto navigation = _controller;
_addAction(tr::lng_forum_topic_edit(tr::now), [=] { _addAction(tr::lng_forum_topic_edit(tr::now), [=] {
navigation->show( navigation->show(
@ -995,6 +1016,7 @@ void Filler::fillContextMenuActions() {
} }
addToggleMuteSubmenu(false); addToggleMuteSubmenu(false);
addToggleUnreadMark(); addToggleUnreadMark();
addToggleTopicClosed();
addToggleFolder(); addToggleFolder();
if (const auto user = _peer->asUser()) { if (const auto user = _peer->asUser()) {
if (!user->isContact()) { if (!user->isContact()) {
@ -1004,6 +1026,7 @@ void Filler::fillContextMenuActions() {
addClearHistory(); addClearHistory();
addDeleteChat(); addDeleteChat();
addLeaveChat(); addLeaveChat();
addDeleteTopic();
} }
void Filler::fillHistoryActions() { void Filler::fillHistoryActions() {
@ -1032,21 +1055,25 @@ void Filler::fillProfileActions() {
addNewMembers(); addNewMembers();
addManageTopic(); addManageTopic();
addManageChat(); addManageChat();
addToggleTopicClosed();
addViewDiscussion(); addViewDiscussion();
addExportChat(); addExportChat();
addBlockUser(); addBlockUser();
addReport(); addReport();
addLeaveChat(); addLeaveChat();
addDeleteContact(); addDeleteContact();
addDeleteTopic();
} }
void Filler::fillRepliesActions() { void Filler::fillRepliesActions() {
if (_thread->asTopic()) { if (_topic) {
addInfo(); addInfo();
addManageTopic(); addManageTopic();
addManageChat(); addManageChat();
addDeleteTopic();
} }
addCreatePoll(); addCreatePoll();
addToggleTopicClosed();
} }
void Filler::fillScheduledActions() { void Filler::fillScheduledActions() {