Support topic choosing in ShareBox.

This commit is contained in:
John Preston 2022-11-01 15:17:13 +04:00
parent 7384cd3463
commit e6c2aa8021
14 changed files with 273 additions and 117 deletions

View file

@ -1141,7 +1141,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
showToast(tr::lng_group_invite_copied(tr::now)); showToast(tr::lng_group_invite_copied(tr::now));
}; };
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions) { Data::ForwardOptions) {
@ -1150,12 +1150,12 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
} }
const auto error = [&] { const auto error = [&] {
for (const auto peer : result) { for (const auto thread : result) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, thread,
{ .text = &comment }); { .text = &comment });
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, thread);
} }
} }
return std::make_pair(QString(), result.front()); return std::make_pair(QString(), result.front());
@ -1164,7 +1164,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
auto text = TextWithEntities(); auto text = TextWithEntities();
if (result.size() > 1) { if (result.size() > 1) {
text.append( text.append(
Ui::Text::Bold(error.second->name()) Ui::Text::Bold(error.second->chatListName())
).append("\n\n"); ).append("\n\n");
} }
text.append(error.first); text.append(error.first);
@ -1188,10 +1188,9 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
} }
const auto owner = &peer->owner(); const auto owner = &peer->owner();
auto &api = peer->session().api(); auto &api = peer->session().api();
for (const auto peer : result) { for (const auto thread : result) {
const auto history = owner->history(peer);
auto message = Api::MessageToSend( auto message = Api::MessageToSend(
Api::SendAction(history, options)); Api::SendAction(thread, options));
message.textWithTags = comment; message.textWithTags = comment;
message.action.clearDraft = false; message.action.clearDraft = false;
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
@ -1204,8 +1203,8 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
auto object = Box<ShareBox>(ShareBox::Descriptor{ auto object = Box<ShareBox>(ShareBox::Descriptor{
.session = &peer->session(), .session = &peer->session(),
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback), // #TODO forum share .submitCallback = std::move(submitCallback),
.filterCallback = [](auto peer) { return peer->canWrite(); }, .filterCallback = [](auto thread) { return thread->canWrite(); },
}); });
*box = Ui::MakeWeak(object.data()); *box = Ui::MakeWeak(object.data());
return object; return object;

View file

@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_context_menu.h" // CopyPostLink. #include "history/view/history_view_context_menu.h" // CopyPostLink.
#include "history/view/history_view_schedule_box.h" #include "history/view/history_view_schedule_box.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_game.h" #include "data/data_game.h"
@ -43,6 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "core/application.h" #include "core/application.h"
@ -57,14 +59,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ShareBox::Inner final : public Ui::RpWidget { class ShareBox::Inner final : public Ui::RpWidget {
public: public:
Inner(QWidget *parent, const Descriptor &descriptor); Inner(
QWidget *parent,
const Descriptor &descriptor,
std::shared_ptr<Ui::BoxShow> show);
void setPeerSelectedChangedCallback( void setPeerSelectedChangedCallback(
Fn<void(PeerData *peer, bool selected)> callback); Fn<void(not_null<Data::Thread*> thread, bool selected)> callback);
void peerUnselected(not_null<PeerData*> peer); void peerUnselected(not_null<PeerData*> peer);
std::vector<not_null<PeerData*>> selected() const; [[nodiscard]] std::vector<not_null<Data::Thread*>> selected() const;
bool hasSelected() const; [[nodiscard]] bool hasSelected() const;
void peopleReceived( void peopleReceived(
const QString &query, const QString &query,
@ -95,11 +100,13 @@ protected:
private: private:
struct Chat { struct Chat {
Chat( Chat(
PeerData *peer, not_null<PeerData*> peer,
const style::PeerListItem &st, const style::PeerListItem &st,
Fn<void()> updateCallback); Fn<void()> updateCallback);
PeerData *peer; not_null<PeerData*> peer;
Data::ForumTopic *topic = nullptr;
rpl::lifetime topicLifetime;
Ui::RoundImageCheckbox checkbox; Ui::RoundImageCheckbox checkbox;
Ui::Text::String name; Ui::Text::String name;
Ui::Animations::Simple nameActive; Ui::Animations::Simple nameActive;
@ -107,11 +114,13 @@ private:
void invalidateCache(); void invalidateCache();
int displayedChatsCount() const; [[nodiscard]] int displayedChatsCount() const;
[[nodiscard]] not_null<Data::Thread*> chatThread(
not_null<Chat*> chat) const;
void paintChat(Painter &p, not_null<Chat*> chat, int index); void paintChat(Painter &p, not_null<Chat*> chat, int index);
void updateChat(not_null<PeerData*> peer); void updateChat(not_null<PeerData*> peer);
void updateChatName(not_null<Chat*> chat, not_null<PeerData*> peer); void updateChatName(not_null<Chat*> chat);
void repaintChat(not_null<PeerData*> peer); void repaintChat(not_null<PeerData*> peer);
int chatIndex(not_null<PeerData*> peer) const; int chatIndex(not_null<PeerData*> peer) const;
void repaintChatAtIndex(int index); void repaintChatAtIndex(int index);
@ -119,6 +128,7 @@ private:
void loadProfilePhotos(int yFrom); void loadProfilePhotos(int yFrom);
void changeCheckState(Chat *chat); void changeCheckState(Chat *chat);
void chooseForumTopic(not_null<Data::Forum*> forum);
enum class ChangeStateWay { enum class ChangeStateWay {
Default, Default,
SkipCallback, SkipCallback,
@ -135,6 +145,7 @@ private:
void refresh(); void refresh();
const Descriptor &_descriptor; const Descriptor &_descriptor;
const std::shared_ptr<Ui::BoxShow> _show;
const style::PeerList &_st; const style::PeerList &_st;
float64 _columnSkip = 0.; float64 _columnSkip = 0.;
@ -152,9 +163,9 @@ private:
std::vector<not_null<Dialogs::Row*>> _filtered; std::vector<not_null<Dialogs::Row*>> _filtered;
std::map<not_null<PeerData*>, std::unique_ptr<Chat>> _dataMap; std::map<not_null<PeerData*>, std::unique_ptr<Chat>> _dataMap;
base::flat_set<not_null<PeerData*>> _selected; base::flat_set<not_null<Data::Thread*>> _selected;
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback; Fn<void(not_null<Data::Thread*>, bool)> _peerSelectedChangedCallback;
bool _searching = false; bool _searching = false;
QString _lastQuery; QString _lastQuery;
@ -247,7 +258,7 @@ void ShareBox::prepare() {
setTitle(tr::lng_share_title()); setTitle(tr::lng_share_title());
_inner = setInnerWidget( _inner = setInnerWidget(
object_ptr<Inner>(this, _descriptor), object_ptr<Inner>(this, _descriptor, _show),
getTopScrollSkip(), getTopScrollSkip(),
getBottomScrollSkip()); getBottomScrollSkip());
@ -293,8 +304,10 @@ void ShareBox::prepare() {
scrollTo(request); scrollTo(request);
}, _inner->lifetime()); }, _inner->lifetime());
_inner->setPeerSelectedChangedCallback([=](PeerData *peer, bool checked) { _inner->setPeerSelectedChangedCallback([=](
innerSelectedChanged(peer, checked); not_null<Data::Thread*> thread,
bool checked) {
innerSelectedChanged(thread, checked);
}); });
Ui::Emoji::SuggestionsController::Init( Ui::Emoji::SuggestionsController::Init(
@ -442,9 +455,11 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
SendMenu::Type ShareBox::sendMenuType() const { SendMenu::Type ShareBox::sendMenuType() const {
const auto selected = _inner->selected(); const auto selected = _inner->selected();
return ranges::all_of(selected, HistoryView::CanScheduleUntilOnline) return ranges::all_of(
selected | ranges::views::transform(&Data::Thread::peer),
HistoryView::CanScheduleUntilOnline)
? SendMenu::Type::ScheduledToUser ? SendMenu::Type::ScheduledToUser
: (selected.size() == 1 && selected.front()->isSelf()) : (selected.size() == 1 && selected.front()->peer()->isSelf())
? SendMenu::Type::Reminder ? SendMenu::Type::Reminder
: SendMenu::Type::Scheduled; : SendMenu::Type::Scheduled;
} }
@ -522,23 +537,51 @@ void ShareBox::applyFilterUpdate(const QString &query) {
_inner->updateFilter(query); _inner->updateFilter(query);
} }
void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { PaintRoundImageCallback ForceRoundUserpicCallback(not_null<PeerData*> peer) {
using AddItemWay = Ui::MultiSelect::AddItemWay; auto userpic = std::shared_ptr<Data::CloudImageView>();
auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; auto cache = std::make_shared<QImage>();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
const auto ratio = style::DevicePixelRatio();
const auto cacheSize = QSize(size, size) * ratio;
if (cache->size() != cacheSize) {
*cache = QImage(cacheSize, QImage::Format_ARGB32_Premultiplied);
cache->setDevicePixelRatio(ratio);
}
auto q = Painter(cache.get());
peer->paintUserpicLeft(q, userpic, 0, 0, outerWidth, size);
q.end();
*cache = Images::Circle(std::move(*cache));
p.drawImage(x, y, *cache);
};
}
void ShareBox::addPeerToMultiSelect(not_null<Data::Thread*> thread) {
auto addItemWay = Ui::MultiSelect::AddItemWay::Default;
const auto peer = thread->peer();
const auto topic = thread->asTopic();
_select->addItem( _select->addItem(
peer->id.value, peer->id.value,
peer->isSelf() ? tr::lng_saved_short(tr::now) : peer->shortName(), (topic
? topic->title()
: peer->isSelf()
? tr::lng_saved_short(tr::now)
: peer->shortName()),
st::activeButtonBg, st::activeButtonBg,
PaintUserpicCallback(peer, true), (topic
? ForceRoundUserpicCallback(peer)
: PaintUserpicCallback(peer, true)),
addItemWay); addItemWay);
} }
void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) { void ShareBox::innerSelectedChanged(
not_null<Data::Thread*> thread,
bool checked) {
if (checked) { if (checked) {
addPeerToMultiSelect(peer); addPeerToMultiSelect(thread);
_select->clearQuery(); _select->clearQuery();
} else { } else {
_select->removeItem(peer->id.value); _select->removeItem(thread->peer()->id.value);
} }
selectedChanged(); selectedChanged();
update(); update();
@ -612,9 +655,13 @@ void ShareBox::scrollAnimationCallback() {
//scrollArea()->scrollToY(scrollTop); //scrollArea()->scrollToY(scrollTop);
} }
ShareBox::Inner::Inner(QWidget *parent, const Descriptor &descriptor) ShareBox::Inner::Inner(
QWidget *parent,
const Descriptor &descriptor,
std::shared_ptr<Ui::BoxShow> show)
: RpWidget(parent) : RpWidget(parent)
, _descriptor(descriptor) , _descriptor(descriptor)
, _show(std::move(show))
, _st(_descriptor.st ? *_descriptor.st : st::shareBoxList) , _st(_descriptor.st ? *_descriptor.st : st::shareBoxList)
, _chatsIndexed( , _chatsIndexed(
std::make_unique<Dialogs::IndexedList>( std::make_unique<Dialogs::IndexedList>(
@ -624,14 +671,16 @@ ShareBox::Inner::Inner(QWidget *parent, const Descriptor &descriptor)
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
const auto self = _descriptor.session->user(); const auto self = _descriptor.session->user();
if (_descriptor.filterCallback(self)) { const auto selfHistory = self->owner().history(self);
_chatsIndexed->addToEnd(self->owner().history(self)); if (_descriptor.filterCallback(selfHistory)) {
_chatsIndexed->addToEnd(selfHistory);
} }
const auto addList = [&](not_null<Dialogs::IndexedList*> list) { const auto addList = [&](not_null<Dialogs::IndexedList*> list) {
for (const auto &row : list->all()) { for (const auto &row : list->all()) {
if (const auto history = row->history()) { if (const auto history = row->history()) {
if (!history->peer->isSelf() if (!history->peer->isSelf()
&& _descriptor.filterCallback(history->peer)) { && (history->asForum()
|| _descriptor.filterCallback(history))) {
_chatsIndexed->addToEnd(history); _chatsIndexed->addToEnd(history);
} }
} }
@ -715,15 +764,16 @@ void ShareBox::Inner::activateSkipPage(int pageHeight, int direction) {
void ShareBox::Inner::updateChat(not_null<PeerData*> peer) { void ShareBox::Inner::updateChat(not_null<PeerData*> peer) {
if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { if (const auto i = _dataMap.find(peer); i != end(_dataMap)) {
updateChatName(i->second.get(), peer); updateChatName(i->second.get());
repaintChat(peer); repaintChat(peer);
} }
} }
void ShareBox::Inner::updateChatName( void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
not_null<Chat*> chat, const auto peer = chat->peer;
not_null<PeerData*> peer) { const auto text = chat->topic
const auto text = peer->isSelf() ? chat->topic->title()
: peer->isSelf()
? tr::lng_saved_messages(tr::now) ? tr::lng_saved_messages(tr::now)
: peer->isRepliesChat() : peer->isRepliesChat()
? tr::lng_replies_messages(tr::now) ? tr::lng_replies_messages(tr::now)
@ -852,7 +902,7 @@ auto ShareBox::Inner::getChat(not_null<Dialogs::Row*> row)
const auto [i, ok] = _dataMap.emplace( const auto [i, ok] = _dataMap.emplace(
peer, peer,
std::make_unique<Chat>(peer, _st.item, [=] { repaintChat(peer); })); std::make_unique<Chat>(peer, _st.item, [=] { repaintChat(peer); }));
updateChatName(i->second.get(), peer); updateChatName(i->second.get());
row->attached = i->second.get(); row->attached = i->second.get();
return i->second.get(); return i->second.get();
} }
@ -896,11 +946,17 @@ void ShareBox::Inner::paintChat(
} }
ShareBox::Inner::Chat::Chat( ShareBox::Inner::Chat::Chat(
PeerData *peer, not_null<PeerData*> peer,
const style::PeerListItem &st, const style::PeerListItem &st,
Fn<void()> updateCallback) Fn<void()> updateCallback)
: peer(peer) : peer(peer)
, checkbox(st.checkbox, updateCallback, PaintUserpicCallback(peer, true)) , checkbox(
st.checkbox,
updateCallback,
PaintUserpicCallback(peer, true),
[=] { return peer->isForum()
? ImageRoundRadius::Large
: ImageRoundRadius::Ellipse; })
, name(st.checkbox.imageRadius * 2) { , name(st.checkbox.imageRadius * 2) {
} }
@ -1022,9 +1078,9 @@ void ShareBox::Inner::resizeEvent(QResizeEvent *e) {
} }
void ShareBox::Inner::changeCheckState(Chat *chat) { void ShareBox::Inner::changeCheckState(Chat *chat) {
if (!chat) return; if (!chat) {
return;
if (!_filter.isEmpty()) { } else if (!_filter.isEmpty()) {
const auto history = chat->peer->owner().history(chat->peer); const auto history = chat->peer->owner().history(chat->peer);
auto row = _chatsIndexed->getRow(history); auto row = _chatsIndexed->getRow(history);
if (!row) { if (!row) {
@ -1036,7 +1092,60 @@ void ShareBox::Inner::changeCheckState(Chat *chat) {
} }
} }
changePeerCheckState(chat, !chat->checkbox.checked()); const auto checked = chat->checkbox.checked();
const auto forum = chat->peer->forum();
if (checked || !forum) {
changePeerCheckState(chat, !checked);
} else {
chooseForumTopic(chat->peer->forum());
}
}
void ShareBox::Inner::chooseForumTopic(not_null<Data::Forum*> forum) {
const auto guard = Ui::MakeWeak(this);
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto chosen = [=](not_null<Data::ForumTopic*> topic) {
if (const auto strong = *weak) {
strong->closeBox();
}
if (!guard) {
return;
}
const auto row = _chatsIndexed->getRow(topic->owningHistory());
if (!row) {
return;
}
const auto chat = getChat(row);
Assert(!chat->topic);
chat->topic = topic;
chat->topic->destroyed(
) | rpl::start_with_next([=] {
changePeerCheckState(chat, false);
}, chat->topicLifetime);
updateChatName(chat);
changePeerCheckState(chat, true);
};
auto initBox = [](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [box] {
box->closeBox();
});
};
auto box = Box<PeerListBox>(
std::make_unique<ChooseTopicBoxController>(
forum,
std::move(chosen)),
[=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
forum->destroyed(
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
});
*weak = box.data();
_show->showBox(std::move(box));
} }
void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) { void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) {
@ -1049,7 +1158,7 @@ void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) {
} }
void ShareBox::Inner::setPeerSelectedChangedCallback( void ShareBox::Inner::setPeerSelectedChangedCallback(
Fn<void(PeerData *peer, bool selected)> callback) { Fn<void(not_null<Data::Thread*> thread, bool selected)> callback) {
_peerSelectedChangedCallback = std::move(callback); _peerSelectedChangedCallback = std::move(callback);
} }
@ -1058,15 +1167,21 @@ void ShareBox::Inner::changePeerCheckState(
bool checked, bool checked,
ChangeStateWay useCallback) { ChangeStateWay useCallback) {
chat->checkbox.setChecked(checked); chat->checkbox.setChecked(checked);
const auto thread = chatThread(chat);
if (checked) { if (checked) {
_selected.insert(chat->peer); _selected.emplace(thread);
setActive(chatIndex(chat->peer)); setActive(chatIndex(chat->peer));
} else { } else {
_selected.remove(chat->peer); _selected.remove(thread);
if (chat->topic) {
chat->topicLifetime.destroy();
chat->topic = nullptr;
updateChatName(chat);
}
} }
if (useCallback != ChangeStateWay::SkipCallback if (useCallback != ChangeStateWay::SkipCallback
&& _peerSelectedChangedCallback) { && _peerSelectedChangedCallback) {
_peerSelectedChangedCallback(chat->peer, checked); _peerSelectedChangedCallback(thread, checked);
} }
} }
@ -1122,9 +1237,11 @@ void ShareBox::Inner::peopleReceived(
const auto feedList = [&](const QVector<MTPPeer> &list) { const auto feedList = [&](const QVector<MTPPeer> &list) {
for (const auto &data : list) { for (const auto &data : list) {
if (const auto peer = _descriptor.session->data().peerLoaded( if (const auto peer = _descriptor.session->data().peerLoaded(
peerFromMTP(data))) { peerFromMTP(data))) {
const auto history = _descriptor.session->data().historyLoaded(peer); const auto history = _descriptor.session->data().history(
if (!_descriptor.filterCallback(peer)) { peer);
if (!history->asForum()
&& !_descriptor.filterCallback(history)) {
continue; continue;
} else if (history && _chatsIndexed->getRow(history)) { } else if (history && _chatsIndexed->getRow(history)) {
continue; continue;
@ -1136,7 +1253,7 @@ void ShareBox::Inner::peopleReceived(
peer, peer,
_st.item, _st.item,
[=] { repaintChat(peer); })); [=] { repaintChat(peer); }));
updateChatName(d_byUsernameFiltered.back().get(), peer); updateChatName(d_byUsernameFiltered.back().get());
} }
} }
}; };
@ -1158,12 +1275,19 @@ void ShareBox::Inner::refresh() {
update(); update();
} }
std::vector<not_null<PeerData*>> ShareBox::Inner::selected() const { not_null<Data::Thread*> ShareBox::Inner::chatThread(
auto result = std::vector<not_null<PeerData*>>(); not_null<Chat*> chat) const {
return chat->topic
? (Data::Thread*)chat->topic
: chat->peer->owner().history(chat->peer).get();
}
std::vector<not_null<Data::Thread*>> ShareBox::Inner::selected() const {
auto result = std::vector<not_null<Data::Thread*>>();
result.reserve(_dataMap.size()); result.reserve(_dataMap.size());
for (const auto &[peer, chat] : _dataMap) { for (const auto &[peer, chat] : _dataMap) {
if (chat->checkbox.checked()) { if (chat->checkbox.checked()) {
result.push_back(peer); result.push_back(chatThread(chat.get()));
} }
} }
return result; return result;
@ -1279,7 +1403,7 @@ void FastShareMessage(
}; };
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions forwardOptions) { Data::ForwardOptions forwardOptions) {
@ -1292,12 +1416,12 @@ void FastShareMessage(
} }
const auto error = [&] { const auto error = [&] {
for (const auto peer : result) { // #TODO forum share for (const auto thread : result) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, thread,
{ .forward = &items, .text = &comment }); { .forward = &items, .text = &comment });
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, thread);
} }
} }
return std::make_pair(QString(), result.front()); return std::make_pair(QString(), result.front());
@ -1306,7 +1430,7 @@ void FastShareMessage(
auto text = TextWithEntities(); auto text = TextWithEntities();
if (result.size() > 1) { if (result.size() > 1) {
text.append( text.append(
Ui::Text::Bold(error.second->name()) Ui::Text::Bold(error.second->chatListName())
).append("\n\n"); ).append("\n\n");
} }
text.append(error.first); text.append(error.first);
@ -1316,17 +1440,16 @@ void FastShareMessage(
return; return;
} }
const auto commonSendFlags = MTPmessages_ForwardMessages::Flag(0) using Flag = MTPmessages_ForwardMessages::Flag;
| MTPmessages_ForwardMessages::Flag::f_with_my_score const auto commonSendFlags = Flag(0)
| (options.scheduled | Flag::f_with_my_score
? MTPmessages_ForwardMessages::Flag::f_schedule_date | (options.scheduled ? Flag::f_schedule_date : Flag(0))
: MTPmessages_ForwardMessages::Flag(0))
| ((forwardOptions != Data::ForwardOptions::PreserveInfo) | ((forwardOptions != Data::ForwardOptions::PreserveInfo)
? MTPmessages_ForwardMessages::Flag::f_drop_author ? Flag::f_drop_author
: MTPmessages_ForwardMessages::Flag(0)) : Flag(0))
| ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions) | ((forwardOptions == Data::ForwardOptions::NoNamesAndCaptions)
? MTPmessages_ForwardMessages::Flag::f_drop_media_captions ? Flag::f_drop_media_captions
: MTPmessages_ForwardMessages::Flag(0)); : Flag(0));
auto msgIds = QVector<MTPint>(); auto msgIds = QVector<MTPint>();
msgIds.reserve(data->msgIds.size()); msgIds.reserve(data->msgIds.size());
for (const auto &fullId : data->msgIds) { for (const auto &fullId : data->msgIds) {
@ -1342,21 +1465,23 @@ void FastShareMessage(
auto &api = owner->session().api(); auto &api = owner->session().api();
auto &histories = owner->histories(); auto &histories = owner->histories();
const auto requestType = Data::Histories::RequestType::Send; const auto requestType = Data::Histories::RequestType::Send;
for (const auto peer : result) { for (const auto thread : result) {
const auto history = owner->history(peer);
if (!comment.text.isEmpty()) { if (!comment.text.isEmpty()) {
auto message = Api::MessageToSend( auto message = Api::MessageToSend(
Api::SendAction(history, options)); Api::SendAction(thread, options));
message.textWithTags = comment; message.textWithTags = comment;
message.action.clearDraft = false; message.action.clearDraft = false;
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
} }
const auto topicRootId = thread->topicRootId();
const auto peer = thread->peer();
histories.sendRequest(history, requestType, [=](Fn<void()> finish) { histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api(); auto &api = history->session().api();
const auto sendFlags = commonSendFlags const auto sendFlags = commonSendFlags
| (topicRootId ? Flag::f_top_msg_id : Flag(0))
| (ShouldSendSilent(peer, options) | (ShouldSendSilent(peer, options)
? MTPmessages_ForwardMessages::Flag::f_silent ? Flag::f_silent
: MTPmessages_ForwardMessages::Flag(0)); : Flag(0));
history->sendRequestId = api.request( history->sendRequestId = api.request(
MTPmessages_ForwardMessages( MTPmessages_ForwardMessages(
MTP_flags(sendFlags), MTP_flags(sendFlags),
@ -1364,7 +1489,7 @@ void FastShareMessage(
MTP_vector<MTPint>(msgIds), MTP_vector<MTPint>(msgIds),
MTP_vector<MTPlong>(generateRandom()), MTP_vector<MTPlong>(generateRandom()),
peer->input, peer->input,
MTPint(), // top_msg_id MTP_int(topicRootId),
MTP_int(options.scheduled), MTP_int(options.scheduled),
MTP_inputPeerEmpty() // send_as MTP_inputPeerEmpty() // send_as
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) { )).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
@ -1397,14 +1522,9 @@ void FastShareMessage(
data->requests.insert(history->sendRequestId); data->requests.insert(history->sendRequestId);
} }
}; };
auto filterCallback = [isGame](PeerData *peer) { auto filterCallback = [isGame](not_null<Data::Thread*> thread) {
if (peer->canWrite()) { // #TODO forum share return thread->canWrite()
if (auto channel = peer->asChannel()) { && (!isGame || !thread->peer()->isBroadcast());
return isGame ? (!channel->isBroadcast()) : true;
}
return true;
}
return false;
}; };
auto copyLinkCallback = canCopyLink auto copyLinkCallback = canCopyLink
? Fn<void()>(std::move(copyCallback)) ? Fn<void()>(std::move(copyCallback))

View file

@ -44,6 +44,7 @@ class IndexedList;
namespace Data { namespace Data {
enum class ForwardOptions; enum class ForwardOptions;
class Thread;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
@ -70,11 +71,11 @@ class ShareBox final : public Ui::BoxContent {
public: public:
using CopyCallback = Fn<void()>; using CopyCallback = Fn<void()>;
using SubmitCallback = Fn<void( using SubmitCallback = Fn<void(
std::vector<not_null<PeerData*>>&&, std::vector<not_null<Data::Thread*>>&&,
TextWithTags&&, TextWithTags&&,
Api::SendOptions, Api::SendOptions,
Data::ForwardOptions option)>; Data::ForwardOptions option)>;
using FilterCallback = Fn<bool(PeerData*)>; using FilterCallback = Fn<bool(not_null<Data::Thread*>)>;
struct Descriptor { struct Descriptor {
not_null<Main::Session*> session; not_null<Main::Session*> session;
@ -125,8 +126,8 @@ private:
int contentHeight() const; int contentHeight() const;
void updateScrollSkips(); void updateScrollSkips();
void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false); void addPeerToMultiSelect(not_null<Data::Thread*> thread);
void innerSelectedChanged(PeerData *peer, bool checked); void innerSelectedChanged(not_null<Data::Thread*> thread, bool checked);
void peopleDone( void peopleDone(
const MTPcontacts_Found &result, const MTPcontacts_Found &result,

View file

@ -133,7 +133,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
showToast(tr::lng_group_invite_copied(tr::now)); showToast(tr::lng_group_invite_copied(tr::now));
}; };
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions) { Data::ForwardOptions) {
@ -142,12 +142,12 @@ object_ptr<ShareBox> ShareInviteLinkBox(
} }
const auto error = [&] { const auto error = [&] {
for (const auto peer : result) { // #TODO forum share for (const auto thread : result) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, thread,
{ .text = &comment }); { .text = &comment });
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, thread);
} }
} }
return std::make_pair(QString(), result.front()); return std::make_pair(QString(), result.front());
@ -156,7 +156,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
auto text = TextWithEntities(); auto text = TextWithEntities();
if (result.size() > 1) { if (result.size() > 1) {
text.append( text.append(
Ui::Text::Bold(error.second->name()) Ui::Text::Bold(error.second->chatListName())
).append("\n\n"); ).append("\n\n");
} }
text.append(error.first); text.append(error.first);
@ -182,10 +182,9 @@ object_ptr<ShareBox> ShareInviteLinkBox(
} }
const auto owner = &peer->owner(); const auto owner = &peer->owner();
auto &api = peer->session().api(); auto &api = peer->session().api();
for (const auto peer : result) { for (const auto thread : result) {
const auto history = owner->history(peer);
auto message = Api::MessageToSend( auto message = Api::MessageToSend(
Api::SendAction(history, options)); Api::SendAction(thread, options));
message.textWithTags = comment; message.textWithTags = comment;
message.action.clearDraft = false; message.action.clearDraft = false;
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
@ -195,8 +194,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
} }
showToast(tr::lng_share_done(tr::now)); showToast(tr::lng_share_done(tr::now));
}; };
auto filterCallback = [](PeerData *peer) { auto filterCallback = [](not_null<Data::Thread*> thread) {
return peer->canWrite(); // #TODO forum share return thread->canWrite(); // #TODO forum share
}; };
const auto scheduleStyle = [&] { const auto scheduleStyle = [&] {

View file

@ -178,6 +178,13 @@ QString GetErrorTextForSending(
return QString(); return QString();
} }
QString GetErrorTextForSending(
not_null<Data::Thread*> thread,
SendingErrorRequest request) {
request.topicRootId = thread->topicRootId();
return GetErrorTextForSending(thread->peer(), std::move(request));
}
void RequestDependentMessageData( void RequestDependentMessageData(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
PeerId peerId, PeerId peerId,

View file

@ -15,6 +15,7 @@ struct SendOptions;
} // namespace Api } // namespace Api
namespace Data { namespace Data {
class Thread;
struct SponsoredFrom; struct SponsoredFrom;
} // namespace Data } // namespace Data
@ -50,6 +51,9 @@ struct SendingErrorRequest {
[[nodiscard]] QString GetErrorTextForSending( [[nodiscard]] QString GetErrorTextForSending(
not_null<PeerData*> peer, not_null<PeerData*> peer,
SendingErrorRequest request); SendingErrorRequest request);
[[nodiscard]] QString GetErrorTextForSending(
not_null<Data::Thread*> thread,
SendingErrorRequest request);
[[nodiscard]] TextWithEntities DropCustomEmoji(TextWithEntities text); [[nodiscard]] TextWithEntities DropCustomEmoji(TextWithEntities text);

View file

@ -171,7 +171,7 @@ void ForwardPanel::updateTexts() {
}).text; }).text;
const auto history = item->history(); const auto history = item->history();
const auto dropCustomEmoji = !history->session().premium() const auto dropCustomEmoji = !history->session().premium()
&& !_to->owningHistory()->peer->isSelf() && !_to->peer()->isSelf()
&& (item->computeDropForwardedInfo() || !keepNames); && (item->computeDropForwardedInfo() || !keepNames);
if (dropCustomEmoji) { if (dropCustomEmoji) {
text = DropCustomEmoji(std::move(text)); text = DropCustomEmoji(std::move(text));

View file

@ -64,7 +64,7 @@ PinnedMemento::PinnedMemento(
: _thread(thread) : _thread(thread)
, _highlightId(highlightId) { , _highlightId(highlightId) {
_list.setAroundPosition({ _list.setAroundPosition({
.fullId = FullMsgId(_thread->owningHistory()->peer->id, highlightId), .fullId = FullMsgId(_thread->peer()->id, highlightId),
.date = TimeId(0), .date = TimeId(0),
}); });
} }
@ -89,7 +89,7 @@ PinnedWidget::PinnedWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<Data::Thread*> thread) not_null<Data::Thread*> thread)
: Window::SectionWidget(parent, controller, thread->owningHistory()->peer) : Window::SectionWidget(parent, controller, thread->peer())
, _thread(thread->migrateToOrMe()) , _thread(thread->migrateToOrMe())
, _history(thread->owningHistory()) , _history(thread->owningHistory())
, _migratedPeer(thread->asHistory() , _migratedPeer(thread->asHistory()
@ -116,7 +116,7 @@ PinnedWidget::PinnedWidget(
Window::ChatThemeValueFromPeer( Window::ChatThemeValueFromPeer(
controller, controller,
thread->owningHistory()->peer thread->peer()
) | rpl::start_with_next([=](std::shared_ptr<Ui::ChatTheme> &&theme) { ) | rpl::start_with_next([=](std::shared_ptr<Ui::ChatTheme> &&theme) {
_theme = std::move(theme); _theme = std::move(theme);
controller->setChatStyleTheme(_theme); controller->setChatStyleTheme(_theme);

View file

@ -79,7 +79,7 @@ void PinnedTracker::refreshViewer() {
} }
_dataLifetime.destroy(); _dataLifetime.destroy();
_viewerAroundId = _aroundId; _viewerAroundId = _aroundId;
const auto peer = _thread->owningHistory()->peer; const auto peer = _thread->peer();
SharedMediaMergedViewer( SharedMediaMergedViewer(
&peer->session(), &peer->session(),
SharedMediaMergedKey( SharedMediaMergedKey(

View file

@ -141,7 +141,7 @@ void ShowChooseBox(
callback(thread); callback(thread);
}; };
auto filter = [=](not_null<Data::Thread*> thread) -> bool { auto filter = [=](not_null<Data::Thread*> thread) -> bool {
const auto peer = thread->owningHistory()->peer; const auto peer = thread->peer();
if (!thread->canWrite()) { if (!thread->canWrite()) {
return false; return false;
} else if (const auto user = peer->asUser()) { } else if (const auto user = peer->asUser()) {

View file

@ -607,7 +607,7 @@ bool MainWidget::sendPaths(not_null<Data::Thread*> thread) {
Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant())); Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant()));
return false; return false;
} else if (const auto error = Data::RestrictionError( } else if (const auto error = Data::RestrictionError(
thread->owningHistory()->peer, thread->peer(),
ChatRestriction::SendMedia)) { ChatRestriction::SendMedia)) {
Ui::show(Ui::MakeInformBox(*error)); Ui::show(Ui::MakeInformBox(*error));
return false; return false;

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/image/image_prepare.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
@ -355,10 +356,15 @@ void RoundCheckbox::prepareInactiveCache() {
_inactiveCacheFg = Ui::PixmapFromImage(std::move(cacheFg)); _inactiveCacheFg = Ui::PixmapFromImage(std::move(cacheFg));
} }
RoundImageCheckbox::RoundImageCheckbox(const style::RoundImageCheckbox &st, Fn<void()> updateCallback, PaintRoundImage &&paintRoundImage) RoundImageCheckbox::RoundImageCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius)
: _st(st) : _st(st)
, _updateCallback(updateCallback) , _updateCallback(updateCallback)
, _paintRoundImage(std::move(paintRoundImage)) , _paintRoundImage(std::move(paintRoundImage))
, _roundingRadius(std::move(roundingRadius))
, _check(_st.check, _updateCallback) { , _check(_st.check, _updateCallback) {
} }
@ -383,6 +389,9 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
} }
if (selectionLevel > 0) { if (selectionLevel > 0) {
const auto radius = _roundingRadius
? _roundingRadius()
: ImageRoundRadius::Ellipse;
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.setOpacity(std::clamp(selectionLevel, 0., 1.)); p.setOpacity(std::clamp(selectionLevel, 0., 1.));
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
@ -390,7 +399,18 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
_fgOverride ? (*_fgOverride) : _st.selectFg->b, _fgOverride ? (*_fgOverride) : _st.selectFg->b,
_st.selectWidth); _st.selectWidth);
p.setPen(pen); p.setPen(pen);
p.drawEllipse(style::rtlrect(x, y, _st.imageRadius * 2, _st.imageRadius * 2, outerWidth)); const auto rect = style::rtlrect(
x,
y,
_st.imageRadius * 2,
_st.imageRadius * 2,
outerWidth);
if (radius == ImageRoundRadius::Ellipse) {
p.drawEllipse(rect);
} else {
const auto pxRadius = st::roundRadiusLarge;
p.drawRoundedRect(rect, pxRadius, pxRadius);
}
p.setOpacity(1.); p.setOpacity(1.);
} }
if (_st.check.size > 0) { if (_st.check.size > 0) {

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
class Painter; class Painter;
enum class ImageRoundRadius;
namespace Ui { namespace Ui {
@ -47,7 +48,11 @@ private:
class RoundImageCheckbox { class RoundImageCheckbox {
public: public:
using PaintRoundImage = Fn<void(Painter &p, int x, int y, int outerWidth, int size)>; using PaintRoundImage = Fn<void(Painter &p, int x, int y, int outerWidth, int size)>;
RoundImageCheckbox(const style::RoundImageCheckbox &st, Fn<void()> updateCallback, PaintRoundImage &&paintRoundImage); RoundImageCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius = nullptr);
void paint(Painter &p, int x, int y, int outerWidth) const; void paint(Painter &p, int x, int y, int outerWidth) const;
float64 checkedAnimationRatio() const; float64 checkedAnimationRatio() const;
@ -71,6 +76,7 @@ private:
const style::RoundImageCheckbox &_st; const style::RoundImageCheckbox &_st;
Fn<void()> _updateCallback; Fn<void()> _updateCallback;
PaintRoundImage _paintRoundImage; PaintRoundImage _paintRoundImage;
Fn<ImageRoundRadius()> _roundingRadius;
QPixmap _wideCache; QPixmap _wideCache;
Ui::Animations::Simple _selection; Ui::Animations::Simple _selection;

View file

@ -1290,7 +1290,7 @@ void PeerMenuShareContactBox(
// There is no async to make weak from controller. // There is no async to make weak from controller.
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>(); const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto callback = [=](not_null<Data::Thread*> thread) { auto callback = [=](not_null<Data::Thread*> thread) {
const auto peer = thread->owningHistory()->peer; const auto peer = thread->peer();
if (!thread->canWrite()) { if (!thread->canWrite()) {
navigation->parentController()->show( navigation->parentController()->show(
Ui::MakeInformBox(tr::lng_forward_share_cant()), Ui::MakeInformBox(tr::lng_forward_share_cant()),
@ -1544,7 +1544,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
weak, weak,
navigation navigation
](not_null<Data::Thread*> thread) mutable { ](not_null<Data::Thread*> thread) mutable {
const auto peer = thread->owningHistory()->peer; const auto peer = thread->peer();
const auto content = navigation->parentController()->content(); const auto content = navigation->parentController()->content();
if (peer->isSelf() if (peer->isSelf()
&& !draft.ids.empty() && !draft.ids.empty()
@ -1604,7 +1604,7 @@ QPointer<Ui::BoxContent> ShowShareGameBox(
ShowAtUnreadMsgId, ShowAtUnreadMsgId,
SectionShow::Way::ClearStack); SectionShow::Way::ClearStack);
}); });
const auto confirmText = thread->owningHistory()->peer->isUser() const auto confirmText = thread->peer()->isUser()
? tr::lng_bot_sure_share_game( ? tr::lng_bot_sure_share_game(
tr::now, tr::now,
lt_user, lt_user,
@ -1621,7 +1621,7 @@ QPointer<Ui::BoxContent> ShowShareGameBox(
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
}; };
auto filter = [](not_null<Data::Thread*> thread) { auto filter = [](not_null<Data::Thread*> thread) {
const auto peer = thread->owningHistory()->peer; const auto peer = thread->peer();
return (thread->canWrite() || thread->asForum()) return (thread->canWrite() || thread->asForum())
&& !peer->amRestricted(ChatRestriction::SendGames) && !peer->amRestricted(ChatRestriction::SendGames)
&& !peer->isSelf(); && !peer->isSelf();