Support external reply to channel posts.

This commit is contained in:
John Preston 2023-10-10 17:51:27 +04:00
parent 394883b986
commit 6c19274eac
7 changed files with 145 additions and 53 deletions

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/admin_log/history_admin_log_item.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "history/view/controls/history_view_forward_panel.h"
#include "history/view/media/history_view_media.h"
#include "history/view/media/history_view_sticker.h"
#include "history/view/media/history_view_web_page.h"
@ -2196,7 +2197,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
return;
}
const auto itemId = item->fullId();
const auto canReply = [&] {
const auto canSendReply = [&] {
const auto peer = item->history()->peer;
const auto topic = item->topic();
return topic
@ -2204,9 +2205,24 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
: (Data::CanSendAnything(peer)
&& (!peer->isChannel() || peer->asChannel()->amIn()));
}();
const auto canReply = canSendReply || [&] {
const auto peer = item->history()->peer;
if (const auto chat = peer->asChat()) {
return !chat->isForbidden();
} else if (const auto channel = peer->asChannel()) {
return !channel->isForbidden();
}
return true;
}();
if (canReply) {
_menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
_widget->replyToMessage({ itemId });
if (canSendReply) {
_widget->replyToMessage({ itemId });
} else {
HistoryView::Controls::ShowReplyToChatBox(
controller->uiShow(),
{ itemId });
}
}, &st::menuIconReply);
}
const auto repliesCount = item->repliesCount();

View File

@ -6222,10 +6222,19 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
_forwardPanel->editOptions(controller()->uiShow());
}
} else if (const auto reply = replyTo()) {
HistoryView::Controls::EditReplyOptions(
controller(),
const auto highlight = [=] {
controller()->showPeerHistory(
reply.messageId.peer,
Window::SectionShow::Way::Forward,
reply.messageId.msg);
};
const auto history = _history;
using namespace HistoryView::Controls;
EditReplyOptions(
controller()->uiShow(),
reply,
_history);
highlight,
[=] { ClearDraftReplyTo(history, reply.messageId); });
} else if (_editMsgId) {
controller()->showPeerHistory(
_peer,
@ -7611,7 +7620,10 @@ bool HistoryWidget::updateCanSendMessage() {
if (!_peer) {
return false;
}
const auto replyTo = (_replyTo && !_editMsgId) ? _replyEditMsg : 0;
const auto checkTopicFromReplyTo = _replyTo
&& !_editMsgId
&& (_replyTo.messageId.peer == _peer->id);
const auto replyTo = checkTopicFromReplyTo ? _replyEditMsg : 0;
const auto topic = replyTo ? replyTo->topic() : nullptr;
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;

View File

@ -414,6 +414,7 @@ private:
const std::shared_ptr<ChatHelpers::Show> _show;
History *_history = nullptr;
MsgId _topicRootId = 0;
rpl::variable<QString> _title;
rpl::variable<QString> _description;
@ -468,6 +469,7 @@ FieldHeader::FieldHeader(
void FieldHeader::setHistory(const SetHistoryArgs &args) {
_history = *args.history;
_topicRootId = args.topicRootId;
}
void FieldHeader::init() {
@ -619,8 +621,24 @@ void FieldHeader::init() {
} else if (isLeftButton && inPhotoEdit) {
_editPhotoRequests.fire({});
} else if (isLeftButton && inPreviewRect) {
const auto reply = replyingToMessage();
if (!isEditingMessage() && readyToForward()) {
_forwardPanel->editOptions(_show);
} else if (!isEditingMessage() && reply) {
using namespace Controls;
const auto highlight = [=] {
_scrollToItemRequests.fire_copy(reply.messageId);
};
const auto history = _history;
const auto topicRootId = _topicRootId;
const auto clearOldReplyTo = [=, id = reply.messageId] {
ClearDraftReplyTo(history, topicRootId, id);
};
EditReplyOptions(
_show,
reply,
highlight,
clearOldReplyTo);
} else {
auto id = isEditingMessage()
? _editMsgId.current()

View File

@ -403,10 +403,38 @@ void ForwardPanel::paint(
});
}
void ClearDraftReplyTo(not_null<Data::Thread*> thread, FullMsgId equalTo) {
ClearDraftReplyTo(
thread->owningHistory(),
thread->topicRootId(),
equalTo);
}
void ClearDraftReplyTo(
not_null<History*> history,
MsgId topicRootId,
FullMsgId equalTo) {
const auto local = history->localDraft(topicRootId);
if (!local || (equalTo && local->reply.messageId != equalTo)) {
return;
}
auto draft = *local;
draft.reply = { .topicRootId = topicRootId };
if (Data::DraftIsNull(&draft)) {
history->clearLocalDraft(topicRootId);
} else {
history->setLocalDraft(
std::make_unique<Data::Draft>(std::move(draft)));
}
if (const auto thread = history->threadFor(topicRootId)) {
history->session().api().saveDraftToCloudDelayed(thread);
}
}
void ShowReplyToChatBox(
not_null<Window::SessionController*> window,
std::shared_ptr<ChatHelpers::Show> show,
FullReplyTo reply,
base::weak_ptr<Data::Thread> oldThread) {
Fn<void()> clearOldDraft) {
class Controller final : public ChooseRecipientBoxController {
public:
using Chosen = not_null<Data::Thread*>;
@ -426,7 +454,15 @@ void ShowReplyToChatBox(
return _singleChosen.events();
}
bool respectSavedMessagesChat() const override {
return false;
}
private:
void prepareViewHook() override {
delegate()->peerListSetTitle(rpl::single(u"Reply in..."_q));
}
rpl::event_stream<Chosen> _singleChosen;
};
@ -436,13 +472,13 @@ void ShowReplyToChatBox(
not_null<Controller*> controller;
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto session = &window->session();
const auto session = &show->session();
const auto state = [&] {
auto controller = std::make_unique<Controller>(session);
const auto controllerRaw = controller.get();
auto box = Box<PeerListBox>(std::move(controller), nullptr);
const auto boxRaw = box.data();
window->uiShow()->show(std::move(box));
show->show(std::move(box));
auto state = State{ boxRaw, controllerRaw };
return boxRaw->lifetime().make_state<State>(std::move(state));
}();
@ -466,26 +502,9 @@ void ShowReplyToChatBox(
thread,
Data::EntryUpdate::Flag::LocalDraftSet);
// Clear old one.
crl::on_main(oldThread, [=] {
const auto old = oldThread.get();
const auto history = old->owningHistory();
const auto topicRootId = old->topicRootId();
if (const auto local = history->localDraft(topicRootId)) {
if (local->reply.messageId == reply.messageId) {
auto draft = *local;
draft.reply = { .topicRootId = topicRootId };
if (Data::DraftIsNull(&draft)) {
history->clearLocalDraft(topicRootId);
} else {
history->setLocalDraft(
std::make_unique<Data::Draft>(
std::move(draft)));
}
old->session().api().saveDraftToCloudDelayed(old);
}
}
});
if (clearOldDraft) {
crl::on_main(&history->session(), clearOldDraft);
}
return true;
};
auto callback = [=, chosen = std::move(chosen)](
@ -502,11 +521,11 @@ void ShowReplyToChatBox(
}
void EditReplyOptions(
not_null<Window::SessionController*> controller,
std::shared_ptr<ChatHelpers::Show> show,
FullReplyTo reply,
not_null<Data::Thread*> thread) {
const auto weak = base::make_weak(thread);
controller->uiShow()->show(Box([=](not_null<Ui::GenericBox*> box) {
Fn<void()> highlight,
Fn<void()> clearOldDraft) {
show->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(rpl::single(u"Reply to Message"_q));
Settings::AddButton(
@ -515,7 +534,7 @@ void EditReplyOptions(
st::settingsButton,
{ &st::menuIconReply }
)->setClickedCallback([=] {
ShowReplyToChatBox(controller, reply, weak);
ShowReplyToChatBox(show, reply, clearOldDraft);
});
Settings::AddButton(
@ -523,12 +542,7 @@ void EditReplyOptions(
rpl::single(u"Show message"_q),
st::settingsButton,
{ &st::menuIconShowInChat }
)->setClickedCallback([=] {
controller->showPeerHistory(
reply.messageId.peer,
Window::SectionShow::Way::Forward,
reply.messageId.msg);
});
)->setClickedCallback(highlight);
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();

View File

@ -71,9 +71,21 @@ private:
};
void EditReplyOptions(
not_null<Window::SessionController*> controller,
void ClearDraftReplyTo(not_null<Data::Thread*> thread, FullMsgId equalTo);
void ClearDraftReplyTo(
not_null<History*> history,
MsgId topicRootId,
FullMsgId equalTo);
void ShowReplyToChatBox(
std::shared_ptr<ChatHelpers::Show> show,
FullReplyTo reply,
not_null<Data::Thread*> thread);
Fn<void()> clearOldDraft = nullptr);
void EditReplyOptions(
std::shared_ptr<ChatHelpers::Show> show,
FullReplyTo reply,
Fn<void()> highlight,
Fn<void()> clearOldDraft = nullptr);
} // namespace HistoryView::Controls

View File

@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_stories.h"
#include "data/data_groups.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_message_reactions.h"
@ -588,20 +589,33 @@ bool AddReplyToMessageAction(
const auto peer = item ? item->history()->peer.get() : nullptr;
if (!item
|| !item->isRegular()
|| !(topic
? Data::CanSendAnything(topic)
: Data::CanSendAnything(peer))
|| (context != Context::History && context != Context::Replies)) {
return false;
}
const auto canSendReply = topic
? Data::CanSendAnything(topic)
: Data::CanSendAnything(peer);
const auto canReply = canSendReply || [&] {
const auto peer = item->history()->peer;
if (const auto chat = peer->asChat()) {
return !chat->isForbidden();
} else if (const auto channel = peer->asChannel()) {
return !channel->isForbidden();
}
return true;
}();
if (!canReply) {
return false;
}
const auto owner = &item->history()->owner();
const auto itemId = item->fullId();
menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
const auto item = owner->message(itemId);
if (!item) {
return;
} else {
list->replyToMessageRequestNotify({ itemId });
}
list->replyToMessageRequestNotify({ item->fullId() });
}, &st::menuIconReply);
return true;
}

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_replies_section.h"
#include "history/view/controls/history_view_compose_controls.h"
#include "history/view/controls/history_view_forward_panel.h"
#include "history/view/history_view_top_bar_widget.h"
#include "history/view/history_view_list_widget.h"
#include "history/view/history_view_schedule_box.h"
@ -318,10 +319,15 @@ RepliesWidget::RepliesWidget(
}, _inner->lifetime());
_inner->replyToMessageRequested(
) | rpl::filter([=] {
return !_joinGroup;
}) | rpl::start_with_next([=](auto fullId) {
replyToMessage(fullId);
) | rpl::start_with_next([=](auto fullId) {
const auto canSendReply = _topic
? Data::CanSendAnything(_topic)
: Data::CanSendAnything(_history->peer);
if (_joinGroup || !canSendReply) {
Controls::ShowReplyToChatBox(controller->uiShow(), { fullId });
} else {
replyToMessage(fullId);
}
}, _inner->lifetime());
_inner->showMessageRequested(