Support sharing games by link to topics.

This commit is contained in:
John Preston 2022-11-01 13:00:17 +04:00
parent 83ec449890
commit 7384cd3463
16 changed files with 152 additions and 122 deletions

View file

@ -57,41 +57,6 @@ private:
};
void ShareBotGame(
not_null<UserData*> bot,
not_null<PeerData*> chat,
const QString &shortName) {
const auto history = chat->owner().history(chat);
auto &histories = history->owner().histories();
const auto randomId = base::RandomValue<uint64>();
const auto replyTo = 0;
const auto topicRootId = 0;
histories.sendPreparedMessage(
history,
replyTo,
topicRootId,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(0),
chat->input,
Data::Histories::ReplyToPlaceholder(),
Data::Histories::TopicRootPlaceholder(),
MTP_inputMediaGame(
MTP_inputGameShortName(
bot->inputUser,
MTP_string(shortName))),
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0), // schedule_date
MTPInputPeer() // send_as
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
chat->session().api().sendMessageFail(error, chat);
});
}
Controller::Controller(
not_null<Main::Session*> session,
rpl::producer<not_null<PeerData*>> add,
@ -165,9 +130,7 @@ AddBotToGroupBoxController::AddBotToGroupBoxController(
Scope scope,
const QString &token,
ChatAdminRights requestedRights)
: ChatsListBoxController((scope == Scope::ShareGame)
? std::make_unique<PeerListGlobalSearchController>(&bot->session())
: nullptr)
: ChatsListBoxController(std::unique_ptr<PeerListSearchController>())
, _controller(controller)
, _bot(bot)
, _scope(scope)
@ -185,42 +148,7 @@ Main::Session &AddBotToGroupBoxController::session() const {
}
void AddBotToGroupBoxController::rowClicked(not_null<PeerListRow*> row) {
if (sharingBotGame()) {
shareBotGame(row->peer());
} else {
addBotToGroup(row->peer());
}
}
void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
auto send = crl::guard(this, [
bot = _bot,
controller = _controller,
chat,
token = _token] {
ShareBotGame(bot, chat, token);
using Way = Window::SectionShow::Way;
controller->hideLayer();
controller->showPeerHistory(chat, Way::ClearStack, ShowAtUnreadMsgId);
});
auto confirmText = [chat] {
if (chat->isUser()) {
return tr::lng_bot_sure_share_game(
tr::now,
lt_user,
chat->name());
}
return tr::lng_bot_sure_share_game_group(
tr::now,
lt_group,
chat->name());
}();
_controller->show(
Ui::MakeConfirmBox({
.text = confirmText,
.confirmed = std::move(send),
}),
Ui::LayerOption::KeepOther);
addBotToGroup(row->peer());
}
void AddBotToGroupBoxController::requestExistingRights(
@ -341,13 +269,6 @@ auto AddBotToGroupBoxController::createRow(not_null<History*> history)
bool AddBotToGroupBoxController::needToCreateRow(
not_null<PeerData*> peer) const {
if (sharingBotGame()) {
if (!peer->canWrite() // #TODO forum forward
|| peer->amRestricted(ChatRestriction::SendGames)) {
return false;
}
return true;
}
if (const auto chat = peer->asChat()) {
if (onlyAdminToGroup()) {
return chat->canAddAdmins();
@ -374,14 +295,10 @@ bool AddBotToGroupBoxController::needToCreateRow(
return false;
}
bool AddBotToGroupBoxController::sharingBotGame() const {
return (_scope == Scope::ShareGame);
}
QString AddBotToGroupBoxController::emptyBoxText() const {
return !session().data().chatsListLoaded()
? tr::lng_contacts_loading(tr::now)
: (sharingBotGame() || _adminToChannel)
: _adminToChannel
? tr::lng_bot_no_chats(tr::now)
: tr::lng_bot_no_groups(tr::now);
}
@ -389,7 +306,7 @@ QString AddBotToGroupBoxController::emptyBoxText() const {
QString AddBotToGroupBoxController::noResultsText() const {
return !session().data().chatsListLoaded()
? tr::lng_contacts_loading(tr::now)
: (sharingBotGame() || _adminToChannel)
: _adminToChannel
? tr::lng_bot_chats_not_found(tr::now)
: tr::lng_bot_groups_not_found(tr::now);
}
@ -463,7 +380,7 @@ bool AddBotToGroupBoxController::onlyAdminToChannel() const {
}
void AddBotToGroupBoxController::prepareViewHook() {
delegate()->peerListSetTitle((sharingBotGame() || _adminToChannel)
delegate()->peerListSetTitle(_adminToChannel
? tr::lng_bot_choose_chat()
: tr::lng_bot_choose_group());
if ((_adminToGroup && !onlyAdminToGroup())

View file

@ -18,7 +18,6 @@ public:
None,
GroupAdmin,
ChannelAdmin,
ShareGame,
All,
};
static void Start(
@ -50,11 +49,9 @@ private:
[[nodiscard]] bool onlyAdminToChannel() const;
bool needToCreateRow(not_null<PeerData*> peer) const;
bool sharingBotGame() const;
QString noResultsText() const;
void updateLabels();
void shareBotGame(not_null<PeerData*> chat);
void addBotToGroup(not_null<PeerData*> chat);
void requestExistingRights(not_null<ChannelData*> channel);

View file

@ -850,7 +850,7 @@ void Histories::sendCreateTopicRequest(
MsgId rootId) {
Expects(history->peer->isChannel());
const auto forum = history->peer->forum();
const auto forum = history->asForum();
Assert(forum != nullptr);
const auto topic = forum->topicFor(rootId);
Assert(topic != nullptr);
@ -879,7 +879,7 @@ void Histories::sendCreateTopicRequest(
bool Histories::isCreatingTopic(
not_null<History*> history,
MsgId rootId) const {
const auto forum = history->peer->forum();
const auto forum = history->asForum();
return forum && forum->creating(rootId);
}
@ -947,7 +947,7 @@ void Histories::checkTopicCreated(FullMsgId rootId, MsgId realRoot) {
_createdTopicIds.emplace(rootId, realRoot);
const auto history = _owner->history(rootId.peer);
if (const auto forum = history->peer->forum()) {
if (const auto forum = history->asForum()) {
forum->created(rootId.msg, realRoot);
}

View file

@ -42,7 +42,7 @@ constexpr auto kMaxMessagesToDeleteMyTopic = 10;
}
[[nodiscard]] bool IsCreating(not_null<History*> history, MsgId rootId) {
if (const auto forum = history->peer->forum()) {
if (const auto forum = history->asForum()) {
return forum->creating(rootId);
}
return false;

View file

@ -97,7 +97,15 @@ Main::Session &Entry::session() const {
}
History *Entry::asHistory() {
return (_flags & Flag::IsHistory) ? static_cast<History*>(this) : nullptr;
return (_flags & Flag::IsHistory)
? static_cast<History*>(this)
: nullptr;
}
Data::Forum *Entry::asForum() {
return (_flags & Flag::IsHistory)
? static_cast<History*>(this)->peer->forum()
: nullptr;
}
Data::Folder *Entry::asFolder() {
@ -122,6 +130,10 @@ const History *Entry::asHistory() const {
return const_cast<Entry*>(this)->asHistory();
}
const Data::Forum *Entry::asForum() const {
return const_cast<Entry*>(this)->asForum();
}
const Data::Folder *Entry::asFolder() const {
return const_cast<Entry*>(this)->asFolder();
}

View file

@ -22,6 +22,7 @@ class Session;
namespace Data {
class Session;
class Forum;
class Folder;
class ForumTopic;
class CloudImageView;
@ -157,11 +158,13 @@ public:
[[nodiscard]] Main::Session &session() const;
History *asHistory();
Data::Forum *asForum();
Data::Folder *asFolder();
Data::Thread *asThread();
Data::ForumTopic *asTopic();
const History *asHistory() const;
const Data::Forum *asForum() const;
const Data::Folder *asFolder() const;
const Data::Thread *asThread() const;
const Data::ForumTopic *asTopic() const;

View file

@ -365,7 +365,7 @@ void FakeRow::invalidateTopic() {
if (_topic) {
return;
} else if (const auto rootId = _item->topicRootId()) {
if (const auto forum = _item->history()->peer->forum()) {
if (const auto forum = _item->history()->asForum()) {
if (!forum->topicDeleted(rootId)) {
forum->requestTopic(rootId, crl::guard(this, [=] {
_topic = _item->topic();

View file

@ -2085,7 +2085,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto itemId = item->fullId();
const auto canReply = [&] {
const auto peer = item->history()->peer;
if (const auto forum = item->history()->peer->forum()) {
if (const auto forum = peer->forum()) {
const auto topicRootId = item->topicRootId();
const auto topic = item->topic();
return topic

View file

@ -632,7 +632,7 @@ void HistoryItem::destroy() {
not_null<Data::Thread*> HistoryItem::notificationThread() const {
if (const auto rootId = topicRootId()) {
if (const auto forum = _history->peer->forum()) {
if (const auto forum = _history->asForum()) {
return forum->enforceTopicFor(rootId);
}
}
@ -641,7 +641,7 @@ not_null<Data::Thread*> HistoryItem::notificationThread() const {
Data::ForumTopic *HistoryItem::topic() const {
if (const auto rootId = topicRootId()) {
if (const auto forum = _history->peer->forum()) {
if (const auto forum = _history->asForum()) {
return forum->topicFor(rootId);
}
}

View file

@ -661,7 +661,7 @@ void HistoryMessage::createComponentsHelper(
const auto to = LookupReplyTo(history(), replyTo);
const auto replyToTop = LookupReplyToTop(to);
config.replyToTop = replyToTop ? replyToTop : replyTo;
const auto forum = history()->peer->forum();
const auto forum = history()->asForum();
config.replyIsTopicPost = LookupReplyIsTopicPost(to)
|| (to && to->Has<HistoryServiceTopicInfo>())
|| (forum && forum->creating(config.replyToTop));

View file

@ -722,7 +722,7 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
return Ui::Text::Link(std::move(full), topicUrl);
};
const auto wrapParentTopic = [&] {
const auto forum = history()->peer->forum();
const auto forum = history()->asForum();
if (!forum || forum->topicDeleted(topicRootId)) {
return wrapTopic(
tr::lng_deleted_message(tr::now),

View file

@ -151,7 +151,7 @@ void RepliesMemento::setReadInformation(
int unreadCount,
MsgId outboxReadTillId) {
if (!_replies) {
if (const auto forum = _history->peer->forum()) {
if (const auto forum = _history->asForum()) {
if (const auto topic = forum->topicFor(_rootId)) {
_replies = topic->replies();
}
@ -576,12 +576,12 @@ HistoryItem *RepliesWidget::lookupRoot() const {
}
Data::ForumTopic *RepliesWidget::lookupTopic() {
if (const auto forum = _history->peer->forum()) {
if (const auto forum = _history->asForum()) {
if (const auto result = forum->topicFor(_rootId)) {
return result;
} else {
forum->requestTopic(_rootId, crl::guard(this, [=] {
if (const auto forum = _history->peer->forum()) {
if (const auto forum = _history->asForum()) {
setTopic(forum->topicFor(_rootId));
}
}));

View file

@ -626,8 +626,7 @@ bool MainWidget::sendPaths(not_null<Data::Thread*> thread) {
bool MainWidget::onFilesOrForwardDrop(
not_null<Data::Thread*> thread,
not_null<const QMimeData*> data) {
const auto history = thread->asHistory();
if (const auto forum = history ? history->peer->forum() : nullptr) {
if (const auto forum = thread->asForum()) {
Window::ShowDropMediaBox(
_controller,
Core::ShareMimeMediaData(data),

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_participants.h"
#include "lang/lang_keys.h"
#include "ui/boxes/confirm_box.h"
#include "base/random.h"
#include "base/options.h"
#include "base/unixtime.h"
#include "boxes/delete_messages_box.h"
@ -86,6 +87,48 @@ namespace {
constexpr auto kTopicsSearchMinCount = 1;
void ShareBotGame(
not_null<UserData*> bot,
not_null<Data::Thread*> thread,
const QString &shortName) {
auto &histories = thread->owner().histories();
const auto history = thread->owningHistory();
const auto randomId = base::RandomValue<uint64>();
const auto replyTo = thread->topicRootId();
const auto topicRootId = replyTo;
auto flags = MTPmessages_SendMedia::Flags(0);
if (replyTo) {
flags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
if (topicRootId) {
flags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
}
}
histories.sendPreparedMessage(
history,
replyTo,
topicRootId,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(flags),
history->peer->input,
Data::Histories::ReplyToPlaceholder(),
Data::Histories::TopicRootPlaceholder(),
MTP_inputMediaGame(
MTP_inputGameShortName(
bot->inputUser,
MTP_string(shortName))),
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0), // schedule_date
MTPInputPeer() // send_as
), [=](const MTPUpdates &, const MTP::Response &) {
}, [=](const MTP::Error &error, const MTP::Response &) {
history->session().api().sendMessageFail(error, history->peer);
});
}
} // namespace
const char kOptionViewProfileInChatsListContextMenu[] =
@ -121,17 +164,15 @@ void MarkAsReadThread(not_null<Data::Thread*> thread) {
};
if (!IsUnreadThread(thread)) {
return;
} else if (const auto forum = thread->asForum()) {
forum->enumerateTopics([](
not_null<Data::ForumTopic*> topic) {
MarkAsReadThread(topic);
});
} else if (const auto history = thread->asHistory()) {
if (const auto forum = history->peer->forum()) {
forum->enumerateTopics([](
not_null<Data::ForumTopic*> topic) {
MarkAsReadThread(topic);
});
} else {
readHistory(history);
if (const auto migrated = history->migrateSibling()) {
readHistory(migrated);
}
readHistory(history);
if (const auto migrated = history->migrateSibling()) {
readHistory(migrated);
}
} else if (const auto topic = thread->asTopic()) {
topic->readTillEnd();
@ -1542,6 +1583,63 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
std::move(successCallback));
}
QPointer<Ui::BoxContent> ShowShareGameBox(
not_null<Window::SessionNavigation*> navigation,
not_null<UserData*> bot,
QString shortName) {
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto chosen = [=](not_null<Data::Thread*> thread) mutable {
const auto confirm = std::make_shared<QPointer<Ui::BoxContent>>();
auto send = crl::guard(thread, [=] {
ShareBotGame(bot, thread, shortName);
using Way = Window::SectionShow::Way;
if (const auto strong = *weak) {
strong->closeBox();
}
if (const auto strong = *confirm) {
strong->closeBox();
}
navigation->showThread(
thread,
ShowAtUnreadMsgId,
SectionShow::Way::ClearStack);
});
const auto confirmText = thread->owningHistory()->peer->isUser()
? tr::lng_bot_sure_share_game(
tr::now,
lt_user,
thread->chatListName())
: tr::lng_bot_sure_share_game_group(
tr::now,
lt_group,
thread->chatListName());
*confirm = navigation->parentController()->show(
Ui::MakeConfirmBox({
.text = confirmText,
.confirmed = std::move(send),
}),
Ui::LayerOption::KeepOther);
};
auto filter = [](not_null<Data::Thread*> thread) {
const auto peer = thread->owningHistory()->peer;
return (thread->canWrite() || thread->asForum())
&& !peer->amRestricted(ChatRestriction::SendGames)
&& !peer->isSelf();
};
auto initBox = [](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [box] {
box->closeBox();
});
};
*weak = navigation->parentController()->show(Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(
&navigation->session(),
std::move(chosen),
std::move(filter)),
std::move(initBox)), Ui::LayerOption::KeepOther);
return weak->data();
}
QPointer<Ui::BoxContent> ShowDropMediaBox(
not_null<Window::SessionNavigation*> navigation,
std::shared_ptr<QMimeData> data,

View file

@ -122,6 +122,10 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
MessageIdsList &&items,
FnMut<void()> &&successCallback = nullptr);
QPointer<Ui::BoxContent> ShowShareGameBox(
not_null<Window::SessionNavigation*> navigation,
not_null<UserData*> bot,
QString shortName);
QPointer<Ui::BoxContent> ShowDropMediaBox(
not_null<Window::SessionNavigation*> navigation,
std::shared_ptr<QMimeData> data,

View file

@ -81,6 +81,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/file_upload.h"
#include "facades.h"
#include "window/themes/window_theme.h"
#include "window/window_peer_menu.h"
#include "settings/settings_main.h"
#include "settings/settings_privacy_security.h"
#include "styles/style_window.h"
@ -387,13 +388,12 @@ void SessionNavigation::showPeerByLinkResolved(
info.messageId,
callback);
}
} else if (bot && info.resolveType == ResolveType::ShareGame) {
Window::ShowShareGameBox(parentController(), bot, info.startToken);
} else if (bot
&& (info.resolveType == ResolveType::AddToGroup
|| info.resolveType == ResolveType::AddToChannel
|| info.resolveType == ResolveType::ShareGame)) {
const auto scope = (info.resolveType == ResolveType::ShareGame)
? Scope::ShareGame
: (info.resolveType == ResolveType::AddToGroup)
|| info.resolveType == ResolveType::AddToChannel)) {
const auto scope = (info.resolveType == ResolveType::AddToGroup)
? (info.startAdminRights ? Scope::GroupAdmin : Scope::All)
: (info.resolveType == ResolveType::AddToChannel)
? Scope::ChannelAdmin