mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-29 13:47:47 +00:00
[Option][GUI] Unquoted forward and forward options
This commit is contained in:
parent
fc8feea983
commit
8c65b8cedd
@ -105,7 +105,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",
|
||||||
@ -127,6 +141,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"
|
||||||
@ -68,7 +69,9 @@ void SendExistingMedia(
|
|||||||
not_null<MediaData*> media,
|
not_null<MediaData*> media,
|
||||||
Fn<MTPInputMedia()> inputMedia,
|
Fn<MTPInputMedia()> inputMedia,
|
||||||
Data::FileOrigin origin,
|
Data::FileOrigin origin,
|
||||||
std::optional<MsgId> localMessageId) {
|
std::optional<MsgId> localMessageId,
|
||||||
|
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();
|
||||||
@ -146,7 +149,6 @@ void SendExistingMedia(
|
|||||||
|
|
||||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
const auto usedFileReference = media->fileReference();
|
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
history,
|
history,
|
||||||
message.action.replyTo,
|
message.action.replyTo,
|
||||||
@ -166,6 +168,7 @@ void SendExistingMedia(
|
|||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
if (error.code() == 400
|
if (error.code() == 400
|
||||||
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
||||||
|
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);
|
||||||
@ -180,15 +183,41 @@ void SendExistingMedia(
|
|||||||
};
|
};
|
||||||
performRequest(performRequest);
|
performRequest(performRequest);
|
||||||
|
|
||||||
|
if (!forwarding) {
|
||||||
api->finishForwarding(message.action);
|
api->finishForwarding(message.action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void SendWebDocument(
|
||||||
|
Api::MessageToSend &&message,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
std::optional<MsgId> localMessageId,
|
||||||
|
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(),
|
||||||
|
std::move(localMessageId),
|
||||||
|
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||||
|
forwarding);
|
||||||
|
}
|
||||||
|
|
||||||
void SendExistingDocument(
|
void SendExistingDocument(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
std::optional<MsgId> localMessageId) {
|
std::optional<MsgId> localMessageId,
|
||||||
|
Fn<void()> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto inputMedia = [=] {
|
const auto inputMedia = [=] {
|
||||||
return MTP_inputMediaDocument(
|
return MTP_inputMediaDocument(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
@ -201,7 +230,9 @@ void SendExistingDocument(
|
|||||||
document,
|
document,
|
||||||
inputMedia,
|
inputMedia,
|
||||||
document->stickerOrGifOrigin(),
|
document->stickerOrGifOrigin(),
|
||||||
std::move(localMessageId));
|
std::move(localMessageId),
|
||||||
|
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||||
|
forwarding);
|
||||||
|
|
||||||
if (document->sticker()) {
|
if (document->sticker()) {
|
||||||
document->owner().stickers().incrementSticker(document);
|
document->owner().stickers().incrementSticker(document);
|
||||||
@ -211,7 +242,9 @@ void SendExistingDocument(
|
|||||||
void SendExistingPhoto(
|
void SendExistingPhoto(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
std::optional<MsgId> localMessageId) {
|
std::optional<MsgId> localMessageId,
|
||||||
|
Fn<void()> doneCallback,
|
||||||
|
bool forwarding) {
|
||||||
const auto inputMedia = [=] {
|
const auto inputMedia = [=] {
|
||||||
return MTP_inputMediaPhoto(
|
return MTP_inputMediaPhoto(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
@ -223,10 +256,15 @@ void SendExistingPhoto(
|
|||||||
photo,
|
photo,
|
||||||
inputMedia,
|
inputMedia,
|
||||||
Data::FileOrigin(),
|
Data::FileOrigin(),
|
||||||
std::move(localMessageId));
|
std::move(localMessageId),
|
||||||
|
(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)
|
||||||
@ -330,7 +368,9 @@ bool SendDice(MessageToSend &message) {
|
|||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
api->sendMessageFail(error, peer, randomId, newId);
|
api->sendMessageFail(error, peer, randomId, newId);
|
||||||
});
|
});
|
||||||
|
if (!forwarding) {
|
||||||
api->finishForwarding(message.action);
|
api->finishForwarding(message.action);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,4 +555,73 @@ 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;
|
||||||
|
}
|
||||||
|
const auto topicRootId = action.replyTo.messageId
|
||||||
|
? action.replyTo.topicRootId
|
||||||
|
: 0;
|
||||||
|
if (action.clearDraft) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||||
|
history->clearLocalDraft(topicRootId);
|
||||||
|
history->clearCloudDraft(topicRootId);
|
||||||
|
}
|
||||||
|
const auto sendAs = action.options.sendAs;
|
||||||
|
|
||||||
|
if (sendAs) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||||
|
}
|
||||||
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
|
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) {
|
||||||
|
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||||
|
MTP_flags(sendFlags),
|
||||||
|
peer->input,
|
||||||
|
action.mtpReplyTo(),
|
||||||
|
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,22 +16,44 @@ 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,
|
||||||
|
std::optional<MsgId> localMessageId = std::nullopt,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
void SendExistingDocument(
|
void SendExistingDocument(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
std::optional<MsgId> localMessageId = std::nullopt);
|
std::optional<MsgId> localMessageId = std::nullopt,
|
||||||
|
Fn<void()> doneCallback = nullptr,
|
||||||
|
bool forwarding = false);
|
||||||
|
|
||||||
void SendExistingPhoto(
|
void SendExistingPhoto(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
std::optional<MsgId> localMessageId = std::nullopt);
|
std::optional<MsgId> localMessageId = std::nullopt,
|
||||||
|
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,
|
||||||
@ -42,4 +64,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"
|
||||||
@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "api/api_websites.h"
|
#include "api/api_websites.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.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_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
@ -3170,6 +3172,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();
|
||||||
@ -3229,6 +3237,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>>();
|
||||||
@ -3307,9 +3316,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));
|
||||||
@ -3318,6 +3332,501 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
||||||
|
const auto topicRootId = action.replyTo.topicRootId;
|
||||||
|
const auto topMsgId = (topicRootId == kGeneralId)
|
||||||
|
? MsgId(0)
|
||||||
|
: topicRootId;
|
||||||
|
if (topMsgId) {
|
||||||
|
sendFlags |= MTPmessages_ForwardMessages::Flag::f_top_msg_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(topMsgId),
|
||||||
|
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
|
||||||
|
action.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
|
||||||
|
action.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,
|
||||||
|
action.mtpReplyTo(),
|
||||||
|
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::nullopt,
|
||||||
|
std::move(doneCallback),
|
||||||
|
true); // forwarding
|
||||||
|
} else if (media->document()) {
|
||||||
|
Api::SendExistingDocument(
|
||||||
|
std::move(message),
|
||||||
|
media->document(),
|
||||||
|
std::nullopt,
|
||||||
|
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 webpage = (!item->media() || !item->media()->webpage())
|
||||||
|
? Data::WebPageDraft{ .removed = true }
|
||||||
|
: Data::WebPageDraft{
|
||||||
|
.id = item->media()->webpage()->id,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto message = MessageToSend(action);
|
||||||
|
message.textWithTags = TextWithTags{
|
||||||
|
item->originalText().text,
|
||||||
|
TextUtilities::ConvertEntitiesToTextTags(item->originalText().entities)
|
||||||
|
};
|
||||||
|
message.action.clearDraft = false;
|
||||||
|
message.webPage = webpage;
|
||||||
|
|
||||||
|
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,
|
||||||
@ -3570,7 +4079,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;
|
||||||
@ -3591,7 +4103,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||||||
: Data::ForumTopic::kGeneralId;
|
: Data::ForumTopic::kGeneralId;
|
||||||
const auto topic = peer->forumTopicFor(topicRootId);
|
const auto topic = peer->forumTopicFor(topicRootId);
|
||||||
if (!(topic ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer))
|
if (!(topic ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer))
|
||||||
|| Api::SendDice(message)) {
|
|| Api::SendDice(message, [=] (const MTPUpdates &result, mtpRequestId requestId) {
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback(result, requestId);
|
||||||
|
}
|
||||||
|
}, forwarding)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
local().saveRecentSentHashtags(textWithTags.text);
|
local().saveRecentSentHashtags(textWithTags.text);
|
||||||
@ -3731,6 +4247,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||||||
draftTopicRootId,
|
draftTopicRootId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
|
if (doneCallback) {
|
||||||
|
doneCallback(result, response.requestId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const auto fail = [=](
|
const auto fail = [=](
|
||||||
const MTP::Error &error,
|
const MTP::Error &error,
|
||||||
@ -3785,7 +4304,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!forwarding) {
|
||||||
finishForwarding(action);
|
finishForwarding(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::sendBotStart(
|
void ApiWrap::sendBotStart(
|
||||||
|
@ -287,6 +287,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,
|
||||||
@ -334,7 +338,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(
|
void sendBotStart(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
PeerData *chat = nullptr,
|
PeerData *chat = nullptr,
|
||||||
|
@ -1154,7 +1154,8 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
|||||||
std::vector<not_null<Data::Thread*>> &&result,
|
std::vector<not_null<Data::Thread*>> &&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 "api/api_premium.h"
|
#include "api/api_premium.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
@ -17,20 +19,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/multi_select.h"
|
#include "ui/widgets/multi_select.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/fields/input_field.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 "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
#include "menu/menu_check_item.h"
|
|
||||||
#include "menu/menu_send.h"
|
#include "menu/menu_send.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_item_helpers.h"
|
#include "history/history_item_helpers.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
||||||
|
#include "window/window_peer_menu.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
@ -51,11 +54,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.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 <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ForwardOptionItem final : public Ui::Menu::Action {
|
||||||
|
public:
|
||||||
|
using Ui::Menu::Action::Action;
|
||||||
|
|
||||||
|
void init(bool checked) {
|
||||||
|
enableMouseSelecting();
|
||||||
|
|
||||||
|
AbstractButton::setDisabled(true);
|
||||||
|
|
||||||
|
_checkView = std::make_unique<Ui::ToggleView>(st::defaultToggle, false);
|
||||||
|
_checkView->checkedChanges(
|
||||||
|
) | rpl::start_with_next([=](bool checked) {
|
||||||
|
setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_checkView->setLocked(checked);
|
||||||
|
_checkView->setChecked(checked, anim::type::normal);
|
||||||
|
AbstractButton::clicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (!_checkView->isLocked()) {
|
||||||
|
_checkView->setChecked(
|
||||||
|
!_checkView->checked(),
|
||||||
|
anim::type::normal);
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<Ui::ToggleView*> checkView() const {
|
||||||
|
return _checkView.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::ToggleView> _checkView;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class ShareBox::Inner final : public Ui::RpWidget {
|
class ShareBox::Inner final : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
Inner(
|
Inner(
|
||||||
@ -65,6 +109,8 @@ public:
|
|||||||
|
|
||||||
void setPeerSelectedChangedCallback(
|
void setPeerSelectedChangedCallback(
|
||||||
Fn<void(not_null<Data::Thread*> thread, bool selected)> callback);
|
Fn<void(not_null<Data::Thread*> thread, 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);
|
||||||
|
|
||||||
[[nodiscard]] std::vector<not_null<Data::Thread*>> selected() const;
|
[[nodiscard]] std::vector<not_null<Data::Thread*>> selected() const;
|
||||||
@ -80,6 +126,9 @@ 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();
|
||||||
|
Fn<void()> goToChatRequest() const;
|
||||||
|
|
||||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||||
rpl::producer<> searchRequests() const;
|
rpl::producer<> searchRequests() const;
|
||||||
@ -172,6 +221,8 @@ private:
|
|||||||
base::flat_set<not_null<Data::Thread*>> _selected;
|
base::flat_set<not_null<Data::Thread*>> _selected;
|
||||||
|
|
||||||
Fn<void(not_null<Data::Thread*>, bool)> _peerSelectedChangedCallback;
|
Fn<void(not_null<Data::Thread*>, bool)> _peerSelectedChangedCallback;
|
||||||
|
Fn<void()> _submitRequest;
|
||||||
|
Fn<void()> _goToChatRequest;
|
||||||
|
|
||||||
bool _searching = false;
|
bool _searching = false;
|
||||||
QString _lastQuery;
|
QString _lastQuery;
|
||||||
@ -181,6 +232,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)
|
||||||
@ -259,7 +311,31 @@ 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.sendersCount = _descriptor.forwardOptions.sendersCount;
|
||||||
|
_forwardOptions.captionsCount = _descriptor.forwardOptions.captionsCount;
|
||||||
|
_forwardOptions.dropNames = (forwardOptions != Data::ForwardOptions::PreserveInfo);
|
||||||
|
_forwardOptions.dropCaptions = (forwardOptions == Data::ForwardOptions::NoNamesAndCaptions);
|
||||||
|
_groupOptions = groupOptions;
|
||||||
|
|
||||||
|
updateAdditionalTitle();
|
||||||
|
|
||||||
_inner = setInnerWidget(
|
_inner = setInnerWidget(
|
||||||
object_ptr<Inner>(this, _descriptor, uiShow()),
|
object_ptr<Inner>(this, _descriptor, uiShow()),
|
||||||
@ -282,11 +358,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(
|
||||||
@ -314,6 +401,17 @@ void ShareBox::prepare() {
|
|||||||
innerSelectedChanged(thread, checked);
|
innerSelectedChanged(thread, 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(),
|
||||||
@ -449,6 +547,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);
|
||||||
}
|
}
|
||||||
@ -475,33 +575,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<Menu::ItemWithCheck>(
|
|
||||||
_menu->menu(),
|
|
||||||
st::popupMenuWithIcons.menu,
|
|
||||||
Ui::CreateChild<QAction>(_menu->menu().get()),
|
|
||||||
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),
|
|
||||||
_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(),
|
||||||
@ -517,14 +590,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.sendersCount
|
|
||||||
= _descriptor.forwardOptions.sendersCount;
|
|
||||||
_forwardOptions.captionsCount
|
|
||||||
= _descriptor.forwardOptions.captionsCount;
|
|
||||||
|
|
||||||
send->setAcceptBoth();
|
send->setAcceptBoth();
|
||||||
send->clicks(
|
send->clicks(
|
||||||
@ -539,6 +618,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) {
|
||||||
scrollToY(0);
|
scrollToY(0);
|
||||||
_inner->updateFilter(query);
|
_inner->updateFilter(query);
|
||||||
@ -587,7 +864,8 @@ void ShareBox::submit(Api::SendOptions options) {
|
|||||||
_inner->selected(),
|
_inner->selected(),
|
||||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||||
options,
|
options,
|
||||||
forwardOptions);
|
forwardOptions,
|
||||||
|
_groupOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,14 +894,29 @@ void ShareBox::copyLink() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareBox::goToChat(not_null<Data::Thread*> thread) {
|
||||||
|
if (_descriptor.goToChatCallback) {
|
||||||
|
const auto forwardOptions = (_forwardOptions.captionsCount
|
||||||
|
&& _forwardOptions.dropCaptions)
|
||||||
|
? Data::ForwardOptions::NoNamesAndCaptions
|
||||||
|
: _forwardOptions.dropNames
|
||||||
|
? Data::ForwardOptions::NoSenderNames
|
||||||
|
: Data::ForwardOptions::PreserveInfo;
|
||||||
|
_descriptor.goToChatCallback(
|
||||||
|
thread,
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1159,6 +1452,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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,6 +1464,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()->peer()->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;
|
||||||
@ -1259,6 +1576,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,
|
||||||
@ -1286,6 +1611,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();
|
||||||
|
|
||||||
@ -1457,6 +1786,20 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||||||
std::shared_ptr<Ui::Show> show,
|
std::shared_ptr<Ui::Show> show,
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MessageIdsList msgIds) {
|
MessageIdsList msgIds) {
|
||||||
|
return [=](
|
||||||
|
std::vector<not_null<Data::Thread*>> &&result,
|
||||||
|
TextWithTags &&comment,
|
||||||
|
Api::SendOptions options,
|
||||||
|
Data::ForwardOptions forwardOptions,
|
||||||
|
Data::GroupingOptions groupingOptions) {
|
||||||
|
const auto window = history->session().tryResolveWindow();
|
||||||
|
if (window) {
|
||||||
|
Window::ShowForwardMessagesBox(
|
||||||
|
window,
|
||||||
|
Data::ForwardDraft{ msgIds, forwardOptions, groupingOptions });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*
|
||||||
struct State final {
|
struct State final {
|
||||||
base::flat_set<mtpRequestId> requests;
|
base::flat_set<mtpRequestId> requests;
|
||||||
};
|
};
|
||||||
@ -1587,11 +1930,19 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||||||
state->requests.insert(threadHistory->sendRequestId);
|
state->requests.insert(threadHistory->sendRequestId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void FastShareMessage(
|
void FastShareMessage(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<HistoryItem*> item) {
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto history = item->history();
|
||||||
|
const auto owner = &history->owner();
|
||||||
|
const auto msgIds = owner->itemOrItsGroup(item);
|
||||||
|
Window::ShowForwardMessagesBox(
|
||||||
|
controller,
|
||||||
|
Data::ForwardDraft{ msgIds });
|
||||||
|
/*
|
||||||
const auto show = controller->uiShow();
|
const auto show = controller->uiShow();
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
const auto owner = &history->owner();
|
const auto owner = &history->owner();
|
||||||
@ -1670,6 +2021,7 @@ void FastShareMessage(
|
|||||||
.premiumRequiredError = SharePremiumRequiredError(),
|
.premiumRequiredError = SharePremiumRequiredError(),
|
||||||
}),
|
}),
|
||||||
Ui::LayerOption::CloseOther);
|
Ui::LayerOption::CloseOther);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SharePremiumRequiredError()
|
auto SharePremiumRequiredError()
|
||||||
|
@ -46,12 +46,14 @@ class IndexedList;
|
|||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
enum class ForwardOptions;
|
enum class ForwardOptions;
|
||||||
|
enum class GroupingOptions;
|
||||||
class Thread;
|
class Thread;
|
||||||
} // 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;
|
||||||
@ -80,19 +82,25 @@ public:
|
|||||||
std::vector<not_null<Data::Thread*>>&&,
|
std::vector<not_null<Data::Thread*>>&&,
|
||||||
TextWithTags&&,
|
TextWithTags&&,
|
||||||
Api::SendOptions,
|
Api::SendOptions,
|
||||||
Data::ForwardOptions)>;
|
Data::ForwardOptions option,
|
||||||
|
Data::GroupingOptions groupOption)>;
|
||||||
using FilterCallback = Fn<bool(not_null<Data::Thread*>)>;
|
using FilterCallback = Fn<bool(not_null<Data::Thread*>)>;
|
||||||
|
|
||||||
[[nodiscard]] static SubmitCallback DefaultForwardCallback(
|
[[nodiscard]] static SubmitCallback DefaultForwardCallback(
|
||||||
std::shared_ptr<Ui::Show> show,
|
std::shared_ptr<Ui::Show> show,
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MessageIdsList msgIds);
|
MessageIdsList msgIds);
|
||||||
|
using GoToChatCallback = Fn<void(
|
||||||
|
Data::Thread*,
|
||||||
|
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;
|
||||||
object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
|
object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
|
||||||
rpl::producer<QString> copyLinkText;
|
rpl::producer<QString> copyLinkText;
|
||||||
const style::MultiSelect *stMultiSelect = nullptr;
|
const style::MultiSelect *stMultiSelect = nullptr;
|
||||||
@ -103,6 +111,8 @@ public:
|
|||||||
int sendersCount = 0;
|
int sendersCount = 0;
|
||||||
int captionsCount = 0;
|
int captionsCount = 0;
|
||||||
bool show = false;
|
bool show = false;
|
||||||
|
bool hasMedia = false;
|
||||||
|
bool isShare = true;
|
||||||
} forwardOptions;
|
} forwardOptions;
|
||||||
HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle;
|
HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle;
|
||||||
|
|
||||||
@ -127,6 +137,7 @@ private:
|
|||||||
void submitScheduled();
|
void submitScheduled();
|
||||||
void submitWhenOnline();
|
void submitWhenOnline();
|
||||||
void copyLink() const;
|
void copyLink() const;
|
||||||
|
void goToChat(not_null<Data::Thread*> thread);
|
||||||
bool searchByUsername(bool useCache = false);
|
bool searchByUsername(bool useCache = false);
|
||||||
|
|
||||||
SendMenu::Type sendMenuType() const;
|
SendMenu::Type sendMenuType() const;
|
||||||
@ -136,6 +147,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;
|
||||||
@ -159,7 +172,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;
|
||||||
|
@ -134,7 +134,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
|||||||
std::vector<not_null<Data::Thread*>> &&result,
|
std::vector<not_null<Data::Thread*>> &&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;
|
||||||
}
|
}
|
||||||
|
@ -1452,6 +1452,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;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +254,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(const QString &mime) const;
|
[[nodiscard]] bool hasMimeType(const QString &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" // CreateMedia.
|
#include "history/history_item.h" // CreateMedia.
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
@ -463,6 +464,11 @@ PollData *Media::poll() const {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const LocationPoint *Media::geoPoint() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const WallPaper *Media::paper() const {
|
const WallPaper *Media::paper() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -1365,6 +1371,10 @@ 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;
|
||||||
|
@ -145,6 +145,7 @@ public:
|
|||||||
virtual const Invoice *invoice() const;
|
virtual const Invoice *invoice() const;
|
||||||
virtual CloudImage *location() const;
|
virtual CloudImage *location() const;
|
||||||
virtual PollData *poll() const;
|
virtual PollData *poll() const;
|
||||||
|
virtual const LocationPoint *geoPoint() const;
|
||||||
virtual const WallPaper *paper() const;
|
virtual const WallPaper *paper() const;
|
||||||
virtual bool paperForBoth() const;
|
virtual bool paperForBoth() const;
|
||||||
virtual FullStoryId storyId() const;
|
virtual FullStoryId storyId() const;
|
||||||
@ -344,6 +345,7 @@ public:
|
|||||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
|
||||||
CloudImage *location() const override;
|
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;
|
||||||
|
@ -370,6 +370,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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +382,7 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
|
|||||||
setForwardDraft(topicRootId, {
|
setForwardDraft(topicRootId, {
|
||||||
.ids = owner().itemsToIds(result.items),
|
.ids = owner().itemsToIds(result.items),
|
||||||
.options = result.options,
|
.options = result.options,
|
||||||
|
.groupOptions = result.groupOptions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -692,7 +694,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,
|
||||||
@ -704,7 +707,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
|||||||
postAuthor,
|
postAuthor,
|
||||||
document,
|
document,
|
||||||
caption,
|
caption,
|
||||||
std::move(markup)),
|
std::move(markup),
|
||||||
|
newGroupId),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,7 +722,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,
|
||||||
@ -730,7 +735,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
|||||||
postAuthor,
|
postAuthor,
|
||||||
photo,
|
photo,
|
||||||
caption,
|
caption,
|
||||||
std::move(markup)),
|
std::move(markup),
|
||||||
|
newGroupId),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +42,16 @@ 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;
|
||||||
|
|
||||||
friend inline auto operator<=>(
|
friend inline auto operator<=>(
|
||||||
const ForwardDraft&,
|
const ForwardDraft&,
|
||||||
@ -56,6 +63,7 @@ using ForwardDrafts = base::flat_map<MsgId, ForwardDraft>;
|
|||||||
struct ResolvedForwardDraft {
|
struct ResolvedForwardDraft {
|
||||||
HistoryItemsList items;
|
HistoryItemsList items;
|
||||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||||
|
GroupingOptions groupOptions = GroupingOptions::GroupAsIs;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
@ -176,7 +184,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,
|
||||||
@ -187,7 +196,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,
|
||||||
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
*/
|
*/
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
|
||||||
|
#include "kotato/kotato_lang.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "calls/calls_instance.h" // Core::App().calls().joinGroupCall.
|
#include "calls/calls_instance.h" // Core::App().calls().joinGroupCall.
|
||||||
@ -615,7 +616,8 @@ HistoryItem::HistoryItem(
|
|||||||
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 groupedId)
|
||||||
: HistoryItem(
|
: HistoryItem(
|
||||||
history,
|
history,
|
||||||
id,
|
id,
|
||||||
@ -638,6 +640,12 @@ HistoryItem::HistoryItem(
|
|||||||
spoiler,
|
spoiler,
|
||||||
/*ttlSeconds = */0);
|
/*ttlSeconds = */0);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
|
if (groupedId) {
|
||||||
|
setGroupId(MessageGroupId::FromRaw(
|
||||||
|
history->peer->id,
|
||||||
|
groupedId,
|
||||||
|
flags & MessageFlag::IsOrWasScheduled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem::HistoryItem(
|
HistoryItem::HistoryItem(
|
||||||
@ -651,7 +659,8 @@ HistoryItem::HistoryItem(
|
|||||||
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 groupedId)
|
||||||
: HistoryItem(
|
: HistoryItem(
|
||||||
history,
|
history,
|
||||||
id,
|
id,
|
||||||
@ -668,6 +677,12 @@ HistoryItem::HistoryItem(
|
|||||||
const auto spoiler = false;
|
const auto spoiler = false;
|
||||||
_media = std::make_unique<Data::MediaPhoto>(this, photo, spoiler);
|
_media = std::make_unique<Data::MediaPhoto>(this, photo, spoiler);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
|
if (groupedId) {
|
||||||
|
setGroupId(MessageGroupId::FromRaw(
|
||||||
|
history->peer->id,
|
||||||
|
groupedId,
|
||||||
|
flags & MessageFlag::IsOrWasScheduled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem::HistoryItem(
|
HistoryItem::HistoryItem(
|
||||||
@ -2327,7 +2342,8 @@ bool HistoryItem::requiresSendInlineRight() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QString> HistoryItem::errorTextForForward(
|
std::optional<QString> HistoryItem::errorTextForForward(
|
||||||
not_null<Data::Thread*> to) const {
|
not_null<Data::Thread*> to,
|
||||||
|
bool isUnquotedForward) const {
|
||||||
const auto requiredRight = requiredSendRight();
|
const auto requiredRight = requiredSendRight();
|
||||||
const auto requiresInline = requiresSendInlineRight();
|
const auto requiresInline = requiresSendInlineRight();
|
||||||
const auto peer = to->peer();
|
const auto peer = to->peer();
|
||||||
@ -2342,6 +2358,13 @@ std::optional<QString> HistoryItem::errorTextForForward(
|
|||||||
&& _media->poll()->publicVotes()
|
&& _media->poll()->publicVotes()
|
||||||
&& peer->isBroadcast()) {
|
&& peer->isBroadcast()) {
|
||||||
return tr::lng_restricted_send_public_polls(tr::now);
|
return tr::lng_restricted_send_public_polls(tr::now);
|
||||||
|
} else if (isUnquotedForward
|
||||||
|
&& _media
|
||||||
|
&& _media->poll()
|
||||||
|
&& _media->poll()->quiz()
|
||||||
|
&& !_media->poll()->voted()
|
||||||
|
&& !_media->poll()->closed()) {
|
||||||
|
return ktr("ktg_forward_quiz_unquoted");
|
||||||
} else if (!Data::CanSend(to, requiredRight, false)) {
|
} else if (!Data::CanSend(to, requiredRight, false)) {
|
||||||
return tr::lng_forward_cant(tr::now);
|
return tr::lng_forward_cant(tr::now);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ public:
|
|||||||
const TextWithEntities &textWithEntities,
|
const TextWithEntities &textWithEntities,
|
||||||
const MTPMessageMedia &media,
|
const MTPMessageMedia &media,
|
||||||
HistoryMessageMarkupData &&markup,
|
HistoryMessageMarkupData &&markup,
|
||||||
uint64 groupedId);
|
uint64 groupedId = 0);
|
||||||
HistoryItem( // Local service message.
|
HistoryItem( // Local service message.
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MsgId id,
|
MsgId id,
|
||||||
@ -155,7 +155,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 groupedId = 0);
|
||||||
HistoryItem( // Local document.
|
HistoryItem( // Local document.
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MsgId id,
|
MsgId id,
|
||||||
@ -167,7 +168,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 groupedId = 0);
|
||||||
HistoryItem( // Local game.
|
HistoryItem( // Local game.
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MsgId id,
|
MsgId id,
|
||||||
@ -428,7 +430,8 @@ public:
|
|||||||
[[nodiscard]] ChatRestriction requiredSendRight() const;
|
[[nodiscard]] ChatRestriction requiredSendRight() const;
|
||||||
[[nodiscard]] bool requiresSendInlineRight() const;
|
[[nodiscard]] bool requiresSendInlineRight() const;
|
||||||
[[nodiscard]] std::optional<QString> errorTextForForward(
|
[[nodiscard]] std::optional<QString> errorTextForForward(
|
||||||
not_null<Data::Thread*> to) const;
|
not_null<Data::Thread*> to,
|
||||||
|
bool isUnquotedForward) const;
|
||||||
[[nodiscard]] const HistoryMessageTranslation *translation() const;
|
[[nodiscard]] const HistoryMessageTranslation *translation() const;
|
||||||
[[nodiscard]] bool translationShowRequiresCheck(LanguageId to) const;
|
[[nodiscard]] bool translationShowRequiresCheck(LanguageId to) const;
|
||||||
bool translationShowRequiresRequest(LanguageId to);
|
bool translationShowRequiresRequest(LanguageId to);
|
||||||
|
@ -74,7 +74,7 @@ QString GetErrorTextForSending(
|
|||||||
}
|
}
|
||||||
if (request.forward) {
|
if (request.forward) {
|
||||||
for (const auto &item : *request.forward) {
|
for (const auto &item : *request.forward) {
|
||||||
if (const auto error = item->errorTextForForward(thread)) {
|
if (const auto error = item->errorTextForForward(thread, request.isUnquotedForward)) {
|
||||||
return *error;
|
return *error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ struct SendingErrorRequest {
|
|||||||
const Data::Story *story = nullptr;
|
const Data::Story *story = nullptr;
|
||||||
const TextWithTags *text = nullptr;
|
const TextWithTags *text = nullptr;
|
||||||
bool ignoreSlowmodeCountdown = false;
|
bool ignoreSlowmodeCountdown = false;
|
||||||
|
bool isUnquotedForward = false;
|
||||||
};
|
};
|
||||||
[[nodiscard]] QString GetErrorTextForSending(
|
[[nodiscard]] QString GetErrorTextForSending(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
@ -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"
|
||||||
@ -6498,11 +6499,14 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
|||||||
Window::SectionShow::Way::Forward,
|
Window::SectionShow::Way::Forward,
|
||||||
_editMsgId);
|
_editMsgId);
|
||||||
} else if (isReadyToForward) {
|
} else if (isReadyToForward) {
|
||||||
if (e->button() != Qt::LeftButton) {
|
if (_forwardPanel->items().empty() || e->button() != Qt::LeftButton) {
|
||||||
_forwardPanel->editToNextOption();
|
return;
|
||||||
} else {
|
|
||||||
_forwardPanel->editOptions(controller()->uiShow());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_history->setForwardDraft(MsgId(), {});
|
||||||
|
Window::ShowForwardMessagesBox(controller(), Data::ForwardDraft{
|
||||||
|
.ids = session().data().itemsToIds(_forwardPanel->items())
|
||||||
|
});
|
||||||
} else if (_replyTo
|
} else if (_replyTo
|
||||||
&& ((e->modifiers() & Qt::ControlModifier)
|
&& ((e->modifiers() & Qt::ControlModifier)
|
||||||
|| (e->button() != Qt::LeftButton))) {
|
|| (e->button() != Qt::LeftButton))) {
|
||||||
@ -6517,6 +6521,116 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
|
if (_menu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_inDetails) {
|
||||||
|
if (readyToForward()) {
|
||||||
|
using Options = Data::ForwardOptions;
|
||||||
|
using GroupingOptions = Data::GroupingOptions;
|
||||||
|
const auto count = _forwardPanel->items().size();
|
||||||
|
const auto hasMediaToGroup = [&] {
|
||||||
|
if (count > 1) {
|
||||||
|
auto grouppableMediaCount = 0;
|
||||||
|
for (const auto item : _forwardPanel->items()) {
|
||||||
|
if (item->media() && item->media()->canBeGrouped()) {
|
||||||
|
grouppableMediaCount++;
|
||||||
|
} else {
|
||||||
|
grouppableMediaCount = 0;
|
||||||
|
}
|
||||||
|
if (grouppableMediaCount > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
|
const auto hasCaptions = [&] {
|
||||||
|
for (const auto item : _forwardPanel->items()) {
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (!item->originalText().text.isEmpty()
|
||||||
|
&& media->allowsEditCaption()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
|
const auto addForwardOption = [=](
|
||||||
|
Options newOptions,
|
||||||
|
const QString &langKey,
|
||||||
|
int settingsKey) {
|
||||||
|
const auto draft = _history->resolveForwardDraft(MsgId());
|
||||||
|
if (_history && draft.options != newOptions) {
|
||||||
|
_menu->addAction(ktr(langKey), [=] {
|
||||||
|
const auto error = GetErrorTextForSending(
|
||||||
|
_history->peer,
|
||||||
|
{
|
||||||
|
.topicRootId = MsgId(),
|
||||||
|
.forward = &_forwardPanel->items(),
|
||||||
|
.ignoreSlowmodeCountdown = true,
|
||||||
|
.isUnquotedForward = newOptions != Options::PreserveInfo,
|
||||||
|
});
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
controller()->showToast(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_history->setForwardDraft(MsgId(), {
|
||||||
|
.ids = session().data().itemsToIds(_forwardPanel->items()),
|
||||||
|
.options = newOptions,
|
||||||
|
.groupOptions = draft.groupOptions,
|
||||||
|
});
|
||||||
|
updateField();
|
||||||
|
if (::Kotato::JsonSettings::GetBool("forward_remember_mode")) {
|
||||||
|
::Kotato::JsonSettings::Set("forward_mode", settingsKey);
|
||||||
|
::Kotato::JsonSettings::Write();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_menu = base::make_unique_q<Ui::PopupMenu>(this);
|
||||||
|
|
||||||
|
addForwardOption(Options::PreserveInfo, "ktg_forward_menu_quoted", 0);
|
||||||
|
addForwardOption(Options::NoSenderNames, "ktg_forward_menu_unquoted", 1);
|
||||||
|
if (hasCaptions) {
|
||||||
|
addForwardOption(Options::NoNamesAndCaptions, "ktg_forward_menu_uncaptioned", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMediaToGroup && count > 1) {
|
||||||
|
const auto addGroupingOption = [=](
|
||||||
|
GroupingOptions newOptions,
|
||||||
|
const QString &langKey,
|
||||||
|
int settingsKey) {
|
||||||
|
const auto draft = _history->resolveForwardDraft(MsgId());
|
||||||
|
if (_history && draft.groupOptions != newOptions) {
|
||||||
|
_menu->addAction(ktr(langKey), [=] {
|
||||||
|
_history->setForwardDraft(MsgId(), {
|
||||||
|
.ids = session().data().itemsToIds(_forwardPanel->items()),
|
||||||
|
.options = draft.options,
|
||||||
|
.groupOptions = newOptions,
|
||||||
|
});
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::editDraftOptions() {
|
void HistoryWidget::editDraftOptions() {
|
||||||
Expects(_history != nullptr);
|
Expects(_history != nullptr);
|
||||||
|
|
||||||
@ -7291,10 +7405,17 @@ bool HistoryWidget::sendExistingDocument(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (document->hasRemoteLocation()) {
|
||||||
Api::SendExistingDocument(
|
Api::SendExistingDocument(
|
||||||
Api::MessageToSend(prepareSendAction(options)),
|
Api::MessageToSend(prepareSendAction(options)),
|
||||||
document,
|
document,
|
||||||
localId);
|
localId);
|
||||||
|
} else {
|
||||||
|
Api::SendWebDocument(
|
||||||
|
Api::MessageToSend(prepareSendAction(options)),
|
||||||
|
document,
|
||||||
|
localId);
|
||||||
|
}
|
||||||
|
|
||||||
if (_fieldAutocomplete->stickersShown()) {
|
if (_fieldAutocomplete->stickersShown()) {
|
||||||
clearFieldText();
|
clearFieldText();
|
||||||
|
@ -299,6 +299,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;
|
||||||
@ -821,6 +822,7 @@ private:
|
|||||||
|
|
||||||
int _topDelta = 0;
|
int _topDelta = 0;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
rpl::event_stream<> _cancelRequests;
|
rpl::event_stream<> _cancelRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "ui/chat/forward_options_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
|
//#include "ui/chat/forward_options_box.h"
|
||||||
#include "ui/effects/spoiler_mess.h"
|
#include "ui/effects/spoiler_mess.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
@ -241,6 +242,7 @@ bool ForwardPanel::empty() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
||||||
|
/*
|
||||||
using Options = Data::ForwardOptions;
|
using Options = Data::ForwardOptions;
|
||||||
const auto now = _data.options;
|
const auto now = _data.options;
|
||||||
const auto count = _data.items.size();
|
const auto count = _data.items.size();
|
||||||
@ -297,6 +299,7 @@ void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
|||||||
},
|
},
|
||||||
optionsChanged,
|
optionsChanged,
|
||||||
changeRecipient));
|
changeRecipient));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPanel::editToNextOption() {
|
void ForwardPanel::editToNextOption() {
|
||||||
|
@ -366,12 +366,32 @@ 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, }},
|
||||||
{ "folders/local", {
|
{ "folders/local", {
|
||||||
.scope = SettingScope::Account,
|
.scope = SettingScope::Account,
|
||||||
.type = SettingType::QJsonArraySetting, }},
|
.type = SettingType::QJsonArraySetting, }},
|
||||||
{ "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;
|
||||||
|
@ -48,6 +48,54 @@ namespace Settings {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
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) {
|
||||||
@ -298,6 +346,71 @@ void SetupKotatoForward(not_null<Ui::VerticalLayout*> container) {
|
|||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
Ui::AddSubsectionTitle(container, rktr("ktg_settings_forward"));
|
Ui::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::settingsButtonNoIcon
|
||||||
|
)->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::settingsButtonNoIcon
|
||||||
|
)->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);
|
||||||
|
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
Ui::AddDividerText(container, rktr("ktg_forward_force_old_unquoted_desc"));
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
|
SettingsMenuJsonSwitch(ktg_settings_forward_retain_selection, forward_retain_selection);
|
||||||
|
SettingsMenuJsonSwitch(ktg_settings_forward_chat_on_click, forward_on_click);
|
||||||
|
|
||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
Ui::AddDividerText(container, rktr("ktg_settings_forward_chat_on_click_description"));
|
Ui::AddDividerText(container, rktr("ktg_settings_forward_chat_on_click_description"));
|
||||||
|
@ -537,6 +537,7 @@ bool MainWidget::setForwardDraft(
|
|||||||
.topicRootId = topicRootId,
|
.topicRootId = topicRootId,
|
||||||
.forward = &items,
|
.forward = &items,
|
||||||
.ignoreSlowmodeCountdown = true,
|
.ignoreSlowmodeCountdown = true,
|
||||||
|
.isUnquotedForward = draft.options != Data::ForwardOptions::PreserveInfo,
|
||||||
});
|
});
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
_controller->show(Ui::MakeInformBox(error));
|
_controller->show(Ui::MakeInformBox(error));
|
||||||
|
@ -77,7 +77,8 @@ namespace Media::Stories {
|
|||||||
std::vector<not_null<Data::Thread*>> &&result,
|
std::vector<not_null<Data::Thread*>> &&result,
|
||||||
TextWithTags &&comment,
|
TextWithTags &&comment,
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
Data::ForwardOptions forwardOptions) {
|
Data::ForwardOptions forwardOptions,
|
||||||
|
Data::GroupingOptions groupingOptions) {
|
||||||
if (state->requests) {
|
if (state->requests) {
|
||||||
return; // Share clicked already.
|
return; // Share clicked already.
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "boxes/peers/add_participants_box.h"
|
#include "boxes/peers/add_participants_box.h"
|
||||||
#include "boxes/peers/edit_forum_topic_box.h"
|
#include "boxes/peers/edit_forum_topic_box.h"
|
||||||
#include "boxes/peers/edit_contact_box.h"
|
#include "boxes/peers/edit_contact_box.h"
|
||||||
|
#include "boxes/share_box.h"
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "inline_bots/bot_attach_web_view.h" // InlineBots::PeerType.
|
#include "inline_bots/bot_attach_web_view.h" // InlineBots::PeerType.
|
||||||
#include "ui/boxes/report_box.h"
|
#include "ui/boxes/report_box.h"
|
||||||
@ -52,9 +53,13 @@ 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_widget.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
@ -77,10 +82,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.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_saved_sublist.h"
|
#include "data/data_saved_sublist.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"
|
||||||
@ -91,6 +99,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>
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
@ -1765,10 +1775,227 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
|||||||
return weak->data();
|
return weak->data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
|
Data::ForwardDraft &&draft,
|
||||||
|
Fn<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 topicRootId = history->topicRootId();
|
||||||
|
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 sendersCount = ItemsForwardSendersCount(items);
|
||||||
|
const auto captionsCount = ItemsForwardCaptionsCount(items);
|
||||||
|
const auto hasOnlyForcedForwardedInfo = captionsCount
|
||||||
|
? 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(
|
||||||
|
navigation->parentController(),
|
||||||
|
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<Data::Thread*>> &&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,
|
||||||
|
{
|
||||||
|
.topicRootId = topicRootId,
|
||||||
|
.forward = &items,
|
||||||
|
.ignoreSlowmodeCountdown = false,
|
||||||
|
.isUnquotedForward = 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->peer()->name())
|
||||||
|
).append("\n\n");
|
||||||
|
}
|
||||||
|
text.append(error.first);
|
||||||
|
Ui::show(
|
||||||
|
Ui::MakeInformBox(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 thread : result) {
|
||||||
|
auto action = Api::SendAction(thread);
|
||||||
|
const auto history = action.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 = [](not_null<Data::Thread*> thread) {
|
||||||
|
return Data::CanSendTexts(thread);
|
||||||
|
};
|
||||||
|
auto copyLinkCallback = canCopyLink
|
||||||
|
? Fn<void()>(std::move(copyCallback))
|
||||||
|
: Fn<void()>();
|
||||||
|
auto goToChatCallback = [navigation, data](
|
||||||
|
Data::Thread* thread,
|
||||||
|
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(thread, 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),
|
||||||
|
.forwardOptions = {
|
||||||
|
.sendersCount = sendersCount,
|
||||||
|
.captionsCount = captionsCount,
|
||||||
|
.show = !hasOnlyForcedForwardedInfo,
|
||||||
|
.hasMedia = hasMediaForGrouping,
|
||||||
|
.isShare = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
}),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
return weak->data();
|
||||||
|
}
|
||||||
|
|
||||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
Data::ForwardDraft &&draft,
|
Data::ForwardDraft &&draft,
|
||||||
Fn<void()> &&successCallback) {
|
Fn<void()> &&successCallback) {
|
||||||
|
const auto window = show->session().tryResolveWindow();
|
||||||
|
return ShowForwardMessagesBox(
|
||||||
|
window,
|
||||||
|
std::move(draft),
|
||||||
|
std::move(successCallback));
|
||||||
|
|
||||||
|
/*
|
||||||
const auto session = &show->session();
|
const auto session = &show->session();
|
||||||
const auto owner = &session->data();
|
const auto owner = &session->data();
|
||||||
const auto itemsList = owner->idsToItems(draft.ids);
|
const auto itemsList = owner->idsToItems(draft.ids);
|
||||||
@ -2082,16 +2309,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||||||
}, state->box->lifetime());
|
}, state->box->lifetime());
|
||||||
|
|
||||||
return QPointer<Ui::BoxContent>(state->box);
|
return QPointer<Ui::BoxContent>(state->box);
|
||||||
}
|
*/
|
||||||
|
|
||||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
|
||||||
Data::ForwardDraft &&draft,
|
|
||||||
Fn<void()> &&successCallback) {
|
|
||||||
return ShowForwardMessagesBox(
|
|
||||||
navigation->uiShow(),
|
|
||||||
std::move(draft),
|
|
||||||
std::move(successCallback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
|
@ -273,8 +273,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…
x
Reference in New Issue
Block a user