[Option][GUI] Unquoted forward and forward options
This commit is contained in:
parent
eec451c949
commit
cee0996b0a
24 changed files with 1679 additions and 161 deletions
|
|
@ -122,7 +122,21 @@
|
||||||
"ktg_group_id_copied": "Group ID copied to clipboard.",
|
"ktg_group_id_copied": "Group ID copied to clipboard.",
|
||||||
"ktg_supergroup_id_copied": "Supergroup ID copied to clipboard.",
|
"ktg_supergroup_id_copied": "Supergroup ID copied to clipboard.",
|
||||||
"ktg_channel_id_copied": "Channel ID copied to clipboard.",
|
"ktg_channel_id_copied": "Channel ID copied to clipboard.",
|
||||||
|
"ktg_forward_go_to_chat": "Go to chat",
|
||||||
"ktg_settings_forward": "Forward",
|
"ktg_settings_forward": "Forward",
|
||||||
|
"ktg_settings_forward_retain_selection": "Retain selection after forward",
|
||||||
|
"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",
|
||||||
"ktg_filters_exclude_not_owned": "Not owned",
|
"ktg_filters_exclude_not_owned": "Not owned",
|
||||||
"ktg_filters_exclude_not_admin": "Not administrated",
|
"ktg_filters_exclude_not_admin": "Not administrated",
|
||||||
"ktg_filters_exclude_owned": "Owned",
|
"ktg_filters_exclude_owned": "Owned",
|
||||||
|
|
@ -144,6 +158,20 @@
|
||||||
"ktg_filters_hide_all_chats_toast": "\"All Chats\" folder is hidden.\nYou can enable it back in Kotatogram Settings.",
|
"ktg_filters_hide_all_chats_toast": "\"All Chats\" folder is hidden.\nYou can enable it back in Kotatogram Settings.",
|
||||||
"ktg_filters_hide_edit_toast": "Edit button is hidden.\nYou can enable it back in Kotatogram Settings.",
|
"ktg_filters_hide_edit_toast": "Edit button is hidden.\nYou can enable it back in Kotatogram Settings.",
|
||||||
"ktg_settings_telegram_sites_autologin": "Auto-login on Telegram sites",
|
"ktg_settings_telegram_sites_autologin": "Auto-login on Telegram sites",
|
||||||
|
"ktg_forward_sender_names_and_captions_removed": "Sender names and captions removed",
|
||||||
|
"ktg_forward_remember_mode": "Remember forward mode",
|
||||||
|
"ktg_forward_mode": "Forward mode",
|
||||||
|
"ktg_forward_mode_quoted": "Quoted",
|
||||||
|
"ktg_forward_mode_unquoted": "Unquoted",
|
||||||
|
"ktg_forward_mode_uncaptioned": "Uncaptioned",
|
||||||
|
"ktg_forward_grouping_mode": "Grouping mode",
|
||||||
|
"ktg_forward_grouping_mode_preserve_albums": "Same as original",
|
||||||
|
"ktg_forward_grouping_mode_regroup": "Regroup media",
|
||||||
|
"ktg_forward_grouping_mode_regroup_desc": "Unquoted and uncaptioned only",
|
||||||
|
"ktg_forward_grouping_mode_separate": "Separate",
|
||||||
|
"ktg_forward_force_old_unquoted": "Old unquoted forward method",
|
||||||
|
"ktg_forward_force_old_unquoted_desc": "Old method copies messages content on client rather than server. Currently it's used only for \"Regroup media\" grouping mode, since new one doesn't support it. If for some reason unquoted forward doesn't work correctly, try switching this option.",
|
||||||
|
"ktg_forward_quiz_unquoted": "Sorry, quizzes that are currently open and unvoted on cannot be forwarded unquoted.",
|
||||||
"ktg_in_app_update_disabled": "In-app updater is disabled.",
|
"ktg_in_app_update_disabled": "In-app updater is disabled.",
|
||||||
"dummy_last_string": ""
|
"dummy_last_string": ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_location.h"
|
||||||
#include "data/data_channel.h" // ChannelData::addsSignature.
|
#include "data/data_channel.h" // ChannelData::addsSignature.
|
||||||
#include "data/data_user.h" // UserData::name
|
#include "data/data_user.h" // UserData::name
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
@ -63,7 +64,9 @@ void SendExistingMedia(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<MediaData*> media,
|
not_null<MediaData*> media,
|
||||||
Fn<MTPInputMedia()> inputMedia,
|
Fn<MTPInputMedia()> inputMedia,
|
||||||
Data::FileOrigin origin) {
|
Data::FileOrigin origin,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false) {
|
||||||
const auto history = message.action.history;
|
const auto history = message.action.history;
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
const auto session = &history->session();
|
const auto session = &history->session();
|
||||||
|
|
@ -142,7 +145,6 @@ void SendExistingMedia(
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
const auto requestType = Data::Histories::RequestType::Send;
|
const auto requestType = Data::Histories::RequestType::Send;
|
||||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||||
const auto usedFileReference = media->fileReference();
|
|
||||||
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||||
MTP_flags(sendFlags),
|
MTP_flags(sendFlags),
|
||||||
peer->input,
|
peer->input,
|
||||||
|
|
@ -154,12 +156,16 @@ void SendExistingMedia(
|
||||||
sentEntities,
|
sentEntities,
|
||||||
MTP_int(message.action.options.scheduled),
|
MTP_int(message.action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
api->applyUpdates(result, randomId);
|
api->applyUpdates(result, randomId);
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback();
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
if (error.code() == 400
|
if (error.code() == 400
|
||||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||||
|
const auto usedFileReference = media->fileReference();
|
||||||
api->refreshFileReference(origin, [=](const auto &result) {
|
api->refreshFileReference(origin, [=](const auto &result) {
|
||||||
if (media->fileReference() != usedFileReference) {
|
if (media->fileReference() != usedFileReference) {
|
||||||
repeatRequest(repeatRequest);
|
repeatRequest(repeatRequest);
|
||||||
|
|
@ -178,14 +184,38 @@ void SendExistingMedia(
|
||||||
};
|
};
|
||||||
performRequest(performRequest);
|
performRequest(performRequest);
|
||||||
|
|
||||||
api->finishForwarding(message.action);
|
if (!forwarding) {
|
||||||
|
api->finishForwarding(message.action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void SendWebDocument(
|
||||||
|
Api::MessageToSend &&message,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Fn<void()> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
|
const auto inputMedia = [=] {
|
||||||
|
return MTP_inputMediaDocumentExternal(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_string(document->url()),
|
||||||
|
MTPint()); // ttl_seconds
|
||||||
|
};
|
||||||
|
SendExistingMedia(
|
||||||
|
std::move(message),
|
||||||
|
document,
|
||||||
|
inputMedia,
|
||||||
|
document->stickerOrGifOrigin(),
|
||||||
|
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||||
|
forwarding);
|
||||||
|
}
|
||||||
|
|
||||||
void SendExistingDocument(
|
void SendExistingDocument(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<DocumentData*> document) {
|
not_null<DocumentData*> document,
|
||||||
|
Fn<void()> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto inputMedia = [=] {
|
const auto inputMedia = [=] {
|
||||||
return MTP_inputMediaDocument(
|
return MTP_inputMediaDocument(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
|
|
@ -197,7 +227,9 @@ void SendExistingDocument(
|
||||||
std::move(message),
|
std::move(message),
|
||||||
document,
|
document,
|
||||||
inputMedia,
|
inputMedia,
|
||||||
document->stickerOrGifOrigin());
|
document->stickerOrGifOrigin(),
|
||||||
|
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||||
|
forwarding);
|
||||||
|
|
||||||
if (document->sticker()) {
|
if (document->sticker()) {
|
||||||
document->owner().stickers().incrementSticker(document);
|
document->owner().stickers().incrementSticker(document);
|
||||||
|
|
@ -206,7 +238,9 @@ void SendExistingDocument(
|
||||||
|
|
||||||
void SendExistingPhoto(
|
void SendExistingPhoto(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<PhotoData*> photo) {
|
not_null<PhotoData*> photo,
|
||||||
|
Fn<void()> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto inputMedia = [=] {
|
const auto inputMedia = [=] {
|
||||||
return MTP_inputMediaPhoto(
|
return MTP_inputMediaPhoto(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
|
|
@ -217,10 +251,15 @@ void SendExistingPhoto(
|
||||||
std::move(message),
|
std::move(message),
|
||||||
photo,
|
photo,
|
||||||
inputMedia,
|
inputMedia,
|
||||||
Data::FileOrigin());
|
Data::FileOrigin(),
|
||||||
|
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||||
|
forwarding);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendDice(MessageToSend &message) {
|
bool SendDice(
|
||||||
|
MessageToSend &message,
|
||||||
|
Fn<void(const MTPUpdates &, mtpRequestId)> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto full = QStringView(message.textWithTags.text).trimmed();
|
const auto full = QStringView(message.textWithTags.text).trimmed();
|
||||||
auto length = 0;
|
auto length = 0;
|
||||||
if (!Ui::Emoji::Find(full.data(), full.data() + full.size(), &length)
|
if (!Ui::Emoji::Find(full.data(), full.data() + full.size(), &length)
|
||||||
|
|
@ -320,8 +359,11 @@ bool SendDice(MessageToSend &message) {
|
||||||
MTP_vector<MTPMessageEntity>(),
|
MTP_vector<MTPMessageEntity>(),
|
||||||
MTP_int(message.action.options.scheduled),
|
MTP_int(message.action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
api->applyUpdates(result, randomId);
|
api->applyUpdates(result, randomId);
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback(result, requestId);
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
api->sendMessageFail(error, peer, randomId, newId);
|
api->sendMessageFail(error, peer, randomId, newId);
|
||||||
|
|
@ -330,7 +372,9 @@ bool SendDice(MessageToSend &message) {
|
||||||
).send();
|
).send();
|
||||||
return history->sendRequestId;
|
return history->sendRequestId;
|
||||||
});
|
});
|
||||||
api->finishForwarding(message.action);
|
if (!forwarding) {
|
||||||
|
api->finishForwarding(message.action);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,4 +528,72 @@ void SendConfirmedFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendLocationPoint(
|
||||||
|
const Data::LocationPoint &data,
|
||||||
|
const SendAction &action,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(const MTP::Error &error)> fail) {
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto session = &history->session();
|
||||||
|
const auto api = &session->api();
|
||||||
|
const auto peer = history->peer;
|
||||||
|
api->sendAction(action);
|
||||||
|
|
||||||
|
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||||
|
if (action.replyTo) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||||
|
}
|
||||||
|
if (action.clearDraft) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||||
|
history->clearLocalDraft();
|
||||||
|
history->clearCloudDraft();
|
||||||
|
}
|
||||||
|
const auto sendAs = action.options.sendAs;
|
||||||
|
|
||||||
|
if (sendAs) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||||
|
}
|
||||||
|
const auto silentPost = action.options.silent
|
||||||
|
|| (peer->isBroadcast() && session->data().notifySilentPosts(peer));
|
||||||
|
if (silentPost) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||||
|
}
|
||||||
|
if (action.options.scheduled) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
|
}
|
||||||
|
auto &histories = history->owner().histories();
|
||||||
|
const auto requestType = Data::Histories::RequestType::Send;
|
||||||
|
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||||
|
const auto replyTo = action.replyTo;
|
||||||
|
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||||
|
MTP_flags(sendFlags),
|
||||||
|
peer->input,
|
||||||
|
MTP_int(replyTo),
|
||||||
|
MTP_inputMediaGeoPoint(
|
||||||
|
MTP_inputGeoPoint(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_double(data.lat()),
|
||||||
|
MTP_double(data.lon()),
|
||||||
|
MTP_int(0))),
|
||||||
|
MTP_string(),
|
||||||
|
MTP_long(base::RandomValue<uint64>()),
|
||||||
|
MTPReplyMarkup(),
|
||||||
|
MTPVector<MTPMessageEntity>(),
|
||||||
|
MTP_int(action.options.scheduled),
|
||||||
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||||
|
)).done([=](const MTPUpdates &result) mutable {
|
||||||
|
api->applyUpdates(result);
|
||||||
|
done();
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) mutable {
|
||||||
|
if (fail) {
|
||||||
|
fail(error);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}).afterRequest(history->sendRequestId
|
||||||
|
).send();
|
||||||
|
return history->sendRequestId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,41 @@ class PhotoData;
|
||||||
class DocumentData;
|
class DocumentData;
|
||||||
struct FileLoadResult;
|
struct FileLoadResult;
|
||||||
|
|
||||||
|
namespace MTP {
|
||||||
|
class Error;
|
||||||
|
} // namespace MTP
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class LocationPoint;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
struct MessageToSend;
|
struct MessageToSend;
|
||||||
struct SendAction;
|
struct SendAction;
|
||||||
|
|
||||||
|
void SendWebDocument(
|
||||||
|
MessageToSend &&message,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
void SendExistingDocument(
|
void SendExistingDocument(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
void SendExistingPhoto(
|
void SendExistingPhoto(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<PhotoData*> photo);
|
not_null<PhotoData*> photo,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
bool SendDice(MessageToSend &message);
|
bool SendDice(
|
||||||
|
MessageToSend &message,
|
||||||
|
Fn<void(const MTPUpdates &, mtpRequestId)> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
void FillMessagePostFlags(
|
void FillMessagePostFlags(
|
||||||
const SendAction &action,
|
const SendAction &action,
|
||||||
|
|
@ -40,4 +61,10 @@ void SendConfirmedFile(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
const std::shared_ptr<FileLoadResult> &file);
|
const std::shared_ptr<FileLoadResult> &file);
|
||||||
|
|
||||||
|
void SendLocationPoint(
|
||||||
|
const Data::LocationPoint &data,
|
||||||
|
const SendAction &action,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(const MTP::Error &error)> fail);
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
#include "api/api_authorizations.h"
|
#include "api/api_authorizations.h"
|
||||||
#include "api/api_attached_stickers.h"
|
#include "api/api_attached_stickers.h"
|
||||||
#include "api/api_blocked_peers.h"
|
#include "api/api_blocked_peers.h"
|
||||||
|
|
@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_poll.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
|
|
@ -3064,6 +3066,12 @@ void ApiWrap::forwardMessages(
|
||||||
Data::ResolvedForwardDraft &&draft,
|
Data::ResolvedForwardDraft &&draft,
|
||||||
const SendAction &action,
|
const SendAction &action,
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
|
if (draft.options != Data::ForwardOptions::PreserveInfo
|
||||||
|
&& (draft.groupOptions == Data::GroupingOptions::RegroupAll
|
||||||
|
|| ::Kotato::JsonSettings::GetBool("forward_force_old_unquoted"))) {
|
||||||
|
forwardMessagesUnquoted(std::move(draft), action, std::move(successCallback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
Expects(!draft.items.empty());
|
Expects(!draft.items.empty());
|
||||||
|
|
||||||
auto &histories = _session->data().histories();
|
auto &histories = _session->data().histories();
|
||||||
|
|
@ -3114,6 +3122,7 @@ void ApiWrap::forwardMessages(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto forwardFrom = draft.items.front()->history()->peer;
|
auto forwardFrom = draft.items.front()->history()->peer;
|
||||||
|
auto forwardGroupId = draft.items.front()->groupId();
|
||||||
auto ids = QVector<MTPint>();
|
auto ids = QVector<MTPint>();
|
||||||
auto randomIds = QVector<MTPlong>();
|
auto randomIds = QVector<MTPlong>();
|
||||||
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
|
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
|
||||||
|
|
@ -3190,9 +3199,14 @@ void ApiWrap::forwardMessages(
|
||||||
localIds->emplace(randomId, newId);
|
localIds->emplace(randomId, newId);
|
||||||
}
|
}
|
||||||
const auto newFrom = item->history()->peer;
|
const auto newFrom = item->history()->peer;
|
||||||
if (forwardFrom != newFrom) {
|
const auto newGroupId = item->groupId();
|
||||||
|
if (item != draft.items.front() &&
|
||||||
|
((draft.groupOptions == Data::GroupingOptions::GroupAsIs
|
||||||
|
&& (forwardGroupId != newGroupId || forwardFrom != newFrom))
|
||||||
|
|| draft.groupOptions == Data::GroupingOptions::Separate)) {
|
||||||
sendAccumulated();
|
sendAccumulated();
|
||||||
forwardFrom = newFrom;
|
forwardFrom = newFrom;
|
||||||
|
forwardGroupId = newGroupId;
|
||||||
}
|
}
|
||||||
ids.push_back(MTP_int(item->id));
|
ids.push_back(MTP_int(item->id));
|
||||||
randomIds.push_back(MTP_long(randomId));
|
randomIds.push_back(MTP_long(randomId));
|
||||||
|
|
@ -3201,6 +3215,487 @@ void ApiWrap::forwardMessages(
|
||||||
_session->data().sendHistoryChangeNotifications();
|
_session->data().sendHistoryChangeNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::forwardMessagesUnquoted(
|
||||||
|
Data::ResolvedForwardDraft &&draft,
|
||||||
|
const SendAction &action,
|
||||||
|
FnMut<void()> &&successCallback) {
|
||||||
|
Expects(!draft.items.empty());
|
||||||
|
|
||||||
|
auto &histories = _session->data().histories();
|
||||||
|
|
||||||
|
struct SharedCallback {
|
||||||
|
int requestsLeft = 0;
|
||||||
|
FnMut<void()> callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LastGroupType {
|
||||||
|
None,
|
||||||
|
Music,
|
||||||
|
Documents,
|
||||||
|
Medias,
|
||||||
|
};
|
||||||
|
const auto shared = successCallback
|
||||||
|
? std::make_shared<SharedCallback>()
|
||||||
|
: std::shared_ptr<SharedCallback>();
|
||||||
|
if (successCallback) {
|
||||||
|
shared->callback = std::move(successCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto count = int(draft.items.size());
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
|
||||||
|
histories.readInbox(history);
|
||||||
|
|
||||||
|
const auto anonymousPost = peer->amAnonymous();
|
||||||
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
|
const auto sendAs = action.options.sendAs;
|
||||||
|
const auto self = _session->user();
|
||||||
|
const auto messageFromId = sendAs
|
||||||
|
? sendAs->id
|
||||||
|
: anonymousPost
|
||||||
|
? PeerId(0)
|
||||||
|
: self->id;
|
||||||
|
|
||||||
|
auto flags = MessageFlags();
|
||||||
|
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
|
||||||
|
FillMessagePostFlags(action, peer, flags);
|
||||||
|
if (silentPost) {
|
||||||
|
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
|
||||||
|
}
|
||||||
|
if (action.options.scheduled) {
|
||||||
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
|
sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
|
||||||
|
}
|
||||||
|
if (sendAs) {
|
||||||
|
sendFlags |= MTPmessages_ForwardMessages::Flag::f_send_as;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto forwardFrom = draft.items.front()->history()->peer;
|
||||||
|
auto currentGroupId = draft.items.front()->groupId();
|
||||||
|
auto lastGroup = LastGroupType::None;
|
||||||
|
auto ids = QVector<MTPint>();
|
||||||
|
auto randomIds = QVector<uint64>();
|
||||||
|
auto fromIter = draft.items.begin();
|
||||||
|
auto toIter = draft.items.begin();
|
||||||
|
auto messageGroupCount = 0;
|
||||||
|
auto messagePostAuthor = peer->isBroadcast() ? _session->user()->name : QString();
|
||||||
|
|
||||||
|
const auto needNextGroup = [&] (not_null<HistoryItem *> item) {
|
||||||
|
auto lastGroupCheck = false;
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
lastGroupCheck = lastGroup != ((item->media()->photo()
|
||||||
|
|| (item->media()->document()
|
||||||
|
&& item->media()->document()->isVideoFile()))
|
||||||
|
? LastGroupType::Medias
|
||||||
|
: (item->media()->document()
|
||||||
|
&& item->media()->document()->isSharedMediaMusic())
|
||||||
|
? LastGroupType::Music
|
||||||
|
: LastGroupType::Documents);
|
||||||
|
} else {
|
||||||
|
lastGroupCheck = lastGroup != LastGroupType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (draft.groupOptions) {
|
||||||
|
case Data::GroupingOptions::GroupAsIs:
|
||||||
|
return forwardFrom != item->history()->peer
|
||||||
|
|| !currentGroupId
|
||||||
|
|| currentGroupId != item->groupId()
|
||||||
|
|| lastGroupCheck
|
||||||
|
|| messageGroupCount >= 10;
|
||||||
|
|
||||||
|
case Data::GroupingOptions::RegroupAll:
|
||||||
|
return lastGroupCheck
|
||||||
|
|| messageGroupCount >= 10;
|
||||||
|
|
||||||
|
case Data::GroupingOptions::Separate:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Unexpected("draft.groupOptions in ApiWrap::forwardMessagesUnquoted::needNextGroup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto isGrouped = [&] {
|
||||||
|
return lastGroup != LastGroupType::None
|
||||||
|
&& messageGroupCount > 1
|
||||||
|
&& messageGroupCount <= 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardQuotedSingle = [&] (not_null<HistoryItem *> item) {
|
||||||
|
if (shared) {
|
||||||
|
++shared->requestsLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentIds = QVector<MTPint>();
|
||||||
|
currentIds.push_back(MTP_int(item->id));
|
||||||
|
|
||||||
|
auto currentRandomId = MTP_long(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),
|
||||||
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
applyUpdates(result);
|
||||||
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
shared->callback();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
sendMessageFail(error, peer);
|
||||||
|
finish();
|
||||||
|
}).afterRequest(
|
||||||
|
history->sendRequestId
|
||||||
|
).send();
|
||||||
|
return history->sendRequestId;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardAlbumUnquoted = [&] {
|
||||||
|
if (shared) {
|
||||||
|
++shared->requestsLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto medias = std::make_shared<QVector<Data::Media*>>();
|
||||||
|
const auto mediaInputs = std::make_shared<QVector<MTPInputSingleMedia>>();
|
||||||
|
const auto mediaRefs = std::make_shared<QVector<QByteArray>>();
|
||||||
|
mediaInputs->reserve(ids.size());
|
||||||
|
mediaRefs->reserve(ids.size());
|
||||||
|
|
||||||
|
const auto newGroupId = base::RandomValue<uint64>();
|
||||||
|
auto msgFlags = NewMessageFlags(peer);
|
||||||
|
|
||||||
|
FillMessagePostFlags(action, peer, msgFlags);
|
||||||
|
|
||||||
|
if (action.options.scheduled) {
|
||||||
|
msgFlags |= MessageFlag::IsOrWasScheduled;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = fromIter, e = toIter; i != e; i++) {
|
||||||
|
const auto item = *i;
|
||||||
|
const auto media = item->media();
|
||||||
|
medias->push_back(media);
|
||||||
|
|
||||||
|
const auto inputMedia = media->photo()
|
||||||
|
? MTP_inputMediaPhoto(MTP_flags(0), media->photo()->mtpInput(), MTPint())
|
||||||
|
: MTP_inputMediaDocument(MTP_flags(0), media->document()->mtpInput(), MTPint(), MTPstring());
|
||||||
|
auto caption = (draft.options != Data::ForwardOptions::NoNamesAndCaptions)
|
||||||
|
? 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);
|
||||||
|
|
||||||
|
const auto newId = FullMsgId(
|
||||||
|
peer->id,
|
||||||
|
_session->data().nextLocalMessageId());
|
||||||
|
auto randomId = randomIds.takeFirst();
|
||||||
|
|
||||||
|
mediaInputs->push_back(MTP_inputSingleMedia(
|
||||||
|
MTP_flags(flags),
|
||||||
|
inputMedia,
|
||||||
|
MTP_long(randomId),
|
||||||
|
MTP_string(caption.text),
|
||||||
|
sentEntities));
|
||||||
|
|
||||||
|
_session->data().registerMessageRandomId(randomId, newId);
|
||||||
|
|
||||||
|
if (const auto photo = media->photo()) {
|
||||||
|
history->addNewLocalMessage(
|
||||||
|
newId.msg,
|
||||||
|
msgFlags,
|
||||||
|
0, // viaBotId
|
||||||
|
0, // replyTo
|
||||||
|
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||||
|
messageFromId,
|
||||||
|
messagePostAuthor,
|
||||||
|
photo,
|
||||||
|
caption,
|
||||||
|
HistoryMessageMarkupData(),
|
||||||
|
newGroupId);
|
||||||
|
} else if (const auto document = media->document()) {
|
||||||
|
history->addNewLocalMessage(
|
||||||
|
newId.msg,
|
||||||
|
msgFlags,
|
||||||
|
0, // viaBotId
|
||||||
|
0, // replyTo
|
||||||
|
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||||
|
messageFromId,
|
||||||
|
messagePostAuthor,
|
||||||
|
document,
|
||||||
|
caption,
|
||||||
|
HistoryMessageMarkupData(),
|
||||||
|
newGroupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
auto performRequest = [=, &histories](const auto &repeatRequest) -> void {
|
||||||
|
mediaRefs->clear();
|
||||||
|
for (auto i = medias->begin(), e = medias->end(); i != e; i++) {
|
||||||
|
const auto media = *i;
|
||||||
|
mediaRefs->push_back(media->photo()
|
||||||
|
? media->photo()->fileReference()
|
||||||
|
: media->document()->fileReference());
|
||||||
|
}
|
||||||
|
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||||
|
history->sendRequestId = request(MTPmessages_SendMultiMedia(
|
||||||
|
MTP_flags(finalFlags),
|
||||||
|
peer->input,
|
||||||
|
MTPint(),
|
||||||
|
MTP_vector<MTPInputSingleMedia>(*mediaInputs),
|
||||||
|
MTP_int(action.options.scheduled),
|
||||||
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
applyUpdates(result);
|
||||||
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
shared->callback();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
if (error.code() == 400
|
||||||
|
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||||
|
auto refreshRequests = mediaRefs->size();
|
||||||
|
auto index = 0;
|
||||||
|
auto wasUpdated = false;
|
||||||
|
for (auto i = medias->begin(), e = medias->end(); i != e; i++) {
|
||||||
|
const auto media = *i;
|
||||||
|
const auto origin = media->document()
|
||||||
|
? media->document()->stickerOrGifOrigin()
|
||||||
|
: Data::FileOrigin();
|
||||||
|
const auto usedFileReference = mediaRefs->value(index);
|
||||||
|
|
||||||
|
refreshFileReference(origin, [=, &refreshRequests, &wasUpdated](const auto &result) {
|
||||||
|
const auto currentMediaReference = media->photo()
|
||||||
|
? media->photo()->fileReference()
|
||||||
|
: media->document()->fileReference();
|
||||||
|
|
||||||
|
if (currentMediaReference != usedFileReference) {
|
||||||
|
wasUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshRequests > 0) {
|
||||||
|
refreshRequests--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasUpdated) {
|
||||||
|
repeatRequest(repeatRequest);
|
||||||
|
} else {
|
||||||
|
sendMessageFail(error, peer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendMessageFail(error, peer);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}).afterRequest(
|
||||||
|
history->sendRequestId
|
||||||
|
).send();
|
||||||
|
return history->sendRequestId;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
performRequest(performRequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardMediaUnquoted = [&] (not_null<HistoryItem *> item) {
|
||||||
|
if (shared) {
|
||||||
|
++shared->requestsLeft;
|
||||||
|
}
|
||||||
|
const auto media = item->media();
|
||||||
|
|
||||||
|
auto message = MessageToSend(action);
|
||||||
|
const auto caption = (draft.options != Data::ForwardOptions::NoNamesAndCaptions
|
||||||
|
&& !media->geoPoint()
|
||||||
|
&& !media->sharedContact())
|
||||||
|
? item->originalText()
|
||||||
|
: TextWithEntities();
|
||||||
|
|
||||||
|
message.textWithTags = TextWithTags{
|
||||||
|
caption.text,
|
||||||
|
TextUtilities::ConvertEntitiesToTextTags(caption.entities)
|
||||||
|
};
|
||||||
|
message.action.clearDraft = false;
|
||||||
|
|
||||||
|
auto doneCallback = [=] () {
|
||||||
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
shared->callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (media->poll()) {
|
||||||
|
const auto poll = *(media->poll());
|
||||||
|
_polls->create(poll,
|
||||||
|
message.action,
|
||||||
|
std::move(doneCallback),
|
||||||
|
nullptr);
|
||||||
|
} else if (media->geoPoint()) {
|
||||||
|
const auto location = *(media->geoPoint());
|
||||||
|
Api::SendLocationPoint(
|
||||||
|
location,
|
||||||
|
message.action,
|
||||||
|
std::move(doneCallback),
|
||||||
|
nullptr);
|
||||||
|
} else if (media->sharedContact()) {
|
||||||
|
const auto contact = media->sharedContact();
|
||||||
|
shareContact(
|
||||||
|
contact->phoneNumber,
|
||||||
|
contact->firstName,
|
||||||
|
contact->lastName,
|
||||||
|
message.action);
|
||||||
|
} else if (media->photo()) {
|
||||||
|
Api::SendExistingPhoto(
|
||||||
|
std::move(message),
|
||||||
|
media->photo(),
|
||||||
|
std::move(doneCallback),
|
||||||
|
true); // forwarding
|
||||||
|
} else if (media->document()) {
|
||||||
|
Api::SendExistingDocument(
|
||||||
|
std::move(message),
|
||||||
|
media->document(),
|
||||||
|
std::move(doneCallback),
|
||||||
|
true); // forwarding
|
||||||
|
} else {
|
||||||
|
Unexpected("Media type in ApiWrap::forwardMessages.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardDiceUnquoted = [&] (not_null<HistoryItem *> item) {
|
||||||
|
if (shared) {
|
||||||
|
++shared->requestsLeft;
|
||||||
|
}
|
||||||
|
const auto dice = dynamic_cast<Data::MediaDice*>(item->media());
|
||||||
|
if (!dice) {
|
||||||
|
Unexpected("Non-dice in ApiWrap::forwardMessages.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message = MessageToSend(action);
|
||||||
|
message.textWithTags.text = dice->emoji();
|
||||||
|
message.action.clearDraft = false;
|
||||||
|
|
||||||
|
Api::SendDice(message, [=] (const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
shared->callback();
|
||||||
|
}
|
||||||
|
}, true); // forwarding
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardMessageUnquoted = [&] (not_null<HistoryItem *> item) {
|
||||||
|
if (shared) {
|
||||||
|
++shared->requestsLeft;
|
||||||
|
}
|
||||||
|
const auto media = item->media();
|
||||||
|
|
||||||
|
const auto webPageId = (!media || !media->webpage())
|
||||||
|
? CancelledWebPageId
|
||||||
|
: media->webpage()->id;
|
||||||
|
|
||||||
|
auto message = MessageToSend(action);
|
||||||
|
message.textWithTags = TextWithTags{
|
||||||
|
item->originalText().text,
|
||||||
|
TextUtilities::ConvertEntitiesToTextTags(item->originalText().entities)
|
||||||
|
};
|
||||||
|
message.action.clearDraft = false;
|
||||||
|
message.webPageId = webPageId;
|
||||||
|
|
||||||
|
session().api().sendMessage(
|
||||||
|
std::move(message),
|
||||||
|
[=] (const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
shared->callback();
|
||||||
|
}
|
||||||
|
}, true); // forwarding
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto sendAccumulated = [&] {
|
||||||
|
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 (const auto dice = dynamic_cast<Data::MediaDice*>(media)) {
|
||||||
|
forwardDiceUnquoted(item);
|
||||||
|
} else if ((media->poll() && !history->peer->isUser())
|
||||||
|
|| media->geoPoint()
|
||||||
|
|| media->sharedContact()
|
||||||
|
|| media->photo()
|
||||||
|
|| media->document()) {
|
||||||
|
forwardMediaUnquoted(item);
|
||||||
|
} else {
|
||||||
|
forwardQuotedSingle(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
forwardMessageUnquoted(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.resize(0);
|
||||||
|
randomIds.resize(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
ids.reserve(count);
|
||||||
|
randomIds.reserve(count);
|
||||||
|
for (auto i = draft.items.begin(), e = draft.items.end(); i != e; /* ++i is in the end */) {
|
||||||
|
const auto item = *i;
|
||||||
|
const auto randomId = base::RandomValue<uint64>();
|
||||||
|
if (needNextGroup(item)) {
|
||||||
|
sendAccumulated();
|
||||||
|
messageGroupCount = 0;
|
||||||
|
forwardFrom = item->history()->peer;
|
||||||
|
currentGroupId = item->groupId();
|
||||||
|
fromIter = i;
|
||||||
|
}
|
||||||
|
ids.push_back(MTP_int(item->id));
|
||||||
|
randomIds.push_back(randomId);
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
lastGroup = ((item->media()->photo()
|
||||||
|
|| (item->media()->document()
|
||||||
|
&& item->media()->document()->isVideoFile()))
|
||||||
|
? LastGroupType::Medias
|
||||||
|
: (item->media()->document()
|
||||||
|
&& item->media()->document()->isSharedMediaMusic())
|
||||||
|
? LastGroupType::Music
|
||||||
|
: LastGroupType::Documents);
|
||||||
|
} else {
|
||||||
|
lastGroup = LastGroupType::None;
|
||||||
|
}
|
||||||
|
toIter = ++i;
|
||||||
|
messageGroupCount++;
|
||||||
|
}
|
||||||
|
sendAccumulated();
|
||||||
|
_session->data().sendHistoryChangeNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::shareContact(
|
void ApiWrap::shareContact(
|
||||||
const QString &phone,
|
const QString &phone,
|
||||||
const QString &firstName,
|
const QString &firstName,
|
||||||
|
|
@ -3433,7 +3928,10 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::sendMessage(MessageToSend &&message) {
|
void ApiWrap::sendMessage(
|
||||||
|
MessageToSend &&message,
|
||||||
|
Fn<void(const MTPUpdates &, mtpRequestId)> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto history = message.action.history;
|
const auto history = message.action.history;
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
auto &textWithTags = message.textWithTags;
|
auto &textWithTags = message.textWithTags;
|
||||||
|
|
@ -3442,7 +3940,12 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
action.generateLocal = true;
|
action.generateLocal = true;
|
||||||
sendAction(action);
|
sendAction(action);
|
||||||
|
|
||||||
if (!peer->canWrite() || Api::SendDice(message)) {
|
if (!peer->canWrite()
|
||||||
|
|| Api::SendDice(message, [=] (const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback(result, requestId);
|
||||||
|
}
|
||||||
|
}, forwarding)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
local().saveRecentSentHashtags(textWithTags.text);
|
local().saveRecentSentHashtags(textWithTags.text);
|
||||||
|
|
@ -3557,11 +4060,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback(result, response.requestId);
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
}).fail([=](
|
}).fail([=](
|
||||||
const MTP::Error &error,
|
const MTP::Error &error,
|
||||||
const MTP::Response &response) {
|
const MTP::Response &response) {
|
||||||
if (error.type() == qstr("MESSAGE_EMPTY")) {
|
if (error.type() == qstr("MESSAGE_EMPTY") && !forwarding) {
|
||||||
lastMessage->destroy();
|
lastMessage->destroy();
|
||||||
} else {
|
} else {
|
||||||
sendMessageFail(error, peer, randomId, newId);
|
sendMessageFail(error, peer, randomId, newId);
|
||||||
|
|
@ -3577,7 +4083,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
finishForwarding(action);
|
if (!forwarding) {
|
||||||
|
finishForwarding(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
|
void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,10 @@ public:
|
||||||
Data::ResolvedForwardDraft &&draft,
|
Data::ResolvedForwardDraft &&draft,
|
||||||
const SendAction &action,
|
const SendAction &action,
|
||||||
FnMut<void()> &&successCallback = nullptr);
|
FnMut<void()> &&successCallback = nullptr);
|
||||||
|
void forwardMessagesUnquoted(
|
||||||
|
Data::ResolvedForwardDraft &&draft,
|
||||||
|
const SendAction &action,
|
||||||
|
FnMut<void()> &&successCallback = nullptr);
|
||||||
void shareContact(
|
void shareContact(
|
||||||
const QString &phone,
|
const QString &phone,
|
||||||
const QString &firstName,
|
const QString &firstName,
|
||||||
|
|
@ -320,7 +324,10 @@ public:
|
||||||
|
|
||||||
void cancelLocalItem(not_null<HistoryItem*> item);
|
void cancelLocalItem(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
void sendMessage(MessageToSend &&message);
|
void sendMessage(
|
||||||
|
MessageToSend &&message,
|
||||||
|
Fn<void(const MTPUpdates &, mtpRequestId)> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
|
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
|
||||||
void sendInlineResult(
|
void sendInlineResult(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
|
|
|
||||||
|
|
@ -1117,7 +1117,8 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) {
|
||||||
std::vector<not_null<PeerData*>> &&result,
|
std::vector<not_null<PeerData*>> &&result,
|
||||||
TextWithTags &&comment,
|
TextWithTags &&comment,
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
Data::ForwardOptions) {
|
Data::ForwardOptions,
|
||||||
|
Data::GroupingOptions) {
|
||||||
if (*sending || result.empty()) {
|
if (*sending || result.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
|
#include "kotato/kotato_settings.h"
|
||||||
#include "dialogs/dialogs_indexed_list.h"
|
#include "dialogs/dialogs_indexed_list.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
@ -24,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/widgets/menu/menu_action.h"
|
#include "ui/widgets/menu/menu_action.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/widgets/dropdown_menu.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
|
|
@ -44,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_info.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
#include "styles/style_media_player.h"
|
#include "styles/style_media_player.h"
|
||||||
|
|
||||||
|
|
@ -58,27 +62,30 @@ public:
|
||||||
|
|
||||||
AbstractButton::setDisabled(true);
|
AbstractButton::setDisabled(true);
|
||||||
|
|
||||||
_checkView = std::make_unique<Ui::CheckView>(st::defaultCheck, false);
|
_checkView = std::make_unique<Ui::ToggleView>(st::defaultToggle, false);
|
||||||
_checkView->checkedChanges(
|
_checkView->checkedChanges(
|
||||||
) | rpl::start_with_next([=](bool checked) {
|
) | rpl::start_with_next([=](bool checked) {
|
||||||
setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr);
|
setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_checkView->setLocked(checked);
|
||||||
_checkView->setChecked(checked, anim::type::normal);
|
_checkView->setChecked(checked, anim::type::normal);
|
||||||
AbstractButton::clicks(
|
AbstractButton::clicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_checkView->setChecked(
|
if (!_checkView->isLocked()) {
|
||||||
!_checkView->checked(),
|
_checkView->setChecked(
|
||||||
anim::type::normal);
|
!_checkView->checked(),
|
||||||
|
anim::type::normal);
|
||||||
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Ui::CheckView*> checkView() const {
|
not_null<Ui::ToggleView*> checkView() const {
|
||||||
return _checkView.get();
|
return _checkView.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::CheckView> _checkView;
|
std::unique_ptr<Ui::ToggleView> _checkView;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -89,10 +96,13 @@ public:
|
||||||
|
|
||||||
void setPeerSelectedChangedCallback(
|
void setPeerSelectedChangedCallback(
|
||||||
Fn<void(PeerData *peer, bool selected)> callback);
|
Fn<void(PeerData *peer, bool selected)> callback);
|
||||||
|
void setSubmitRequest(Fn<void()> callback);
|
||||||
|
void setGoToChatRequest(Fn<void()> callback);
|
||||||
void peerUnselected(not_null<PeerData*> peer);
|
void peerUnselected(not_null<PeerData*> peer);
|
||||||
|
|
||||||
std::vector<not_null<PeerData*>> selected() const;
|
std::vector<not_null<PeerData*>> selected() const;
|
||||||
bool hasSelected() const;
|
bool hasSelected() const;
|
||||||
|
Fn<void()> goToChatRequest() const;
|
||||||
|
|
||||||
void peopleReceived(
|
void peopleReceived(
|
||||||
const QString &query,
|
const QString &query,
|
||||||
|
|
@ -104,6 +114,8 @@ public:
|
||||||
void activateSkipPage(int pageHeight, int direction);
|
void activateSkipPage(int pageHeight, int direction);
|
||||||
void updateFilter(QString filter = QString());
|
void updateFilter(QString filter = QString());
|
||||||
void selectActive();
|
void selectActive();
|
||||||
|
void tryGoToChat();
|
||||||
|
void selectionMade();
|
||||||
|
|
||||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||||
rpl::producer<> searchRequests() const;
|
rpl::producer<> searchRequests() const;
|
||||||
|
|
@ -183,6 +195,8 @@ private:
|
||||||
base::flat_set<not_null<PeerData*>> _selected;
|
base::flat_set<not_null<PeerData*>> _selected;
|
||||||
|
|
||||||
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
|
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
|
||||||
|
Fn<void()> _submitRequest;
|
||||||
|
Fn<void()> _goToChatRequest;
|
||||||
|
|
||||||
bool _searching = false;
|
bool _searching = false;
|
||||||
QString _lastQuery;
|
QString _lastQuery;
|
||||||
|
|
@ -192,6 +206,7 @@ private:
|
||||||
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
|
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
|
||||||
rpl::event_stream<> _searchRequests;
|
rpl::event_stream<> _searchRequests;
|
||||||
|
|
||||||
|
bool _hadSelection = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
ShareBox::ShareBox(QWidget*, Descriptor &&descriptor)
|
ShareBox::ShareBox(QWidget*, Descriptor &&descriptor)
|
||||||
|
|
@ -280,7 +295,30 @@ void ShareBox::prepare() {
|
||||||
_select->resizeToWidth(st::boxWideWidth);
|
_select->resizeToWidth(st::boxWideWidth);
|
||||||
Ui::SendPendingMoveResizeEvents(_select);
|
Ui::SendPendingMoveResizeEvents(_select);
|
||||||
|
|
||||||
setTitle(tr::lng_share_title());
|
setTitle(_descriptor.forwardOptions.isShare ? tr::lng_share_title() : tr::lng_selected_forward());
|
||||||
|
|
||||||
|
const auto forwardOptions = [] {
|
||||||
|
switch (::Kotato::JsonSettings::GetInt("forward_mode")) {
|
||||||
|
case 1: return Data::ForwardOptions::NoSenderNames;
|
||||||
|
case 2: return Data::ForwardOptions::NoNamesAndCaptions;
|
||||||
|
default: return Data::ForwardOptions::PreserveInfo;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto groupOptions = [] {
|
||||||
|
switch (::Kotato::JsonSettings::GetInt("forward_grouping_mode")) {
|
||||||
|
case 1: return Data::GroupingOptions::RegroupAll;
|
||||||
|
case 2: return Data::GroupingOptions::Separate;
|
||||||
|
default: return Data::GroupingOptions::GroupAsIs;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
_forwardOptions.hasCaptions = _descriptor.forwardOptions.hasCaptions;
|
||||||
|
_forwardOptions.dropNames = (forwardOptions != Data::ForwardOptions::PreserveInfo);
|
||||||
|
_forwardOptions.dropCaptions = (forwardOptions == Data::ForwardOptions::NoNamesAndCaptions);
|
||||||
|
_groupOptions = groupOptions;
|
||||||
|
|
||||||
|
updateAdditionalTitle();
|
||||||
|
|
||||||
_inner = setInnerWidget(
|
_inner = setInnerWidget(
|
||||||
object_ptr<Inner>(this, _descriptor),
|
object_ptr<Inner>(this, _descriptor),
|
||||||
|
|
@ -303,11 +341,22 @@ void ShareBox::prepare() {
|
||||||
});
|
});
|
||||||
_select->setResizedCallback([=] { updateScrollSkips(); });
|
_select->setResizedCallback([=] { updateScrollSkips(); });
|
||||||
_select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) {
|
_select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) {
|
||||||
if (modifiers.testFlag(Qt::ControlModifier)
|
if ((modifiers.testFlag(Qt::ControlModifier)
|
||||||
|
&& !::Kotato::JsonSettings::GetBool("forward_on_click"))
|
||||||
|| modifiers.testFlag(Qt::MetaModifier)) {
|
|| modifiers.testFlag(Qt::MetaModifier)) {
|
||||||
submit({});
|
submit({});
|
||||||
|
} else if (modifiers.testFlag(Qt::ShiftModifier)) {
|
||||||
|
if (_inner->selected().size() == 1 && _inner->goToChatRequest()) {
|
||||||
|
_inner->goToChatRequest()();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_inner->selectActive();
|
_inner->selectActive();
|
||||||
|
if (!modifiers.testFlag(Qt::ControlModifier)
|
||||||
|
|| ::Kotato::JsonSettings::GetBool("forward_on_click")) {
|
||||||
|
_inner->tryGoToChat();
|
||||||
|
} else {
|
||||||
|
_inner->selectionMade();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
|
|
@ -333,6 +382,17 @@ void ShareBox::prepare() {
|
||||||
innerSelectedChanged(peer, checked);
|
innerSelectedChanged(peer, checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_inner->setSubmitRequest([=] {
|
||||||
|
submit({});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_descriptor.goToChatCallback) {
|
||||||
|
_inner->setGoToChatRequest([=] {
|
||||||
|
const auto singleChat = _inner->selected().at(0);
|
||||||
|
goToChat(singleChat);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ui::Emoji::SuggestionsController::Init(
|
Ui::Emoji::SuggestionsController::Init(
|
||||||
getDelegate()->outerContainer(),
|
getDelegate()->outerContainer(),
|
||||||
_comment->entity(),
|
_comment->entity(),
|
||||||
|
|
@ -467,6 +527,8 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
|
||||||
_inner->activateSkipPage(contentHeight(), -1);
|
_inner->activateSkipPage(contentHeight(), -1);
|
||||||
} else if (e->key() == Qt::Key_PageDown) {
|
} else if (e->key() == Qt::Key_PageDown) {
|
||||||
_inner->activateSkipPage(contentHeight(), 1);
|
_inner->activateSkipPage(contentHeight(), 1);
|
||||||
|
} else if (e->key() == Qt::Key_Escape && !_select->getQuery().isEmpty()) {
|
||||||
|
_select->clearQuery();
|
||||||
} else {
|
} else {
|
||||||
BoxContent::keyPressEvent(e);
|
BoxContent::keyPressEvent(e);
|
||||||
}
|
}
|
||||||
|
|
@ -491,34 +553,6 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
|
||||||
}
|
}
|
||||||
_menu.emplace(parent, st::popupMenuWithIcons);
|
_menu.emplace(parent, st::popupMenuWithIcons);
|
||||||
|
|
||||||
if (_descriptor.forwardOptions.show) {
|
|
||||||
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
|
|
||||||
auto item = base::make_unique_q<ForwardOptionItem>(
|
|
||||||
_menu->menu(),
|
|
||||||
st::popupMenuWithIcons.menu,
|
|
||||||
new QAction(QString(), _menu->menu()),
|
|
||||||
nullptr,
|
|
||||||
nullptr);
|
|
||||||
std::move(
|
|
||||||
text
|
|
||||||
) | rpl::start_with_next([action = item->action()](QString text) {
|
|
||||||
action->setText(text);
|
|
||||||
}, item->lifetime());
|
|
||||||
item->init(checked);
|
|
||||||
const auto view = item->checkView();
|
|
||||||
_menu->addAction(std::move(item));
|
|
||||||
return view;
|
|
||||||
};
|
|
||||||
Ui::FillForwardOptions(
|
|
||||||
std::move(createView),
|
|
||||||
_descriptor.forwardOptions.messagesCount,
|
|
||||||
_forwardOptions,
|
|
||||||
[=](Ui::ForwardOptions value) { _forwardOptions = value; },
|
|
||||||
_menu->lifetime());
|
|
||||||
|
|
||||||
_menu->addSeparator();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = SendMenu::FillSendMenu(
|
const auto result = SendMenu::FillSendMenu(
|
||||||
_menu.get(),
|
_menu.get(),
|
||||||
sendMenuType(),
|
sendMenuType(),
|
||||||
|
|
@ -533,11 +567,20 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
|
||||||
|
|
||||||
void ShareBox::createButtons() {
|
void ShareBox::createButtons() {
|
||||||
clearButtons();
|
clearButtons();
|
||||||
|
if (!_descriptor.forwardOptions.isShare && _descriptor.forwardOptions.show) {
|
||||||
|
const auto moreButton = addTopButton(st::infoTopBarMenu);
|
||||||
|
moreButton->setClickedCallback([=] { showForwardMenu(moreButton.data()); });
|
||||||
|
}
|
||||||
|
|
||||||
if (_hasSelected) {
|
if (_hasSelected) {
|
||||||
|
if (_descriptor.goToChatCallback && _inner->selected().size() == 1) {
|
||||||
|
const auto singleChat = _inner->selected().at(0);
|
||||||
|
addLeftButton(rktr("ktg_forward_go_to_chat"), [=] { goToChat(singleChat); });
|
||||||
|
}
|
||||||
|
|
||||||
const auto send = addButton(tr::lng_share_confirm(), [=] {
|
const auto send = addButton(tr::lng_share_confirm(), [=] {
|
||||||
submit({});
|
submit({});
|
||||||
});
|
});
|
||||||
_forwardOptions.hasCaptions = _descriptor.forwardOptions.hasCaptions;
|
|
||||||
|
|
||||||
send->setAcceptBoth();
|
send->setAcceptBoth();
|
||||||
send->clicks(
|
send->clicks(
|
||||||
|
|
@ -552,6 +595,204 @@ void ShareBox::createButtons() {
|
||||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShareBox::showForwardMenu(not_null<Ui::IconButton*> button) {
|
||||||
|
if (_topMenu) {
|
||||||
|
_topMenu->hideAnimated(Ui::InnerDropdown::HideOption::IgnoreShow);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_topMenu = base::make_unique_q<Ui::DropdownMenu>(window());
|
||||||
|
const auto weak = _topMenu.get();
|
||||||
|
_topMenu->setHiddenCallback([=] {
|
||||||
|
weak->deleteLater();
|
||||||
|
if (_topMenu == weak) {
|
||||||
|
button->setForceRippled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_topMenu->setShowStartCallback([=] {
|
||||||
|
if (_topMenu == weak) {
|
||||||
|
button->setForceRippled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_topMenu->setHideStartCallback([=] {
|
||||||
|
if (_topMenu == weak) {
|
||||||
|
button->setForceRippled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button->installEventFilter(_topMenu);
|
||||||
|
|
||||||
|
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
|
||||||
|
auto item = base::make_unique_q<ForwardOptionItem>(
|
||||||
|
_topMenu->menu(),
|
||||||
|
st::popupMenuWithIcons.menu,
|
||||||
|
new QAction(QString(), _topMenu->menu()),
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
std::move(
|
||||||
|
text
|
||||||
|
) | rpl::start_with_next([action = item->action()](QString text) {
|
||||||
|
action->setText(text);
|
||||||
|
}, item->lifetime());
|
||||||
|
item->init(checked);
|
||||||
|
const auto view = item->checkView();
|
||||||
|
_topMenu->addAction(std::move(item));
|
||||||
|
return view;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto forwardOptions = (_forwardOptions.dropCaptions)
|
||||||
|
? Data::ForwardOptions::NoNamesAndCaptions
|
||||||
|
: _forwardOptions.dropNames
|
||||||
|
? Data::ForwardOptions::NoSenderNames
|
||||||
|
: Data::ForwardOptions::PreserveInfo;
|
||||||
|
|
||||||
|
const auto quoted = createView(
|
||||||
|
rktr("ktg_forward_menu_quoted"),
|
||||||
|
forwardOptions == Data::ForwardOptions::PreserveInfo);
|
||||||
|
const auto noNames = createView(
|
||||||
|
rktr("ktg_forward_menu_unquoted"),
|
||||||
|
forwardOptions == Data::ForwardOptions::NoSenderNames);
|
||||||
|
const auto noCaptions = createView(
|
||||||
|
rktr("ktg_forward_menu_uncaptioned"),
|
||||||
|
forwardOptions == Data::ForwardOptions::NoNamesAndCaptions);
|
||||||
|
|
||||||
|
const auto onForwardOptionChange = [=, this](int mode, bool value) {
|
||||||
|
if (value) {
|
||||||
|
quoted->setLocked(mode == 0 && value);
|
||||||
|
noNames->setLocked(mode == 1 && value);
|
||||||
|
noCaptions->setLocked(mode == 2 && value);
|
||||||
|
quoted->setChecked(quoted->isLocked(), anim::type::normal);
|
||||||
|
noNames->setChecked(noNames->isLocked(), anim::type::normal);
|
||||||
|
noCaptions->setChecked(noCaptions->isLocked(), anim::type::normal);
|
||||||
|
_forwardOptions.dropNames = (mode != 0 && value);
|
||||||
|
_forwardOptions.dropCaptions = (mode == 2 && value);
|
||||||
|
if (::Kotato::JsonSettings::GetBool("forward_remember_mode")) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_mode", mode);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
updateAdditionalTitle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quoted->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onForwardOptionChange(0, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
|
||||||
|
noNames->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onForwardOptionChange(1, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
|
||||||
|
noCaptions->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onForwardOptionChange(2, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
|
||||||
|
if (_descriptor.forwardOptions.hasMedia) {
|
||||||
|
_topMenu->addSeparator();
|
||||||
|
|
||||||
|
const auto groupAsIs = createView(
|
||||||
|
rktr("ktg_forward_menu_default_albums"),
|
||||||
|
_groupOptions == Data::GroupingOptions::GroupAsIs);
|
||||||
|
const auto groupAll = createView(
|
||||||
|
rktr("ktg_forward_menu_group_all_media"),
|
||||||
|
_groupOptions == Data::GroupingOptions::RegroupAll);
|
||||||
|
const auto groupNone = createView(
|
||||||
|
rktr("ktg_forward_menu_separate_messages"),
|
||||||
|
_groupOptions == Data::GroupingOptions::Separate);
|
||||||
|
|
||||||
|
const auto onGroupOptionChange = [=, this](int mode, bool value) {
|
||||||
|
if (value) {
|
||||||
|
groupAsIs->setLocked(mode == 0 && value);
|
||||||
|
groupAll->setLocked(mode == 1 && value);
|
||||||
|
groupNone->setLocked(mode == 2 && value);
|
||||||
|
groupAsIs->setChecked(groupAsIs->isLocked(), anim::type::normal);
|
||||||
|
groupAll->setChecked(groupAll->isLocked(), anim::type::normal);
|
||||||
|
groupNone->setChecked(groupNone->isLocked(), anim::type::normal);
|
||||||
|
_groupOptions = (mode == 2)
|
||||||
|
? Data::GroupingOptions::Separate
|
||||||
|
: (mode == 1)
|
||||||
|
? Data::GroupingOptions::RegroupAll
|
||||||
|
: Data::GroupingOptions::GroupAsIs;
|
||||||
|
if (::Kotato::JsonSettings::GetBool("forward_remember_mode")) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_grouping_mode", mode);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
updateAdditionalTitle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
groupAsIs->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onGroupOptionChange(0, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
|
||||||
|
groupAll->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onGroupOptionChange(1, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
|
||||||
|
groupNone->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool value) {
|
||||||
|
onGroupOptionChange(2, value);
|
||||||
|
}, _topMenu->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto parentTopLeft = window()->mapToGlobal(QPoint());
|
||||||
|
const auto buttonTopLeft = button->mapToGlobal(QPoint());
|
||||||
|
const auto parentRect = QRect(parentTopLeft, window()->size());
|
||||||
|
const auto buttonRect = QRect(buttonTopLeft, button->size());
|
||||||
|
_topMenu->move(
|
||||||
|
buttonRect.x() + buttonRect.width() - _topMenu->width() - parentRect.x(),
|
||||||
|
buttonRect.y() + buttonRect.height() - parentRect.y() - style::ConvertScale(18));
|
||||||
|
_topMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareBox::updateAdditionalTitle() {
|
||||||
|
if (!_descriptor.forwardOptions.show || _descriptor.forwardOptions.isShare) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
const auto forwardOptions = (_forwardOptions.dropCaptions)
|
||||||
|
? Data::ForwardOptions::NoNamesAndCaptions
|
||||||
|
: _forwardOptions.dropNames
|
||||||
|
? Data::ForwardOptions::NoSenderNames
|
||||||
|
: Data::ForwardOptions::PreserveInfo;
|
||||||
|
|
||||||
|
switch (forwardOptions) {
|
||||||
|
case Data::ForwardOptions::NoSenderNames:
|
||||||
|
result += ktr("ktg_forward_subtitle_unquoted");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Data::ForwardOptions::NoNamesAndCaptions:
|
||||||
|
result += ktr("ktg_forward_subtitle_uncaptioned");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_descriptor.forwardOptions.hasMedia
|
||||||
|
&& _groupOptions != Data::GroupingOptions::GroupAsIs) {
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
result += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_groupOptions) {
|
||||||
|
case Data::GroupingOptions::RegroupAll:
|
||||||
|
result += ktr("ktg_forward_subtitle_group_all_media");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Data::GroupingOptions::Separate:
|
||||||
|
result += ktr("ktg_forward_subtitle_separate_messages");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAdditionalTitle(rpl::single(result));
|
||||||
|
}
|
||||||
|
|
||||||
void ShareBox::applyFilterUpdate(const QString &query) {
|
void ShareBox::applyFilterUpdate(const QString &query) {
|
||||||
onScrollToY(0);
|
onScrollToY(0);
|
||||||
_inner->updateFilter(query);
|
_inner->updateFilter(query);
|
||||||
|
|
@ -591,7 +832,8 @@ void ShareBox::submit(Api::SendOptions options) {
|
||||||
_inner->selected(),
|
_inner->selected(),
|
||||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||||
options,
|
options,
|
||||||
forwardOptions);
|
forwardOptions,
|
||||||
|
_groupOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -612,14 +854,29 @@ void ShareBox::copyLink() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareBox::goToChat(not_null<PeerData*> peer) {
|
||||||
|
if (_descriptor.goToChatCallback) {
|
||||||
|
const auto forwardOptions = (_forwardOptions.hasCaptions
|
||||||
|
&& _forwardOptions.dropCaptions)
|
||||||
|
? Data::ForwardOptions::NoNamesAndCaptions
|
||||||
|
: _forwardOptions.dropNames
|
||||||
|
? Data::ForwardOptions::NoSenderNames
|
||||||
|
: Data::ForwardOptions::PreserveInfo;
|
||||||
|
_descriptor.goToChatCallback(
|
||||||
|
peer,
|
||||||
|
forwardOptions,
|
||||||
|
_groupOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ShareBox::selectedChanged() {
|
void ShareBox::selectedChanged() {
|
||||||
auto hasSelected = _inner->hasSelected();
|
auto hasSelected = _inner->hasSelected();
|
||||||
if (_hasSelected != hasSelected) {
|
if (_hasSelected != hasSelected) {
|
||||||
_hasSelected = hasSelected;
|
_hasSelected = hasSelected;
|
||||||
createButtons();
|
|
||||||
_comment->toggle(_hasSelected, anim::type::normal);
|
_comment->toggle(_hasSelected, anim::type::normal);
|
||||||
_comment->resizeToWidth(st::boxWideWidth);
|
_comment->resizeToWidth(st::boxWideWidth);
|
||||||
}
|
}
|
||||||
|
createButtons();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1036,6 +1293,11 @@ void ShareBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
if (e->button() == Qt::LeftButton) {
|
if (e->button() == Qt::LeftButton) {
|
||||||
updateUpon(e->pos());
|
updateUpon(e->pos());
|
||||||
changeCheckState(getChatAtIndex(_upon));
|
changeCheckState(getChatAtIndex(_upon));
|
||||||
|
if (!e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
tryGoToChat();
|
||||||
|
} else {
|
||||||
|
selectionMade();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1043,6 +1305,25 @@ void ShareBox::Inner::selectActive() {
|
||||||
changeCheckState(getChatAtIndex(_active > 0 ? _active : 0));
|
changeCheckState(getChatAtIndex(_active > 0 ? _active : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareBox::Inner::tryGoToChat() {
|
||||||
|
if (!_hadSelection
|
||||||
|
&& _selected.size() == 1) {
|
||||||
|
if (_submitRequest && _selected.front()->isSelf()) {
|
||||||
|
_submitRequest();
|
||||||
|
} else if (_goToChatRequest
|
||||||
|
&& ::Kotato::JsonSettings::GetBool("forward_on_click")) {
|
||||||
|
_goToChatRequest();
|
||||||
|
}
|
||||||
|
_hadSelection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareBox::Inner::selectionMade() {
|
||||||
|
if (!_hadSelection) {
|
||||||
|
_hadSelection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ShareBox::Inner::resizeEvent(QResizeEvent *e) {
|
void ShareBox::Inner::resizeEvent(QResizeEvent *e) {
|
||||||
_columnSkip = (width() - _columnCount * _st.item.checkbox.imageRadius * 2) / float64(_columnCount + 1);
|
_columnSkip = (width() - _columnCount * _st.item.checkbox.imageRadius * 2) / float64(_columnCount + 1);
|
||||||
_rowWidthReal = _st.item.checkbox.imageRadius * 2 + _columnSkip;
|
_rowWidthReal = _st.item.checkbox.imageRadius * 2 + _columnSkip;
|
||||||
|
|
@ -1083,6 +1364,14 @@ void ShareBox::Inner::setPeerSelectedChangedCallback(
|
||||||
_peerSelectedChangedCallback = std::move(callback);
|
_peerSelectedChangedCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareBox::Inner::setSubmitRequest(Fn<void()> callback) {
|
||||||
|
_submitRequest = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareBox::Inner::setGoToChatRequest(Fn<void()> callback) {
|
||||||
|
_goToChatRequest = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
void ShareBox::Inner::changePeerCheckState(
|
void ShareBox::Inner::changePeerCheckState(
|
||||||
not_null<Chat*> chat,
|
not_null<Chat*> chat,
|
||||||
bool checked,
|
bool checked,
|
||||||
|
|
@ -1104,6 +1393,10 @@ bool ShareBox::Inner::hasSelected() const {
|
||||||
return _selected.size();
|
return _selected.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fn<void()> ShareBox::Inner::goToChatRequest() const {
|
||||||
|
return _goToChatRequest;
|
||||||
|
}
|
||||||
|
|
||||||
void ShareBox::Inner::updateFilter(QString filter) {
|
void ShareBox::Inner::updateFilter(QString filter) {
|
||||||
_lastQuery = filter.toLower().trimmed();
|
_lastQuery = filter.toLower().trimmed();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,13 @@ class IndexedList;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
enum class ForwardOptions;
|
enum class ForwardOptions;
|
||||||
|
enum class GroupingOptions;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MultiSelect;
|
class MultiSelect;
|
||||||
class InputField;
|
class InputField;
|
||||||
|
class DropdownMenu;
|
||||||
struct ScrollToRequest;
|
struct ScrollToRequest;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class SlideWrap;
|
class SlideWrap;
|
||||||
|
|
@ -70,14 +72,20 @@ public:
|
||||||
std::vector<not_null<PeerData*>>&&,
|
std::vector<not_null<PeerData*>>&&,
|
||||||
TextWithTags&&,
|
TextWithTags&&,
|
||||||
Api::SendOptions,
|
Api::SendOptions,
|
||||||
Data::ForwardOptions option)>;
|
Data::ForwardOptions option,
|
||||||
|
Data::GroupingOptions groupOption)>;
|
||||||
using FilterCallback = Fn<bool(PeerData*)>;
|
using FilterCallback = Fn<bool(PeerData*)>;
|
||||||
|
using GoToChatCallback = Fn<void(
|
||||||
|
PeerData*,
|
||||||
|
Data::ForwardOptions option,
|
||||||
|
Data::GroupingOptions groupOption)>;
|
||||||
|
|
||||||
struct Descriptor {
|
struct Descriptor {
|
||||||
not_null<Main::Session*> session;
|
not_null<Main::Session*> session;
|
||||||
CopyCallback copyCallback;
|
CopyCallback copyCallback;
|
||||||
SubmitCallback submitCallback;
|
SubmitCallback submitCallback;
|
||||||
FilterCallback filterCallback;
|
FilterCallback filterCallback;
|
||||||
|
GoToChatCallback goToChatCallback;
|
||||||
Window::SessionNavigation *navigation = nullptr;
|
Window::SessionNavigation *navigation = nullptr;
|
||||||
Fn<void(not_null<Ui::InputField*>)> initSpellchecker;
|
Fn<void(not_null<Ui::InputField*>)> initSpellchecker;
|
||||||
Fn<void(not_null<Ui::InputField*>)> initEditLink;
|
Fn<void(not_null<Ui::InputField*>)> initEditLink;
|
||||||
|
|
@ -90,6 +98,8 @@ public:
|
||||||
int messagesCount = 0;
|
int messagesCount = 0;
|
||||||
bool show = false;
|
bool show = false;
|
||||||
bool hasCaptions = false;
|
bool hasCaptions = false;
|
||||||
|
bool hasMedia = false;
|
||||||
|
bool isShare = true;
|
||||||
} forwardOptions;
|
} forwardOptions;
|
||||||
};
|
};
|
||||||
ShareBox(QWidget*, Descriptor &&descriptor);
|
ShareBox(QWidget*, Descriptor &&descriptor);
|
||||||
|
|
@ -109,6 +119,7 @@ private:
|
||||||
void submitSilent();
|
void submitSilent();
|
||||||
void submitScheduled();
|
void submitScheduled();
|
||||||
void copyLink();
|
void copyLink();
|
||||||
|
void goToChat(not_null<PeerData*> peer);
|
||||||
bool searchByUsername(bool useCache = false);
|
bool searchByUsername(bool useCache = false);
|
||||||
|
|
||||||
SendMenu::Type sendMenuType() const;
|
SendMenu::Type sendMenuType() const;
|
||||||
|
|
@ -118,6 +129,8 @@ private:
|
||||||
void applyFilterUpdate(const QString &query);
|
void applyFilterUpdate(const QString &query);
|
||||||
void selectedChanged();
|
void selectedChanged();
|
||||||
void createButtons();
|
void createButtons();
|
||||||
|
bool showForwardMenu(not_null<Ui::IconButton*> button);
|
||||||
|
void updateAdditionalTitle();
|
||||||
int getTopScrollSkip() const;
|
int getTopScrollSkip() const;
|
||||||
int getBottomScrollSkip() const;
|
int getBottomScrollSkip() const;
|
||||||
int contentHeight() const;
|
int contentHeight() const;
|
||||||
|
|
@ -141,7 +154,9 @@ private:
|
||||||
object_ptr<Ui::RpWidget> _bottomWidget;
|
object_ptr<Ui::RpWidget> _bottomWidget;
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
base::unique_qptr<Ui::DropdownMenu> _topMenu;
|
||||||
Ui::ForwardOptions _forwardOptions;
|
Ui::ForwardOptions _forwardOptions;
|
||||||
|
Data::GroupingOptions _groupOptions;
|
||||||
|
|
||||||
class Inner;
|
class Inner;
|
||||||
QPointer<Inner> _inner;
|
QPointer<Inner> _inner;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/toasts/common_toasts.h"
|
#include "ui/toasts/common_toasts.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "history/history_message.h" // GetErrorTextForSending.
|
#include "history/history_message.h" // GetErrorTextForSending.
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
@ -130,7 +131,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
||||||
std::vector<not_null<PeerData*>> &&result,
|
std::vector<not_null<PeerData*>> &&result,
|
||||||
TextWithTags &&comment,
|
TextWithTags &&comment,
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
Data::ForwardOptions) {
|
Data::ForwardOptions,
|
||||||
|
Data::GroupingOptions) {
|
||||||
if (*sending || result.empty()) {
|
if (*sending || result.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1279,6 +1279,10 @@ void DocumentData::refreshFileReference(const QByteArray &value) {
|
||||||
_videoThumbnail.location.refreshFileReference(value);
|
_videoThumbnail.location.refreshFileReference(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString DocumentData::url() const {
|
||||||
|
return _url;
|
||||||
|
}
|
||||||
|
|
||||||
QString DocumentData::filename() const {
|
QString DocumentData::filename() const {
|
||||||
return _filename;
|
return _filename;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,7 @@ public:
|
||||||
// to (this) received from the server "same" document.
|
// to (this) received from the server "same" document.
|
||||||
void collectLocalData(not_null<DocumentData*> local);
|
void collectLocalData(not_null<DocumentData*> local);
|
||||||
|
|
||||||
|
[[nodiscard]] QString url() const;
|
||||||
[[nodiscard]] QString filename() const;
|
[[nodiscard]] QString filename() const;
|
||||||
[[nodiscard]] QString mimeString() const;
|
[[nodiscard]] QString mimeString() const;
|
||||||
[[nodiscard]] bool hasMimeType(QLatin1String mime) const;
|
[[nodiscard]] bool hasMimeType(QLatin1String mime) const;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
|
|
@ -322,6 +323,10 @@ PollData *Media::poll() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LocationPoint *Media::geoPoint() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool Media::uploading() const {
|
bool Media::uploading() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -382,7 +387,7 @@ bool Media::forceForwardedInfo() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Media::errorTextForForward(not_null<PeerData*> peer) const {
|
QString Media::errorTextForForward(not_null<PeerData*> peer, bool unquoted) const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -571,7 +576,7 @@ bool MediaPhoto::allowsEditMedia() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MediaPhoto::errorTextForForward(not_null<PeerData*> peer) const {
|
QString MediaPhoto::errorTextForForward(not_null<PeerData*> peer, bool unquoted) const {
|
||||||
return Data::RestrictionError(
|
return Data::RestrictionError(
|
||||||
peer,
|
peer,
|
||||||
ChatRestriction::SendMedia
|
ChatRestriction::SendMedia
|
||||||
|
|
@ -876,7 +881,7 @@ bool MediaFile::dropForwardedInfo() const {
|
||||||
return _document->isSong();
|
return _document->isSong();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MediaFile::errorTextForForward(not_null<PeerData*> peer) const {
|
QString MediaFile::errorTextForForward(not_null<PeerData*> peer, bool unquoted) const {
|
||||||
if (const auto sticker = _document->sticker()) {
|
if (const auto sticker = _document->sticker()) {
|
||||||
if (const auto error = Data::RestrictionError(
|
if (const auto error = Data::RestrictionError(
|
||||||
peer,
|
peer,
|
||||||
|
|
@ -1092,6 +1097,10 @@ Data::CloudImage *MediaLocation::location() const {
|
||||||
return _location;
|
return _location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LocationPoint *MediaLocation::geoPoint() const {
|
||||||
|
return &_point;
|
||||||
|
}
|
||||||
|
|
||||||
ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
|
ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
|
||||||
const auto type = tr::lng_maps_point(tr::now);
|
const auto type = tr::lng_maps_point(tr::now);
|
||||||
const auto hasMiniImages = false;
|
const auto hasMiniImages = false;
|
||||||
|
|
@ -1390,7 +1399,7 @@ TextForMimeData MediaGame::clipboardText() const {
|
||||||
return TextForMimeData();
|
return TextForMimeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MediaGame::errorTextForForward(not_null<PeerData*> peer) const {
|
QString MediaGame::errorTextForForward(not_null<PeerData*> peer, bool unquoted) const {
|
||||||
return Data::RestrictionError(
|
return Data::RestrictionError(
|
||||||
peer,
|
peer,
|
||||||
ChatRestriction::SendGames
|
ChatRestriction::SendGames
|
||||||
|
|
@ -1538,10 +1547,13 @@ TextForMimeData MediaPoll::clipboardText() const {
|
||||||
return TextForMimeData::Simple(text);
|
return TextForMimeData::Simple(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MediaPoll::errorTextForForward(not_null<PeerData*> peer) const {
|
QString MediaPoll::errorTextForForward(not_null<PeerData*> peer, bool unquoted) const {
|
||||||
if (_poll->publicVotes() && peer->isChannel() && !peer->isMegagroup()) {
|
if (_poll->publicVotes() && peer->isChannel() && !peer->isMegagroup()) {
|
||||||
return tr::lng_restricted_send_public_polls(tr::now);
|
return tr::lng_restricted_send_public_polls(tr::now);
|
||||||
}
|
}
|
||||||
|
if (unquoted && _poll->quiz() && !_poll->voted() && !_poll->closed()) {
|
||||||
|
return ktr("ktg_forward_quiz_unquoted");
|
||||||
|
}
|
||||||
return Data::RestrictionError(
|
return Data::RestrictionError(
|
||||||
peer,
|
peer,
|
||||||
ChatRestriction::SendPolls
|
ChatRestriction::SendPolls
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ public:
|
||||||
virtual const Invoice *invoice() const;
|
virtual const Invoice *invoice() const;
|
||||||
virtual Data::CloudImage *location() const;
|
virtual Data::CloudImage *location() const;
|
||||||
virtual PollData *poll() const;
|
virtual PollData *poll() const;
|
||||||
|
virtual const LocationPoint *geoPoint() const;
|
||||||
|
|
||||||
virtual bool uploading() const;
|
virtual bool uploading() const;
|
||||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||||
|
|
@ -111,7 +112,7 @@ public:
|
||||||
virtual bool forwardedBecomesUnread() const;
|
virtual bool forwardedBecomesUnread() const;
|
||||||
virtual bool dropForwardedInfo() const;
|
virtual bool dropForwardedInfo() const;
|
||||||
virtual bool forceForwardedInfo() const;
|
virtual bool forceForwardedInfo() const;
|
||||||
virtual QString errorTextForForward(not_null<PeerData*> peer) const;
|
virtual QString errorTextForForward(not_null<PeerData*> peer, bool unquoted = false) const;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool consumeMessageText(
|
[[nodiscard]] virtual bool consumeMessageText(
|
||||||
const TextWithEntities &text);
|
const TextWithEntities &text);
|
||||||
|
|
@ -166,7 +167,7 @@ public:
|
||||||
TextForMimeData clipboardText() const override;
|
TextForMimeData clipboardText() const override;
|
||||||
bool allowsEditCaption() const override;
|
bool allowsEditCaption() const override;
|
||||||
bool allowsEditMedia() const override;
|
bool allowsEditMedia() const override;
|
||||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
QString errorTextForForward(not_null<PeerData*> peer, bool unquoted = false) const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
|
@ -206,7 +207,7 @@ public:
|
||||||
bool allowsEditMedia() const override;
|
bool allowsEditMedia() const override;
|
||||||
bool forwardedBecomesUnread() const override;
|
bool forwardedBecomesUnread() const override;
|
||||||
bool dropForwardedInfo() const override;
|
bool dropForwardedInfo() const override;
|
||||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
QString errorTextForForward(not_null<PeerData*> peer, bool unquoted = false) const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
|
@ -264,6 +265,7 @@ public:
|
||||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
|
||||||
Data::CloudImage *location() const override;
|
Data::CloudImage *location() const override;
|
||||||
|
const LocationPoint *geoPoint() const override;
|
||||||
ItemPreview toPreview(ToPreviewOptions options) const override;
|
ItemPreview toPreview(ToPreviewOptions options) const override;
|
||||||
TextWithEntities notificationText() const override;
|
TextWithEntities notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
|
@ -364,7 +366,7 @@ public:
|
||||||
TextWithEntities notificationText() const override;
|
TextWithEntities notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
TextForMimeData clipboardText() const override;
|
TextForMimeData clipboardText() const override;
|
||||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
QString errorTextForForward(not_null<PeerData*> peer, bool unquoted = false) const override;
|
||||||
bool dropForwardedInfo() const override;
|
bool dropForwardedInfo() const override;
|
||||||
|
|
||||||
bool consumeMessageText(const TextWithEntities &text) override;
|
bool consumeMessageText(const TextWithEntities &text) override;
|
||||||
|
|
@ -426,7 +428,7 @@ public:
|
||||||
TextWithEntities notificationText() const override;
|
TextWithEntities notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
TextForMimeData clipboardText() const override;
|
TextForMimeData clipboardText() const override;
|
||||||
QString errorTextForForward(not_null<PeerData*> peer) const override;
|
QString errorTextForForward(not_null<PeerData*> peer, bool unquoted = false) const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,7 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
|
||||||
return Data::ResolvedForwardDraft{
|
return Data::ResolvedForwardDraft{
|
||||||
.items = owner().idsToItems(draft.ids),
|
.items = owner().idsToItems(draft.ids),
|
||||||
.options = draft.options,
|
.options = draft.options,
|
||||||
|
.groupOptions = draft.groupOptions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +361,7 @@ Data::ResolvedForwardDraft History::resolveForwardDraft() {
|
||||||
setForwardDraft({
|
setForwardDraft({
|
||||||
.ids = owner().itemsToIds(result.items),
|
.ids = owner().itemsToIds(result.items),
|
||||||
.options = result.options,
|
.options = result.options,
|
||||||
|
.groupOptions = result.groupOptions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -614,7 +616,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup) {
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId) {
|
||||||
return addNewItem(
|
return addNewItem(
|
||||||
makeMessage(
|
makeMessage(
|
||||||
id,
|
id,
|
||||||
|
|
@ -626,7 +629,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||||
postAuthor,
|
postAuthor,
|
||||||
document,
|
document,
|
||||||
caption,
|
caption,
|
||||||
std::move(markup)),
|
std::move(markup),
|
||||||
|
newGroupId),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -640,7 +644,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup) {
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId) {
|
||||||
return addNewItem(
|
return addNewItem(
|
||||||
makeMessage(
|
makeMessage(
|
||||||
id,
|
id,
|
||||||
|
|
@ -652,7 +657,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||||
postAuthor,
|
postAuthor,
|
||||||
photo,
|
photo,
|
||||||
caption,
|
caption,
|
||||||
std::move(markup)),
|
std::move(markup),
|
||||||
|
newGroupId),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,14 +51,22 @@ enum class ForwardOptions {
|
||||||
NoNamesAndCaptions,
|
NoNamesAndCaptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GroupingOptions {
|
||||||
|
GroupAsIs,
|
||||||
|
RegroupAll,
|
||||||
|
Separate,
|
||||||
|
};
|
||||||
|
|
||||||
struct ForwardDraft {
|
struct ForwardDraft {
|
||||||
MessageIdsList ids;
|
MessageIdsList ids;
|
||||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||||
|
GroupingOptions groupOptions = GroupingOptions::GroupAsIs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResolvedForwardDraft {
|
struct ResolvedForwardDraft {
|
||||||
HistoryItemsList items;
|
HistoryItemsList items;
|
||||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||||
|
GroupingOptions groupOptions = GroupingOptions::GroupAsIs;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
@ -193,7 +201,8 @@ public:
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup);
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId = 0);
|
||||||
not_null<HistoryItem*> addNewLocalMessage(
|
not_null<HistoryItem*> addNewLocalMessage(
|
||||||
MsgId id,
|
MsgId id,
|
||||||
MessageFlags flags,
|
MessageFlags flags,
|
||||||
|
|
@ -204,7 +213,8 @@ public:
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup);
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId = 0);
|
||||||
not_null<HistoryItem*> addNewLocalMessage(
|
not_null<HistoryItem*> addNewLocalMessage(
|
||||||
MsgId id,
|
MsgId id,
|
||||||
MessageFlags flags,
|
MessageFlags flags,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
#include "window/window_peer_menu.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
#include "mtproto/mtproto_config.h"
|
#include "mtproto/mtproto_config.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
@ -127,14 +128,15 @@ QString GetErrorTextForSending(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const HistoryItemsList &items,
|
const HistoryItemsList &items,
|
||||||
const TextWithTags &comment,
|
const TextWithTags &comment,
|
||||||
bool ignoreSlowmodeCountdown) {
|
bool ignoreSlowmodeCountdown,
|
||||||
|
bool unquoted) {
|
||||||
if (!peer->canWrite()) {
|
if (!peer->canWrite()) {
|
||||||
return tr::lng_forward_cant(tr::now);
|
return tr::lng_forward_cant(tr::now);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &item : items) {
|
for (const auto &item : items) {
|
||||||
if (const auto media = item->media()) {
|
if (const auto media = item->media()) {
|
||||||
const auto error = media->errorTextForForward(peer);
|
const auto error = media->errorTextForForward(peer, unquoted);
|
||||||
if (!error.isEmpty() && error != qstr("skip")) {
|
if (!error.isEmpty() && error != qstr("skip")) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
@ -189,6 +191,10 @@ QString GetErrorTextForSending(
|
||||||
}
|
}
|
||||||
|
|
||||||
void FastShareMessage(not_null<HistoryItem*> item) {
|
void FastShareMessage(not_null<HistoryItem*> item) {
|
||||||
|
Window::ShowForwardMessagesBox(
|
||||||
|
App::wnd()->sessionController(),
|
||||||
|
item->history()->owner().itemOrItsGroup(item));
|
||||||
|
/*
|
||||||
struct ShareData {
|
struct ShareData {
|
||||||
ShareData(not_null<PeerData*> peer, MessageIdsList &&ids)
|
ShareData(not_null<PeerData*> peer, MessageIdsList &&ids)
|
||||||
: peer(peer)
|
: peer(peer)
|
||||||
|
|
@ -372,6 +378,7 @@ void FastShareMessage(not_null<HistoryItem*> item) {
|
||||||
.hasCaptions = hasCaptions,
|
.hasCaptions = hasCaptions,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestDependentMessageData(
|
void RequestDependentMessageData(
|
||||||
|
|
@ -435,8 +442,9 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
|
||||||
QString GetErrorTextForSending(
|
QString GetErrorTextForSending(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const HistoryItemsList &items,
|
const HistoryItemsList &items,
|
||||||
bool ignoreSlowmodeCountdown) {
|
bool ignoreSlowmodeCountdown,
|
||||||
return GetErrorTextForSending(peer, items, {}, ignoreSlowmodeCountdown);
|
bool unquoted) {
|
||||||
|
return GetErrorTextForSending(peer, items, {}, ignoreSlowmodeCountdown, unquoted);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HistoryMessage::CreateConfig {
|
struct HistoryMessage::CreateConfig {
|
||||||
|
|
@ -717,7 +725,8 @@ HistoryMessage::HistoryMessage(
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup)
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId)
|
||||||
: HistoryItem(
|
: HistoryItem(
|
||||||
history,
|
history,
|
||||||
id,
|
id,
|
||||||
|
|
@ -733,6 +742,11 @@ HistoryMessage::HistoryMessage(
|
||||||
|
|
||||||
_media = std::make_unique<Data::MediaFile>(this, document);
|
_media = std::make_unique<Data::MediaFile>(this, document);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
|
|
||||||
|
if (newGroupId) {
|
||||||
|
setGroupId(
|
||||||
|
MessageGroupId::FromRaw(history->peer->id, newGroupId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryMessage::HistoryMessage(
|
HistoryMessage::HistoryMessage(
|
||||||
|
|
@ -746,7 +760,8 @@ HistoryMessage::HistoryMessage(
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup)
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId)
|
||||||
: HistoryItem(
|
: HistoryItem(
|
||||||
history,
|
history,
|
||||||
id,
|
id,
|
||||||
|
|
@ -762,6 +777,11 @@ HistoryMessage::HistoryMessage(
|
||||||
|
|
||||||
_media = std::make_unique<Data::MediaPhoto>(this, photo);
|
_media = std::make_unique<Data::MediaPhoto>(this, photo);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
|
|
||||||
|
if (newGroupId) {
|
||||||
|
setGroupId(
|
||||||
|
MessageGroupId::FromRaw(history->peer->id, newGroupId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryMessage::HistoryMessage(
|
HistoryMessage::HistoryMessage(
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,14 @@ void RequestDependentMessageData(
|
||||||
[[nodiscard]] QString GetErrorTextForSending(
|
[[nodiscard]] QString GetErrorTextForSending(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const HistoryItemsList &items,
|
const HistoryItemsList &items,
|
||||||
bool ignoreSlowmodeCountdown = false);
|
bool ignoreSlowmodeCountdown = false,
|
||||||
|
bool unquoted = false);
|
||||||
[[nodiscard]] QString GetErrorTextForSending(
|
[[nodiscard]] QString GetErrorTextForSending(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const HistoryItemsList &items,
|
const HistoryItemsList &items,
|
||||||
const TextWithTags &comment,
|
const TextWithTags &comment,
|
||||||
bool ignoreSlowmodeCountdown = false);
|
bool ignoreSlowmodeCountdown = false,
|
||||||
|
bool unquoted = false);
|
||||||
void FastShareMessage(not_null<HistoryItem*> item);
|
void FastShareMessage(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
class HistoryMessage final : public HistoryItem {
|
class HistoryMessage final : public HistoryItem {
|
||||||
|
|
@ -95,7 +97,8 @@ public:
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup); // local document
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId = 0); // local document
|
||||||
HistoryMessage(
|
HistoryMessage(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MsgId id,
|
MsgId id,
|
||||||
|
|
@ -107,7 +110,8 @@ public:
|
||||||
const QString &postAuthor,
|
const QString &postAuthor,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
const TextWithEntities &caption,
|
const TextWithEntities &caption,
|
||||||
HistoryMessageMarkupData &&markup); // local photo
|
HistoryMessageMarkupData &&markup,
|
||||||
|
uint64 newGroupId = 0); // local photo
|
||||||
HistoryMessage(
|
HistoryMessage(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MsgId id,
|
MsgId id,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_widget.h"
|
#include "history/history_widget.h"
|
||||||
|
|
||||||
#include "kotato/kotato_settings.h"
|
#include "kotato/kotato_settings.h"
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
#include "api/api_editing.h"
|
#include "api/api_editing.h"
|
||||||
#include "api/api_bot.h"
|
#include "api/api_bot.h"
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
|
|
@ -37,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/chat/forward_options_box.h"
|
//#include "ui/chat/forward_options_box.h"
|
||||||
#include "ui/chat/message_bar.h"
|
#include "ui/chat/message_bar.h"
|
||||||
#include "ui/chat/attach/attach_send_files_way.h"
|
#include "ui/chat/attach/attach_send_files_way.h"
|
||||||
#include "ui/chat/choose_send_as.h"
|
#include "ui/chat/choose_send_as.h"
|
||||||
|
|
@ -5822,12 +5823,61 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
st::historyReplyHeight).contains(e->pos());
|
st::historyReplyHeight).contains(e->pos());
|
||||||
if (_replyForwardPressed && !_fieldBarCancel->isHidden()) {
|
if (_replyForwardPressed && !_fieldBarCancel->isHidden()) {
|
||||||
updateField();
|
updateField();
|
||||||
|
} else if (_inReplyEditForward) {
|
||||||
|
if (readyToForward()) {
|
||||||
|
if (_toForward.items.empty() || e->button() != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto draft = std::move(_toForward);
|
||||||
|
session().data().cancelForwarding(_history);
|
||||||
|
auto list = session().data().itemsToIds(draft.items);
|
||||||
|
Window::ShowForwardMessagesBox(controller(), {
|
||||||
|
.ids = session().data().itemsToIds(draft.items),
|
||||||
|
.options = draft.options,
|
||||||
|
.groupOptions = draft.groupOptions,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
} else if (_inReplyEditForward) {
|
||||||
if (readyToForward()) {
|
if (readyToForward()) {
|
||||||
using Options = Data::ForwardOptions;
|
using Options = Data::ForwardOptions;
|
||||||
const auto now = _toForward.options;
|
using GroupingOptions = Data::GroupingOptions;
|
||||||
const auto count = _toForward.items.size();
|
const auto count = _toForward.items.size();
|
||||||
const auto dropNames = (now != Options::PreserveInfo);
|
const auto hasMediaToGroup = [&] {
|
||||||
|
if (count > 1) {
|
||||||
|
auto grouppableMediaCount = 0;
|
||||||
|
for (const auto item : _toForward.items) {
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
grouppableMediaCount++;
|
||||||
|
} else {
|
||||||
|
grouppableMediaCount = 0;
|
||||||
|
}
|
||||||
|
if (grouppableMediaCount > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
const auto hasCaptions = [&] {
|
const auto hasCaptions = [&] {
|
||||||
for (const auto item : _toForward.items) {
|
for (const auto item : _toForward.items) {
|
||||||
if (const auto media = item->media()) {
|
if (const auto media = item->media()) {
|
||||||
|
|
@ -5839,68 +5889,76 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
const auto hasOnlyForcedForwardedInfo = [&] {
|
const auto addForwardOption = [=](
|
||||||
if (hasCaptions) {
|
Options newOptions,
|
||||||
return false;
|
const QString &langKey,
|
||||||
}
|
int settingsKey) {
|
||||||
for (const auto item : _toForward.items) {
|
|
||||||
if (const auto media = item->media()) {
|
|
||||||
if (!media->forceForwardedInfo()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}();
|
|
||||||
const auto dropCaptions = (now == Options::NoNamesAndCaptions);
|
|
||||||
const auto weak = Ui::MakeWeak(this);
|
|
||||||
const auto changeRecipient = crl::guard(weak, [=] {
|
|
||||||
if (_toForward.items.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto draft = std::move(_toForward);
|
|
||||||
session().data().cancelForwarding(_history);
|
|
||||||
auto list = session().data().itemsToIds(draft.items);
|
|
||||||
Window::ShowForwardMessagesBox(controller(), {
|
|
||||||
.ids = session().data().itemsToIds(draft.items),
|
|
||||||
.options = draft.options,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (hasOnlyForcedForwardedInfo) {
|
|
||||||
changeRecipient();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto optionsChanged = crl::guard(weak, [=](
|
|
||||||
Ui::ForwardOptions options) {
|
|
||||||
const auto newOptions = (options.hasCaptions
|
|
||||||
&& options.dropCaptions)
|
|
||||||
? Options::NoNamesAndCaptions
|
|
||||||
: options.dropNames
|
|
||||||
? Options::NoSenderNames
|
|
||||||
: Options::PreserveInfo;
|
|
||||||
if (_history && _toForward.options != newOptions) {
|
if (_history && _toForward.options != newOptions) {
|
||||||
_toForward.options = newOptions;
|
_menu->addAction(ktr(langKey), [=] {
|
||||||
_history->setForwardDraft({
|
const auto error = GetErrorTextForSending(
|
||||||
.ids = session().data().itemsToIds(_toForward.items),
|
_history->peer,
|
||||||
.options = newOptions,
|
_toForward.items,
|
||||||
|
true,
|
||||||
|
newOptions != Options::PreserveInfo);
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
Ui::ShowMultilineToast({
|
||||||
|
.text = { error }
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_toForward.options = newOptions;
|
||||||
|
_history->setForwardDraft({
|
||||||
|
.ids = session().data().itemsToIds(_toForward.items),
|
||||||
|
.options = newOptions,
|
||||||
|
.groupOptions = _toForward.groupOptions,
|
||||||
|
});
|
||||||
|
updateField();
|
||||||
|
if (::Kotato::JsonSettings::GetBool("forward_remember_mode")) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_mode", settingsKey);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
updateField();
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
controller()->show(Box(
|
|
||||||
Ui::ForwardOptionsBox,
|
_menu = base::make_unique_q<Ui::PopupMenu>(this);
|
||||||
count,
|
|
||||||
Ui::ForwardOptions{
|
addForwardOption(Options::PreserveInfo, "ktg_forward_menu_quoted", 0);
|
||||||
.dropNames = dropNames,
|
addForwardOption(Options::NoSenderNames, "ktg_forward_menu_unquoted", 1);
|
||||||
.hasCaptions = hasCaptions,
|
if (hasCaptions) {
|
||||||
.dropCaptions = dropCaptions,
|
addForwardOption(Options::NoNamesAndCaptions, "ktg_forward_menu_uncaptioned", 2);
|
||||||
},
|
}
|
||||||
optionsChanged,
|
|
||||||
changeRecipient));
|
if (hasMediaToGroup && count > 1) {
|
||||||
} else {
|
const auto addGroupingOption = [=](
|
||||||
Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId());
|
GroupingOptions newOptions,
|
||||||
|
const QString &langKey,
|
||||||
|
int settingsKey) {
|
||||||
|
if (_history && _toForward.groupOptions != newOptions) {
|
||||||
|
_menu->addAction(ktr(langKey), [=] {
|
||||||
|
_toForward.groupOptions = newOptions;
|
||||||
|
_history->setForwardDraft({
|
||||||
|
.ids = session().data().itemsToIds(_toForward.items),
|
||||||
|
.options = _toForward.options,
|
||||||
|
.groupOptions = newOptions,
|
||||||
|
});
|
||||||
|
updateForwardingTexts();
|
||||||
|
updateField();
|
||||||
|
if (::Kotato::JsonSettings::GetBool("forward_remember_mode")) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_grouping_mode", settingsKey);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_menu->addSeparator();
|
||||||
|
addGroupingOption(GroupingOptions::GroupAsIs, "ktg_forward_menu_default_albums", 0);
|
||||||
|
addGroupingOption(GroupingOptions::RegroupAll, "ktg_forward_menu_group_all_media", 1);
|
||||||
|
addGroupingOption(GroupingOptions::Separate, "ktg_forward_menu_separate_messages", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_menu->popup(QCursor::pos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6464,9 +6522,15 @@ bool HistoryWidget::sendExistingDocument(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Api::SendExistingDocument(
|
if (document->hasRemoteLocation()) {
|
||||||
Api::MessageToSend(prepareSendAction(options)),
|
Api::SendExistingDocument(
|
||||||
document);
|
Api::MessageToSend(prepareSendAction(options)),
|
||||||
|
document);
|
||||||
|
} else {
|
||||||
|
Api::SendWebDocument(
|
||||||
|
Api::MessageToSend(prepareSendAction(options)),
|
||||||
|
document);
|
||||||
|
}
|
||||||
|
|
||||||
if (_fieldAutocomplete->stickersShown()) {
|
if (_fieldAutocomplete->stickersShown()) {
|
||||||
clearFieldText();
|
clearFieldText();
|
||||||
|
|
@ -7258,6 +7322,8 @@ void HistoryWidget::updateForwardingTexts() {
|
||||||
auto insertedPeers = base::flat_set<not_null<PeerData*>>();
|
auto insertedPeers = base::flat_set<not_null<PeerData*>>();
|
||||||
auto insertedNames = base::flat_set<QString>();
|
auto insertedNames = base::flat_set<QString>();
|
||||||
auto fullname = QString();
|
auto fullname = QString();
|
||||||
|
auto hasMediaToGroup = false;
|
||||||
|
auto grouppableMediaCount = 0;
|
||||||
auto names = std::vector<QString>();
|
auto names = std::vector<QString>();
|
||||||
names.reserve(_toForward.items.size());
|
names.reserve(_toForward.items.size());
|
||||||
for (const auto item : _toForward.items) {
|
for (const auto item : _toForward.items) {
|
||||||
|
|
@ -7278,8 +7344,20 @@ void HistoryWidget::updateForwardingTexts() {
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Corrupt forwarded information in message.");
|
Unexpected("Corrupt forwarded information in message.");
|
||||||
}
|
}
|
||||||
|
if (!hasMediaToGroup) {
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
grouppableMediaCount++;
|
||||||
|
} else {
|
||||||
|
grouppableMediaCount = 0;
|
||||||
|
}
|
||||||
|
if (grouppableMediaCount > 1) {
|
||||||
|
hasMediaToGroup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!keepNames) {
|
if (!keepCaptions) {
|
||||||
|
from = ktr("ktg_forward_sender_names_and_captions_removed");
|
||||||
|
} else if (!keepNames) {
|
||||||
from = tr::lng_forward_sender_names_removed(tr::now);
|
from = tr::lng_forward_sender_names_removed(tr::now);
|
||||||
} else if (names.size() > 2) {
|
} else if (names.size() > 2) {
|
||||||
from = tr::lng_forwarding_from(tr::now, lt_count, names.size() - 1, lt_user, names[0]);
|
from = tr::lng_forwarding_from(tr::now, lt_count, names.size() - 1, lt_user, names[0]);
|
||||||
|
|
@ -7296,8 +7374,19 @@ void HistoryWidget::updateForwardingTexts() {
|
||||||
.generateImages = false,
|
.generateImages = false,
|
||||||
}).text;
|
}).text;
|
||||||
} else {
|
} else {
|
||||||
text = Ui::Text::PlainLink(
|
auto forwardText = tr::lng_forward_messages(tr::now, lt_count, count);
|
||||||
tr::lng_forward_messages(tr::now, lt_count, count));
|
|
||||||
|
switch (_toForward.groupOptions) {
|
||||||
|
case Data::GroupingOptions::RegroupAll:
|
||||||
|
forwardText += ", " + ktr("ktg_forward_subtitle_group_all_media");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Data::GroupingOptions::Separate:
|
||||||
|
forwardText += ", " + ktr("ktg_forward_subtitle_separate_messages");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = Ui::Text::PlainLink(forwardText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());
|
_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,7 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void leaveEventHook(QEvent *e) override;
|
void leaveEventHook(QEvent *e) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
|
@ -797,6 +798,7 @@ private:
|
||||||
|
|
||||||
int _topDelta = 0;
|
int _topDelta = 0;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
rpl::event_stream<> _cancelRequests;
|
rpl::event_stream<> _cancelRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -427,6 +427,12 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
|
||||||
{ "profile_top_mute", {
|
{ "profile_top_mute", {
|
||||||
.type = SettingType::BoolSetting,
|
.type = SettingType::BoolSetting,
|
||||||
.defaultValue = false, }},
|
.defaultValue = false, }},
|
||||||
|
{ "forward_retain_selection", {
|
||||||
|
.type = SettingType::BoolSetting,
|
||||||
|
.defaultValue = false, }},
|
||||||
|
{ "forward_on_click", {
|
||||||
|
.type = SettingType::BoolSetting,
|
||||||
|
.defaultValue = false, }},
|
||||||
{ "auto_scroll_unfocused", {
|
{ "auto_scroll_unfocused", {
|
||||||
.type = SettingType::BoolSetting,
|
.type = SettingType::BoolSetting,
|
||||||
.defaultValue = false, }},
|
.defaultValue = false, }},
|
||||||
|
|
@ -436,6 +442,20 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
|
||||||
{ "telegram_sites_autologin", {
|
{ "telegram_sites_autologin", {
|
||||||
.type = SettingType::BoolSetting,
|
.type = SettingType::BoolSetting,
|
||||||
.defaultValue = true, }},
|
.defaultValue = true, }},
|
||||||
|
{ "forward_remember_mode", {
|
||||||
|
.type = SettingType::BoolSetting,
|
||||||
|
.defaultValue = true, }},
|
||||||
|
{ "forward_mode", {
|
||||||
|
.type = SettingType::IntSetting,
|
||||||
|
.defaultValue = 0,
|
||||||
|
.limitHandler = IntLimit(0, 2), }},
|
||||||
|
{ "forward_grouping_mode", {
|
||||||
|
.type = SettingType::IntSetting,
|
||||||
|
.defaultValue = 0,
|
||||||
|
.limitHandler = IntLimit(0, 2), }},
|
||||||
|
{ "forward_force_old_unquoted", {
|
||||||
|
.type = SettingType::BoolSetting,
|
||||||
|
.defaultValue = false, }},
|
||||||
};
|
};
|
||||||
|
|
||||||
using OldOptionKey = QString;
|
using OldOptionKey = QString;
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,54 @@ QString FileDialogTypeDescription(int value) {
|
||||||
return Platform::FileDialog::ImplementationTypeDescription(typedValue);
|
return Platform::FileDialog::ImplementationTypeDescription(typedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ForwardModeLabel(int mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
return ktr("ktg_forward_mode_quoted");
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return ktr("ktg_forward_mode_unquoted");
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return ktr("ktg_forward_mode_uncaptioned");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Unexpected("Boost in Settings::ForwardModeLabel.");
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GroupingModeLabel(int mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
return ktr("ktg_forward_grouping_mode_preserve_albums");
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return ktr("ktg_forward_grouping_mode_regroup");
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return ktr("ktg_forward_grouping_mode_separate");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Unexpected("Boost in Settings::GroupingModeLabel.");
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GroupingModeDescription(int mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return ktr("ktg_forward_grouping_mode_regroup_desc");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Unexpected("Boost in Settings::GroupingModeLabel.");
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
QString NetBoostLabel(int boost) {
|
QString NetBoostLabel(int boost) {
|
||||||
switch (boost) {
|
switch (boost) {
|
||||||
|
|
@ -394,6 +442,71 @@ void SetupKotatoForward(not_null<Ui::VerticalLayout*> container) {
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, rktr("ktg_settings_forward"));
|
AddSubsectionTitle(container, rktr("ktg_settings_forward"));
|
||||||
|
|
||||||
|
SettingsMenuJsonSwitch(ktg_forward_remember_mode, forward_remember_mode);
|
||||||
|
|
||||||
|
auto forwardModeText = rpl::single(
|
||||||
|
ForwardModeLabel(::Kotato::JsonSettings::GetInt("forward_mode"))
|
||||||
|
) | rpl::then(
|
||||||
|
::Kotato::JsonSettings::Events(
|
||||||
|
"forward_mode"
|
||||||
|
) | rpl::map([] {
|
||||||
|
return ForwardModeLabel(::Kotato::JsonSettings::GetInt("forward_mode"));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
AddButtonWithLabel(
|
||||||
|
container,
|
||||||
|
rktr("ktg_forward_mode"),
|
||||||
|
forwardModeText,
|
||||||
|
st::settingsButton
|
||||||
|
)->addClickHandler([=] {
|
||||||
|
Ui::show(Box<::Kotato::RadioBox>(
|
||||||
|
ktr("ktg_forward_mode"),
|
||||||
|
::Kotato::JsonSettings::GetInt("forward_mode"),
|
||||||
|
3,
|
||||||
|
ForwardModeLabel,
|
||||||
|
[=] (int value) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_mode", value);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}, false));
|
||||||
|
});
|
||||||
|
|
||||||
|
auto forwardGroupingModeText = rpl::single(
|
||||||
|
GroupingModeLabel(::Kotato::JsonSettings::GetInt("forward_grouping_mode"))
|
||||||
|
) | rpl::then(
|
||||||
|
::Kotato::JsonSettings::Events(
|
||||||
|
"forward_grouping_mode"
|
||||||
|
) | rpl::map([] {
|
||||||
|
return GroupingModeLabel(::Kotato::JsonSettings::GetInt("forward_grouping_mode"));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
AddButtonWithLabel(
|
||||||
|
container,
|
||||||
|
rktr("ktg_forward_grouping_mode"),
|
||||||
|
forwardGroupingModeText,
|
||||||
|
st::settingsButton
|
||||||
|
)->addClickHandler([=] {
|
||||||
|
Ui::show(Box<::Kotato::RadioBox>(
|
||||||
|
ktr("ktg_forward_grouping_mode"),
|
||||||
|
::Kotato::JsonSettings::GetInt("forward_grouping_mode"),
|
||||||
|
3,
|
||||||
|
GroupingModeLabel,
|
||||||
|
GroupingModeDescription,
|
||||||
|
[=] (int value) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_grouping_mode", value);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}, false));
|
||||||
|
});
|
||||||
|
|
||||||
|
SettingsMenuJsonSwitch(ktg_forward_force_old_unquoted, forward_force_old_unquoted);
|
||||||
|
|
||||||
|
AddSkip(container);
|
||||||
|
AddDividerText(container, rktr("ktg_forward_force_old_unquoted_desc"));
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
SettingsMenuJsonSwitch(ktg_settings_forward_retain_selection, forward_retain_selection);
|
||||||
|
SettingsMenuJsonSwitch(ktg_settings_forward_chat_on_click, forward_on_click);
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddDividerText(container, rktr("ktg_settings_forward_chat_on_click_description"));
|
AddDividerText(container, rktr("ktg_settings_forward_chat_on_click_description"));
|
||||||
|
|
|
||||||
|
|
@ -500,7 +500,8 @@ bool MainWidget::setForwardDraft(PeerId peerId, Data::ForwardDraft &&draft) {
|
||||||
const auto error = GetErrorTextForSending(
|
const auto error = GetErrorTextForSending(
|
||||||
peer,
|
peer,
|
||||||
session().data().idsToItems(draft.ids),
|
session().data().idsToItems(draft.ids),
|
||||||
true);
|
true,
|
||||||
|
draft.options != Data::ForwardOptions::PreserveInfo);
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
Ui::show(Box<Ui::InformBox>(error), Ui::LayerOption::KeepOther);
|
Ui::show(Box<Ui::InformBox>(error), Ui::LayerOption::KeepOther);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/pin_messages_box.h"
|
#include "boxes/pin_messages_box.h"
|
||||||
#include "boxes/peers/add_participants_box.h"
|
#include "boxes/peers/add_participants_box.h"
|
||||||
#include "boxes/peers/edit_contact_box.h"
|
#include "boxes/peers/edit_contact_box.h"
|
||||||
|
#include "boxes/share_box.h"
|
||||||
#include "ui/boxes/report_box.h"
|
#include "ui/boxes/report_box.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
|
@ -35,11 +36,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_blocked_peers.h"
|
#include "api/api_blocked_peers.h"
|
||||||
#include "api/api_chat_filters.h"
|
#include "api/api_chat_filters.h"
|
||||||
#include "api/api_polls.h"
|
#include "api/api_polls.h"
|
||||||
|
#include "api/api_sending.h"
|
||||||
#include "api/api_updates.h"
|
#include "api/api_updates.h"
|
||||||
|
#include "api/api_text_entities.h"
|
||||||
#include "mtproto/mtproto_config.h"
|
#include "mtproto/mtproto_config.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_message.h" // GetErrorTextForSending.
|
#include "history/history_message.h" // GetErrorTextForSending.
|
||||||
|
#include "history/history_widget.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
#include "window/window_adaptive.h" // Adaptive::isThreeColumn
|
#include "window/window_adaptive.h" // Adaptive::isThreeColumn
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
|
@ -56,9 +61,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "data/data_game.h"
|
||||||
|
#include "data/data_web_page.h"
|
||||||
#include "data/data_scheduled_messages.h"
|
#include "data/data_scheduled_messages.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
#include "dialogs/dialogs_key.h"
|
#include "dialogs/dialogs_key.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "export/export_manager.h"
|
#include "export/export_manager.h"
|
||||||
|
|
@ -68,6 +76,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_window.h" // st::windowMinWidth
|
#include "styles/style_window.h" // st::windowMinWidth
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtGui/QClipboard>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
@ -1086,6 +1096,7 @@ void BlockSenderFromRepliesBox(
|
||||||
Window::ClearReply{ id });
|
Window::ClearReply{ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
Data::ForwardDraft &&draft,
|
Data::ForwardDraft &&draft,
|
||||||
|
|
@ -1132,15 +1143,245 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
std::move(initBox)), Ui::LayerOption::KeepOther);
|
std::move(initBox)), Ui::LayerOption::KeepOther);
|
||||||
return weak->data();
|
return weak->data();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
|
Data::ForwardDraft &&draft,
|
||||||
|
FnMut<void()> &&successCallback) {
|
||||||
|
struct ShareData {
|
||||||
|
ShareData(not_null<PeerData*> peer, Data::ForwardDraft &&fwdDraft, FnMut<void()> &&callback)
|
||||||
|
: peer(peer)
|
||||||
|
, draft(std::move(fwdDraft))
|
||||||
|
, submitCallback(std::move(callback)) {
|
||||||
|
}
|
||||||
|
not_null<PeerData*> peer;
|
||||||
|
Data::ForwardDraft draft;
|
||||||
|
int requestsLeft = 0;
|
||||||
|
FnMut<void()> submitCallback;
|
||||||
|
};
|
||||||
|
const auto weak = std::make_shared<QPointer<ShareBox>>();
|
||||||
|
const auto firstItem = navigation->session().data().message(draft.ids[0]);
|
||||||
|
const auto history = firstItem->history();
|
||||||
|
const auto owner = &history->owner();
|
||||||
|
const auto session = &history->session();
|
||||||
|
const auto isGame = firstItem->getMessageBot()
|
||||||
|
&& firstItem->media()
|
||||||
|
&& (firstItem->media()->game() != nullptr);
|
||||||
|
|
||||||
|
const auto items = history->owner().idsToItems(draft.ids);
|
||||||
|
const auto hasCaptions = ranges::any_of(items, [](auto item) {
|
||||||
|
return item->media()
|
||||||
|
&& !item->originalText().text.isEmpty()
|
||||||
|
&& item->media()->allowsEditCaption();
|
||||||
|
});
|
||||||
|
const auto hasOnlyForcedForwardedInfo = hasCaptions
|
||||||
|
? false
|
||||||
|
: ranges::all_of(items, [](auto item) {
|
||||||
|
return item->media() && item->media()->forceForwardedInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto canCopyLink = [=] {
|
||||||
|
if (draft.ids.size() > 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto groupId = firstItem->groupId();
|
||||||
|
|
||||||
|
for (const auto item : items) {
|
||||||
|
if (groupId != item->groupId()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (firstItem->hasDirectLink() || isGame);
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto hasMediaForGrouping = [=] {
|
||||||
|
if (draft.ids.size() > 1) {
|
||||||
|
auto grouppableMediaCount = 0;
|
||||||
|
for (const auto item : items) {
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
grouppableMediaCount++;
|
||||||
|
} else {
|
||||||
|
grouppableMediaCount = 0;
|
||||||
|
}
|
||||||
|
if (grouppableMediaCount > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto data = std::make_shared<ShareData>(history->peer, std::move(draft), std::move(successCallback));
|
||||||
|
|
||||||
|
auto copyCallback = [=]() {
|
||||||
|
if (const auto item = owner->message(data->draft.ids[0])) {
|
||||||
|
if (item->hasDirectLink()) {
|
||||||
|
HistoryView::CopyPostLink(
|
||||||
|
session,
|
||||||
|
item->fullId(),
|
||||||
|
HistoryView::Context::History);
|
||||||
|
} else if (const auto bot = item->getMessageBot()) {
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto game = media->game()) {
|
||||||
|
const auto link = session->createInternalLinkFull(
|
||||||
|
bot->username
|
||||||
|
+ qsl("?game=")
|
||||||
|
+ game->shortName);
|
||||||
|
|
||||||
|
QGuiApplication::clipboard()->setText(link);
|
||||||
|
|
||||||
|
Ui::Toast::Show(tr::lng_share_game_link_copied(tr::now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto submitCallback = [=](
|
||||||
|
std::vector<not_null<PeerData*>> &&result,
|
||||||
|
TextWithTags &&comment,
|
||||||
|
Api::SendOptions options,
|
||||||
|
Data::ForwardOptions forwardOptions,
|
||||||
|
Data::GroupingOptions groupOptions) {
|
||||||
|
if (data->requestsLeft > 0) {
|
||||||
|
return; // Share clicked already.
|
||||||
|
}
|
||||||
|
auto items = history->owner().idsToItems(data->draft.ids);
|
||||||
|
if (items.empty() || result.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto error = [&] {
|
||||||
|
for (const auto peer : result) {
|
||||||
|
const auto error = GetErrorTextForSending(
|
||||||
|
peer,
|
||||||
|
items,
|
||||||
|
comment,
|
||||||
|
false, /* ignoreSlowmodeCountdown */
|
||||||
|
forwardOptions != Data::ForwardOptions::PreserveInfo);
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
return std::make_pair(error, peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_pair(QString(), result.front());
|
||||||
|
}();
|
||||||
|
if (!error.first.isEmpty()) {
|
||||||
|
auto text = TextWithEntities();
|
||||||
|
if (result.size() > 1) {
|
||||||
|
text.append(
|
||||||
|
Ui::Text::Bold(error.second->name)
|
||||||
|
).append("\n\n");
|
||||||
|
}
|
||||||
|
text.append(error.first);
|
||||||
|
Ui::show(
|
||||||
|
Box<Ui::InformBox>(text),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto checkAndClose = [=] {
|
||||||
|
data->requestsLeft--;
|
||||||
|
if (!data->requestsLeft) {
|
||||||
|
Ui::Toast::Show(tr::lng_share_done(tr::now));
|
||||||
|
Ui::hideLayer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto &api = owner->session().api();
|
||||||
|
|
||||||
|
data->draft.options = forwardOptions;
|
||||||
|
data->draft.groupOptions = groupOptions;
|
||||||
|
|
||||||
|
for (const auto peer : result) {
|
||||||
|
const auto history = owner->history(peer);
|
||||||
|
auto action = Api::SendAction(history);
|
||||||
|
action.options = options;
|
||||||
|
action.clearDraft = false;
|
||||||
|
|
||||||
|
if (!comment.text.isEmpty()) {
|
||||||
|
auto message = ApiWrap::MessageToSend(action);
|
||||||
|
message.textWithTags = comment;
|
||||||
|
api.sendMessage(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
data->requestsLeft++;
|
||||||
|
auto resolved = history->resolveForwardDraft(data->draft);
|
||||||
|
|
||||||
|
api.forwardMessages(std::move(resolved), action, [=] {
|
||||||
|
checkAndClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data->submitCallback
|
||||||
|
&& !::Kotato::JsonSettings::GetBool("forward_retain_selection")) {
|
||||||
|
data->submitCallback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto filterCallback = [](PeerData *peer) {
|
||||||
|
return peer->canWrite();
|
||||||
|
};
|
||||||
|
auto copyLinkCallback = canCopyLink
|
||||||
|
? Fn<void()>(std::move(copyCallback))
|
||||||
|
: Fn<void()>();
|
||||||
|
auto goToChatCallback = [navigation, data](
|
||||||
|
PeerData *peer,
|
||||||
|
Data::ForwardOptions forwardOptions,
|
||||||
|
Data::GroupingOptions groupOptions) {
|
||||||
|
if (data->submitCallback
|
||||||
|
&& !::Kotato::JsonSettings::GetBool("forward_retain_selection")) {
|
||||||
|
data->submitCallback();
|
||||||
|
}
|
||||||
|
data->draft.options = forwardOptions;
|
||||||
|
data->draft.groupOptions = groupOptions;
|
||||||
|
navigation->parentController()->content()->setForwardDraft(peer->id, std::move(data->draft));
|
||||||
|
};
|
||||||
|
*weak = Ui::show(
|
||||||
|
Box<ShareBox>(ShareBox::Descriptor{
|
||||||
|
.session = session,
|
||||||
|
.copyCallback = std::move(copyLinkCallback),
|
||||||
|
.submitCallback = std::move(submitCallback),
|
||||||
|
.filterCallback = std::move(filterCallback),
|
||||||
|
.goToChatCallback = std::move(goToChatCallback),
|
||||||
|
.navigation = navigation,
|
||||||
|
.forwardOptions = {
|
||||||
|
.messagesCount = int(draft.ids.size()),
|
||||||
|
.show = !hasOnlyForcedForwardedInfo,
|
||||||
|
.hasCaptions = hasCaptions,
|
||||||
|
.hasMedia = hasMediaForGrouping,
|
||||||
|
.isShare = false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
return weak->data();
|
||||||
|
}
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
MessageIdsList &&items,
|
MessageIdsList &&items,
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
|
const auto options = [] {
|
||||||
|
switch (::Kotato::JsonSettings::GetInt("forward_mode")) {
|
||||||
|
case 1: return Data::ForwardOptions::NoSenderNames;
|
||||||
|
case 2: return Data::ForwardOptions::NoNamesAndCaptions;
|
||||||
|
default: return Data::ForwardOptions::PreserveInfo;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto groupOptions = [] {
|
||||||
|
switch (::Kotato::JsonSettings::GetInt("forward_grouping_mode")) {
|
||||||
|
case 1: return Data::GroupingOptions::RegroupAll;
|
||||||
|
case 2: return Data::GroupingOptions::Separate;
|
||||||
|
default: return Data::GroupingOptions::GroupAsIs;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
return ShowForwardMessagesBox(
|
return ShowForwardMessagesBox(
|
||||||
navigation,
|
navigation,
|
||||||
Data::ForwardDraft{ .ids = std::move(items) },
|
Data::ForwardDraft{
|
||||||
std::move(successCallback));
|
.ids = std::move(items),
|
||||||
|
.options = options,
|
||||||
|
.groupOptions = groupOptions,
|
||||||
|
}, std::move(successCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
|
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
|
||||||
|
|
|
||||||
|
|
@ -172,8 +172,8 @@ PRIVATE
|
||||||
ui/chat/chat_theme.h
|
ui/chat/chat_theme.h
|
||||||
ui/chat/continuous_scroll.cpp
|
ui/chat/continuous_scroll.cpp
|
||||||
ui/chat/continuous_scroll.h
|
ui/chat/continuous_scroll.h
|
||||||
ui/chat/forward_options_box.cpp
|
#ui/chat/forward_options_box.cpp
|
||||||
ui/chat/forward_options_box.h
|
#ui/chat/forward_options_box.h
|
||||||
ui/chat/group_call_bar.cpp
|
ui/chat/group_call_bar.cpp
|
||||||
ui/chat/group_call_bar.h
|
ui/chat/group_call_bar.h
|
||||||
ui/chat/group_call_userpics.cpp
|
ui/chat/group_call_userpics.cpp
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue