mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Start shortcut messages sending.
This commit is contained in:
parent
dd7ccada2f
commit
d05c4e0990
38 changed files with 2109 additions and 70 deletions
|
@ -454,6 +454,8 @@ PRIVATE
|
||||||
data/business/data_business_common.h
|
data/business/data_business_common.h
|
||||||
data/business/data_business_info.cpp
|
data/business/data_business_info.cpp
|
||||||
data/business/data_business_info.h
|
data/business/data_business_info.h
|
||||||
|
data/business/data_shortcut_messages.cpp
|
||||||
|
data/business/data_shortcut_messages.h
|
||||||
data/notify/data_notify_settings.cpp
|
data/notify/data_notify_settings.cpp
|
||||||
data/notify/data_notify_settings.h
|
data/notify/data_notify_settings.h
|
||||||
data/notify/data_peer_notify_settings.cpp
|
data/notify/data_peer_notify_settings.cpp
|
||||||
|
@ -1287,6 +1289,8 @@ PRIVATE
|
||||||
profile/profile_cover_drop_area.h
|
profile/profile_cover_drop_area.h
|
||||||
settings/business/settings_away_message.cpp
|
settings/business/settings_away_message.cpp
|
||||||
settings/business/settings_away_message.h
|
settings/business/settings_away_message.h
|
||||||
|
settings/business/settings_shortcut_messages.cpp
|
||||||
|
settings/business/settings_shortcut_messages.h
|
||||||
settings/business/settings_chatbots.cpp
|
settings/business/settings_chatbots.cpp
|
||||||
settings/business/settings_chatbots.h
|
settings/business/settings_chatbots.h
|
||||||
settings/business/settings_greeting.cpp
|
settings/business/settings_greeting.cpp
|
||||||
|
|
|
@ -22,6 +22,7 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
||||||
struct SendOptions {
|
struct SendOptions {
|
||||||
PeerData *sendAs = nullptr;
|
PeerData *sendAs = nullptr;
|
||||||
TimeId scheduled = 0;
|
TimeId scheduled = 0;
|
||||||
|
BusinessShortcutId shortcutId = 0;
|
||||||
bool silent = false;
|
bool silent = false;
|
||||||
bool handleSupportSwitch = false;
|
bool handleSupportSwitch = false;
|
||||||
bool hideViaBot = false;
|
bool hideViaBot = false;
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_updates.h"
|
#include "api/api_updates.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_poll.h"
|
#include "data/data_poll.h"
|
||||||
|
@ -64,6 +65,9 @@ void Polls::create(
|
||||||
if (action.options.scheduled) {
|
if (action.options.scheduled) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
const auto sendAs = action.options.sendAs;
|
const auto sendAs = action.options.sendAs;
|
||||||
if (sendAs) {
|
if (sendAs) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||||
|
@ -85,7 +89,7 @@ void Polls::create(
|
||||||
MTPVector<MTPMessageEntity>(),
|
MTPVector<MTPMessageEntity>(),
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_text_entities.h"
|
#include "api/api_text_entities.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_channel.h" // ChannelData::addsSignature.
|
#include "data/data_channel.h" // ChannelData::addsSignature.
|
||||||
|
@ -128,6 +129,9 @@ void SendExistingMedia(
|
||||||
flags |= MessageFlag::IsOrWasScheduled;
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (message.action.options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
session->data().registerMessageRandomId(randomId, newId);
|
session->data().registerMessageRandomId(randomId, newId);
|
||||||
|
|
||||||
|
@ -146,10 +150,12 @@ void SendExistingMedia(
|
||||||
|
|
||||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
|
const auto session = &history->session();
|
||||||
|
const auto &action = message.action;
|
||||||
const auto usedFileReference = media->fileReference();
|
const auto usedFileReference = media->fileReference();
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
history,
|
history,
|
||||||
message.action.replyTo,
|
action.replyTo,
|
||||||
randomId,
|
randomId,
|
||||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||||
MTP_flags(sendFlags),
|
MTP_flags(sendFlags),
|
||||||
|
@ -160,9 +166,9 @@ void SendExistingMedia(
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
MTPReplyMarkup(),
|
MTPReplyMarkup(),
|
||||||
sentEntities,
|
sentEntities,
|
||||||
MTP_int(message.action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
if (error.code() == 400
|
if (error.code() == 400
|
||||||
|
@ -260,7 +266,10 @@ bool SendDice(MessageToSend &message) {
|
||||||
message.textWithTags = TextWithTags();
|
message.textWithTags = TextWithTags();
|
||||||
message.action.clearDraft = false;
|
message.action.clearDraft = false;
|
||||||
message.action.generateLocal = true;
|
message.action.generateLocal = true;
|
||||||
api->sendAction(message.action);
|
|
||||||
|
|
||||||
|
const auto &action = message.action;
|
||||||
|
api->sendAction(action);
|
||||||
|
|
||||||
const auto newId = FullMsgId(
|
const auto newId = FullMsgId(
|
||||||
peer->id,
|
peer->id,
|
||||||
|
@ -270,17 +279,17 @@ bool SendDice(MessageToSend &message) {
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
auto flags = NewMessageFlags(peer);
|
auto flags = NewMessageFlags(peer);
|
||||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||||
if (message.action.replyTo) {
|
if (action.replyTo) {
|
||||||
flags |= MessageFlag::HasReplyInfo;
|
flags |= MessageFlag::HasReplyInfo;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||||
}
|
}
|
||||||
const auto anonymousPost = peer->amAnonymous();
|
const auto anonymousPost = peer->amAnonymous();
|
||||||
const auto silentPost = ShouldSendSilent(peer, message.action.options);
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
InnerFillMessagePostFlags(action.options, peer, flags);
|
||||||
if (silentPost) {
|
if (silentPost) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||||
}
|
}
|
||||||
const auto sendAs = message.action.options.sendAs;
|
const auto sendAs = action.options.sendAs;
|
||||||
const auto messageFromId = sendAs
|
const auto messageFromId = sendAs
|
||||||
? sendAs->id
|
? sendAs->id
|
||||||
: anonymousPost
|
: anonymousPost
|
||||||
|
@ -293,10 +302,13 @@ bool SendDice(MessageToSend &message) {
|
||||||
? session->user()->name()
|
? session->user()->name()
|
||||||
: QString();
|
: QString();
|
||||||
|
|
||||||
if (message.action.options.scheduled) {
|
if (action.options.scheduled) {
|
||||||
flags |= MessageFlag::IsOrWasScheduled;
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
session->data().registerMessageRandomId(randomId, newId);
|
session->data().registerMessageRandomId(randomId, newId);
|
||||||
|
|
||||||
|
@ -305,8 +317,8 @@ bool SendDice(MessageToSend &message) {
|
||||||
newId.msg,
|
newId.msg,
|
||||||
flags,
|
flags,
|
||||||
viaBotId,
|
viaBotId,
|
||||||
message.action.replyTo,
|
action.replyTo,
|
||||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||||
messageFromId,
|
messageFromId,
|
||||||
messagePostAuthor,
|
messagePostAuthor,
|
||||||
TextWithEntities(),
|
TextWithEntities(),
|
||||||
|
@ -314,7 +326,7 @@ bool SendDice(MessageToSend &message) {
|
||||||
HistoryMessageMarkupData());
|
HistoryMessageMarkupData());
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
history,
|
history,
|
||||||
message.action.replyTo,
|
action.replyTo,
|
||||||
randomId,
|
randomId,
|
||||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||||
MTP_flags(sendFlags),
|
MTP_flags(sendFlags),
|
||||||
|
@ -325,14 +337,14 @@ bool SendDice(MessageToSend &message) {
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
MTPReplyMarkup(),
|
MTPReplyMarkup(),
|
||||||
MTP_vector<MTPMessageEntity>(),
|
MTP_vector<MTPMessageEntity>(),
|
||||||
MTP_int(message.action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
api->sendMessageFail(error, peer, randomId, newId);
|
api->sendMessageFail(error, peer, randomId, newId);
|
||||||
});
|
});
|
||||||
api->finishForwarding(message.action);
|
api->finishForwarding(action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_premium.h"
|
#include "api/api_premium.h"
|
||||||
#include "api/api_user_names.h"
|
#include "api/api_user_names.h"
|
||||||
#include "api/api_websites.h"
|
#include "api/api_websites.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
|
@ -3140,7 +3141,9 @@ void ApiWrap::sharedMediaDone(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::sendAction(const SendAction &action) {
|
void ApiWrap::sendAction(const SendAction &action) {
|
||||||
if (!action.options.scheduled && !action.replaceMediaOf) {
|
if (!action.options.scheduled
|
||||||
|
&& !action.options.shortcutId
|
||||||
|
&& !action.replaceMediaOf) {
|
||||||
const auto topicRootId = action.replyTo.topicRootId;
|
const auto topicRootId = action.replyTo.topicRootId;
|
||||||
const auto topic = topicRootId
|
const auto topic = topicRootId
|
||||||
? action.history->peer->forumTopicFor(topicRootId)
|
? action.history->peer->forumTopicFor(topicRootId)
|
||||||
|
@ -3175,11 +3178,13 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_session->data().sendHistoryChangeNotifications();
|
_session->data().sendHistoryChangeNotifications();
|
||||||
_session->changes().historyUpdated(
|
if (!action.options.shortcutId) {
|
||||||
history,
|
_session->changes().historyUpdated(
|
||||||
(action.options.scheduled
|
history,
|
||||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
(action.options.scheduled
|
||||||
: Data::HistoryUpdate::Flag::MessageSent));
|
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||||
|
: Data::HistoryUpdate::Flag::MessageSent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::forwardMessages(
|
void ApiWrap::forwardMessages(
|
||||||
|
@ -3208,7 +3213,7 @@ void ApiWrap::forwardMessages(
|
||||||
const auto history = action.history;
|
const auto history = action.history;
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
|
|
||||||
if (!action.options.scheduled) {
|
if (!action.options.scheduled && !action.options.shortcutId) {
|
||||||
histories.readInbox(history);
|
histories.readInbox(history);
|
||||||
}
|
}
|
||||||
const auto anonymousPost = peer->amAnonymous();
|
const auto anonymousPost = peer->amAnonymous();
|
||||||
|
@ -3226,6 +3231,9 @@ void ApiWrap::forwardMessages(
|
||||||
flags |= MessageFlag::IsOrWasScheduled;
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
sendFlags |= SendFlag::f_schedule_date;
|
sendFlags |= SendFlag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= SendFlag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
if (draft.options != Data::ForwardOptions::PreserveInfo) {
|
if (draft.options != Data::ForwardOptions::PreserveInfo) {
|
||||||
sendFlags |= SendFlag::f_drop_author;
|
sendFlags |= SendFlag::f_drop_author;
|
||||||
}
|
}
|
||||||
|
@ -3265,7 +3273,7 @@ void ApiWrap::forwardMessages(
|
||||||
MTP_int(topMsgId),
|
MTP_int(topMsgId),
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
applyUpdates(result);
|
applyUpdates(result);
|
||||||
if (shared && !--shared->requestsLeft) {
|
if (shared && !--shared->requestsLeft) {
|
||||||
|
@ -3728,6 +3736,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
|
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
|
||||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
|
||||||
|
mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
const auto viaBotId = UserId();
|
const auto viaBotId = UserId();
|
||||||
lastMessage = history->addNewLocalMessage(
|
lastMessage = history->addNewLocalMessage(
|
||||||
newId.msg,
|
newId.msg,
|
||||||
|
@ -3763,6 +3775,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const auto mtpShortcut = Data::ShortcutIdToMTP(
|
||||||
|
_session,
|
||||||
|
action.options.shortcutId);
|
||||||
if (exactWebPage
|
if (exactWebPage
|
||||||
&& !ignoreWebPage
|
&& !ignoreWebPage
|
||||||
&& (manualWebPage || sending.empty())) {
|
&& (manualWebPage || sending.empty())) {
|
||||||
|
@ -3779,9 +3794,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
MTPReplyMarkup(),
|
MTPReplyMarkup(),
|
||||||
sentEntities,
|
sentEntities,
|
||||||
MTP_int(message.action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
mtpShortcut
|
||||||
), done, fail);
|
), done, fail);
|
||||||
} else {
|
} else {
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
|
@ -3798,7 +3813,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
sentEntities,
|
sentEntities,
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
mtpShortcut
|
||||||
), done, fail);
|
), done, fail);
|
||||||
}
|
}
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
@ -3887,6 +3902,9 @@ void ApiWrap::sendInlineResult(
|
||||||
flags |= MessageFlag::IsOrWasScheduled;
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
sendFlags |= SendFlag::f_schedule_date;
|
sendFlags |= SendFlag::f_schedule_date;
|
||||||
}
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= SendFlag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
if (action.options.hideViaBot) {
|
if (action.options.hideViaBot) {
|
||||||
sendFlags |= SendFlag::f_hide_via;
|
sendFlags |= SendFlag::f_hide_via;
|
||||||
}
|
}
|
||||||
|
@ -3932,7 +3950,7 @@ void ApiWrap::sendInlineResult(
|
||||||
MTP_string(data->getId()),
|
MTP_string(data->getId()),
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
@ -4061,7 +4079,8 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
: Flag(0))
|
: Flag(0))
|
||||||
| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
|
| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
|
||||||
| (options.scheduled ? Flag::f_schedule_date : Flag(0))
|
| (options.scheduled ? Flag::f_schedule_date : Flag(0))
|
||||||
| (options.sendAs ? Flag::f_send_as : Flag(0));
|
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||||
|
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0));
|
||||||
|
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
|
@ -4081,7 +4100,7 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
sentEntities,
|
sentEntities,
|
||||||
MTP_int(options.scheduled),
|
MTP_int(options.scheduled),
|
||||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(_session, options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (done) done(true);
|
if (done) done(true);
|
||||||
if (updateRecentStickers) {
|
if (updateRecentStickers) {
|
||||||
|
@ -4166,7 +4185,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||||
? Flag::f_silent
|
? Flag::f_silent
|
||||||
: Flag(0))
|
: Flag(0))
|
||||||
| (album->options.scheduled ? Flag::f_schedule_date : Flag(0))
|
| (album->options.scheduled ? Flag::f_schedule_date : Flag(0))
|
||||||
| (sendAs ? Flag::f_send_as : Flag(0));
|
| (sendAs ? Flag::f_send_as : Flag(0))
|
||||||
|
| (album->options.shortcutId
|
||||||
|
? Flag::f_quick_reply_shortcut
|
||||||
|
: Flag(0));
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
const auto peer = history->peer;
|
const auto peer = history->peer;
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
|
@ -4180,7 +4202,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||||
MTP_vector<MTPInputSingleMedia>(medias),
|
MTP_vector<MTPInputSingleMedia>(medias),
|
||||||
MTP_int(album->options.scheduled),
|
MTP_int(album->options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(_session, album->options.shortcutId)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
_sendingAlbums.remove(groupId);
|
_sendingAlbums.remove(groupId);
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||||
#include "chat_helpers/share_message_phrase_factory.h"
|
#include "chat_helpers/share_message_phrase_factory.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_game.h"
|
#include "data/data_game.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
@ -1543,11 +1544,15 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||||
const auto threadHistory = thread->owningHistory();
|
const auto threadHistory = thread->owningHistory();
|
||||||
histories.sendRequest(threadHistory, requestType, [=](
|
histories.sendRequest(threadHistory, requestType, [=](
|
||||||
Fn<void()> finish) {
|
Fn<void()> finish) {
|
||||||
auto &api = threadHistory->session().api();
|
const auto session = &threadHistory->session();
|
||||||
|
auto &api = session->api();
|
||||||
const auto sendFlags = commonSendFlags
|
const auto sendFlags = commonSendFlags
|
||||||
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
|
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
|
||||||
| (ShouldSendSilent(peer, options)
|
| (ShouldSendSilent(peer, options)
|
||||||
? Flag::f_silent
|
? Flag::f_silent
|
||||||
|
: Flag(0))
|
||||||
|
| (options.shortcutId
|
||||||
|
? Flag::f_quick_reply_shortcut
|
||||||
: Flag(0));
|
: Flag(0));
|
||||||
threadHistory->sendRequestId = api.request(
|
threadHistory->sendRequestId = api.request(
|
||||||
MTPmessages_ForwardMessages(
|
MTPmessages_ForwardMessages(
|
||||||
|
@ -1559,7 +1564,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||||
MTP_int(topMsgId),
|
MTP_int(topMsgId),
|
||||||
MTP_int(options.scheduled),
|
MTP_int(options.scheduled),
|
||||||
MTP_inputPeerEmpty(), // send_as
|
MTP_inputPeerEmpty(), // send_as
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(session, options.shortcutId)
|
||||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||||
threadHistory->session().api().applyUpdates(updates);
|
threadHistory->session().api().applyUpdates(updates);
|
||||||
state->requests.remove(reqId);
|
state->requests.remove(reqId);
|
||||||
|
|
|
@ -191,7 +191,7 @@ struct AwaySchedule {
|
||||||
struct AwaySettings {
|
struct AwaySettings {
|
||||||
BusinessRecipients recipients;
|
BusinessRecipients recipients;
|
||||||
AwaySchedule schedule;
|
AwaySchedule schedule;
|
||||||
int shortcutId = 0;
|
BusinessShortcutId shortcutId = 0;
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return schedule.type != AwayScheduleType::Never;
|
return schedule.type != AwayScheduleType::Never;
|
||||||
|
@ -205,7 +205,7 @@ struct AwaySettings {
|
||||||
struct GreetingSettings {
|
struct GreetingSettings {
|
||||||
BusinessRecipients recipients;
|
BusinessRecipients recipients;
|
||||||
int noActivityDays = 0;
|
int noActivityDays = 0;
|
||||||
int shortcutId = 0;
|
BusinessShortcutId shortcutId = 0;
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return noActivityDays > 0;
|
return noActivityDays > 0;
|
||||||
|
|
539
Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
Normal file
539
Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
|
|
||||||
|
#include "api/api_hash.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "api/api_text_entities.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item_helpers.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
|
||||||
|
|
||||||
|
[[nodiscard]] MsgId RemoteToLocalMsgId(MsgId id) {
|
||||||
|
Expects(IsServerMsgId(id));
|
||||||
|
|
||||||
|
return ServerMaxMsgId + id + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MsgId LocalToRemoteMsgId(MsgId id) {
|
||||||
|
Expects(IsShortcutMsgId(id));
|
||||||
|
|
||||||
|
return (id - ServerMaxMsgId - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool TooEarlyForRequest(crl::time received) {
|
||||||
|
return (received > 0) && (received + kRequestTimeLimit > crl::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MTPMessage PrepareMessage(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
const MTPMessage &message) {
|
||||||
|
return message.match([&](const MTPDmessageEmpty &data) {
|
||||||
|
return MTP_messageEmpty(
|
||||||
|
data.vflags(),
|
||||||
|
data.vid(),
|
||||||
|
data.vpeer_id() ? *data.vpeer_id() : MTPPeer());
|
||||||
|
}, [&](const MTPDmessageService &data) {
|
||||||
|
return MTP_messageService(
|
||||||
|
data.vflags(),
|
||||||
|
data.vid(),
|
||||||
|
data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
|
||||||
|
data.vpeer_id(),
|
||||||
|
data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
|
||||||
|
data.vdate(),
|
||||||
|
data.vaction(),
|
||||||
|
MTP_int(data.vttl_period().value_or_empty()));
|
||||||
|
}, [&](const MTPDmessage &data) {
|
||||||
|
return MTP_message(
|
||||||
|
MTP_flags(data.vflags().v | MTPDmessage::Flag::f_quick_reply_shortcut_id),
|
||||||
|
data.vid(),
|
||||||
|
data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
|
||||||
|
MTPint(), // from_boosts_applied
|
||||||
|
data.vpeer_id(),
|
||||||
|
data.vsaved_peer_id() ? *data.vsaved_peer_id() : MTPPeer(),
|
||||||
|
data.vfwd_from() ? *data.vfwd_from() : MTPMessageFwdHeader(),
|
||||||
|
MTP_long(data.vvia_bot_id().value_or_empty()),
|
||||||
|
data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
|
||||||
|
data.vdate(),
|
||||||
|
data.vmessage(),
|
||||||
|
data.vmedia() ? *data.vmedia() : MTPMessageMedia(),
|
||||||
|
data.vreply_markup() ? *data.vreply_markup() : MTPReplyMarkup(),
|
||||||
|
(data.ventities()
|
||||||
|
? *data.ventities()
|
||||||
|
: MTPVector<MTPMessageEntity>()),
|
||||||
|
MTP_int(data.vviews().value_or_empty()),
|
||||||
|
MTP_int(data.vforwards().value_or_empty()),
|
||||||
|
data.vreplies() ? *data.vreplies() : MTPMessageReplies(),
|
||||||
|
MTP_int(data.vedit_date().value_or_empty()),
|
||||||
|
MTP_bytes(data.vpost_author().value_or_empty()),
|
||||||
|
MTP_long(data.vgrouped_id().value_or_empty()),
|
||||||
|
MTPMessageReactions(),
|
||||||
|
MTPVector<MTPRestrictionReason>(),
|
||||||
|
MTP_int(data.vttl_period().value_or_empty()),
|
||||||
|
MTP_int(shortcutId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool IsShortcutMsgId(MsgId id) {
|
||||||
|
return (id > ScheduledMaxMsgId) && (id < ShortcutMaxMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortcutMessages::ShortcutMessages(not_null<Session*> owner)
|
||||||
|
: _session(&owner->session())
|
||||||
|
, _history(owner->history(_session->userPeerId()))
|
||||||
|
, _clearTimer([=] { clearOldRequests(); }) {
|
||||||
|
owner->itemRemoved(
|
||||||
|
) | rpl::filter([](not_null<const HistoryItem*> item) {
|
||||||
|
return item->isBusinessShortcut();
|
||||||
|
}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||||
|
remove(item);
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortcutMessages::~ShortcutMessages() {
|
||||||
|
for (const auto &request : _requests) {
|
||||||
|
_session->api().request(request.second.requestId).cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::clearOldRequests() {
|
||||||
|
const auto now = crl::now();
|
||||||
|
while (true) {
|
||||||
|
const auto i = ranges::find_if(_requests, [&](const auto &value) {
|
||||||
|
const auto &request = value.second;
|
||||||
|
return !request.requestId
|
||||||
|
&& (request.lastReceived + kRequestTimeLimit <= now);
|
||||||
|
});
|
||||||
|
if (i == end(_requests)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_requests.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgId ShortcutMessages::localMessageId(MsgId remoteId) const {
|
||||||
|
return RemoteToLocalMsgId(remoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgId ShortcutMessages::lookupId(not_null<const HistoryItem*> item) const {
|
||||||
|
Expects(item->isBusinessShortcut());
|
||||||
|
Expects(!item->isSending());
|
||||||
|
Expects(!item->hasFailed());
|
||||||
|
|
||||||
|
return LocalToRemoteMsgId(item->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShortcutMessages::count(BusinessShortcutId shortcutId) const {
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
return (i != end(_data)) ? i->second.items.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
|
||||||
|
const auto &message = update.vmessage();
|
||||||
|
const auto shortcutId = BusinessShortcutIdFromMessage(message);
|
||||||
|
if (!shortcutId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &list = _data[shortcutId];
|
||||||
|
append(shortcutId, list, message);
|
||||||
|
sort(list);
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::apply(
|
||||||
|
const MTPDupdateDeleteQuickReplyMessages &update) {
|
||||||
|
const auto shortcutId = update.vshortcut_id().v;
|
||||||
|
if (!shortcutId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto i = _data.find(shortcutId);
|
||||||
|
if (i == end(_data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &id : update.vmessages().v) {
|
||||||
|
const auto &list = i->second;
|
||||||
|
const auto j = list.itemById.find(id.v);
|
||||||
|
if (j != end(list.itemById)) {
|
||||||
|
j->second->destroy();
|
||||||
|
i = _data.find(shortcutId);
|
||||||
|
if (i == end(_data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::apply(const MTPDupdateDeleteQuickReply &update) {
|
||||||
|
const auto shortcutId = update.vshortcut_id().v;
|
||||||
|
if (!shortcutId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto i = _data.find(shortcutId);
|
||||||
|
while (i != end(_data)) {
|
||||||
|
Assert(!i->second.itemById.empty());
|
||||||
|
i->second.itemById.back().second->destroy();
|
||||||
|
i = _data.find(shortcutId);
|
||||||
|
}
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::apply(
|
||||||
|
const MTPDupdateMessageID &update,
|
||||||
|
not_null<HistoryItem*> local) {
|
||||||
|
const auto id = update.vid().v;
|
||||||
|
const auto i = _data.find(local->shortcutId());
|
||||||
|
Assert(i != end(_data));
|
||||||
|
auto &list = i->second;
|
||||||
|
const auto j = list.itemById.find(id);
|
||||||
|
if (j != end(list.itemById) || !IsServerMsgId(id)) {
|
||||||
|
local->destroy();
|
||||||
|
} else {
|
||||||
|
Assert(!list.itemById.contains(local->id));
|
||||||
|
local->setRealId(localMessageId(id));
|
||||||
|
list.itemById.emplace(id, local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::appendSending(not_null<HistoryItem*> item) {
|
||||||
|
Expects(item->isSending());
|
||||||
|
Expects(item->isBusinessShortcut());
|
||||||
|
|
||||||
|
const auto shortcutId = item->shortcutId();
|
||||||
|
auto &list = _data[shortcutId];
|
||||||
|
list.items.emplace_back(item);
|
||||||
|
sort(list);
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::removeSending(not_null<HistoryItem*> item) {
|
||||||
|
Expects(item->isSending() || item->hasFailed());
|
||||||
|
Expects(item->isBusinessShortcut());
|
||||||
|
|
||||||
|
item->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ShortcutMessages::updates(BusinessShortcutId shortcutId) {
|
||||||
|
request(shortcutId);
|
||||||
|
|
||||||
|
return _updates.events(
|
||||||
|
) | rpl::filter([=](BusinessShortcutId value) {
|
||||||
|
return (value == shortcutId);
|
||||||
|
}) | rpl::to_empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::MessagesSlice ShortcutMessages::list(BusinessShortcutId shortcutId) {
|
||||||
|
auto result = Data::MessagesSlice();
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
if (i == end(_data)) {
|
||||||
|
const auto i = _requests.find(shortcutId);
|
||||||
|
if (i == end(_requests)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.fullCount = result.skippedAfter = result.skippedBefore = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const auto &list = i->second.items;
|
||||||
|
result.skippedAfter = result.skippedBefore = 0;
|
||||||
|
result.fullCount = int(list.size());
|
||||||
|
result.ids = ranges::views::all(
|
||||||
|
list
|
||||||
|
) | ranges::views::transform(
|
||||||
|
&HistoryItem::fullId
|
||||||
|
) | ranges::to_vector;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::preloadShortcuts() {
|
||||||
|
if (_shortcutsLoaded || _shortcutsRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto owner = &_session->data();
|
||||||
|
_shortcutsRequestId = owner->session().api().request(
|
||||||
|
MTPmessages_GetQuickReplies(MTP_long(_shortcutsHash))
|
||||||
|
).done([=](const MTPmessages_QuickReplies &result) {
|
||||||
|
result.match([&](const MTPDmessages_quickReplies &data) {
|
||||||
|
owner->processUsers(data.vusers());
|
||||||
|
owner->processChats(data.vchats());
|
||||||
|
owner->processMessages(
|
||||||
|
data.vmessages(),
|
||||||
|
NewMessageType::Existing);
|
||||||
|
auto shortcuts = Shortcuts();
|
||||||
|
const auto messages = &owner->shortcutMessages();
|
||||||
|
for (const auto &reply : data.vquick_replies().v) {
|
||||||
|
const auto &data = reply.data();
|
||||||
|
const auto id = BusinessShortcutId(data.vshortcut_id().v);
|
||||||
|
shortcuts.list.emplace(id, Shortcut{
|
||||||
|
.name = qs(data.vshortcut()),
|
||||||
|
.topMessageId = messages->localMessageId(
|
||||||
|
data.vtop_message().v),
|
||||||
|
.count = data.vcount().v,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (auto &[id, shortcut] : _shortcuts.list) {
|
||||||
|
if (id < 0) {
|
||||||
|
shortcuts.list.emplace(id, shortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto changed = !_shortcutsLoaded
|
||||||
|
|| (shortcuts != _shortcuts);
|
||||||
|
if (changed) {
|
||||||
|
_shortcuts = std::move(shortcuts);
|
||||||
|
_shortcutsLoaded = true;
|
||||||
|
_shortcutsChanged.fire({});
|
||||||
|
}
|
||||||
|
}, [&](const MTPDmessages_quickRepliesNotModified &) {
|
||||||
|
if (!_shortcutsLoaded) {
|
||||||
|
_shortcutsLoaded = true;
|
||||||
|
_shortcutsChanged.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Shortcuts &ShortcutMessages::shortcuts() const {
|
||||||
|
return _shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShortcutMessages::shortcutsLoaded() const {
|
||||||
|
return _shortcutsLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ShortcutMessages::shortcutsChanged() const {
|
||||||
|
return _shortcutsChanged.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
BusinessShortcutId ShortcutMessages::emplaceShortcut(QString name) {
|
||||||
|
Expects(_shortcutsLoaded);
|
||||||
|
|
||||||
|
for (auto &[id, shortcut] : _shortcuts.list) {
|
||||||
|
if (shortcut.name == name) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto result = --_localShortcutId;
|
||||||
|
_shortcuts.list.emplace(result, Shortcut{ name });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
|
||||||
|
const auto i = _shortcuts.list.find(id);
|
||||||
|
|
||||||
|
Ensures(i != end(_shortcuts.list));
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::request(BusinessShortcutId shortcutId) {
|
||||||
|
auto &request = _requests[shortcutId];
|
||||||
|
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
const auto hash = (i != end(_data))
|
||||||
|
? countListHash(i->second)
|
||||||
|
: uint64(0);
|
||||||
|
request.requestId = _session->api().request(
|
||||||
|
MTPmessages_GetQuickReplyMessages(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_int(shortcutId),
|
||||||
|
MTPVector<MTPint>(),
|
||||||
|
MTP_long(hash))
|
||||||
|
).done([=](const MTPmessages_Messages &result) {
|
||||||
|
parse(shortcutId, result);
|
||||||
|
}).fail([=] {
|
||||||
|
_requests.remove(shortcutId);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::parse(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
const MTPmessages_Messages &list) {
|
||||||
|
auto &request = _requests[shortcutId];
|
||||||
|
request.lastReceived = crl::now();
|
||||||
|
request.requestId = 0;
|
||||||
|
if (!_clearTimer.isActive()) {
|
||||||
|
_clearTimer.callOnce(kRequestTimeLimit * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.match([&](const MTPDmessages_messagesNotModified &data) {
|
||||||
|
}, [&](const auto &data) {
|
||||||
|
_session->data().processUsers(data.vusers());
|
||||||
|
_session->data().processChats(data.vchats());
|
||||||
|
|
||||||
|
const auto &messages = data.vmessages().v;
|
||||||
|
if (messages.isEmpty()) {
|
||||||
|
clearNotSending(shortcutId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto received = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
auto clear = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
auto &list = _data.emplace(shortcutId, List()).first->second;
|
||||||
|
for (const auto &message : messages) {
|
||||||
|
if (const auto item = append(shortcutId, list, message)) {
|
||||||
|
received.emplace(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &owned : list.items) {
|
||||||
|
const auto item = owned.get();
|
||||||
|
if (!item->isSending() && !received.contains(item)) {
|
||||||
|
clear.emplace(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updated(shortcutId, received, clear);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem *ShortcutMessages::append(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
List &list,
|
||||||
|
const MTPMessage &message) {
|
||||||
|
const auto id = message.match([&](const auto &data) {
|
||||||
|
return data.vid().v;
|
||||||
|
});
|
||||||
|
const auto i = list.itemById.find(id);
|
||||||
|
if (i != end(list.itemById)) {
|
||||||
|
const auto existing = i->second;
|
||||||
|
message.match([&](const MTPDmessage &data) {
|
||||||
|
if (data.is_edit_hide()) {
|
||||||
|
existing->applyEdition(HistoryMessageEdition(_session, data));
|
||||||
|
} else {
|
||||||
|
existing->updateSentContent({
|
||||||
|
qs(data.vmessage()),
|
||||||
|
Api::EntitiesFromMTP(
|
||||||
|
_session,
|
||||||
|
data.ventities().value_or_empty())
|
||||||
|
}, data.vmedia());
|
||||||
|
existing->updateReplyMarkup(
|
||||||
|
HistoryMessageMarkupData(data.vreply_markup()));
|
||||||
|
existing->updateForwardedInfo(data.vfwd_from());
|
||||||
|
}
|
||||||
|
existing->updateDate(data.vdate().v);
|
||||||
|
_history->owner().requestItemTextRefresh(existing);
|
||||||
|
}, [&](const auto &data) {});
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsServerMsgId(id)) {
|
||||||
|
LOG(("API Error: Bad id in quick reply messages: %1.").arg(id));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto item = _session->data().addNewMessage(
|
||||||
|
localMessageId(id),
|
||||||
|
PrepareMessage(shortcutId, message),
|
||||||
|
MessageFlags(), // localFlags
|
||||||
|
NewMessageType::Existing);
|
||||||
|
if (!item
|
||||||
|
|| item->history() != _history
|
||||||
|
|| item->shortcutId() != shortcutId) {
|
||||||
|
LOG(("API Error: Bad data received in quick reply messages."));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
list.items.emplace_back(item);
|
||||||
|
list.itemById.emplace(id, item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::clearNotSending(BusinessShortcutId shortcutId) {
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
if (i == end(_data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto clear = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
for (const auto &owned : i->second.items) {
|
||||||
|
if (!owned->isSending() && !owned->hasFailed()) {
|
||||||
|
clear.emplace(owned.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updated(shortcutId, {}, clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::updated(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
const base::flat_set<not_null<HistoryItem*>> &added,
|
||||||
|
const base::flat_set<not_null<HistoryItem*>> &clear) {
|
||||||
|
if (!clear.empty()) {
|
||||||
|
for (const auto &item : clear) {
|
||||||
|
item->destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
if (i != end(_data)) {
|
||||||
|
sort(i->second);
|
||||||
|
}
|
||||||
|
if (!added.empty() || !clear.empty()) {
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::sort(List &list) {
|
||||||
|
ranges::sort(list.items, ranges::less(), &HistoryItem::position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutMessages::remove(not_null<const HistoryItem*> item) {
|
||||||
|
const auto shortcutId = item->shortcutId();
|
||||||
|
const auto i = _data.find(shortcutId);
|
||||||
|
Assert(i != end(_data));
|
||||||
|
auto &list = i->second;
|
||||||
|
|
||||||
|
if (!item->isSending() && !item->hasFailed()) {
|
||||||
|
list.itemById.remove(lookupId(item));
|
||||||
|
}
|
||||||
|
const auto k = ranges::find(list.items, item, &OwnedItem::get);
|
||||||
|
Assert(k != list.items.end());
|
||||||
|
k->release();
|
||||||
|
list.items.erase(k);
|
||||||
|
|
||||||
|
if (list.items.empty()) {
|
||||||
|
_data.erase(i);
|
||||||
|
}
|
||||||
|
_updates.fire_copy(shortcutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 ShortcutMessages::countListHash(const List &list) const {
|
||||||
|
using namespace Api;
|
||||||
|
|
||||||
|
auto hash = HashInit();
|
||||||
|
auto &&serverside = ranges::views::all(
|
||||||
|
list.items
|
||||||
|
) | ranges::views::filter([](const OwnedItem &item) {
|
||||||
|
return !item->isSending() && !item->hasFailed();
|
||||||
|
}) | ranges::views::reverse;
|
||||||
|
for (const auto &item : serverside) {
|
||||||
|
HashUpdate(hash, lookupId(item.get()).bare);
|
||||||
|
if (const auto edited = item->Get<HistoryMessageEdited>()) {
|
||||||
|
HashUpdate(hash, edited->date);
|
||||||
|
} else {
|
||||||
|
HashUpdate(hash, TimeId(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HashFinalize(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
MTPInputQuickReplyShortcut ShortcutIdToMTP(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
BusinessShortcutId id) {
|
||||||
|
if (id >= 0) {
|
||||||
|
return MTP_inputQuickReplyShortcutId(MTP_int(id));
|
||||||
|
}
|
||||||
|
return MTP_inputQuickReplyShortcut(MTP_string(
|
||||||
|
session->data().shortcutMessages().lookupShortcut(id).name));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
125
Telegram/SourceFiles/data/business/data_shortcut_messages.h
Normal file
125
Telegram/SourceFiles/data/business/data_shortcut_messages.h
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class Session;
|
||||||
|
struct MessagesSlice;
|
||||||
|
|
||||||
|
struct Shortcut {
|
||||||
|
QString name;
|
||||||
|
MsgId topMessageId = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const Shortcut &a,
|
||||||
|
const Shortcut &b) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Shortcuts {
|
||||||
|
base::flat_map<BusinessShortcutId, Shortcut> list;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const Shortcuts &a,
|
||||||
|
const Shortcuts &b) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsShortcutMsgId(MsgId id);
|
||||||
|
|
||||||
|
class ShortcutMessages final {
|
||||||
|
public:
|
||||||
|
explicit ShortcutMessages(not_null<Session*> owner);
|
||||||
|
~ShortcutMessages();
|
||||||
|
|
||||||
|
[[nodiscard]] MsgId lookupId(not_null<const HistoryItem*> item) const;
|
||||||
|
[[nodiscard]] int count(BusinessShortcutId shortcutId) const;
|
||||||
|
[[nodiscard]] MsgId localMessageId(MsgId remoteId) const;
|
||||||
|
|
||||||
|
void apply(const MTPDupdateQuickReplyMessage &update);
|
||||||
|
void apply(const MTPDupdateDeleteQuickReplyMessages &update);
|
||||||
|
void apply(const MTPDupdateDeleteQuickReply &update);
|
||||||
|
void apply(
|
||||||
|
const MTPDupdateMessageID &update,
|
||||||
|
not_null<HistoryItem*> local);
|
||||||
|
|
||||||
|
void appendSending(not_null<HistoryItem*> item);
|
||||||
|
void removeSending(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> updates(BusinessShortcutId shortcutId);
|
||||||
|
[[nodiscard]] Data::MessagesSlice list(BusinessShortcutId shortcutId);
|
||||||
|
|
||||||
|
void preloadShortcuts();
|
||||||
|
[[nodiscard]] const Shortcuts &shortcuts() const;
|
||||||
|
[[nodiscard]] bool shortcutsLoaded() const;
|
||||||
|
[[nodiscard]] rpl::producer<> shortcutsChanged() const;
|
||||||
|
[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
|
||||||
|
[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
|
||||||
|
struct List {
|
||||||
|
std::vector<OwnedItem> items;
|
||||||
|
base::flat_map<MsgId, not_null<HistoryItem*>> itemById;
|
||||||
|
};
|
||||||
|
struct Request {
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
crl::time lastReceived = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void request(BusinessShortcutId shortcutId);
|
||||||
|
void parse(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
const MTPmessages_Messages &list);
|
||||||
|
HistoryItem *append(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
List &list,
|
||||||
|
const MTPMessage &message);
|
||||||
|
void clearNotSending(BusinessShortcutId shortcutId);
|
||||||
|
void updated(
|
||||||
|
BusinessShortcutId shortcutId,
|
||||||
|
const base::flat_set<not_null<HistoryItem*>> &added,
|
||||||
|
const base::flat_set<not_null<HistoryItem*>> &clear);
|
||||||
|
void sort(List &list);
|
||||||
|
void remove(not_null<const HistoryItem*> item);
|
||||||
|
[[nodiscard]] uint64 countListHash(const List &list) const;
|
||||||
|
void clearOldRequests();
|
||||||
|
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
const not_null<History*> _history;
|
||||||
|
|
||||||
|
base::Timer _clearTimer;
|
||||||
|
base::flat_map<BusinessShortcutId, List> _data;
|
||||||
|
base::flat_map<BusinessShortcutId, Request> _requests;
|
||||||
|
rpl::event_stream<BusinessShortcutId> _updates;
|
||||||
|
|
||||||
|
Shortcuts _shortcuts;
|
||||||
|
rpl::event_stream<> _shortcutsChanged;
|
||||||
|
BusinessShortcutId _localShortcutId = 0;
|
||||||
|
uint64 _shortcutsHash = 0;
|
||||||
|
mtpRequestId _shortcutsRequestId = 0;
|
||||||
|
bool _shortcutsLoaded = false;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] MTPInputQuickReplyShortcut ShortcutIdToMTP(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
BusinessShortcutId id);
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -54,6 +54,7 @@ Q_DECLARE_METATYPE(MsgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
using StoryId = int32;
|
using StoryId = int32;
|
||||||
|
using BusinessShortcutId = int32;
|
||||||
|
|
||||||
struct FullStoryId {
|
struct FullStoryId {
|
||||||
PeerId peer = 0;
|
PeerId peer = 0;
|
||||||
|
@ -77,7 +78,8 @@ constexpr auto ServerMaxStoryId = StoryId(1 << 30);
|
||||||
constexpr auto StoryMsgIds = int64(ServerMaxStoryId);
|
constexpr auto StoryMsgIds = int64(ServerMaxStoryId);
|
||||||
constexpr auto EndStoryMsgId = MsgId(StartStoryMsgId.bare + StoryMsgIds);
|
constexpr auto EndStoryMsgId = MsgId(StartStoryMsgId.bare + StoryMsgIds);
|
||||||
constexpr auto ServerMaxMsgId = MsgId(1LL << 56);
|
constexpr auto ServerMaxMsgId = MsgId(1LL << 56);
|
||||||
constexpr auto ScheduledMsgIdsRange = (1LL << 32);
|
constexpr auto ScheduledMaxMsgId = MsgId(ServerMaxMsgId + (1LL << 32));
|
||||||
|
constexpr auto ShortcutMaxMsgId = MsgId(ScheduledMaxMsgId + (1LL << 32));
|
||||||
constexpr auto ShowAtUnreadMsgId = MsgId(0);
|
constexpr auto ShowAtUnreadMsgId = MsgId(0);
|
||||||
|
|
||||||
constexpr auto SpecialMsgIdShift = EndStoryMsgId.bare;
|
constexpr auto SpecialMsgIdShift = EndStoryMsgId.bare;
|
||||||
|
|
|
@ -96,8 +96,7 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool IsScheduledMsgId(MsgId id) {
|
bool IsScheduledMsgId(MsgId id) {
|
||||||
return (id > ServerMaxMsgId)
|
return (id > ServerMaxMsgId) && (id < ScheduledMaxMsgId);
|
||||||
&& (id < ServerMaxMsgId + ScheduledMsgIdsRange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
|
ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
|
||||||
|
|
|
@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
|
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
|
||||||
#include "data/business/data_business_chatbots.h"
|
#include "data/business/data_business_chatbots.h"
|
||||||
#include "data/business/data_business_info.h"
|
#include "data/business/data_business_info.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_bot_app.h"
|
#include "data/data_bot_app.h"
|
||||||
|
@ -256,14 +257,12 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
, _watchForOfflineTimer([=] { checkLocalUsersWentOffline(); })
|
, _watchForOfflineTimer([=] { checkLocalUsersWentOffline(); })
|
||||||
, _groups(this)
|
, _groups(this)
|
||||||
, _chatsFilters(std::make_unique<ChatFilters>(this))
|
, _chatsFilters(std::make_unique<ChatFilters>(this))
|
||||||
, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
|
|
||||||
, _cloudThemes(std::make_unique<CloudThemes>(session))
|
, _cloudThemes(std::make_unique<CloudThemes>(session))
|
||||||
, _sendActionManager(std::make_unique<SendActionManager>())
|
, _sendActionManager(std::make_unique<SendActionManager>())
|
||||||
, _streaming(std::make_unique<Streaming>(this))
|
, _streaming(std::make_unique<Streaming>(this))
|
||||||
, _mediaRotation(std::make_unique<MediaRotation>())
|
, _mediaRotation(std::make_unique<MediaRotation>())
|
||||||
, _histories(std::make_unique<Histories>(this))
|
, _histories(std::make_unique<Histories>(this))
|
||||||
, _stickers(std::make_unique<Stickers>(this))
|
, _stickers(std::make_unique<Stickers>(this))
|
||||||
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this))
|
|
||||||
, _reactions(std::make_unique<Reactions>(this))
|
, _reactions(std::make_unique<Reactions>(this))
|
||||||
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
|
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
|
||||||
, _forumIcons(std::make_unique<ForumIcons>(this))
|
, _forumIcons(std::make_unique<ForumIcons>(this))
|
||||||
|
@ -272,7 +271,10 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
, _stories(std::make_unique<Stories>(this))
|
, _stories(std::make_unique<Stories>(this))
|
||||||
, _savedMessages(std::make_unique<SavedMessages>(this))
|
, _savedMessages(std::make_unique<SavedMessages>(this))
|
||||||
, _chatbots(std::make_unique<Chatbots>(this))
|
, _chatbots(std::make_unique<Chatbots>(this))
|
||||||
, _businessInfo(std::make_unique<BusinessInfo>(this)) {
|
, _businessInfo(std::make_unique<BusinessInfo>(this))
|
||||||
|
, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
|
||||||
|
, _shortcutMessages(std::make_unique<ShortcutMessages>(this))
|
||||||
|
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this)) {
|
||||||
_cache->open(_session->local().cacheKey());
|
_cache->open(_session->local().cacheKey());
|
||||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||||
|
|
||||||
|
@ -396,6 +398,7 @@ void Session::clear() {
|
||||||
|
|
||||||
_histories->unloadAll();
|
_histories->unloadAll();
|
||||||
_scheduledMessages = nullptr;
|
_scheduledMessages = nullptr;
|
||||||
|
_shortcutMessages = nullptr;
|
||||||
_sponsoredMessages = nullptr;
|
_sponsoredMessages = nullptr;
|
||||||
_dependentMessages.clear();
|
_dependentMessages.clear();
|
||||||
base::take(_messages);
|
base::take(_messages);
|
||||||
|
|
|
@ -44,6 +44,7 @@ class Folder;
|
||||||
class LocationPoint;
|
class LocationPoint;
|
||||||
class WallPaper;
|
class WallPaper;
|
||||||
class ScheduledMessages;
|
class ScheduledMessages;
|
||||||
|
class ShortcutMessages;
|
||||||
class SendActionManager;
|
class SendActionManager;
|
||||||
class SponsoredMessages;
|
class SponsoredMessages;
|
||||||
class Reactions;
|
class Reactions;
|
||||||
|
@ -102,6 +103,9 @@ public:
|
||||||
[[nodiscard]] ScheduledMessages &scheduledMessages() const {
|
[[nodiscard]] ScheduledMessages &scheduledMessages() const {
|
||||||
return *_scheduledMessages;
|
return *_scheduledMessages;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] ShortcutMessages &shortcutMessages() const {
|
||||||
|
return *_shortcutMessages;
|
||||||
|
}
|
||||||
[[nodiscard]] SendActionManager &sendActionManager() const {
|
[[nodiscard]] SendActionManager &sendActionManager() const {
|
||||||
return *_sendActionManager;
|
return *_sendActionManager;
|
||||||
}
|
}
|
||||||
|
@ -1058,14 +1062,12 @@ private:
|
||||||
|
|
||||||
Groups _groups;
|
Groups _groups;
|
||||||
const std::unique_ptr<ChatFilters> _chatsFilters;
|
const std::unique_ptr<ChatFilters> _chatsFilters;
|
||||||
std::unique_ptr<ScheduledMessages> _scheduledMessages;
|
|
||||||
const std::unique_ptr<CloudThemes> _cloudThemes;
|
const std::unique_ptr<CloudThemes> _cloudThemes;
|
||||||
const std::unique_ptr<SendActionManager> _sendActionManager;
|
const std::unique_ptr<SendActionManager> _sendActionManager;
|
||||||
const std::unique_ptr<Streaming> _streaming;
|
const std::unique_ptr<Streaming> _streaming;
|
||||||
const std::unique_ptr<MediaRotation> _mediaRotation;
|
const std::unique_ptr<MediaRotation> _mediaRotation;
|
||||||
const std::unique_ptr<Histories> _histories;
|
const std::unique_ptr<Histories> _histories;
|
||||||
const std::unique_ptr<Stickers> _stickers;
|
const std::unique_ptr<Stickers> _stickers;
|
||||||
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
|
|
||||||
const std::unique_ptr<Reactions> _reactions;
|
const std::unique_ptr<Reactions> _reactions;
|
||||||
const std::unique_ptr<EmojiStatuses> _emojiStatuses;
|
const std::unique_ptr<EmojiStatuses> _emojiStatuses;
|
||||||
const std::unique_ptr<ForumIcons> _forumIcons;
|
const std::unique_ptr<ForumIcons> _forumIcons;
|
||||||
|
@ -1075,8 +1077,11 @@ private:
|
||||||
const std::unique_ptr<SavedMessages> _savedMessages;
|
const std::unique_ptr<SavedMessages> _savedMessages;
|
||||||
const std::unique_ptr<Chatbots> _chatbots;
|
const std::unique_ptr<Chatbots> _chatbots;
|
||||||
const std::unique_ptr<BusinessInfo> _businessInfo;
|
const std::unique_ptr<BusinessInfo> _businessInfo;
|
||||||
|
std::unique_ptr<ScheduledMessages> _scheduledMessages;
|
||||||
|
std::unique_ptr<ShortcutMessages> _shortcutMessages;
|
||||||
|
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
|
||||||
|
|
||||||
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
MsgId _nonHistoryEntryId = ShortcutMaxMsgId;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ using SparseUnsortedIdsSlice = AbstractSparseIds<std::vector<MsgId>>;
|
||||||
class SparseIdsMergedSlice {
|
class SparseIdsMergedSlice {
|
||||||
public:
|
public:
|
||||||
using UniversalMsgId = MsgId;
|
using UniversalMsgId = MsgId;
|
||||||
static constexpr MsgId kScheduledTopicId
|
static constexpr MsgId kScheduledTopicId = ScheduledMaxMsgId;
|
||||||
= ServerMaxMsgId + ScheduledMsgIdsRange;
|
|
||||||
|
|
||||||
struct Key {
|
struct Key {
|
||||||
Key(
|
Key(
|
||||||
|
|
|
@ -145,6 +145,15 @@ TimeId DateFromMessage(const MTPmessage &message) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BusinessShortcutId BusinessShortcutIdFromMessage(
|
||||||
|
const MTPmessage &message) {
|
||||||
|
return message.match([](const MTPDmessage &data) {
|
||||||
|
return data.vquick_reply_shortcut_id().value_or_empty();
|
||||||
|
}, [](const auto &) {
|
||||||
|
return BusinessShortcutId();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool GoodStickerDimensions(int width, int height) {
|
bool GoodStickerDimensions(int width, int height) {
|
||||||
// Show all .webp (except very large ones) as stickers,
|
// Show all .webp (except very large ones) as stickers,
|
||||||
// allow to open them in media viewer to see details.
|
// allow to open them in media viewer to see details.
|
||||||
|
|
|
@ -109,10 +109,13 @@ using FilterId = int32;
|
||||||
|
|
||||||
using MessageIdsList = std::vector<FullMsgId>;
|
using MessageIdsList = std::vector<FullMsgId>;
|
||||||
|
|
||||||
PeerId PeerFromMessage(const MTPmessage &message);
|
[[nodiscard]] PeerId PeerFromMessage(const MTPmessage &message);
|
||||||
MTPDmessage::Flags FlagsFromMessage(const MTPmessage &message);
|
[[nodiscard]] MTPDmessage::Flags FlagsFromMessage(
|
||||||
MsgId IdFromMessage(const MTPmessage &message);
|
const MTPmessage &message);
|
||||||
TimeId DateFromMessage(const MTPmessage &message);
|
[[nodiscard]] MsgId IdFromMessage(const MTPmessage &message);
|
||||||
|
[[nodiscard]] TimeId DateFromMessage(const MTPmessage &message);
|
||||||
|
[[nodiscard]] BusinessShortcutId BusinessShortcutIdFromMessage(
|
||||||
|
const MTPmessage &message);
|
||||||
|
|
||||||
[[nodiscard]] inline MTPint MTP_int(MsgId id) noexcept {
|
[[nodiscard]] inline MTPint MTP_int(MsgId id) noexcept {
|
||||||
return MTP_int(id.bare);
|
return MTP_int(id.bare);
|
||||||
|
|
|
@ -210,6 +210,12 @@ public:
|
||||||
[[nodiscard]] bool isSponsored() const;
|
[[nodiscard]] bool isSponsored() const;
|
||||||
[[nodiscard]] bool skipNotification() const;
|
[[nodiscard]] bool skipNotification() const;
|
||||||
[[nodiscard]] bool isUserpicSuggestion() const;
|
[[nodiscard]] bool isUserpicSuggestion() const;
|
||||||
|
[[nodiscard]] BusinessShortcutId shortcutId() const {
|
||||||
|
return _shortcutId;
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool isBusinessShortcut() const {
|
||||||
|
return _shortcutId != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void addLogEntryOriginal(
|
void addLogEntryOriginal(
|
||||||
WebPageId localId,
|
WebPageId localId,
|
||||||
|
@ -662,6 +668,7 @@ private:
|
||||||
TimeId _date = 0;
|
TimeId _date = 0;
|
||||||
TimeId _ttlDestroyAt = 0;
|
TimeId _ttlDestroyAt = 0;
|
||||||
int _boostsApplied = 0;
|
int _boostsApplied = 0;
|
||||||
|
BusinessShortcutId _shortcutId = 0;
|
||||||
|
|
||||||
HistoryView::Element *_mainView = nullptr;
|
HistoryView::Element *_mainView = nullptr;
|
||||||
MessageGroupId _groupId = MessageGroupId();
|
MessageGroupId _groupId = MessageGroupId();
|
||||||
|
|
|
@ -203,6 +203,13 @@ void ContentWidget::applyAdditionalScroll(int additionalScroll) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContentWidget::applyMaxVisibleHeight(int maxVisibleHeight) {
|
||||||
|
if (_maxVisibleHeight != maxVisibleHeight) {
|
||||||
|
_maxVisibleHeight = maxVisibleHeight;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<int> ContentWidget::desiredHeightValue() const {
|
rpl::producer<int> ContentWidget::desiredHeightValue() const {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
return rpl::combine(
|
return rpl::combine(
|
||||||
|
@ -328,6 +335,10 @@ rpl::producer<bool> ContentWidget::desiredBottomShadowVisibility() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Ui::ScrollArea*> ContentWidget::scroll() const {
|
||||||
|
return _scroll.data();
|
||||||
|
}
|
||||||
|
|
||||||
Key ContentMemento::key() const {
|
Key ContentMemento::key() const {
|
||||||
if (const auto topic = this->topic()) {
|
if (const auto topic = this->topic()) {
|
||||||
return Key(topic);
|
return Key(topic);
|
||||||
|
|
|
@ -81,6 +81,7 @@ public:
|
||||||
const QRect &newGeometry,
|
const QRect &newGeometry,
|
||||||
int topDelta);
|
int topDelta);
|
||||||
void applyAdditionalScroll(int additionalScroll);
|
void applyAdditionalScroll(int additionalScroll);
|
||||||
|
void applyMaxVisibleHeight(int maxVisibleHeight);
|
||||||
int scrollTillBottom(int forHeight) const;
|
int scrollTillBottom(int forHeight) const;
|
||||||
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
||||||
[[nodiscard]] virtual const Ui::RoundRect *bottomSkipRounding() const {
|
[[nodiscard]] virtual const Ui::RoundRect *bottomSkipRounding() const {
|
||||||
|
@ -115,9 +116,13 @@ protected:
|
||||||
doSetInnerWidget(std::move(inner)));
|
doSetInnerWidget(std::move(inner)));
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Controller*> controller() const {
|
[[nodiscard]] not_null<Controller*> controller() const {
|
||||||
return _controller;
|
return _controller;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] not_null<Ui::ScrollArea*> scroll() const;
|
||||||
|
[[nodiscard]] int maxVisibleHeight() const {
|
||||||
|
return _maxVisibleHeight;
|
||||||
|
}
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
@ -151,6 +156,7 @@ private:
|
||||||
base::unique_qptr<Ui::RpWidget> _searchWrap = nullptr;
|
base::unique_qptr<Ui::RpWidget> _searchWrap = nullptr;
|
||||||
QPointer<Ui::InputField> _searchField;
|
QPointer<Ui::InputField> _searchField;
|
||||||
int _innerDesiredHeight = 0;
|
int _innerDesiredHeight = 0;
|
||||||
|
int _maxVisibleHeight = 0;
|
||||||
bool _isStackBottom = false;
|
bool _isStackBottom = false;
|
||||||
|
|
||||||
// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
|
// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
|
||||||
|
|
|
@ -291,9 +291,10 @@ QRect LayerWidget::countGeometry(int newWidth) {
|
||||||
const auto newBottom = newTop;
|
const auto newBottom = newTop;
|
||||||
|
|
||||||
const auto bottomRadius = st::boxRadius;
|
const auto bottomRadius = st::boxRadius;
|
||||||
|
const auto maxVisibleHeight = windowHeight - newTop;
|
||||||
// Top rounding is included in _contentHeight.
|
// Top rounding is included in _contentHeight.
|
||||||
auto desiredHeight = _contentHeight + bottomRadius;
|
auto desiredHeight = _contentHeight + bottomRadius;
|
||||||
accumulate_min(desiredHeight, windowHeight - newTop - newBottom);
|
accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
|
||||||
|
|
||||||
// First resize content to new width and get the new desired height.
|
// First resize content to new width and get the new desired height.
|
||||||
const auto contentLeft = 0;
|
const auto contentLeft = 0;
|
||||||
|
@ -308,7 +309,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
|
||||||
|
|
||||||
desiredHeight += additionalScroll;
|
desiredHeight += additionalScroll;
|
||||||
contentHeight += additionalScroll;
|
contentHeight += additionalScroll;
|
||||||
_tillBottom = (newTop + desiredHeight >= windowHeight);
|
_tillBottom = (desiredHeight >= maxVisibleHeight);
|
||||||
if (_tillBottom) {
|
if (_tillBottom) {
|
||||||
additionalScroll += contentBottom;
|
additionalScroll += contentBottom;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +322,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
|
||||||
contentTop,
|
contentTop,
|
||||||
contentWidth,
|
contentWidth,
|
||||||
contentHeight,
|
contentHeight,
|
||||||
}, expanding, additionalScroll);
|
}, expanding, additionalScroll, maxVisibleHeight);
|
||||||
|
|
||||||
return QRect(newLeft, newTop, newWidth, desiredHeight);
|
return QRect(newLeft, newTop, newWidth, desiredHeight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,11 @@ void SectionWidget::init() {
|
||||||
const auto full = !_content->scrollBottomSkip();
|
const auto full = !_content->scrollBottomSkip();
|
||||||
const auto height = size.height() - (full ? 0 : st::boxRadius);
|
const auto height = size.height() - (full ? 0 : st::boxRadius);
|
||||||
const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
|
const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
|
||||||
_content->updateGeometry(wrapGeometry, expanding, additionalScroll);
|
_content->updateGeometry(
|
||||||
|
wrapGeometry,
|
||||||
|
expanding,
|
||||||
|
additionalScroll,
|
||||||
|
size.height());
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_connecting = std::make_unique<Window::ConnectionState>(
|
_connecting = std::make_unique<Window::ConnectionState>(
|
||||||
|
|
|
@ -934,13 +934,17 @@ object_ptr<Ui::RpWidget> WrapWidget::createTopBarSurrogate(
|
||||||
void WrapWidget::updateGeometry(
|
void WrapWidget::updateGeometry(
|
||||||
QRect newGeometry,
|
QRect newGeometry,
|
||||||
bool expanding,
|
bool expanding,
|
||||||
int additionalScroll) {
|
int additionalScroll,
|
||||||
|
int maxVisibleHeight) {
|
||||||
auto scrollChanged = (_additionalScroll != additionalScroll);
|
auto scrollChanged = (_additionalScroll != additionalScroll);
|
||||||
auto geometryChanged = (geometry() != newGeometry);
|
auto geometryChanged = (geometry() != newGeometry);
|
||||||
auto shrinkingContent = (additionalScroll < _additionalScroll);
|
auto shrinkingContent = (additionalScroll < _additionalScroll);
|
||||||
_additionalScroll = additionalScroll;
|
_additionalScroll = additionalScroll;
|
||||||
|
_maxVisibleHeight = maxVisibleHeight;
|
||||||
_expanding = expanding;
|
_expanding = expanding;
|
||||||
|
|
||||||
|
_content->applyMaxVisibleHeight(maxVisibleHeight);
|
||||||
|
|
||||||
if (geometryChanged) {
|
if (geometryChanged) {
|
||||||
if (shrinkingContent) {
|
if (shrinkingContent) {
|
||||||
setGeometry(newGeometry);
|
setGeometry(newGeometry);
|
||||||
|
|
|
@ -124,7 +124,8 @@ public:
|
||||||
void updateGeometry(
|
void updateGeometry(
|
||||||
QRect newGeometry,
|
QRect newGeometry,
|
||||||
bool expanding,
|
bool expanding,
|
||||||
int additionalScroll);
|
int additionalScroll,
|
||||||
|
int maxVisibleHeight);
|
||||||
[[nodiscard]] int scrollBottomSkip() const;
|
[[nodiscard]] int scrollBottomSkip() const;
|
||||||
[[nodiscard]] int scrollTillBottom(int forHeight) const;
|
[[nodiscard]] int scrollTillBottom(int forHeight) const;
|
||||||
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
||||||
|
@ -207,6 +208,7 @@ private:
|
||||||
std::unique_ptr<Controller> _controller;
|
std::unique_ptr<Controller> _controller;
|
||||||
object_ptr<ContentWidget> _content = { nullptr };
|
object_ptr<ContentWidget> _content = { nullptr };
|
||||||
int _additionalScroll = 0;
|
int _additionalScroll = 0;
|
||||||
|
int _maxVisibleHeight = 0;
|
||||||
bool _expanding = false;
|
bool _expanding = false;
|
||||||
rpl::variable<bool> _grabbingForExpanding = false;
|
rpl::variable<bool> _grabbingForExpanding = false;
|
||||||
object_ptr<TopBar> _topBar = { nullptr };
|
object_ptr<TopBar> _topBar = { nullptr };
|
||||||
|
|
|
@ -44,7 +44,10 @@ Widget::Widget(
|
||||||
, _self(controller->key().settingsSelf())
|
, _self(controller->key().settingsSelf())
|
||||||
, _type(controller->section().settingsType())
|
, _type(controller->section().settingsType())
|
||||||
, _inner([&] {
|
, _inner([&] {
|
||||||
auto inner = _type->create(this, controller->parentController());
|
auto inner = _type->create(
|
||||||
|
this,
|
||||||
|
controller->parentController(),
|
||||||
|
scroll());
|
||||||
if (inner->hasFlexibleTopBar()) {
|
if (inner->hasFlexibleTopBar()) {
|
||||||
auto filler = setInnerWidget(object_ptr<Ui::RpWidget>(this));
|
auto filler = setInnerWidget(object_ptr<Ui::RpWidget>(this));
|
||||||
filler->resize(1, 1);
|
filler->resize(1, 1);
|
||||||
|
@ -229,6 +232,12 @@ rpl::producer<QString> Widget::title() {
|
||||||
return _inner->title();
|
return _inner->title();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::paintEvent(QPaintEvent *e) {
|
||||||
|
if (!_inner->paintOuter(this, maxVisibleHeight(), e->rect())) {
|
||||||
|
ContentWidget::paintEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||||
auto result = std::make_shared<Memento>(self(), _type);
|
auto result = std::make_shared<Memento>(self(), _type);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
|
|
@ -84,6 +84,8 @@ private:
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
void restoreState(not_null<Memento*> memento);
|
void restoreState(not_null<Memento*> memento);
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||||
|
|
||||||
not_null<UserData*> _self;
|
not_null<UserData*> _self;
|
||||||
|
|
|
@ -79,7 +79,8 @@ Session::Session(
|
||||||
not_null<Account*> account,
|
not_null<Account*> account,
|
||||||
const MTPUser &user,
|
const MTPUser &user,
|
||||||
std::unique_ptr<SessionSettings> settings)
|
std::unique_ptr<SessionSettings> settings)
|
||||||
: _account(account)
|
: _userId(user.c_user().vid())
|
||||||
|
, _account(account)
|
||||||
, _settings(std::move(settings))
|
, _settings(std::move(settings))
|
||||||
, _changes(std::make_unique<Data::Changes>(this))
|
, _changes(std::make_unique<Data::Changes>(this))
|
||||||
, _api(std::make_unique<ApiWrap>(this))
|
, _api(std::make_unique<ApiWrap>(this))
|
||||||
|
@ -89,7 +90,6 @@ Session::Session(
|
||||||
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
|
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
|
||||||
, _storage(std::make_unique<Storage::Facade>())
|
, _storage(std::make_unique<Storage::Facade>())
|
||||||
, _data(std::make_unique<Data::Session>(this))
|
, _data(std::make_unique<Data::Session>(this))
|
||||||
, _userId(user.c_user().vid())
|
|
||||||
, _user(_data->processUser(user))
|
, _user(_data->processUser(user))
|
||||||
, _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
|
, _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
|
||||||
, _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
|
, _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
|
||||||
|
|
|
@ -199,6 +199,7 @@ private:
|
||||||
|
|
||||||
void parseColorIndices(const MTPDhelp_peerColors &data);
|
void parseColorIndices(const MTPDhelp_peerColors &data);
|
||||||
|
|
||||||
|
const UserId _userId;
|
||||||
const not_null<Account*> _account;
|
const not_null<Account*> _account;
|
||||||
|
|
||||||
const std::unique_ptr<SessionSettings> _settings;
|
const std::unique_ptr<SessionSettings> _settings;
|
||||||
|
@ -212,7 +213,6 @@ private:
|
||||||
|
|
||||||
// _data depends on _downloader / _uploader.
|
// _data depends on _downloader / _uploader.
|
||||||
const std::unique_ptr<Data::Session> _data;
|
const std::unique_ptr<Data::Session> _data;
|
||||||
const UserId _userId;
|
|
||||||
const not_null<UserData*> _user;
|
const not_null<UserData*> _user;
|
||||||
|
|
||||||
// _emojiStickersPack depends on _data.
|
// _emojiStickersPack depends on _data.
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_chat_participant_status.h"
|
#include "data/data_chat_participant_status.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
@ -119,6 +120,7 @@ namespace Media::Stories {
|
||||||
message.action.clearDraft = false;
|
message.action.clearDraft = false;
|
||||||
api->sendMessage(std::move(message));
|
api->sendMessage(std::move(message));
|
||||||
}
|
}
|
||||||
|
const auto session = &thread->session();
|
||||||
const auto threadPeer = thread->peer();
|
const auto threadPeer = thread->peer();
|
||||||
const auto threadHistory = thread->owningHistory();
|
const auto threadHistory = thread->owningHistory();
|
||||||
const auto randomId = base::RandomValue<uint64>();
|
const auto randomId = base::RandomValue<uint64>();
|
||||||
|
@ -132,6 +134,12 @@ namespace Media::Stories {
|
||||||
if (silentPost) {
|
if (silentPost) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||||
}
|
}
|
||||||
|
if (options.scheduled) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
|
}
|
||||||
|
if (options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
const auto done = [=] {
|
const auto done = [=] {
|
||||||
if (!--state->requests) {
|
if (!--state->requests) {
|
||||||
if (show->valid()) {
|
if (show->valid()) {
|
||||||
|
@ -155,7 +163,7 @@ namespace Media::Stories {
|
||||||
MTPVector<MTPMessageEntity>(),
|
MTPVector<MTPMessageEntity>(),
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
MTP_inputPeerEmpty(),
|
MTP_inputPeerEmpty(),
|
||||||
MTPInputQuickReplyShortcut()
|
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||||
), [=](
|
), [=](
|
||||||
const MTPUpdates &result,
|
const MTPUpdates &result,
|
||||||
const MTP::Response &response) {
|
const MTP::Response &response) {
|
||||||
|
|
|
@ -10,10 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/business/data_business_info.h"
|
#include "data/business/data_business_info.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/business/settings_recipients_helper.h"
|
#include "settings/business/settings_recipients_helper.h"
|
||||||
|
#include "settings/business/settings_shortcut_messages.h"
|
||||||
#include "ui/boxes/choose_date_time.h"
|
#include "ui/boxes/choose_date_time.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -37,10 +39,13 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<QString> title() override;
|
[[nodiscard]] rpl::producer<QString> title() override;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupContent(not_null<Window::SessionController*> controller);
|
void setupContent(not_null<Window::SessionController*> controller);
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
|
rpl::event_stream<Type> _showOther;
|
||||||
rpl::variable<Data::BusinessRecipients> _recipients;
|
rpl::variable<Data::BusinessRecipients> _recipients;
|
||||||
rpl::variable<Data::AwaySchedule> _schedule;
|
rpl::variable<Data::AwaySchedule> _schedule;
|
||||||
rpl::variable<bool> _enabled;
|
rpl::variable<bool> _enabled;
|
||||||
|
@ -197,6 +202,10 @@ rpl::producer<QString> AwayMessage::title() {
|
||||||
return tr::lng_away_title();
|
return tr::lng_away_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<Type> AwayMessage::sectionShowOther() {
|
||||||
|
return _showOther.events();
|
||||||
|
}
|
||||||
|
|
||||||
void AwayMessage::setupContent(
|
void AwayMessage::setupContent(
|
||||||
not_null<Window::SessionController*> controller) {
|
not_null<Window::SessionController*> controller) {
|
||||||
using namespace Data;
|
using namespace Data;
|
||||||
|
@ -258,7 +267,9 @@ void AwayMessage::setupContent(
|
||||||
st::settingsButtonLightNoIcon
|
st::settingsButtonLightNoIcon
|
||||||
));
|
));
|
||||||
create->setClickedCallback([=] {
|
create->setClickedCallback([=] {
|
||||||
|
const auto owner = &controller->session().data();
|
||||||
|
const auto id = owner->shortcutMessages().emplaceShortcut("away");
|
||||||
|
_showOther.fire(ShortcutMessagesId(id));
|
||||||
});
|
});
|
||||||
Ui::AddSkip(createInner);
|
Ui::AddSkip(createInner);
|
||||||
Ui::AddDivider(createInner);
|
Ui::AddDivider(createInner);
|
||||||
|
|
|
@ -6,7 +6,7 @@ For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "settings/business/settings_chatbots.h"
|
#include "settings/business/settings_chatbots.h"
|
||||||
//
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/business/data_business_chatbots.h"
|
#include "data/business/data_business_chatbots.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
|
@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/event_filter.h"
|
#include "base/event_filter.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/business/data_business_info.h"
|
#include "data/business/data_business_info.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "settings/business/settings_shortcut_messages.h"
|
||||||
#include "settings/business/settings_recipients_helper.h"
|
#include "settings/business/settings_recipients_helper.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
@ -50,6 +52,7 @@ private:
|
||||||
|
|
||||||
Ui::RoundRect _bottomSkipRounding;
|
Ui::RoundRect _bottomSkipRounding;
|
||||||
|
|
||||||
|
rpl::event_stream<Type> _showOther;
|
||||||
rpl::variable<Data::BusinessRecipients> _recipients;
|
rpl::variable<Data::BusinessRecipients> _recipients;
|
||||||
rpl::variable<int> _noActivityDays;
|
rpl::variable<int> _noActivityDays;
|
||||||
rpl::variable<bool> _enabled;
|
rpl::variable<bool> _enabled;
|
||||||
|
@ -229,6 +232,28 @@ void Greeting::setupContent(
|
||||||
object_ptr<Ui::VerticalLayout>(content)));
|
object_ptr<Ui::VerticalLayout>(content)));
|
||||||
const auto inner = wrap->entity();
|
const auto inner = wrap->entity();
|
||||||
|
|
||||||
|
const auto createWrap = inner->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
inner,
|
||||||
|
object_ptr<Ui::VerticalLayout>(inner)));
|
||||||
|
const auto createInner = createWrap->entity();
|
||||||
|
Ui::AddSkip(createInner);
|
||||||
|
const auto create = createInner->add(object_ptr<Ui::SettingsButton>(
|
||||||
|
createInner,
|
||||||
|
tr::lng_greeting_create(),
|
||||||
|
st::settingsButtonLightNoIcon
|
||||||
|
));
|
||||||
|
create->setClickedCallback([=] {
|
||||||
|
const auto owner = &controller->session().data();
|
||||||
|
const auto id = owner->shortcutMessages().emplaceShortcut("hello");
|
||||||
|
_showOther.fire(ShortcutMessagesId(id));
|
||||||
|
});
|
||||||
|
Ui::AddSkip(createInner);
|
||||||
|
Ui::AddDivider(createInner);
|
||||||
|
|
||||||
|
createWrap->toggleOn(rpl::single(true));
|
||||||
|
|
||||||
|
Ui::AddSkip(inner);
|
||||||
AddBusinessRecipientsSelector(inner, {
|
AddBusinessRecipientsSelector(inner, {
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.title = tr::lng_greeting_recipients(),
|
.title = tr::lng_greeting_recipients(),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "settings/settings_type.h"
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
|
||||||
|
[[nodiscard]] Type ShortcutMessagesId(int shortcutId);
|
||||||
|
|
||||||
|
} // namespace Settings
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer_values.h" // AmPremiumValue.
|
#include "data/data_peer_values.h" // AmPremiumValue.
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/business/data_business_info.h"
|
#include "data/business/data_business_info.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "info/info_wrap_widget.h" // Info::Wrap.
|
#include "info/info_wrap_widget.h" // Info::Wrap.
|
||||||
#include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
|
#include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -359,6 +360,7 @@ void Business::setupContent() {
|
||||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
|
||||||
_controller->session().data().businessInfo().preloadTimezones();
|
_controller->session().data().businessInfo().preloadTimezones();
|
||||||
|
_controller->session().data().shortcutMessages().preloadShortcuts();
|
||||||
|
|
||||||
Ui::AddSkip(content, st::settingsFromFileTop);
|
Ui::AddSkip(content, st::settingsFromFileTop);
|
||||||
|
|
||||||
|
@ -566,7 +568,8 @@ template <>
|
||||||
struct SectionFactory<Business> : AbstractSectionFactory {
|
struct SectionFactory<Business> : AbstractSectionFactory {
|
||||||
object_ptr<AbstractSection> create(
|
object_ptr<AbstractSection> create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> controller
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::ScrollArea*> scroll
|
||||||
) const final override {
|
) const final override {
|
||||||
return object_ptr<Business>(parent, controller);
|
return object_ptr<Business>(parent, controller);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,13 @@ public:
|
||||||
}
|
}
|
||||||
virtual void setStepDataReference(std::any &data) {
|
virtual void setStepDataReference(std::any &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool paintOuter(
|
||||||
|
not_null<QWidget*> outer,
|
||||||
|
int maxVisibleHeight,
|
||||||
|
QRect clip) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class IconType {
|
enum class IconType {
|
||||||
|
|
|
@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
#include "settings/settings_type.h"
|
#include "settings/settings_type.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ScrollArea;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Ui::Menu {
|
namespace Ui::Menu {
|
||||||
struct MenuCallback;
|
struct MenuCallback;
|
||||||
} // namespace Ui::Menu
|
} // namespace Ui::Menu
|
||||||
|
@ -27,7 +31,8 @@ class AbstractSection;
|
||||||
struct AbstractSectionFactory {
|
struct AbstractSectionFactory {
|
||||||
[[nodiscard]] virtual object_ptr<AbstractSection> create(
|
[[nodiscard]] virtual object_ptr<AbstractSection> create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> controller) const = 0;
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::ScrollArea*> scroll) const = 0;
|
||||||
[[nodiscard]] virtual bool hasCustomTopBar() const {
|
[[nodiscard]] virtual bool hasCustomTopBar() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +44,8 @@ template <typename SectionType>
|
||||||
struct SectionFactory : AbstractSectionFactory {
|
struct SectionFactory : AbstractSectionFactory {
|
||||||
object_ptr<AbstractSection> create(
|
object_ptr<AbstractSection> create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> controller
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::ScrollArea*> scroll
|
||||||
) const final override {
|
) const final override {
|
||||||
return object_ptr<SectionType>(parent, controller);
|
return object_ptr<SectionType>(parent, controller);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ struct Factory : AbstractSectionFactory {
|
||||||
|
|
||||||
object_ptr<AbstractSection> create(
|
object_ptr<AbstractSection> create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> controller
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::ScrollArea*> scroll
|
||||||
) const final override {
|
) const final override {
|
||||||
return object_ptr<NotificationsType>(parent, controller, type);
|
return object_ptr<NotificationsType>(parent, controller, type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1267,7 +1267,8 @@ template <>
|
||||||
struct SectionFactory<Premium> : AbstractSectionFactory {
|
struct SectionFactory<Premium> : AbstractSectionFactory {
|
||||||
object_ptr<AbstractSection> create(
|
object_ptr<AbstractSection> create(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<Window::SessionController*> controller
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::ScrollArea*> scroll
|
||||||
) const final override {
|
) const final override {
|
||||||
return object_ptr<Premium>(parent, controller);
|
return object_ptr<Premium>(parent, controller);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue