mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +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_info.cpp
|
||||
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.h
|
||||
data/notify/data_peer_notify_settings.cpp
|
||||
|
@ -1287,6 +1289,8 @@ PRIVATE
|
|||
profile/profile_cover_drop_area.h
|
||||
settings/business/settings_away_message.cpp
|
||||
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.h
|
||||
settings/business/settings_greeting.cpp
|
||||
|
|
|
@ -22,6 +22,7 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
|||
struct SendOptions {
|
||||
PeerData *sendAs = nullptr;
|
||||
TimeId scheduled = 0;
|
||||
BusinessShortcutId shortcutId = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool hideViaBot = false;
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_updates.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/random.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_poll.h"
|
||||
|
@ -64,6 +65,9 @@ void Polls::create(
|
|||
if (action.options.scheduled) {
|
||||
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;
|
||||
if (sendAs) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||
|
@ -85,7 +89,7 @@ void Polls::create(
|
|||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_text_entities.h"
|
||||
#include "base/random.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h" // ChannelData::addsSignature.
|
||||
|
@ -128,6 +129,9 @@ void SendExistingMedia(
|
|||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
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);
|
||||
|
||||
|
@ -146,10 +150,12 @@ void SendExistingMedia(
|
|||
|
||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
auto &histories = history->owner().histories();
|
||||
const auto session = &history->session();
|
||||
const auto &action = message.action;
|
||||
const auto usedFileReference = media->fileReference();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
message.action.replyTo,
|
||||
action.replyTo,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
|
@ -160,9 +166,9 @@ void SendExistingMedia(
|
|||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities,
|
||||
MTP_int(message.action.options.scheduled),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (error.code() == 400
|
||||
|
@ -260,7 +266,10 @@ bool SendDice(MessageToSend &message) {
|
|||
message.textWithTags = TextWithTags();
|
||||
message.action.clearDraft = false;
|
||||
message.action.generateLocal = true;
|
||||
api->sendAction(message.action);
|
||||
|
||||
|
||||
const auto &action = message.action;
|
||||
api->sendAction(action);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
peer->id,
|
||||
|
@ -270,17 +279,17 @@ bool SendDice(MessageToSend &message) {
|
|||
auto &histories = history->owner().histories();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
if (action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, message.action.options);
|
||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
InnerFillMessagePostFlags(action.options, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
const auto sendAs = message.action.options.sendAs;
|
||||
const auto sendAs = action.options.sendAs;
|
||||
const auto messageFromId = sendAs
|
||||
? sendAs->id
|
||||
: anonymousPost
|
||||
|
@ -293,10 +302,13 @@ bool SendDice(MessageToSend &message) {
|
|||
? session->user()->name()
|
||||
: QString();
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
if (action.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
}
|
||||
if (action.options.shortcutId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
|
@ -305,8 +317,8 @@ bool SendDice(MessageToSend &message) {
|
|||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
message.action.replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
action.replyTo,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
TextWithEntities(),
|
||||
|
@ -314,7 +326,7 @@ bool SendDice(MessageToSend &message) {
|
|||
HistoryMessageMarkupData());
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
message.action.replyTo,
|
||||
action.replyTo,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
|
@ -325,14 +337,14 @@ bool SendDice(MessageToSend &message) {
|
|||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTP_vector<MTPMessageEntity>(),
|
||||
MTP_int(message.action.options.scheduled),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
});
|
||||
api->finishForwarding(message.action);
|
||||
api->finishForwarding(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_premium.h"
|
||||
#include "api/api_user_names.h"
|
||||
#include "api/api_websites.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_web_page.h"
|
||||
|
@ -3140,7 +3141,9 @@ void ApiWrap::sharedMediaDone(
|
|||
}
|
||||
|
||||
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 topic = topicRootId
|
||||
? action.history->peer->forumTopicFor(topicRootId)
|
||||
|
@ -3175,11 +3178,13 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
|||
}
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
_session->changes().historyUpdated(
|
||||
history,
|
||||
(action.options.scheduled
|
||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||
: Data::HistoryUpdate::Flag::MessageSent));
|
||||
if (!action.options.shortcutId) {
|
||||
_session->changes().historyUpdated(
|
||||
history,
|
||||
(action.options.scheduled
|
||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||
: Data::HistoryUpdate::Flag::MessageSent));
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::forwardMessages(
|
||||
|
@ -3208,7 +3213,7 @@ void ApiWrap::forwardMessages(
|
|||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
|
||||
if (!action.options.scheduled) {
|
||||
if (!action.options.scheduled && !action.options.shortcutId) {
|
||||
histories.readInbox(history);
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
|
@ -3226,6 +3231,9 @@ void ApiWrap::forwardMessages(
|
|||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= SendFlag::f_schedule_date;
|
||||
}
|
||||
if (action.options.shortcutId) {
|
||||
sendFlags |= SendFlag::f_quick_reply_shortcut;
|
||||
}
|
||||
if (draft.options != Data::ForwardOptions::PreserveInfo) {
|
||||
sendFlags |= SendFlag::f_drop_author;
|
||||
}
|
||||
|
@ -3265,7 +3273,7 @@ void ApiWrap::forwardMessages(
|
|||
MTP_int(topMsgId),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
if (shared && !--shared->requestsLeft) {
|
||||
|
@ -3728,6 +3736,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
sendFlags |= MTPmessages_SendMessage::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();
|
||||
lastMessage = history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
|
@ -3763,6 +3775,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
};
|
||||
const auto mtpShortcut = Data::ShortcutIdToMTP(
|
||||
_session,
|
||||
action.options.shortcutId);
|
||||
if (exactWebPage
|
||||
&& !ignoreWebPage
|
||||
&& (manualWebPage || sending.empty())) {
|
||||
|
@ -3779,9 +3794,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities,
|
||||
MTP_int(message.action.options.scheduled),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
mtpShortcut
|
||||
), done, fail);
|
||||
} else {
|
||||
histories.sendPreparedMessage(
|
||||
|
@ -3798,7 +3813,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
sentEntities,
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
mtpShortcut
|
||||
), done, fail);
|
||||
}
|
||||
isFirst = false;
|
||||
|
@ -3887,6 +3902,9 @@ void ApiWrap::sendInlineResult(
|
|||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= SendFlag::f_schedule_date;
|
||||
}
|
||||
if (action.options.shortcutId) {
|
||||
sendFlags |= SendFlag::f_quick_reply_shortcut;
|
||||
}
|
||||
if (action.options.hideViaBot) {
|
||||
sendFlags |= SendFlag::f_hide_via;
|
||||
}
|
||||
|
@ -3932,7 +3950,7 @@ void ApiWrap::sendInlineResult(
|
|||
MTP_string(data->getId()),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
|
@ -4061,7 +4079,8 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
: Flag(0))
|
||||
| (!sentEntities.v.isEmpty() ? Flag::f_entities : 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();
|
||||
const auto peer = history->peer;
|
||||
|
@ -4081,7 +4100,7 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
sentEntities,
|
||||
MTP_int(options.scheduled),
|
||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(_session, options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (done) done(true);
|
||||
if (updateRecentStickers) {
|
||||
|
@ -4166,7 +4185,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
? Flag::f_silent
|
||||
: 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();
|
||||
const auto peer = history->peer;
|
||||
histories.sendPreparedMessage(
|
||||
|
@ -4180,7 +4202,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
MTP_vector<MTPInputSingleMedia>(medias),
|
||||
MTP_int(album->options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(_session, album->options.shortcutId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
_sendingAlbums.remove(groupId);
|
||||
}, [=](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 "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/share_message_phrase_factory.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_game.h"
|
||||
#include "data/data_histories.h"
|
||||
|
@ -1543,11 +1544,15 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
const auto threadHistory = thread->owningHistory();
|
||||
histories.sendRequest(threadHistory, requestType, [=](
|
||||
Fn<void()> finish) {
|
||||
auto &api = threadHistory->session().api();
|
||||
const auto session = &threadHistory->session();
|
||||
auto &api = session->api();
|
||||
const auto sendFlags = commonSendFlags
|
||||
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (ShouldSendSilent(peer, options)
|
||||
? Flag::f_silent
|
||||
: Flag(0))
|
||||
| (options.shortcutId
|
||||
? Flag::f_quick_reply_shortcut
|
||||
: Flag(0));
|
||||
threadHistory->sendRequestId = api.request(
|
||||
MTPmessages_ForwardMessages(
|
||||
|
@ -1559,7 +1564,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
MTP_int(topMsgId),
|
||||
MTP_int(options.scheduled),
|
||||
MTP_inputPeerEmpty(), // send_as
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(session, options.shortcutId)
|
||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||
threadHistory->session().api().applyUpdates(updates);
|
||||
state->requests.remove(reqId);
|
||||
|
|
|
@ -191,7 +191,7 @@ struct AwaySchedule {
|
|||
struct AwaySettings {
|
||||
BusinessRecipients recipients;
|
||||
AwaySchedule schedule;
|
||||
int shortcutId = 0;
|
||||
BusinessShortcutId shortcutId = 0;
|
||||
|
||||
explicit operator bool() const {
|
||||
return schedule.type != AwayScheduleType::Never;
|
||||
|
@ -205,7 +205,7 @@ struct AwaySettings {
|
|||
struct GreetingSettings {
|
||||
BusinessRecipients recipients;
|
||||
int noActivityDays = 0;
|
||||
int shortcutId = 0;
|
||||
BusinessShortcutId shortcutId = 0;
|
||||
|
||||
explicit operator bool() const {
|
||||
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 BusinessShortcutId = int32;
|
||||
|
||||
struct FullStoryId {
|
||||
PeerId peer = 0;
|
||||
|
@ -77,7 +78,8 @@ constexpr auto ServerMaxStoryId = StoryId(1 << 30);
|
|||
constexpr auto StoryMsgIds = int64(ServerMaxStoryId);
|
||||
constexpr auto EndStoryMsgId = MsgId(StartStoryMsgId.bare + StoryMsgIds);
|
||||
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 SpecialMsgIdShift = EndStoryMsgId.bare;
|
||||
|
|
|
@ -96,8 +96,7 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
|
|||
} // namespace
|
||||
|
||||
bool IsScheduledMsgId(MsgId id) {
|
||||
return (id > ServerMaxMsgId)
|
||||
&& (id < ServerMaxMsgId + ScheduledMsgIdsRange);
|
||||
return (id > ServerMaxMsgId) && (id < ScheduledMaxMsgId);
|
||||
}
|
||||
|
||||
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 "data/business/data_business_chatbots.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_bot_app.h"
|
||||
|
@ -256,14 +257,12 @@ Session::Session(not_null<Main::Session*> session)
|
|||
, _watchForOfflineTimer([=] { checkLocalUsersWentOffline(); })
|
||||
, _groups(this)
|
||||
, _chatsFilters(std::make_unique<ChatFilters>(this))
|
||||
, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
|
||||
, _cloudThemes(std::make_unique<CloudThemes>(session))
|
||||
, _sendActionManager(std::make_unique<SendActionManager>())
|
||||
, _streaming(std::make_unique<Streaming>(this))
|
||||
, _mediaRotation(std::make_unique<MediaRotation>())
|
||||
, _histories(std::make_unique<Histories>(this))
|
||||
, _stickers(std::make_unique<Stickers>(this))
|
||||
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this))
|
||||
, _reactions(std::make_unique<Reactions>(this))
|
||||
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
|
||||
, _forumIcons(std::make_unique<ForumIcons>(this))
|
||||
|
@ -272,7 +271,10 @@ Session::Session(not_null<Main::Session*> session)
|
|||
, _stories(std::make_unique<Stories>(this))
|
||||
, _savedMessages(std::make_unique<SavedMessages>(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());
|
||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||
|
||||
|
@ -396,6 +398,7 @@ void Session::clear() {
|
|||
|
||||
_histories->unloadAll();
|
||||
_scheduledMessages = nullptr;
|
||||
_shortcutMessages = nullptr;
|
||||
_sponsoredMessages = nullptr;
|
||||
_dependentMessages.clear();
|
||||
base::take(_messages);
|
||||
|
|
|
@ -44,6 +44,7 @@ class Folder;
|
|||
class LocationPoint;
|
||||
class WallPaper;
|
||||
class ScheduledMessages;
|
||||
class ShortcutMessages;
|
||||
class SendActionManager;
|
||||
class SponsoredMessages;
|
||||
class Reactions;
|
||||
|
@ -102,6 +103,9 @@ public:
|
|||
[[nodiscard]] ScheduledMessages &scheduledMessages() const {
|
||||
return *_scheduledMessages;
|
||||
}
|
||||
[[nodiscard]] ShortcutMessages &shortcutMessages() const {
|
||||
return *_shortcutMessages;
|
||||
}
|
||||
[[nodiscard]] SendActionManager &sendActionManager() const {
|
||||
return *_sendActionManager;
|
||||
}
|
||||
|
@ -1058,14 +1062,12 @@ private:
|
|||
|
||||
Groups _groups;
|
||||
const std::unique_ptr<ChatFilters> _chatsFilters;
|
||||
std::unique_ptr<ScheduledMessages> _scheduledMessages;
|
||||
const std::unique_ptr<CloudThemes> _cloudThemes;
|
||||
const std::unique_ptr<SendActionManager> _sendActionManager;
|
||||
const std::unique_ptr<Streaming> _streaming;
|
||||
const std::unique_ptr<MediaRotation> _mediaRotation;
|
||||
const std::unique_ptr<Histories> _histories;
|
||||
const std::unique_ptr<Stickers> _stickers;
|
||||
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
|
||||
const std::unique_ptr<Reactions> _reactions;
|
||||
const std::unique_ptr<EmojiStatuses> _emojiStatuses;
|
||||
const std::unique_ptr<ForumIcons> _forumIcons;
|
||||
|
@ -1075,8 +1077,11 @@ private:
|
|||
const std::unique_ptr<SavedMessages> _savedMessages;
|
||||
const std::unique_ptr<Chatbots> _chatbots;
|
||||
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;
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ using SparseUnsortedIdsSlice = AbstractSparseIds<std::vector<MsgId>>;
|
|||
class SparseIdsMergedSlice {
|
||||
public:
|
||||
using UniversalMsgId = MsgId;
|
||||
static constexpr MsgId kScheduledTopicId
|
||||
= ServerMaxMsgId + ScheduledMsgIdsRange;
|
||||
static constexpr MsgId kScheduledTopicId = ScheduledMaxMsgId;
|
||||
|
||||
struct 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) {
|
||||
// Show all .webp (except very large ones) as stickers,
|
||||
// allow to open them in media viewer to see details.
|
||||
|
|
|
@ -109,10 +109,13 @@ using FilterId = int32;
|
|||
|
||||
using MessageIdsList = std::vector<FullMsgId>;
|
||||
|
||||
PeerId PeerFromMessage(const MTPmessage &message);
|
||||
MTPDmessage::Flags FlagsFromMessage(const MTPmessage &message);
|
||||
MsgId IdFromMessage(const MTPmessage &message);
|
||||
TimeId DateFromMessage(const MTPmessage &message);
|
||||
[[nodiscard]] PeerId PeerFromMessage(const MTPmessage &message);
|
||||
[[nodiscard]] MTPDmessage::Flags FlagsFromMessage(
|
||||
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 {
|
||||
return MTP_int(id.bare);
|
||||
|
|
|
@ -210,6 +210,12 @@ public:
|
|||
[[nodiscard]] bool isSponsored() const;
|
||||
[[nodiscard]] bool skipNotification() const;
|
||||
[[nodiscard]] bool isUserpicSuggestion() const;
|
||||
[[nodiscard]] BusinessShortcutId shortcutId() const {
|
||||
return _shortcutId;
|
||||
}
|
||||
[[nodiscard]] bool isBusinessShortcut() const {
|
||||
return _shortcutId != 0;
|
||||
}
|
||||
|
||||
void addLogEntryOriginal(
|
||||
WebPageId localId,
|
||||
|
@ -662,6 +668,7 @@ private:
|
|||
TimeId _date = 0;
|
||||
TimeId _ttlDestroyAt = 0;
|
||||
int _boostsApplied = 0;
|
||||
BusinessShortcutId _shortcutId = 0;
|
||||
|
||||
HistoryView::Element *_mainView = nullptr;
|
||||
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 {
|
||||
using namespace rpl::mappers;
|
||||
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 {
|
||||
if (const auto topic = this->topic()) {
|
||||
return Key(topic);
|
||||
|
|
|
@ -81,6 +81,7 @@ public:
|
|||
const QRect &newGeometry,
|
||||
int topDelta);
|
||||
void applyAdditionalScroll(int additionalScroll);
|
||||
void applyMaxVisibleHeight(int maxVisibleHeight);
|
||||
int scrollTillBottom(int forHeight) const;
|
||||
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
||||
[[nodiscard]] virtual const Ui::RoundRect *bottomSkipRounding() const {
|
||||
|
@ -115,9 +116,13 @@ protected:
|
|||
doSetInnerWidget(std::move(inner)));
|
||||
}
|
||||
|
||||
not_null<Controller*> controller() const {
|
||||
[[nodiscard]] not_null<Controller*> controller() const {
|
||||
return _controller;
|
||||
}
|
||||
[[nodiscard]] not_null<Ui::ScrollArea*> scroll() const;
|
||||
[[nodiscard]] int maxVisibleHeight() const {
|
||||
return _maxVisibleHeight;
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
@ -151,6 +156,7 @@ private:
|
|||
base::unique_qptr<Ui::RpWidget> _searchWrap = nullptr;
|
||||
QPointer<Ui::InputField> _searchField;
|
||||
int _innerDesiredHeight = 0;
|
||||
int _maxVisibleHeight = 0;
|
||||
bool _isStackBottom = false;
|
||||
|
||||
// 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 bottomRadius = st::boxRadius;
|
||||
const auto maxVisibleHeight = windowHeight - newTop;
|
||||
// Top rounding is included in _contentHeight.
|
||||
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.
|
||||
const auto contentLeft = 0;
|
||||
|
@ -308,7 +309,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
|
|||
|
||||
desiredHeight += additionalScroll;
|
||||
contentHeight += additionalScroll;
|
||||
_tillBottom = (newTop + desiredHeight >= windowHeight);
|
||||
_tillBottom = (desiredHeight >= maxVisibleHeight);
|
||||
if (_tillBottom) {
|
||||
additionalScroll += contentBottom;
|
||||
}
|
||||
|
@ -321,7 +322,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
|
|||
contentTop,
|
||||
contentWidth,
|
||||
contentHeight,
|
||||
}, expanding, additionalScroll);
|
||||
}, expanding, additionalScroll, maxVisibleHeight);
|
||||
|
||||
return QRect(newLeft, newTop, newWidth, desiredHeight);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,11 @@ void SectionWidget::init() {
|
|||
const auto full = !_content->scrollBottomSkip();
|
||||
const auto height = size.height() - (full ? 0 : st::boxRadius);
|
||||
const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
|
||||
_content->updateGeometry(wrapGeometry, expanding, additionalScroll);
|
||||
_content->updateGeometry(
|
||||
wrapGeometry,
|
||||
expanding,
|
||||
additionalScroll,
|
||||
size.height());
|
||||
}, lifetime());
|
||||
|
||||
_connecting = std::make_unique<Window::ConnectionState>(
|
||||
|
|
|
@ -934,13 +934,17 @@ object_ptr<Ui::RpWidget> WrapWidget::createTopBarSurrogate(
|
|||
void WrapWidget::updateGeometry(
|
||||
QRect newGeometry,
|
||||
bool expanding,
|
||||
int additionalScroll) {
|
||||
int additionalScroll,
|
||||
int maxVisibleHeight) {
|
||||
auto scrollChanged = (_additionalScroll != additionalScroll);
|
||||
auto geometryChanged = (geometry() != newGeometry);
|
||||
auto shrinkingContent = (additionalScroll < _additionalScroll);
|
||||
_additionalScroll = additionalScroll;
|
||||
_maxVisibleHeight = maxVisibleHeight;
|
||||
_expanding = expanding;
|
||||
|
||||
_content->applyMaxVisibleHeight(maxVisibleHeight);
|
||||
|
||||
if (geometryChanged) {
|
||||
if (shrinkingContent) {
|
||||
setGeometry(newGeometry);
|
||||
|
|
|
@ -124,7 +124,8 @@ public:
|
|||
void updateGeometry(
|
||||
QRect newGeometry,
|
||||
bool expanding,
|
||||
int additionalScroll);
|
||||
int additionalScroll,
|
||||
int maxVisibleHeight);
|
||||
[[nodiscard]] int scrollBottomSkip() const;
|
||||
[[nodiscard]] int scrollTillBottom(int forHeight) const;
|
||||
[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
|
||||
|
@ -207,6 +208,7 @@ private:
|
|||
std::unique_ptr<Controller> _controller;
|
||||
object_ptr<ContentWidget> _content = { nullptr };
|
||||
int _additionalScroll = 0;
|
||||
int _maxVisibleHeight = 0;
|
||||
bool _expanding = false;
|
||||
rpl::variable<bool> _grabbingForExpanding = false;
|
||||
object_ptr<TopBar> _topBar = { nullptr };
|
||||
|
|
|
@ -44,7 +44,10 @@ Widget::Widget(
|
|||
, _self(controller->key().settingsSelf())
|
||||
, _type(controller->section().settingsType())
|
||||
, _inner([&] {
|
||||
auto inner = _type->create(this, controller->parentController());
|
||||
auto inner = _type->create(
|
||||
this,
|
||||
controller->parentController(),
|
||||
scroll());
|
||||
if (inner->hasFlexibleTopBar()) {
|
||||
auto filler = setInnerWidget(object_ptr<Ui::RpWidget>(this));
|
||||
filler->resize(1, 1);
|
||||
|
@ -229,6 +232,12 @@ rpl::producer<QString> Widget::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() {
|
||||
auto result = std::make_shared<Memento>(self(), _type);
|
||||
saveState(result.get());
|
||||
|
|
|
@ -84,6 +84,8 @@ private:
|
|||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
not_null<UserData*> _self;
|
||||
|
|
|
@ -79,7 +79,8 @@ Session::Session(
|
|||
not_null<Account*> account,
|
||||
const MTPUser &user,
|
||||
std::unique_ptr<SessionSettings> settings)
|
||||
: _account(account)
|
||||
: _userId(user.c_user().vid())
|
||||
, _account(account)
|
||||
, _settings(std::move(settings))
|
||||
, _changes(std::make_unique<Data::Changes>(this))
|
||||
, _api(std::make_unique<ApiWrap>(this))
|
||||
|
@ -89,7 +90,6 @@ Session::Session(
|
|||
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
|
||||
, _storage(std::make_unique<Storage::Facade>())
|
||||
, _data(std::make_unique<Data::Session>(this))
|
||||
, _userId(user.c_user().vid())
|
||||
, _user(_data->processUser(user))
|
||||
, _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
|
||||
, _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
|
||||
|
|
|
@ -199,6 +199,7 @@ private:
|
|||
|
||||
void parseColorIndices(const MTPDhelp_peerColors &data);
|
||||
|
||||
const UserId _userId;
|
||||
const not_null<Account*> _account;
|
||||
|
||||
const std::unique_ptr<SessionSettings> _settings;
|
||||
|
@ -212,7 +213,6 @@ private:
|
|||
|
||||
// _data depends on _downloader / _uploader.
|
||||
const std::unique_ptr<Data::Session> _data;
|
||||
const UserId _userId;
|
||||
const not_null<UserData*> _user;
|
||||
|
||||
// _emojiStickersPack depends on _data.
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/random.h"
|
||||
#include "boxes/share_box.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_forum_topic.h"
|
||||
#include "data/data_histories.h"
|
||||
|
@ -119,6 +120,7 @@ namespace Media::Stories {
|
|||
message.action.clearDraft = false;
|
||||
api->sendMessage(std::move(message));
|
||||
}
|
||||
const auto session = &thread->session();
|
||||
const auto threadPeer = thread->peer();
|
||||
const auto threadHistory = thread->owningHistory();
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
|
@ -132,6 +134,12 @@ namespace Media::Stories {
|
|||
if (silentPost) {
|
||||
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 = [=] {
|
||||
if (!--state->requests) {
|
||||
if (show->valid()) {
|
||||
|
@ -155,7 +163,7 @@ namespace Media::Stories {
|
|||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(action.options.scheduled),
|
||||
MTP_inputPeerEmpty(),
|
||||
MTPInputQuickReplyShortcut()
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId)
|
||||
), [=](
|
||||
const MTPUpdates &result,
|
||||
const MTP::Response &response) {
|
||||
|
|
|
@ -10,10 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/business/settings_recipients_helper.h"
|
||||
#include "settings/business/settings_shortcut_messages.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
@ -37,10 +39,13 @@ public:
|
|||
|
||||
[[nodiscard]] rpl::producer<QString> title() override;
|
||||
|
||||
[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
|
||||
|
||||
private:
|
||||
void setupContent(not_null<Window::SessionController*> controller);
|
||||
void save();
|
||||
|
||||
rpl::event_stream<Type> _showOther;
|
||||
rpl::variable<Data::BusinessRecipients> _recipients;
|
||||
rpl::variable<Data::AwaySchedule> _schedule;
|
||||
rpl::variable<bool> _enabled;
|
||||
|
@ -197,6 +202,10 @@ rpl::producer<QString> AwayMessage::title() {
|
|||
return tr::lng_away_title();
|
||||
}
|
||||
|
||||
rpl::producer<Type> AwayMessage::sectionShowOther() {
|
||||
return _showOther.events();
|
||||
}
|
||||
|
||||
void AwayMessage::setupContent(
|
||||
not_null<Window::SessionController*> controller) {
|
||||
using namespace Data;
|
||||
|
@ -258,7 +267,9 @@ void AwayMessage::setupContent(
|
|||
st::settingsButtonLightNoIcon
|
||||
));
|
||||
create->setClickedCallback([=] {
|
||||
|
||||
const auto owner = &controller->session().data();
|
||||
const auto id = owner->shortcutMessages().emplaceShortcut("away");
|
||||
_showOther.fire(ShortcutMessagesId(id));
|
||||
});
|
||||
Ui::AddSkip(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
|
||||
*/
|
||||
#include "settings/business/settings_chatbots.h"
|
||||
//
|
||||
|
||||
#include "core/application.h"
|
||||
#include "data/business/data_business_chatbots.h"
|
||||
#include "data/data_session.h"
|
||||
|
|
|
@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/event_filter.h"
|
||||
#include "core/application.h"
|
||||
#include "data/business/data_business_info.h"
|
||||
#include "data/business/data_shortcut_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/business/settings_shortcut_messages.h"
|
||||
#include "settings/business/settings_recipients_helper.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
|
@ -50,6 +52,7 @@ private:
|
|||
|
||||
Ui::RoundRect _bottomSkipRounding;
|
||||
|
||||
rpl::event_stream<Type> _showOther;
|
||||
rpl::variable<Data::BusinessRecipients> _recipients;
|
||||
rpl::variable<int> _noActivityDays;
|
||||
rpl::variable<bool> _enabled;
|
||||
|
@ -229,6 +232,28 @@ void Greeting::setupContent(
|
|||
object_ptr<Ui::VerticalLayout>(content)));
|
||||
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, {
|
||||
.controller = controller,
|
||||
.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_session.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/settings/info_settings_widget.h" // SectionCustomTopBarData.
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -359,6 +360,7 @@ void Business::setupContent() {
|
|||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
|
||||
_controller->session().data().businessInfo().preloadTimezones();
|
||||
_controller->session().data().shortcutMessages().preloadShortcuts();
|
||||
|
||||
Ui::AddSkip(content, st::settingsFromFileTop);
|
||||
|
||||
|
@ -566,7 +568,8 @@ template <>
|
|||
struct SectionFactory<Business> : AbstractSectionFactory {
|
||||
object_ptr<AbstractSection> create(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::ScrollArea*> scroll
|
||||
) const final override {
|
||||
return object_ptr<Business>(parent, controller);
|
||||
}
|
||||
|
|
|
@ -90,6 +90,13 @@ public:
|
|||
}
|
||||
virtual void setStepDataReference(std::any &data) {
|
||||
}
|
||||
|
||||
virtual bool paintOuter(
|
||||
not_null<QWidget*> outer,
|
||||
int maxVisibleHeight,
|
||||
QRect clip) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
enum class IconType {
|
||||
|
|
|
@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/object_ptr.h"
|
||||
#include "settings/settings_type.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Ui::Menu {
|
||||
struct MenuCallback;
|
||||
} // namespace Ui::Menu
|
||||
|
@ -27,7 +31,8 @@ class AbstractSection;
|
|||
struct AbstractSectionFactory {
|
||||
[[nodiscard]] virtual object_ptr<AbstractSection> create(
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
|
@ -39,7 +44,8 @@ template <typename SectionType>
|
|||
struct SectionFactory : AbstractSectionFactory {
|
||||
object_ptr<AbstractSection> create(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::ScrollArea*> scroll
|
||||
) const final override {
|
||||
return object_ptr<SectionType>(parent, controller);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ struct Factory : AbstractSectionFactory {
|
|||
|
||||
object_ptr<AbstractSection> create(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::ScrollArea*> scroll
|
||||
) const final override {
|
||||
return object_ptr<NotificationsType>(parent, controller, type);
|
||||
}
|
||||
|
|
|
@ -1267,7 +1267,8 @@ template <>
|
|||
struct SectionFactory<Premium> : AbstractSectionFactory {
|
||||
object_ptr<AbstractSection> create(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::ScrollArea*> scroll
|
||||
) const final override {
|
||||
return object_ptr<Premium>(parent, controller);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue