mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-29 05:37:45 +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_supergroup_id_copied": "Supergroup 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_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_admin": "Not administrated",
|
||||
"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_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_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.",
|
||||
"dummy_last_string": ""
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_location.h"
|
||||
#include "data/data_channel.h" // ChannelData::addsSignature.
|
||||
#include "data/data_user.h" // UserData::name
|
||||
#include "data/data_session.h"
|
||||
@ -68,7 +69,9 @@ void SendExistingMedia(
|
||||
not_null<MediaData*> media,
|
||||
Fn<MTPInputMedia()> inputMedia,
|
||||
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 peer = history->peer;
|
||||
const auto session = &history->session();
|
||||
@ -146,7 +149,6 @@ void SendExistingMedia(
|
||||
|
||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
auto &histories = history->owner().histories();
|
||||
const auto usedFileReference = media->fileReference();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
message.action.replyTo,
|
||||
@ -166,6 +168,7 @@ void SendExistingMedia(
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
||||
const auto usedFileReference = media->fileReference();
|
||||
api->refreshFileReference(origin, [=](const auto &result) {
|
||||
if (media->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
@ -180,15 +183,41 @@ void SendExistingMedia(
|
||||
};
|
||||
performRequest(performRequest);
|
||||
|
||||
if (!forwarding) {
|
||||
api->finishForwarding(message.action);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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(
|
||||
MessageToSend &&message,
|
||||
not_null<DocumentData*> document,
|
||||
std::optional<MsgId> localMessageId) {
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void()> doneCallback,
|
||||
bool forwarding) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaDocument(
|
||||
MTP_flags(0),
|
||||
@ -201,7 +230,9 @@ void SendExistingDocument(
|
||||
document,
|
||||
inputMedia,
|
||||
document->stickerOrGifOrigin(),
|
||||
std::move(localMessageId));
|
||||
std::move(localMessageId),
|
||||
(doneCallback ? std::move(doneCallback) : nullptr),
|
||||
forwarding);
|
||||
|
||||
if (document->sticker()) {
|
||||
document->owner().stickers().incrementSticker(document);
|
||||
@ -211,7 +242,9 @@ void SendExistingDocument(
|
||||
void SendExistingPhoto(
|
||||
MessageToSend &&message,
|
||||
not_null<PhotoData*> photo,
|
||||
std::optional<MsgId> localMessageId) {
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void()> doneCallback,
|
||||
bool forwarding) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaPhoto(
|
||||
MTP_flags(0),
|
||||
@ -223,10 +256,15 @@ void SendExistingPhoto(
|
||||
photo,
|
||||
inputMedia,
|
||||
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();
|
||||
auto length = 0;
|
||||
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) {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
});
|
||||
if (!forwarding) {
|
||||
api->finishForwarding(message.action);
|
||||
}
|
||||
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
|
||||
|
@ -16,22 +16,44 @@ class PhotoData;
|
||||
class DocumentData;
|
||||
struct FileLoadResult;
|
||||
|
||||
namespace MTP {
|
||||
class Error;
|
||||
} // namespace MTP
|
||||
|
||||
namespace Data {
|
||||
class LocationPoint;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct MessageToSend;
|
||||
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(
|
||||
MessageToSend &&message,
|
||||
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(
|
||||
MessageToSend &&message,
|
||||
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(
|
||||
const SendAction &action,
|
||||
@ -42,4 +64,10 @@ void SendConfirmedFile(
|
||||
not_null<Main::Session*> session,
|
||||
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
|
||||
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "apiwrap.h"
|
||||
|
||||
#include "kotato/kotato_settings.h"
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_attached_stickers.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 "data/notify/data_notify_settings.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
@ -3170,6 +3172,12 @@ void ApiWrap::forwardMessages(
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
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());
|
||||
|
||||
auto &histories = _session->data().histories();
|
||||
@ -3229,6 +3237,7 @@ void ApiWrap::forwardMessages(
|
||||
}
|
||||
|
||||
auto forwardFrom = draft.items.front()->history()->peer;
|
||||
auto forwardGroupId = draft.items.front()->groupId();
|
||||
auto ids = QVector<MTPint>();
|
||||
auto randomIds = QVector<MTPlong>();
|
||||
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
|
||||
@ -3307,9 +3316,14 @@ void ApiWrap::forwardMessages(
|
||||
localIds->emplace(randomId, newId);
|
||||
}
|
||||
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();
|
||||
forwardFrom = newFrom;
|
||||
forwardGroupId = newGroupId;
|
||||
}
|
||||
ids.push_back(MTP_int(item->id));
|
||||
randomIds.push_back(MTP_long(randomId));
|
||||
@ -3318,6 +3332,501 @@ void ApiWrap::forwardMessages(
|
||||
_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(
|
||||
const QString &phone,
|
||||
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 peer = history->peer;
|
||||
auto &textWithTags = message.textWithTags;
|
||||
@ -3591,7 +4103,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
: Data::ForumTopic::kGeneralId;
|
||||
const auto topic = peer->forumTopicFor(topicRootId);
|
||||
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;
|
||||
}
|
||||
local().saveRecentSentHashtags(textWithTags.text);
|
||||
@ -3731,6 +4247,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
draftTopicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
if (doneCallback) {
|
||||
doneCallback(result, response.requestId);
|
||||
}
|
||||
};
|
||||
const auto fail = [=](
|
||||
const MTP::Error &error,
|
||||
@ -3785,7 +4304,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
if (!forwarding) {
|
||||
finishForwarding(action);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::sendBotStart(
|
||||
|
@ -287,6 +287,10 @@ public:
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void forwardMessagesUnquoted(
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
@ -334,7 +338,10 @@ public:
|
||||
|
||||
void cancelLocalItem(not_null<HistoryItem*> item);
|
||||
|
||||
void sendMessage(MessageToSend &&message);
|
||||
void sendMessage(
|
||||
MessageToSend &&message,
|
||||
Fn<void(const MTPUpdates &, mtpRequestId)> doneCallback = nullptr,
|
||||
bool forwarding = false);
|
||||
void sendBotStart(
|
||||
not_null<UserData*> bot,
|
||||
PeerData *chat = nullptr,
|
||||
|
@ -1154,7 +1154,8 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
Data::ForwardOptions,
|
||||
Data::GroupingOptions) {
|
||||
if (*sending || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/share_box.h"
|
||||
|
||||
#include "kotato/kotato_lang.h"
|
||||
#include "kotato/kotato_settings.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "base/random.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/multi_select.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/dropdown_menu.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/painter.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "menu/menu_check_item.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "window/window_session_controller.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 "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#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 {
|
||||
public:
|
||||
Inner(
|
||||
@ -65,6 +109,8 @@ public:
|
||||
|
||||
void setPeerSelectedChangedCallback(
|
||||
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);
|
||||
|
||||
[[nodiscard]] std::vector<not_null<Data::Thread*>> selected() const;
|
||||
@ -80,6 +126,9 @@ public:
|
||||
void activateSkipPage(int pageHeight, int direction);
|
||||
void updateFilter(QString filter = QString());
|
||||
void selectActive();
|
||||
void tryGoToChat();
|
||||
void selectionMade();
|
||||
Fn<void()> goToChatRequest() const;
|
||||
|
||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||
rpl::producer<> searchRequests() const;
|
||||
@ -172,6 +221,8 @@ private:
|
||||
base::flat_set<not_null<Data::Thread*>> _selected;
|
||||
|
||||
Fn<void(not_null<Data::Thread*>, bool)> _peerSelectedChangedCallback;
|
||||
Fn<void()> _submitRequest;
|
||||
Fn<void()> _goToChatRequest;
|
||||
|
||||
bool _searching = false;
|
||||
QString _lastQuery;
|
||||
@ -181,6 +232,7 @@ private:
|
||||
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
|
||||
rpl::event_stream<> _searchRequests;
|
||||
|
||||
bool _hadSelection = false;
|
||||
};
|
||||
|
||||
ShareBox::ShareBox(QWidget*, Descriptor &&descriptor)
|
||||
@ -259,7 +311,31 @@ void ShareBox::prepare() {
|
||||
_select->resizeToWidth(st::boxWideWidth);
|
||||
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(
|
||||
object_ptr<Inner>(this, _descriptor, uiShow()),
|
||||
@ -282,11 +358,22 @@ void ShareBox::prepare() {
|
||||
});
|
||||
_select->setResizedCallback([=] { updateScrollSkips(); });
|
||||
_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)) {
|
||||
submit({});
|
||||
} else if (modifiers.testFlag(Qt::ShiftModifier)) {
|
||||
if (_inner->selected().size() == 1 && _inner->goToChatRequest()) {
|
||||
_inner->goToChatRequest()();
|
||||
}
|
||||
} else {
|
||||
_inner->selectActive();
|
||||
if (!modifiers.testFlag(Qt::ControlModifier)
|
||||
|| ::Kotato::JsonSettings::GetBool("forward_on_click")) {
|
||||
_inner->tryGoToChat();
|
||||
} else {
|
||||
_inner->selectionMade();
|
||||
}
|
||||
}
|
||||
});
|
||||
rpl::combine(
|
||||
@ -314,6 +401,17 @@ void ShareBox::prepare() {
|
||||
innerSelectedChanged(thread, checked);
|
||||
});
|
||||
|
||||
_inner->setSubmitRequest([=] {
|
||||
submit({});
|
||||
});
|
||||
|
||||
if (_descriptor.goToChatCallback) {
|
||||
_inner->setGoToChatRequest([=] {
|
||||
const auto singleChat = _inner->selected().at(0);
|
||||
goToChat(singleChat);
|
||||
});
|
||||
}
|
||||
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
getDelegate()->outerContainer(),
|
||||
_comment->entity(),
|
||||
@ -449,6 +547,8 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
|
||||
_inner->activateSkipPage(contentHeight(), -1);
|
||||
} else if (e->key() == Qt::Key_PageDown) {
|
||||
_inner->activateSkipPage(contentHeight(), 1);
|
||||
} else if (e->key() == Qt::Key_Escape && !_select->getQuery().isEmpty()) {
|
||||
_select->clearQuery();
|
||||
} else {
|
||||
BoxContent::keyPressEvent(e);
|
||||
}
|
||||
@ -475,33 +575,6 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
|
||||
}
|
||||
_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(
|
||||
_menu.get(),
|
||||
sendMenuType(),
|
||||
@ -517,14 +590,20 @@ void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
|
||||
|
||||
void ShareBox::createButtons() {
|
||||
clearButtons();
|
||||
if (!_descriptor.forwardOptions.isShare && _descriptor.forwardOptions.show) {
|
||||
const auto moreButton = addTopButton(st::infoTopBarMenu);
|
||||
moreButton->setClickedCallback([=] { showForwardMenu(moreButton.data()); });
|
||||
}
|
||||
|
||||
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(), [=] {
|
||||
submit({});
|
||||
});
|
||||
_forwardOptions.sendersCount
|
||||
= _descriptor.forwardOptions.sendersCount;
|
||||
_forwardOptions.captionsCount
|
||||
= _descriptor.forwardOptions.captionsCount;
|
||||
|
||||
send->setAcceptBoth();
|
||||
send->clicks(
|
||||
@ -539,6 +618,204 @@ void ShareBox::createButtons() {
|
||||
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) {
|
||||
scrollToY(0);
|
||||
_inner->updateFilter(query);
|
||||
@ -587,7 +864,8 @@ void ShareBox::submit(Api::SendOptions options) {
|
||||
_inner->selected(),
|
||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||
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() {
|
||||
auto hasSelected = _inner->hasSelected();
|
||||
if (_hasSelected != hasSelected) {
|
||||
_hasSelected = hasSelected;
|
||||
createButtons();
|
||||
_comment->toggle(_hasSelected, anim::type::normal);
|
||||
_comment->resizeToWidth(st::boxWideWidth);
|
||||
}
|
||||
createButtons();
|
||||
update();
|
||||
}
|
||||
|
||||
@ -1159,6 +1452,11 @@ void ShareBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
updateUpon(e->pos());
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
_columnSkip = (width() - _columnCount * _st.item.checkbox.imageRadius * 2) / float64(_columnCount + 1);
|
||||
_rowWidthReal = _st.item.checkbox.imageRadius * 2 + _columnSkip;
|
||||
@ -1259,6 +1576,14 @@ void ShareBox::Inner::setPeerSelectedChangedCallback(
|
||||
_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(
|
||||
not_null<Chat*> chat,
|
||||
bool checked,
|
||||
@ -1286,6 +1611,10 @@ bool ShareBox::Inner::hasSelected() const {
|
||||
return _selected.size();
|
||||
}
|
||||
|
||||
Fn<void()> ShareBox::Inner::goToChatRequest() const {
|
||||
return _goToChatRequest;
|
||||
}
|
||||
|
||||
void ShareBox::Inner::updateFilter(QString filter) {
|
||||
_lastQuery = filter.toLower().trimmed();
|
||||
|
||||
@ -1457,6 +1786,20 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<History*> history,
|
||||
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 {
|
||||
base::flat_set<mtpRequestId> requests;
|
||||
};
|
||||
@ -1587,11 +1930,19 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
state->requests.insert(threadHistory->sendRequestId);
|
||||
}
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
void FastShareMessage(
|
||||
not_null<Window::SessionController*> controller,
|
||||
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 history = item->history();
|
||||
const auto owner = &history->owner();
|
||||
@ -1670,6 +2021,7 @@ void FastShareMessage(
|
||||
.premiumRequiredError = SharePremiumRequiredError(),
|
||||
}),
|
||||
Ui::LayerOption::CloseOther);
|
||||
*/
|
||||
}
|
||||
|
||||
auto SharePremiumRequiredError()
|
||||
|
@ -46,12 +46,14 @@ class IndexedList;
|
||||
|
||||
namespace Data {
|
||||
enum class ForwardOptions;
|
||||
enum class GroupingOptions;
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class MultiSelect;
|
||||
class InputField;
|
||||
class DropdownMenu;
|
||||
struct ScrollToRequest;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
@ -80,19 +82,25 @@ public:
|
||||
std::vector<not_null<Data::Thread*>>&&,
|
||||
TextWithTags&&,
|
||||
Api::SendOptions,
|
||||
Data::ForwardOptions)>;
|
||||
Data::ForwardOptions option,
|
||||
Data::GroupingOptions groupOption)>;
|
||||
using FilterCallback = Fn<bool(not_null<Data::Thread*>)>;
|
||||
|
||||
[[nodiscard]] static SubmitCallback DefaultForwardCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<History*> history,
|
||||
MessageIdsList msgIds);
|
||||
using GoToChatCallback = Fn<void(
|
||||
Data::Thread*,
|
||||
Data::ForwardOptions option,
|
||||
Data::GroupingOptions groupOption)>;
|
||||
|
||||
struct Descriptor {
|
||||
not_null<Main::Session*> session;
|
||||
CopyCallback copyCallback;
|
||||
SubmitCallback submitCallback;
|
||||
FilterCallback filterCallback;
|
||||
GoToChatCallback goToChatCallback;
|
||||
object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
|
||||
rpl::producer<QString> copyLinkText;
|
||||
const style::MultiSelect *stMultiSelect = nullptr;
|
||||
@ -103,6 +111,8 @@ public:
|
||||
int sendersCount = 0;
|
||||
int captionsCount = 0;
|
||||
bool show = false;
|
||||
bool hasMedia = false;
|
||||
bool isShare = true;
|
||||
} forwardOptions;
|
||||
HistoryView::ScheduleBoxStyleArgs scheduleBoxStyle;
|
||||
|
||||
@ -127,6 +137,7 @@ private:
|
||||
void submitScheduled();
|
||||
void submitWhenOnline();
|
||||
void copyLink() const;
|
||||
void goToChat(not_null<Data::Thread*> thread);
|
||||
bool searchByUsername(bool useCache = false);
|
||||
|
||||
SendMenu::Type sendMenuType() const;
|
||||
@ -136,6 +147,8 @@ private:
|
||||
void applyFilterUpdate(const QString &query);
|
||||
void selectedChanged();
|
||||
void createButtons();
|
||||
bool showForwardMenu(not_null<Ui::IconButton*> button);
|
||||
void updateAdditionalTitle();
|
||||
int getTopScrollSkip() const;
|
||||
int getBottomScrollSkip() const;
|
||||
int contentHeight() const;
|
||||
@ -159,7 +172,9 @@ private:
|
||||
object_ptr<Ui::RpWidget> _bottomWidget;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
base::unique_qptr<Ui::DropdownMenu> _topMenu;
|
||||
Ui::ForwardOptions _forwardOptions;
|
||||
Data::GroupingOptions _groupOptions;
|
||||
|
||||
class Inner;
|
||||
QPointer<Inner> _inner;
|
||||
|
@ -134,7 +134,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
Data::ForwardOptions,
|
||||
Data::GroupingOptions) {
|
||||
if (*sending || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1452,6 +1452,10 @@ void DocumentData::refreshFileReference(const QByteArray &value) {
|
||||
_videoThumbnail.location.refreshFileReference(value);
|
||||
}
|
||||
|
||||
QString DocumentData::url() const {
|
||||
return _url;
|
||||
}
|
||||
|
||||
QString DocumentData::filename() const {
|
||||
return _filename;
|
||||
}
|
||||
|
@ -254,6 +254,7 @@ public:
|
||||
// to (this) received from the server "same" document.
|
||||
void collectLocalData(not_null<DocumentData*> local);
|
||||
|
||||
[[nodiscard]] QString url() const;
|
||||
[[nodiscard]] QString filename() const;
|
||||
[[nodiscard]] QString mimeString() 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 "kotato/kotato_lang.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h" // CreateMedia.
|
||||
#include "history/history_location_manager.h"
|
||||
@ -463,6 +464,11 @@ PollData *Media::poll() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const LocationPoint *Media::geoPoint() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const WallPaper *Media::paper() const {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1365,6 +1371,10 @@ CloudImage *MediaLocation::location() const {
|
||||
return _location;
|
||||
}
|
||||
|
||||
const LocationPoint *MediaLocation::geoPoint() const {
|
||||
return &_point;
|
||||
}
|
||||
|
||||
ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
|
||||
const auto type = tr::lng_maps_point(tr::now);
|
||||
const auto hasMiniImages = false;
|
||||
|
@ -145,6 +145,7 @@ public:
|
||||
virtual const Invoice *invoice() const;
|
||||
virtual CloudImage *location() const;
|
||||
virtual PollData *poll() const;
|
||||
virtual const LocationPoint *geoPoint() const;
|
||||
virtual const WallPaper *paper() const;
|
||||
virtual bool paperForBoth() const;
|
||||
virtual FullStoryId storyId() const;
|
||||
@ -344,6 +345,7 @@ public:
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
CloudImage *location() const override;
|
||||
const LocationPoint *geoPoint() const override;
|
||||
ItemPreview toPreview(ToPreviewOptions options) const override;
|
||||
TextWithEntities notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
|
@ -370,6 +370,7 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
|
||||
return Data::ResolvedForwardDraft{
|
||||
.items = owner().idsToItems(draft.ids),
|
||||
.options = draft.options,
|
||||
.groupOptions = draft.groupOptions,
|
||||
};
|
||||
}
|
||||
|
||||
@ -381,6 +382,7 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
|
||||
setForwardDraft(topicRootId, {
|
||||
.ids = owner().itemsToIds(result.items),
|
||||
.options = result.options,
|
||||
.groupOptions = result.groupOptions,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
@ -692,7 +694,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
const QString &postAuthor,
|
||||
not_null<DocumentData*> document,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup) {
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 newGroupId) {
|
||||
return addNewItem(
|
||||
makeMessage(
|
||||
id,
|
||||
@ -704,7 +707,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
postAuthor,
|
||||
document,
|
||||
caption,
|
||||
std::move(markup)),
|
||||
std::move(markup),
|
||||
newGroupId),
|
||||
true);
|
||||
}
|
||||
|
||||
@ -718,7 +722,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
const QString &postAuthor,
|
||||
not_null<PhotoData*> photo,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup) {
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 newGroupId) {
|
||||
return addNewItem(
|
||||
makeMessage(
|
||||
id,
|
||||
@ -730,7 +735,8 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
postAuthor,
|
||||
photo,
|
||||
caption,
|
||||
std::move(markup)),
|
||||
std::move(markup),
|
||||
newGroupId),
|
||||
true);
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,16 @@ enum class ForwardOptions {
|
||||
NoNamesAndCaptions,
|
||||
};
|
||||
|
||||
enum class GroupingOptions {
|
||||
GroupAsIs,
|
||||
RegroupAll,
|
||||
Separate,
|
||||
};
|
||||
|
||||
struct ForwardDraft {
|
||||
MessageIdsList ids;
|
||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||
GroupingOptions groupOptions = GroupingOptions::GroupAsIs;
|
||||
|
||||
friend inline auto operator<=>(
|
||||
const ForwardDraft&,
|
||||
@ -56,6 +63,7 @@ using ForwardDrafts = base::flat_map<MsgId, ForwardDraft>;
|
||||
struct ResolvedForwardDraft {
|
||||
HistoryItemsList items;
|
||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||
GroupingOptions groupOptions = GroupingOptions::GroupAsIs;
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
@ -176,7 +184,8 @@ public:
|
||||
const QString &postAuthor,
|
||||
not_null<DocumentData*> document,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup);
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 newGroupId = 0);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MessageFlags flags,
|
||||
@ -187,7 +196,8 @@ public:
|
||||
const QString &postAuthor,
|
||||
not_null<PhotoData*> photo,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup);
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 newGroupId = 0);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MessageFlags flags,
|
||||
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/history_item.h"
|
||||
|
||||
#include "kotato/kotato_lang.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "calls/calls_instance.h" // Core::App().calls().joinGroupCall.
|
||||
@ -615,7 +616,8 @@ HistoryItem::HistoryItem(
|
||||
const QString &postAuthor,
|
||||
not_null<DocumentData*> document,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup)
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 groupedId)
|
||||
: HistoryItem(
|
||||
history,
|
||||
id,
|
||||
@ -638,6 +640,12 @@ HistoryItem::HistoryItem(
|
||||
spoiler,
|
||||
/*ttlSeconds = */0);
|
||||
setText(caption);
|
||||
if (groupedId) {
|
||||
setGroupId(MessageGroupId::FromRaw(
|
||||
history->peer->id,
|
||||
groupedId,
|
||||
flags & MessageFlag::IsOrWasScheduled));
|
||||
}
|
||||
}
|
||||
|
||||
HistoryItem::HistoryItem(
|
||||
@ -651,7 +659,8 @@ HistoryItem::HistoryItem(
|
||||
const QString &postAuthor,
|
||||
not_null<PhotoData*> photo,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup)
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 groupedId)
|
||||
: HistoryItem(
|
||||
history,
|
||||
id,
|
||||
@ -668,6 +677,12 @@ HistoryItem::HistoryItem(
|
||||
const auto spoiler = false;
|
||||
_media = std::make_unique<Data::MediaPhoto>(this, photo, spoiler);
|
||||
setText(caption);
|
||||
if (groupedId) {
|
||||
setGroupId(MessageGroupId::FromRaw(
|
||||
history->peer->id,
|
||||
groupedId,
|
||||
flags & MessageFlag::IsOrWasScheduled));
|
||||
}
|
||||
}
|
||||
|
||||
HistoryItem::HistoryItem(
|
||||
@ -2327,7 +2342,8 @@ bool HistoryItem::requiresSendInlineRight() const {
|
||||
}
|
||||
|
||||
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 requiresInline = requiresSendInlineRight();
|
||||
const auto peer = to->peer();
|
||||
@ -2342,6 +2358,13 @@ std::optional<QString> HistoryItem::errorTextForForward(
|
||||
&& _media->poll()->publicVotes()
|
||||
&& peer->isBroadcast()) {
|
||||
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)) {
|
||||
return tr::lng_forward_cant(tr::now);
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
const TextWithEntities &textWithEntities,
|
||||
const MTPMessageMedia &media,
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 groupedId);
|
||||
uint64 groupedId = 0);
|
||||
HistoryItem( // Local service message.
|
||||
not_null<History*> history,
|
||||
MsgId id,
|
||||
@ -155,7 +155,8 @@ public:
|
||||
const QString &postAuthor,
|
||||
not_null<PhotoData*> photo,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup);
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 groupedId = 0);
|
||||
HistoryItem( // Local document.
|
||||
not_null<History*> history,
|
||||
MsgId id,
|
||||
@ -167,7 +168,8 @@ public:
|
||||
const QString &postAuthor,
|
||||
not_null<DocumentData*> document,
|
||||
const TextWithEntities &caption,
|
||||
HistoryMessageMarkupData &&markup);
|
||||
HistoryMessageMarkupData &&markup,
|
||||
uint64 groupedId = 0);
|
||||
HistoryItem( // Local game.
|
||||
not_null<History*> history,
|
||||
MsgId id,
|
||||
@ -428,7 +430,8 @@ public:
|
||||
[[nodiscard]] ChatRestriction requiredSendRight() const;
|
||||
[[nodiscard]] bool requiresSendInlineRight() const;
|
||||
[[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]] bool translationShowRequiresCheck(LanguageId to) const;
|
||||
bool translationShowRequiresRequest(LanguageId to);
|
||||
|
@ -74,7 +74,7 @@ QString GetErrorTextForSending(
|
||||
}
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ struct SendingErrorRequest {
|
||||
const Data::Story *story = nullptr;
|
||||
const TextWithTags *text = nullptr;
|
||||
bool ignoreSlowmodeCountdown = false;
|
||||
bool isUnquotedForward = false;
|
||||
};
|
||||
[[nodiscard]] QString GetErrorTextForSending(
|
||||
not_null<PeerData*> peer,
|
||||
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_widget.h"
|
||||
|
||||
#include "kotato/kotato_settings.h"
|
||||
#include "kotato/kotato_lang.h"
|
||||
#include "api/api_editing.h"
|
||||
#include "api/api_bot.h"
|
||||
#include "api/api_chat_participants.h"
|
||||
@ -6498,11 +6499,14 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
|
||||
Window::SectionShow::Way::Forward,
|
||||
_editMsgId);
|
||||
} else if (isReadyToForward) {
|
||||
if (e->button() != Qt::LeftButton) {
|
||||
_forwardPanel->editToNextOption();
|
||||
} else {
|
||||
_forwardPanel->editOptions(controller()->uiShow());
|
||||
if (_forwardPanel->items().empty() || e->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
_history->setForwardDraft(MsgId(), {});
|
||||
Window::ShowForwardMessagesBox(controller(), Data::ForwardDraft{
|
||||
.ids = session().data().itemsToIds(_forwardPanel->items())
|
||||
});
|
||||
} else if (_replyTo
|
||||
&& ((e->modifiers() & Qt::ControlModifier)
|
||||
|| (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() {
|
||||
Expects(_history != nullptr);
|
||||
|
||||
@ -7291,10 +7405,17 @@ bool HistoryWidget::sendExistingDocument(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document->hasRemoteLocation()) {
|
||||
Api::SendExistingDocument(
|
||||
Api::MessageToSend(prepareSendAction(options)),
|
||||
document,
|
||||
localId);
|
||||
} else {
|
||||
Api::SendWebDocument(
|
||||
Api::MessageToSend(prepareSendAction(options)),
|
||||
document,
|
||||
localId);
|
||||
}
|
||||
|
||||
if (_fieldAutocomplete->stickersShown()) {
|
||||
clearFieldText();
|
||||
|
@ -299,6 +299,7 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
@ -821,6 +822,7 @@ private:
|
||||
|
||||
int _topDelta = 0;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
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_forum_topic.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/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@ -241,6 +242,7 @@ bool ForwardPanel::empty() const {
|
||||
}
|
||||
|
||||
void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
||||
/*
|
||||
using Options = Data::ForwardOptions;
|
||||
const auto now = _data.options;
|
||||
const auto count = _data.items.size();
|
||||
@ -297,6 +299,7 @@ void ForwardPanel::editOptions(std::shared_ptr<ChatHelpers::Show> show) {
|
||||
},
|
||||
optionsChanged,
|
||||
changeRecipient));
|
||||
*/
|
||||
}
|
||||
|
||||
void ForwardPanel::editToNextOption() {
|
||||
|
@ -366,12 +366,32 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
|
||||
{ "profile_top_mute", {
|
||||
.type = SettingType::BoolSetting,
|
||||
.defaultValue = false, }},
|
||||
{ "forward_retain_selection", {
|
||||
.type = SettingType::BoolSetting,
|
||||
.defaultValue = false, }},
|
||||
{ "forward_on_click", {
|
||||
.type = SettingType::BoolSetting,
|
||||
.defaultValue = false, }},
|
||||
{ "folders/local", {
|
||||
.scope = SettingScope::Account,
|
||||
.type = SettingType::QJsonArraySetting, }},
|
||||
{ "telegram_sites_autologin", {
|
||||
.type = SettingType::BoolSetting,
|
||||
.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;
|
||||
|
@ -48,6 +48,54 @@ namespace Settings {
|
||||
|
||||
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) {
|
||||
switch (boost) {
|
||||
@ -298,6 +346,71 @@ void SetupKotatoForward(not_null<Ui::VerticalLayout*> container) {
|
||||
Ui::AddSkip(container);
|
||||
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::AddDividerText(container, rktr("ktg_settings_forward_chat_on_click_description"));
|
||||
|
@ -537,6 +537,7 @@ bool MainWidget::setForwardDraft(
|
||||
.topicRootId = topicRootId,
|
||||
.forward = &items,
|
||||
.ignoreSlowmodeCountdown = true,
|
||||
.isUnquotedForward = draft.options != Data::ForwardOptions::PreserveInfo,
|
||||
});
|
||||
if (!error.isEmpty()) {
|
||||
_controller->show(Ui::MakeInformBox(error));
|
||||
|
@ -77,7 +77,8 @@ namespace Media::Stories {
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions forwardOptions) {
|
||||
Data::ForwardOptions forwardOptions,
|
||||
Data::GroupingOptions groupingOptions) {
|
||||
if (state->requests) {
|
||||
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/edit_forum_topic_box.h"
|
||||
#include "boxes/peers/edit_contact_box.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "inline_bots/bot_attach_web_view.h" // InlineBots::PeerType.
|
||||
#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_chat_filters.h"
|
||||
#include "api/api_polls.h"
|
||||
#include "api/api_sending.h"
|
||||
#include "api/api_updates.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "mtproto/mtproto_config.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/view/history_view_context_menu.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_topic.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_scheduled_messages.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "core/application.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_menu_icons.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QAction>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
@ -1765,10 +1775,227 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||
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(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
Data::ForwardDraft &&draft,
|
||||
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 owner = &session->data();
|
||||
const auto itemsList = owner->idsToItems(draft.ids);
|
||||
@ -2082,16 +2309,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
}, state->box->lifetime());
|
||||
|
||||
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(
|
||||
|
@ -273,8 +273,8 @@ PRIVATE
|
||||
ui/chat/chat_theme.h
|
||||
ui/chat/continuous_scroll.cpp
|
||||
ui/chat/continuous_scroll.h
|
||||
ui/chat/forward_options_box.cpp
|
||||
ui/chat/forward_options_box.h
|
||||
#ui/chat/forward_options_box.cpp
|
||||
#ui/chat/forward_options_box.h
|
||||
ui/chat/group_call_bar.cpp
|
||||
ui/chat/group_call_bar.h
|
||||
ui/chat/group_call_userpics.cpp
|
||||
|
Loading…
x
Reference in New Issue
Block a user