2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-30 06:07:45 +00:00

Forward options

Fixes #66.
This commit is contained in:
RadRussianRus 2020-07-15 16:47:26 +03:00
parent d2b630bbca
commit 0eeeb18ecd
18 changed files with 988 additions and 55 deletions

View File

@ -2620,4 +2620,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ktg_settings_forward_chat_on_click" = "Open chat on click";
"ktg_settings_forward_chat_on_click_description" = "You can hold Ctrl to select multiple chats regardless of this option.";
"ktg_forward_menu_quoted" = "Quoted";
"ktg_forward_menu_unquoted" = "Unquoted with captions";
"ktg_forward_menu_uncaptioned" = "Unquoted without captions";
"ktg_forward_menu_default_albums" = "Preserve albums";
"ktg_forward_menu_group_all_media" = "Group all media";
"ktg_forward_menu_separate_messages" = "Separate messages";
"ktg_forward_subtitle_unquoted" = "unquoted";
"ktg_forward_subtitle_uncaptioned" = "uncaptioned";
"ktg_forward_subtitle_group_all_media" = "as albums";
"ktg_forward_subtitle_separate_messages" = "one by one";
// Keys finished

View File

@ -173,6 +173,16 @@
"ktg_settings_forward_chat_on_click_description": "You can hold Ctrl to select multiple chats regardless of this option.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -173,6 +173,16 @@
"ktg_settings_forward_chat_on_click_description": "Puoi tenere premuto Ctrl per selezionare più chat insieme indipendentemente da questa opzione.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -181,6 +181,16 @@
"ktg_settings_forward_chat_on_click_description": "You can hold Ctrl to select multiple chats regardless of this option.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -173,6 +173,16 @@
"ktg_settings_forward_chat_on_click_description": "Você pode pressionar Ctrl para selecionar vários chats, independentemente desta opção.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -180,6 +180,16 @@
"ktg_settings_forward_chat_on_click_description": "Удерживайте Ctrl для выбора нескольких чатов вне зависимости от этой настройки.",
// New strings
"ktg_forward_menu_quoted": "С автором",
"ktg_forward_menu_unquoted": "Без автора с подписями",
"ktg_forward_menu_uncaptioned": "Без автора и подписей",
"ktg_forward_menu_default_albums": "Оригинальные альбомы",
"ktg_forward_menu_group_all_media": "Объединить все медиа",
"ktg_forward_menu_separate_messages": "Отдельные сообщения",
"ktg_forward_subtitle_unquoted": "без автора",
"ktg_forward_subtitle_uncaptioned": "без подписей",
"ktg_forward_subtitle_group_all_media": "альбомами",
"ktg_forward_subtitle_separate_messages": "по одному",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -173,6 +173,16 @@
"ktg_settings_forward_chat_on_click_description": "Ctrl tuşunu uzun tutarak birden fazla sohbet seçmek için, bu seçenek aktifligine rağmen.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -180,6 +180,16 @@
"ktg_settings_forward_chat_on_click_description": "Ви можете вибрати декілька чатів утримуючи Ctrl незалежно від цього параметра.",
// New strings
"ktg_forward_menu_quoted": "Quoted",
"ktg_forward_menu_unquoted": "Unquoted with captions",
"ktg_forward_menu_uncaptioned": "Unquoted without captions",
"ktg_forward_menu_default_albums": "Preserve albums",
"ktg_forward_menu_group_all_media": "Group all media",
"ktg_forward_menu_separate_messages": "Separate messages",
"ktg_forward_subtitle_unquoted": "unquoted",
"ktg_forward_subtitle_uncaptioned": "uncaptioned",
"ktg_forward_subtitle_group_all_media": "as albums",
"ktg_forward_subtitle_separate_messages": "one by one",
// This string should always be last for better work with Git.
"dummy_last_string": ""

View File

@ -3890,7 +3890,7 @@ void ApiWrap::forwardMessages(
}
const auto count = int(items.size());
const auto genClientSideMessage = action.generateLocal && (count < 2);
const auto genClientSideMessage = action.generateLocal && (count < 2) && cForwardQuoted();
const auto history = action.history;
const auto peer = history->peer;
@ -3916,18 +3916,47 @@ void ApiWrap::forwardMessages(
auto forwardFrom = items.front()->history()->peer;
auto currentGroupId = items.front()->groupId();
auto isLastGrouped = false;
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
auto fromIter = items.begin();
auto toIter = items.begin();
const auto sendAccumulated = [&] {
const auto needNextGroup = [&] (not_null<HistoryItem *> item) {
if (cForwardAlbumsAsIs()) {
const auto newFrom = item->history()->peer;
const auto newGroupId = item->groupId();
return forwardFrom != newFrom
|| currentGroupId != newGroupId;
} else if (cForwardGrouped()) {
if (item->media() && item->media()->canBeGrouped()) {
return !isLastGrouped;
} else {
return isLastGrouped;
}
} else {
return false;
}
};
const auto isGrouped = [&] {
return (cForwardAlbumsAsIs()
&& currentGroupId != MessageGroupId())
|| (!cForwardAlbumsAsIs()
&& cForwardGrouped()
&& items.front()->media()
&& items.front()->media()->canBeGrouped());
};
const auto forwardQuoted = [&] {
if (shared) {
++shared->requestsLeft;
}
const auto finalFlags = sendFlags
| (currentGroupId == MessageGroupId()
? MTPmessages_ForwardMessages::Flag(0)
: MTPmessages_ForwardMessages::Flag::f_grouped);
| (isGrouped()
? MTPmessages_ForwardMessages::Flag::f_grouped
: MTPmessages_ForwardMessages::Flag(0));
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
@ -3957,6 +3986,308 @@ void ApiWrap::forwardMessages(
).send();
return history->sendRequestId;
});
};
const auto forwardQuotedSingle = [&] (not_null<HistoryItem *> item) {
if (shared) {
++shared->requestsLeft;
}
auto currentIds = QVector<MTPint>();
currentIds.push_back(MTP_int(item->id));
auto currentRandomId = randomIds.takeFirst();
auto currentRandomIds = QVector<MTPlong>();
currentRandomIds.push_back(currentRandomId);
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(sendFlags),
forwardFrom->input,
MTP_vector<MTPint>(currentIds),
MTP_vector<MTPlong>(currentRandomIds),
peer->input,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
finish();
}).fail([=, ids = localIds](const RPCError &error) {
auto found = false;
for (const auto &[randomId, itemId] : *ids) {
if (currentRandomId == MTP_long(randomId)) {
sendMessageFail(error, peer, randomId, itemId);
found = true;
break;
}
}
if (!found) {
sendMessageFail(error, peer);
}
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
};
const auto forwardAlbumUnquoted = [&] {
if (shared) {
++shared->requestsLeft;
}
auto medias = QVector<MTPInputSingleMedia>();
medias.reserve(ids.size());
for (auto i = fromIter, e = toIter; i != e; i++) {
const auto item = *i;
const auto media = item->media();
auto inputMedia = media->photo()
? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint())
: MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint());
auto caption = cForwardCaptioned()
? item->originalText()
: TextWithEntities();
auto sentEntities = Api::EntitiesToMTP(
_session,
caption.entities,
Api::ConvertOption::SkipLocal);
const auto flags = !sentEntities.v.isEmpty()
? MTPDinputSingleMedia::Flag::f_entities
: MTPDinputSingleMedia::Flag(0);
auto randomId = randomIds.takeFirst();
medias.push_back(MTP_inputSingleMedia(
MTP_flags(flags),
inputMedia,
randomId,
MTP_string(caption.text),
sentEntities));
}
const auto finalFlags = MTPmessages_SendMultiMedia::Flags(0)
| (action.options.silent
? MTPmessages_SendMultiMedia::Flag::f_silent
: MTPmessages_SendMultiMedia::Flag(0))
| (action.options.scheduled
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
: MTPmessages_SendMultiMedia::Flag(0));
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendMultiMedia(
MTP_flags(finalFlags),
peer->input,
MTPint(),
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
finish();
}).fail([=, ids = localIds](const RPCError &error) {
if (ids) {
for (const auto &[randomId, itemId] : *ids) {
sendMessageFail(error, peer, randomId, itemId);
}
} else {
sendMessageFail(error, peer);
}
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
};
const auto forwardMediaUnquoted = [&] (not_null<HistoryItem *> item) {
if (shared) {
++shared->requestsLeft;
}
const auto media = item->media();
auto newSendFlags = MTPmessages_SendMedia::Flags(0)
| (action.options.silent
? MTPmessages_SendMedia::Flag::f_silent
: MTPmessages_SendMedia::Flag(0))
| (action.options.scheduled
? MTPmessages_SendMedia::Flag::f_schedule_date
: MTPmessages_SendMedia::Flag(0));
auto caption = (cForwardCaptioned()
&& !media->geoPoint()
&& !media->sharedContact())
? item->originalText()
: TextWithEntities();
auto sentEntities = Api::EntitiesToMTP(
_session,
caption.entities,
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
newSendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
auto inputMedia = media->poll()
? PollDataToInputMedia(media->poll())
: media->geoPoint()
? MTP_inputMediaGeoPoint(
MTP_inputGeoPoint(
MTP_double(media->geoPoint()->lat()),
MTP_double(media->geoPoint()->lon())))
: media->sharedContact()
? MTP_inputMediaContact(
MTP_string(media->sharedContact()->phoneNumber),
MTP_string(media->sharedContact()->firstName),
MTP_string(media->sharedContact()->lastName),
MTPstring())
: media->photo()
? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint())
: MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint());
const auto requestType = Data::Histories::RequestType::Send;
const auto currentRandomId = randomIds.takeFirst();
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(newSendFlags),
peer->input,
MTPint(),
inputMedia,
MTP_string(caption.text),
currentRandomId,
MTPReplyMarkup(),
sentEntities,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
finish();
}).fail([=, ids = localIds](const RPCError &error) {
if (ids) {
auto found = false;
for (const auto &[randomId, itemId] : *ids) {
if (currentRandomId == MTP_long(randomId)) {
sendMessageFail(error, peer, randomId, itemId);
found = true;
break;
}
}
if (!found) {
sendMessageFail(error, peer);
}
} else {
sendMessageFail(error, peer);
}
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
};
const auto forwardMessageUnquoted = [&] (not_null<HistoryItem *> item) {
if (shared) {
++shared->requestsLeft;
}
const auto media = item->media();
auto newSendFlags = MTPmessages_SendMessage::Flag(0)
| (!media || !media->webpage()
? MTPmessages_SendMessage::Flag::f_no_webpage
: MTPmessages_SendMessage::Flag(0))
| (action.options.silent
? MTPmessages_SendMessage::Flag::f_silent
: MTPmessages_SendMessage::Flag(0))
| (action.options.scheduled
? MTPmessages_SendMessage::Flag::f_schedule_date
: MTPmessages_SendMessage::Flag(0));
auto sentEntities = Api::EntitiesToMTP(
_session,
item->originalText().entities,
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
newSendFlags |= MTPmessages_SendMessage::Flag::f_entities;
}
const auto requestType = Data::Histories::RequestType::Send;
const auto currentRandomId = randomIds.takeFirst();
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(newSendFlags),
peer->input,
MTPint(),
MTP_string(item->originalText().text),
currentRandomId,
MTPReplyMarkup(),
sentEntities,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
if (shared && !--shared->requestsLeft) {
shared->callback();
}
finish();
}).fail([=, ids = localIds](const RPCError &error) {
if (ids) {
auto found = false;
for (const auto &[randomId, itemId] : *ids) {
if (currentRandomId == MTP_long(randomId)) {
sendMessageFail(error, peer, randomId, itemId);
found = true;
break;
}
}
if (!found) {
sendMessageFail(error, peer);
}
} else {
sendMessageFail(error, peer);
}
finish();
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
};
const auto sendAccumulated = [&] {
if (cForwardQuoted()) {
forwardQuoted();
} else if (isGrouped()) {
forwardAlbumUnquoted();
} else {
for (auto i = fromIter, e = toIter; i != e; i++) {
const auto item = *i;
const auto media = item->media();
if (media && !media->webpage()) {
if (media->poll()
|| media->geoPoint()
|| media->sharedContact()
|| media->photo()
|| media->document()) {
forwardMediaUnquoted(item);
} else {
forwardQuotedSingle(item);
}
} else {
forwardMessageUnquoted(item);
}
}
}
ids.resize(0);
randomIds.resize(0);
@ -3965,7 +4296,8 @@ void ApiWrap::forwardMessages(
ids.reserve(count);
randomIds.reserve(count);
for (const auto item : items) {
for (auto i = items.begin(), e = items.end(); i != e; /* ++i is in the end */) {
const auto item = *i;
const auto randomId = rand_value<uint64>();
if (genClientSideMessage) {
if (const auto message = item->toHistoryMessage()) {
@ -3994,16 +4326,18 @@ void ApiWrap::forwardMessages(
localIds->emplace(randomId, newId);
}
}
const auto newFrom = item->history()->peer;
const auto newGroupId = item->groupId();
if (forwardFrom != newFrom
|| currentGroupId != newGroupId) {
if (needNextGroup(item)) {
sendAccumulated();
forwardFrom = newFrom;
currentGroupId = newGroupId;
forwardFrom = item->history()->peer;
currentGroupId = item->groupId();
fromIter = i;
}
ids.push_back(MTP_int(item->id));
randomIds.push_back(MTP_long(randomId));
if (item->media() && item->media()->canBeGrouped()) {
isLastGrouped = true;
}
toIter = ++i;
}
sendAccumulated();
_session->data().sendHistoryChangeNotifications();

View File

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text_options.h"
#include "chat_helpers/message_field.h"
@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_history.h"
#include "styles/style_info.h"
class ShareBox::Inner final : public Ui::RpWidget, private base::Subscriber {
public:
@ -166,13 +168,15 @@ ShareBox::ShareBox(
CopyCallback &&copyCallback,
SubmitCallback &&submitCallback,
FilterCallback &&filterCallback,
GoToChatCallback &&goToChatCallback)
GoToChatCallback &&goToChatCallback,
bool hasMedia)
: _navigation(navigation)
, _api(&_navigation->session().mtp())
, _copyCallback(std::move(copyCallback))
, _submitCallback(std::move(submitCallback))
, _filterCallback(std::move(filterCallback))
, _goToChatCallback(goToChatCallback ? std::move(goToChatCallback) : nullptr)
, _hasMediaMessages(hasMedia)
, _select(
this,
st::contactsMultiSelect,
@ -226,6 +230,7 @@ void ShareBox::prepare() {
Ui::SendPendingMoveResizeEvents(_select);
setTitle(tr::lng_selected_forward());
updateAdditionalTitle();
_inner = setInnerWidget(
object_ptr<Inner>(
@ -447,6 +452,9 @@ SendMenu::Type ShareBox::sendMenuType() const {
void ShareBox::createButtons() {
clearButtons();
const auto moreButton = addTopButton(st::infoTopBarMenu);
moreButton->setClickedCallback([=] { showMenu(moreButton.data()); });
if (_hasSelected) {
if (_goToChatCallback && _inner->selected().size() == 1) {
const auto singleChat = _inner->selected().at(0);
@ -467,6 +475,109 @@ void ShareBox::createButtons() {
addButton(tr::lng_cancel(), [=] { closeBox(); });
}
bool ShareBox::showMenu(not_null<Ui::IconButton*> button) {
if (_menu) {
_menu->hideAnimated(Ui::InnerDropdown::HideOption::IgnoreShow);
return true;
}
_menu = base::make_unique_q<Ui::DropdownMenu>(window());
const auto weak = _menu.get();
_menu->setHiddenCallback([=] {
weak->deleteLater();
if (_menu == weak) {
button->setForceRippled(false);
}
});
_menu->setShowStartCallback([=] {
if (_menu == weak) {
button->setForceRippled(true);
}
});
_menu->setHideStartCallback([=] {
if (_menu == weak) {
button->setForceRippled(false);
}
});
button->installEventFilter(_menu);
if (!cForwardQuoted()) {
_menu->addAction(tr::ktg_forward_menu_quoted(tr::now), [=] {
cSetForwardQuoted(true);
updateAdditionalTitle();
});
}
if (cForwardQuoted() || !cForwardCaptioned()) {
_menu->addAction(tr::ktg_forward_menu_unquoted(tr::now), [=] {
cSetForwardQuoted(false);
cSetForwardCaptioned(true);
updateAdditionalTitle();
});
}
if (cForwardQuoted() || cForwardCaptioned()) {
_menu->addAction(tr::ktg_forward_menu_uncaptioned(tr::now), [=] {
cSetForwardQuoted(false);
cSetForwardCaptioned(false);
updateAdditionalTitle();
});
}
if (_hasMediaMessages) {
_menu->addSeparator();
if (!cForwardAlbumsAsIs()) {
_menu->addAction(tr::ktg_forward_menu_default_albums(tr::now), [=] {
cSetForwardAlbumsAsIs(true);
updateAdditionalTitle();
});
}
if (cForwardAlbumsAsIs() || !cForwardGrouped()) {
_menu->addAction(tr::ktg_forward_menu_group_all_media(tr::now), [=] {
cSetForwardAlbumsAsIs(false);
cSetForwardGrouped(true);
updateAdditionalTitle();
});
}
if (cForwardAlbumsAsIs() || cForwardGrouped()) {
_menu->addAction(tr::ktg_forward_menu_separate_messages(tr::now), [=] {
cSetForwardAlbumsAsIs(false);
cSetForwardGrouped(false);
updateAdditionalTitle();
});
}
}
const auto parentTopLeft = window()->mapToGlobal({ 0, 0 });
const auto buttonTopLeft = button->mapToGlobal({ 0, 0 });
const auto parentRect = QRect(parentTopLeft, window()->size());
const auto buttonRect = QRect(buttonTopLeft, button->size());
_menu->move(
buttonRect.x() + buttonRect.width() - _menu->width() - parentRect.x(),
buttonRect.y() + buttonRect.height() - parentRect.y() - style::ConvertScale(18));
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
return true;
}
void ShareBox::updateAdditionalTitle() {
QString result;
if (!cForwardQuoted()) {
result += (cForwardCaptioned()
? tr::ktg_forward_subtitle_unquoted(tr::now)
: tr::ktg_forward_subtitle_uncaptioned(tr::now));
}
if (_hasMediaMessages && !cForwardAlbumsAsIs()) {
if (!result.isEmpty()) {
result += ", ";
}
result += (cForwardGrouped()
? tr::ktg_forward_subtitle_group_all_media(tr::now)
: tr::ktg_forward_subtitle_separate_messages(tr::now));
}
setAdditionalTitle(rpl::single(result));
}
void ShareBox::applyFilterUpdate(const QString &query) {
onScrollToY(0);
_inner->updateFilter(query);

View File

@ -38,6 +38,7 @@ class IndexedList;
namespace Ui {
class MultiSelect;
class InputField;
class DropdownMenu;
struct ScrollToRequest;
template <typename Widget>
class SlideWrap;
@ -67,7 +68,8 @@ public:
CopyCallback &&copyCallback,
SubmitCallback &&submitCallback,
FilterCallback &&filterCallback,
GoToChatCallback &&goToChatCallback = nullptr);
GoToChatCallback &&goToChatCallback = nullptr,
bool hasMedia = false);
protected:
void prepare() override;
@ -94,6 +96,8 @@ private:
void applyFilterUpdate(const QString &query);
void selectedChanged();
void createButtons();
bool showMenu(not_null<Ui::IconButton*> button);
void updateAdditionalTitle();
int getTopScrollSkip() const;
int getBottomScrollSkip() const;
int contentHeight() const;
@ -115,6 +119,8 @@ private:
FilterCallback _filterCallback;
GoToChatCallback _goToChatCallback;
bool _hasMediaMessages = false;
object_ptr<Ui::MultiSelect> _select;
object_ptr<Ui::SlideWrap<Ui::InputField>> _comment;
@ -136,4 +142,6 @@ private:
Ui::Animations::Simple _scrollAnimation;
base::unique_qptr<Ui::DropdownMenu> _menu;
};

View File

@ -182,6 +182,10 @@ PollData *Media::poll() const {
return nullptr;
}
const LocationPoint *Media::geoPoint() const {
return nullptr;
}
bool Media::uploading() const {
return false;
}
@ -808,6 +812,10 @@ Data::CloudImage *MediaLocation::location() const {
return _location;
}
const LocationPoint *MediaLocation::geoPoint() const {
return &_point;
}
QString MediaLocation::chatListText() const {
return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title);
}

View File

@ -82,6 +82,7 @@ public:
virtual const Invoice *invoice() const;
virtual Data::CloudImage *location() const;
virtual PollData *poll() const;
virtual const LocationPoint *geoPoint() const;
virtual bool uploading() const;
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
@ -245,6 +246,7 @@ public:
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
Data::CloudImage *location() const override;
const LocationPoint *geoPoint() const override;
QString chatListText() const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;

View File

@ -5172,6 +5172,9 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
updateField();
} else if (_inReplyEditForward) {
if (readyToForward()) {
if (e->button() != Qt::LeftButton) {
return;
}
const auto items = std::move(_toForward);
session().data().cancelForwarding(_history);
auto list = ranges::view::all(
@ -5189,6 +5192,92 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
}
}
void HistoryWidget::contextMenuEvent(QContextMenuEvent *e) {
if (_menu) {
return;
}
const auto hasSecondLayer = (_editMsgId
|| _replyToId
|| readyToForward()
|| _kbReplyTo);
_replyForwardPressed = hasSecondLayer && QRect(
0,
_field->y() - st::historySendPadding - st::historyReplyHeight,
st::historyReplySkip,
st::historyReplyHeight).contains(e->pos());
if (_replyForwardPressed && !_fieldBarCancel->isHidden()) {
return;
} else if (_inReplyEditForward) {
if (readyToForward()) {
const auto count = int(_toForward.size());
auto hasMediaToGroup = false;
if (count > 1) {
auto grouppableMediaCount = 0;
for (const auto item : _toForward) {
if (item->media() && item->media()->canBeGrouped()) {
grouppableMediaCount++;
} else {
grouppableMediaCount = 0;
}
if (grouppableMediaCount > 1) {
hasMediaToGroup = true;
break;
}
}
}
_menu = base::make_unique_q<Ui::PopupMenu>(this);
if (!cForwardQuoted()) {
_menu->addAction(tr::ktg_forward_menu_quoted(tr::now), [=] {
cSetForwardQuoted(true);
updateForwardingTexts();
});
}
if (cForwardQuoted() || !cForwardCaptioned()) {
_menu->addAction(tr::ktg_forward_menu_unquoted(tr::now), [=] {
cSetForwardQuoted(false);
cSetForwardCaptioned(true);
updateForwardingTexts();
});
}
if (cForwardQuoted() || cForwardCaptioned()) {
_menu->addAction(tr::ktg_forward_menu_uncaptioned(tr::now), [=] {
cSetForwardQuoted(false);
cSetForwardCaptioned(false);
updateForwardingTexts();
});
}
if (hasMediaToGroup && count > 1) {
_menu->addSeparator();
if (!cForwardAlbumsAsIs()) {
_menu->addAction(tr::ktg_forward_menu_default_albums(tr::now), [=] {
cSetForwardAlbumsAsIs(true);
updateForwardingTexts();
});
}
if (cForwardAlbumsAsIs() || !cForwardGrouped()) {
_menu->addAction(tr::ktg_forward_menu_group_all_media(tr::now), [=] {
cSetForwardAlbumsAsIs(false);
cSetForwardGrouped(true);
updateForwardingTexts();
});
}
if (cForwardAlbumsAsIs() || cForwardGrouped()) {
_menu->addAction(tr::ktg_forward_menu_separate_messages(tr::now), [=] {
cSetForwardAlbumsAsIs(false);
cSetForwardGrouped(false);
updateForwardingTexts();
});
}
}
_menu->popup(QCursor::pos());
}
}
}
void HistoryWidget::keyPressEvent(QKeyEvent *e) {
if (!_history) return;
@ -6319,6 +6408,8 @@ void HistoryWidget::updateForwardingTexts() {
auto insertedPeers = base::flat_set<not_null<PeerData*>>();
auto insertedNames = base::flat_set<QString>();
auto fullname = QString();
auto hasMediaToGroup = false;
auto grouppableMediaCount = 0;
auto names = std::vector<QString>();
names.reserve(_toForward.size());
for (const auto item : _toForward) {
@ -6339,7 +6430,18 @@ void HistoryWidget::updateForwardingTexts() {
} else {
Unexpected("Corrupt forwarded information in message.");
}
if (!hasMediaToGroup) {
if (item->media() && item->media()->canBeGrouped()) {
grouppableMediaCount++;
} else {
grouppableMediaCount = 0;
}
if (grouppableMediaCount > 1) {
hasMediaToGroup = true;
}
}
}
if (names.size() > 2) {
from = tr::lng_forwarding_from(tr::now, lt_count, names.size() - 1, lt_user, names[0]);
} else if (names.size() < 2) {
@ -6348,10 +6450,20 @@ void HistoryWidget::updateForwardingTexts() {
from = tr::lng_forwarding_from_two(tr::now, lt_user, names[0], lt_second_user, names[1]);
}
if (count < 2) {
if (count < 2 && cForwardQuoted()) {
text = _toForward.front()->inReplyText();
} else {
text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count));
text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count)
+ (cForwardQuoted()
? QString()
: qsl(", ") + (cForwardCaptioned()
? tr::ktg_forward_subtitle_unquoted(tr::now)
: tr::ktg_forward_subtitle_uncaptioned(tr::now)))
+ (cForwardAlbumsAsIs() || !hasMediaToGroup
? QString()
: qsl(", ") + (cForwardGrouped()
? tr::ktg_forward_subtitle_group_all_media(tr::now)
: tr::ktg_forward_subtitle_separate_messages(tr::now))));
}
}
_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());

View File

@ -287,6 +287,7 @@ protected:
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void leaveEventHook(QEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
@ -769,4 +770,6 @@ private:
int _topDelta = 0;
base::unique_qptr<Ui::PopupMenu> _menu;
};

View File

@ -219,3 +219,8 @@ QString gApiHash;
bool gUseEnvApi = true;
bool gApiFromStartParams = false;
bool gForwardQuoted = true;
bool gForwardCaptioned = true;
bool gForwardAlbumsAsIs = true;
bool gForwardGrouped = false;

View File

@ -129,3 +129,8 @@ DeclareSetting(int, ApiId);
DeclareSetting(QString, ApiHash);
DeclareSetting(bool, UseEnvApi);
DeclareSetting(bool, ApiFromStartParams);
DeclareSetting(bool, ForwardQuoted);
DeclareSetting(bool, ForwardCaptioned);
DeclareSetting(bool, ForwardAlbumsAsIs);
DeclareSetting(bool, ForwardGrouped);

View File

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "api/api_chat_filters.h"
#include "api/api_text_entities.h"
#include "mtproto/mtproto_config.h"
#include "history/history.h"
#include "history/history_item.h"
@ -1045,6 +1046,15 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
FnMut<void()> submitCallback;
};
struct MsgIdsGroup {
MsgIdsGroup() = default;
MsgIdsGroup(not_null<HistoryItem*> item, MTPint fullId, bool isGrouped = false)
: grouped(isGrouped) {
add(item, fullId);
}
void add(not_null<HistoryItem*> item, MTPint fullId) {
items.push_back(item);
ids.push_back(fullId);
}
HistoryItemsList items;
QVector<MTPint> ids;
bool grouped = false;
@ -1058,6 +1068,23 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
&& firstItem->media()
&& (firstItem->media()->game() != nullptr);
const auto canCopyLink = items.size() == 1 && (firstItem->hasDirectLink() || isGame);
auto hasMediaForGrouping = false;
if (items.size() > 1) {
auto grouppableMediaCount = 0;
for (const auto item : history->owner().idsToItems(items)) {
if (item->media() && item->media()->canBeGrouped()) {
grouppableMediaCount++;
} else {
grouppableMediaCount = 0;
}
if (grouppableMediaCount > 1) {
hasMediaForGrouping = true;
break;
}
}
}
const auto data = std::make_shared<ShareData>(history->peer, std::move(items), std::move(successCallback));
auto copyCallback = [=]() {
@ -1128,37 +1155,261 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
: MTPmessages_ForwardMessages::Flag(0));
const auto groupedSendFlags = sendFlags | MTPmessages_ForwardMessages::Flag::f_grouped;
// Regroup messages if needed
auto groupedMsgIds = QVector<MsgIdsGroup>();
for (const auto fullId : data->msgIds) {
auto item = navigation->session().data().message(fullId);
auto group = owner->groups().find(item);
const auto item = navigation->session().data().message(fullId);
const auto group = owner->groups().find(item);
const auto canBeGrouped = hasMediaForGrouping && item->media() && item->media()->canBeGrouped();
if (groupedMsgIds.size()) {
auto prevItem = groupedMsgIds.back().items.back();
auto prevGroup = owner->groups().find(prevItem);
if (prevGroup == group) {
groupedMsgIds.back().items.push_back(item);
groupedMsgIds.back().ids.push_back(MTP_int(fullId.msg));
continue;
if (groupedMsgIds.size() > 0) {
auto lastGroup = &groupedMsgIds.back();
if (cForwardAlbumsAsIs()) {
if (owner->groups().find(lastGroup->items.back()) == group) {
lastGroup->add(item, MTP_int(fullId.msg));
continue;
}
} else {
if (lastGroup->grouped) {
if (lastGroup->items.size() < 10 && canBeGrouped) {
lastGroup->add(item, MTP_int(fullId.msg));
continue;
}
} else {
if (!canBeGrouped) {
lastGroup->add(item, MTP_int(fullId.msg));
continue;
}
}
}
}
MsgIdsGroup msgIdGroupInst;
msgIdGroupInst.items.push_back(item);
msgIdGroupInst.ids.push_back(MTP_int(fullId.msg));
msgIdGroupInst.grouped = (group != nullptr);
groupedMsgIds.push_back(msgIdGroupInst);
groupedMsgIds.push_back(MsgIdsGroup(
item,
MTP_int(fullId.msg),
cForwardAlbumsAsIs()
? (group != nullptr)
: canBeGrouped
? cForwardGrouped()
: false));
}
auto generateRandom = [&] (int size) {
const auto generateRandom = [&] (int size) {
auto result = QVector<MTPlong>(size);
for (auto &value : result) {
value = rand_value<MTPlong>();
}
return result;
};
const auto checkAndClose = [=] (mtpRequestId requestId) {
data->requests.remove(requestId);
if (data->requests.empty()) {
Ui::Toast::Show(tr::lng_share_done(tr::now));
Ui::hideLayer();
}
};
auto &api = owner->session().api();
auto &histories = owner->histories();
const auto requestType = Data::Histories::RequestType::Send;
const auto forwardQuoted = [&] (
MsgIdsGroup &&group,
not_null<History*> history,
MTPmessages_ForwardMessages::Flags flags) {
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api();
history->sendRequestId = api.request(MTPmessages_ForwardMessages(
MTP_flags(flags),
data->peer->input,
MTP_vector<MTPint>(group.ids),
MTP_vector<MTPlong>(generateRandom(group.ids.size())),
history->peer->input,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &updates, mtpRequestId requestId) {
history->session().api().applyUpdates(updates);
checkAndClose(requestId);
finish();
}).fail([=](const RPCError &error) {
finish();
}).afterRequest(history->sendRequestId).send();
return history->sendRequestId;
});
data->requests.insert(history->sendRequestId);
};
const auto forwardAlbumUnquoted = [&] (MsgIdsGroup &&group, not_null<History*> history) {
auto medias = QVector<MTPInputSingleMedia>();
medias.reserve(group.items.size());
auto randomIds = generateRandom(group.items.size());
for (const auto item : group.items) {
const auto media = item->media();
const auto inputMedia = media->photo()
? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint())
: MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint());
const auto caption = cForwardCaptioned()
? item->originalText()
: TextWithEntities();
const auto sentEntities = Api::EntitiesToMTP(
session,
caption.entities,
Api::ConvertOption::SkipLocal);
const auto flags = !sentEntities.v.isEmpty()
? MTPDinputSingleMedia::Flag::f_entities
: MTPDinputSingleMedia::Flag(0);
const auto randomId = randomIds.takeFirst();
medias.push_back(MTP_inputSingleMedia(
MTP_flags(flags),
inputMedia,
randomId,
MTP_string(caption.text),
sentEntities));
}
const auto flags = MTPmessages_SendMultiMedia::Flags(0)
| (options.silent
? MTPmessages_SendMultiMedia::Flag::f_silent
: MTPmessages_SendMultiMedia::Flag(0))
| (options.scheduled
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
: MTPmessages_SendMultiMedia::Flag(0));
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api();
history->sendRequestId = api.request(MTPmessages_SendMultiMedia(
MTP_flags(flags),
history->peer->input,
MTPint(),
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &updates, mtpRequestId requestId) {
history->session().api().applyUpdates(updates);
checkAndClose(requestId);
finish();
}).fail([=](const RPCError &error) {
finish();
}).afterRequest(history->sendRequestId).send();
return history->sendRequestId;
});
data->requests.insert(history->sendRequestId);
};
const auto forwardMediaUnquoted = [&] (not_null<HistoryItem *> item, not_null<History*> history) {
const auto media = item->media();
auto newSendFlags = MTPmessages_SendMedia::Flags(0)
| (options.silent
? MTPmessages_SendMedia::Flag::f_silent
: MTPmessages_SendMedia::Flag(0))
| (options.scheduled
? MTPmessages_SendMedia::Flag::f_schedule_date
: MTPmessages_SendMedia::Flag(0));
const auto caption = (cForwardCaptioned()
&& !media->geoPoint()
&& !media->sharedContact())
? item->originalText()
: TextWithEntities();
const auto sentEntities = Api::EntitiesToMTP(
session,
caption.entities,
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
newSendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto inputMedia = media->poll()
? PollDataToInputMedia(media->poll())
: media->geoPoint()
? MTP_inputMediaGeoPoint(
MTP_inputGeoPoint(
MTP_double(media->geoPoint()->lat()),
MTP_double(media->geoPoint()->lon())))
: media->sharedContact()
? MTP_inputMediaContact(
MTP_string(media->sharedContact()->phoneNumber),
MTP_string(media->sharedContact()->firstName),
MTP_string(media->sharedContact()->lastName),
MTPstring())
: media->photo()
? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint())
: MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint());
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api();
history->sendRequestId = api.request(MTPmessages_SendMedia(
MTP_flags(newSendFlags),
history->peer->input,
MTPint(),
inputMedia,
MTP_string(caption.text),
rand_value<MTPlong>(),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &updates, mtpRequestId requestId) {
history->session().api().applyUpdates(updates);
checkAndClose(requestId);
finish();
}).fail([=](const RPCError &error) {
finish();
}).afterRequest(history->sendRequestId).send();
return history->sendRequestId;
});
data->requests.insert(history->sendRequestId);
};
const auto forwardMessageUnquoted = [&] (not_null<HistoryItem *> item, not_null<History*> history) {
const auto media = item->media();
auto newSendFlags = MTPmessages_SendMessage::Flag(0)
| (!media || !media->webpage()
? MTPmessages_SendMessage::Flag::f_no_webpage
: MTPmessages_SendMessage::Flag(0))
| (options.silent
? MTPmessages_SendMessage::Flag::f_silent
: MTPmessages_SendMessage::Flag(0))
| (options.scheduled
? MTPmessages_SendMessage::Flag::f_schedule_date
: MTPmessages_SendMessage::Flag(0));
const auto sentEntities = Api::EntitiesToMTP(
session,
item->originalText().entities,
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
newSendFlags |= MTPmessages_SendMessage::Flag::f_entities;
}
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api();
history->sendRequestId = api.request(MTPmessages_SendMessage(
MTP_flags(newSendFlags),
history->peer->input,
MTPint(),
MTP_string(item->originalText().text),
rand_value<MTPlong>(),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &updates, mtpRequestId requestId) {
history->session().api().applyUpdates(updates);
checkAndClose(requestId);
finish();
}).fail([=](const RPCError &error) {
finish();
}).afterRequest(history->sendRequestId).send();
return history->sendRequestId;
});
data->requests.insert(history->sendRequestId);
};
for (const auto peer : result) {
const auto history = owner->history(peer);
if (!comment.text.isEmpty()) {
@ -1168,30 +1419,39 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
message.action.clearDraft = false;
api.sendMessage(std::move(message));
}
for (auto group : groupedMsgIds) {
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
auto &api = history->session().api();
history->sendRequestId = api.request(MTPmessages_ForwardMessages(
MTP_flags(group.grouped ? groupedSendFlags : sendFlags),
data->peer->input,
MTP_vector<MTPint>(group.ids),
MTP_vector<MTPlong>(generateRandom(group.ids.size())),
peer->input,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &updates, mtpRequestId requestId) {
history->session().api().applyUpdates(updates);
data->requests.remove(requestId);
if (data->requests.empty()) {
Ui::Toast::Show(tr::lng_share_done(tr::now));
Ui::hideLayer();
for (auto &group : groupedMsgIds) {
if (cForwardQuoted()) {
// Forward regrouped messages as is
const auto flags = group.grouped ? groupedSendFlags : sendFlags;
forwardQuoted(std::move(group), history, flags);
} else if (group.grouped) {
// Sending albums without author
forwardAlbumUnquoted(std::move(group), history);
} else {
for (const auto item : group.items) {
const auto media = item->media();
if (media && !media->webpage()) {
if (media->poll()
|| media->geoPoint()
|| media->sharedContact()
|| media->photo()
|| media->document()) {
// Send media messages without author
forwardMediaUnquoted(item, history);
} else {
// Forward message if type doesn't support forwarding unquoted
forwardQuoted(
MsgIdsGroup(item, MTP_int(item->fullId().msg)),
history,
sendFlags);
}
} else {
// Send messages without author
forwardMessageUnquoted(item, history);
}
finish();
}).fail([=](const RPCError &error) {
finish();
}).afterRequest(history->sendRequestId).send();
return history->sendRequestId;
});
data->requests.insert(history->sendRequestId);
}
}
}
}
if (data->submitCallback && !cForwardRetainSelection()) {
@ -1212,7 +1472,8 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
std::move(copyLinkCallback),
std::move(submitCallback),
std::move(filterCallback),
std::move(goToChatCallback)));
std::move(goToChatCallback),
hasMediaForGrouping));
return weak->data();
}