mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 21:27:07 +02:00
Update API scheme on layer 148: Drafts in topics.
This commit is contained in:
parent
791addd0ee
commit
89d0a71591
60 changed files with 861 additions and 541 deletions
|
@ -336,7 +336,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
|
|||
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
|
||||
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
|
||||
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
|
||||
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
|
||||
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update;
|
||||
updateReadFeaturedStickers#571d2742 = Update;
|
||||
updateRecentStickers#9a422c20 = Update;
|
||||
updateConfig#a229dd06 = Update;
|
||||
|
@ -389,7 +389,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
|
|||
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
|
||||
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
|
||||
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
|
||||
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update;
|
||||
updateAttachMenuBots#17b7a20b = Update;
|
||||
updateWebViewResultSent#1592b79d query_id:long = Update;
|
||||
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
|
||||
|
@ -593,6 +593,7 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
|
|||
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
|
||||
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
||||
|
||||
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
||||
|
||||
|
@ -958,12 +959,17 @@ channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdmi
|
|||
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
|
||||
|
||||
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter;
|
||||
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
|
||||
|
||||
popularContact#5ce14175 client_id:long importers:int = PopularContact;
|
||||
|
||||
|
@ -1322,7 +1328,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
|
|||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
|
||||
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
|
||||
|
@ -1459,7 +1465,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword
|
|||
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
|
||||
|
||||
forumTopicDeleted#23f109b id:int = ForumTopic;
|
||||
forumTopic#5920d6dc flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings = ForumTopic;
|
||||
forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;
|
||||
|
||||
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;
|
||||
|
||||
|
@ -1611,8 +1617,8 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
|
|||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMessage#6460114f flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.8?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMedia#a2e8e1de flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.8?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||
|
@ -1656,14 +1662,14 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
|
|||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendInlineBotResult#15b11c73 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.8?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getAllDrafts#6a3f8d65 = Updates;
|
||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||
|
@ -1692,7 +1698,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
|
|||
messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
|
||||
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMultiMedia#68463a19 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.8?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
|
||||
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
|
||||
|
@ -1865,6 +1871,7 @@ channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
|
|||
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
|
||||
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
|
||||
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
|
||||
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
|
||||
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
|
||||
|
|
|
@ -351,6 +351,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
|||
case ButtonType::RequestPhone: {
|
||||
HideSingleUseKeyboard(controller, item);
|
||||
const auto itemId = item->id;
|
||||
const auto topicRootId = item->topicRootId();
|
||||
const auto history = item->history();
|
||||
controller->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_bot_share_phone(),
|
||||
|
@ -362,6 +363,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
|||
auto action = Api::SendAction(history);
|
||||
action.clearDraft = false;
|
||||
action.replyTo = itemId;
|
||||
action.topicRootId = topicRootId;
|
||||
history->session().api().shareContact(
|
||||
history->session().user(),
|
||||
action);
|
||||
|
@ -381,10 +383,12 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
|||
}
|
||||
}
|
||||
const auto replyToId = MsgId(0);
|
||||
const auto topicRootId = MsgId(0);
|
||||
Window::PeerMenuCreatePoll(
|
||||
controller,
|
||||
item->history()->peer,
|
||||
replyToId,
|
||||
topicRootId,
|
||||
chosen,
|
||||
disabled);
|
||||
} break;
|
||||
|
|
|
@ -37,6 +37,7 @@ struct SendAction {
|
|||
not_null<History*> history;
|
||||
SendOptions options;
|
||||
MsgId replyTo = 0;
|
||||
MsgId topicRootId = 0;
|
||||
bool clearDraft = true;
|
||||
bool generateLocal = true;
|
||||
MsgId replaceMediaOf = 0;
|
||||
|
|
|
@ -42,16 +42,20 @@ void Polls::create(
|
|||
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto topicRootId = action.replyTo ? action.topicRootId : 0;
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (action.replyTo) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto clearCloudDraft = action.clearDraft;
|
||||
if (clearCloudDraft) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||
history->clearLocalDraft();
|
||||
history->clearCloudDraft();
|
||||
history->startSavingCloudDraft();
|
||||
history->clearLocalDraft(topicRootId);
|
||||
history->clearCloudDraft(topicRootId);
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
}
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
if (silentPost) {
|
||||
|
@ -65,16 +69,17 @@ void Polls::create(
|
|||
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||
}
|
||||
auto &histories = history->owner().histories();
|
||||
const auto replyTo = action.replyTo;
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
action.replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
PollDataToInputMedia(&data),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
|
@ -85,6 +90,7 @@ void Polls::create(
|
|||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
_session->changes().historyUpdated(
|
||||
|
@ -96,6 +102,7 @@ void Polls::create(
|
|||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
fail();
|
||||
|
|
|
@ -86,6 +86,9 @@ void SendExistingMedia(
|
|||
if (message.action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (message.action.topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, message.action.options);
|
||||
|
@ -118,7 +121,6 @@ void SendExistingMedia(
|
|||
if (!sentEntities.v.isEmpty()) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
|
||||
}
|
||||
const auto replyTo = message.action.replyTo;
|
||||
const auto captionText = caption.text;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
|
@ -133,7 +135,7 @@ void SendExistingMedia(
|
|||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
replyTo,
|
||||
message.action.replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
|
@ -146,12 +148,14 @@ void SendExistingMedia(
|
|||
const auto usedFileReference = media->fileReference();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
message.action.replyTo,
|
||||
message.action.topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
inputMedia(),
|
||||
MTP_string(captionText),
|
||||
MTP_long(randomId),
|
||||
|
@ -269,6 +273,9 @@ bool SendDice(MessageToSend &message) {
|
|||
if (message.action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (message.action.topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(message.action);
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
|
@ -289,7 +296,6 @@ bool SendDice(MessageToSend &message) {
|
|||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? session->user()->name()
|
||||
: QString();
|
||||
const auto replyTo = message.action.replyTo;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
|
@ -312,12 +318,14 @@ bool SendDice(MessageToSend &message) {
|
|||
HistoryMessageMarkupData());
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
message.action.replyTo,
|
||||
message.action.topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
MTP_inputMediaDice(MTP_string(emoji)),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
|
@ -368,9 +376,13 @@ void SendConfirmedFile(
|
|||
const auto peer = history->peer;
|
||||
|
||||
if (!isEditing) {
|
||||
file->to.replyTo = session->data().histories().convertTopicReplyTo(
|
||||
const auto histories = &session->data().histories();
|
||||
file->to.replyTo = histories->convertTopicReplyTo(
|
||||
history,
|
||||
file->to.replyTo);
|
||||
file->to.topicRootId = histories->convertTopicReplyTo(
|
||||
history,
|
||||
file->to.topicRootId);
|
||||
}
|
||||
|
||||
session->uploader().upload(newId, file);
|
||||
|
@ -378,6 +390,7 @@ void SendConfirmedFile(
|
|||
auto action = SendAction(history, file->to.options);
|
||||
action.clearDraft = false;
|
||||
action.replyTo = file->to.replyTo;
|
||||
action.topicRootId = file->to.topicRootId;
|
||||
action.generateLocal = true;
|
||||
session->api().sendAction(action);
|
||||
|
||||
|
|
|
@ -2443,12 +2443,14 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
case mtpc_updateDraftMessage: {
|
||||
const auto &data = update.c_updateDraftMessage();
|
||||
const auto peerId = peerFromMTP(data.vpeer());
|
||||
const auto topicRootId = data.vtop_msg_id().value_or_empty();
|
||||
data.vdraft().match([&](const MTPDdraftMessage &data) {
|
||||
Data::ApplyPeerCloudDraft(&session(), peerId, data);
|
||||
Data::ApplyPeerCloudDraft(&session(), peerId, topicRootId, data);
|
||||
}, [&](const MTPDdraftMessageEmpty &data) {
|
||||
Data::ClearPeerCloudDraft(
|
||||
&session(),
|
||||
peerId,
|
||||
topicRootId,
|
||||
data.vdate().value_or_empty());
|
||||
});
|
||||
} break;
|
||||
|
|
|
@ -562,7 +562,7 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
|||
}
|
||||
const auto type = DetectSeenType(item);
|
||||
const auto unseen = (type == Ui::WhoReadType::Seen)
|
||||
? item->unread()
|
||||
? item->unread(item->history())
|
||||
: item->isUnreadMedia();
|
||||
if (unseen) {
|
||||
return false;
|
||||
|
|
|
@ -1812,8 +1812,8 @@ void ApiWrap::sendNotifySettingsUpdates() {
|
|||
session().mtp().sendAnything();
|
||||
}
|
||||
|
||||
void ApiWrap::saveDraftToCloudDelayed(not_null<History*> history) {
|
||||
_draftsSaveRequestIds.emplace(history, 0);
|
||||
void ApiWrap::saveDraftToCloudDelayed(not_null<Data::Thread*> thread) {
|
||||
_draftsSaveRequestIds.emplace(base::make_weak(thread.get()), 0);
|
||||
if (!_draftsSaveTimer.isActive()) {
|
||||
_draftsSaveTimer.callOnce(kSaveCloudDraftTimeout);
|
||||
}
|
||||
|
@ -2004,33 +2004,46 @@ void ApiWrap::saveCurrentDraftToCloud() {
|
|||
Core::App().saveCurrentDraftsToHistories();
|
||||
|
||||
for (const auto &controller : _session->windows()) {
|
||||
if (const auto history = controller->activeChatCurrent().history()) {
|
||||
if (const auto thread = controller->activeChatCurrent().thread()) {
|
||||
const auto history = thread->owningHistory();
|
||||
_session->local().writeDrafts(history);
|
||||
|
||||
const auto localDraft = history->localDraft();
|
||||
const auto cloudDraft = history->cloudDraft();
|
||||
if (!Data::draftsAreEqual(localDraft, cloudDraft)
|
||||
const auto topicRootId = thread->topicRootId();
|
||||
const auto localDraft = history->localDraft(topicRootId);
|
||||
const auto cloudDraft = history->cloudDraft(topicRootId);
|
||||
if (!Data::DraftsAreEqual(localDraft, cloudDraft)
|
||||
&& !_session->supportMode()) {
|
||||
saveDraftToCloudDelayed(history);
|
||||
saveDraftToCloudDelayed(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::saveDraftsToCloud() {
|
||||
for (auto i = _draftsSaveRequestIds.begin(), e = _draftsSaveRequestIds.end(); i != e; ++i) {
|
||||
if (i->second) continue; // sent already
|
||||
for (auto i = begin(_draftsSaveRequestIds); i != end(_draftsSaveRequestIds);) {
|
||||
const auto weak = i->first;
|
||||
const auto thread = weak.get();
|
||||
if (!thread) {
|
||||
i = _draftsSaveRequestIds.erase(i);
|
||||
continue;
|
||||
}
|
||||
auto &requestId = i->second;
|
||||
++i;
|
||||
if (requestId) {
|
||||
continue; // sent already
|
||||
}
|
||||
|
||||
auto history = i->first;
|
||||
auto cloudDraft = history->cloudDraft();
|
||||
auto localDraft = history->localDraft();
|
||||
const auto history = thread->owningHistory();
|
||||
const auto topicRootId = thread->topicRootId();
|
||||
auto cloudDraft = history->cloudDraft(topicRootId);
|
||||
auto localDraft = history->localDraft(topicRootId);
|
||||
if (cloudDraft && cloudDraft->saveRequestId) {
|
||||
request(base::take(cloudDraft->saveRequestId)).cancel();
|
||||
}
|
||||
if (!_session->supportMode()) {
|
||||
cloudDraft = history->createCloudDraft(localDraft);
|
||||
cloudDraft = history->createCloudDraft(topicRootId, localDraft);
|
||||
} else if (!cloudDraft) {
|
||||
cloudDraft = history->createCloudDraft(nullptr);
|
||||
cloudDraft = history->createCloudDraft(topicRootId, nullptr);
|
||||
}
|
||||
|
||||
auto flags = MTPmessages_SaveDraft::Flags(0);
|
||||
|
@ -2040,6 +2053,9 @@ void ApiWrap::saveDraftsToCloud() {
|
|||
}
|
||||
if (cloudDraft->msgId) {
|
||||
flags |= MTPmessages_SaveDraft::Flag::f_reply_to_msg_id;
|
||||
if (cloudDraft->topicRootId) {
|
||||
flags |= MTPmessages_SaveDraft::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
if (!textWithTags.tags.isEmpty()) {
|
||||
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
||||
|
@ -2049,44 +2065,45 @@ void ApiWrap::saveDraftsToCloud() {
|
|||
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
|
||||
Api::ConvertOption::SkipLocal);
|
||||
|
||||
history->startSavingCloudDraft();
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
|
||||
MTP_flags(flags),
|
||||
MTP_int(cloudDraft->msgId),
|
||||
MTP_int(cloudDraft->topicRootId),
|
||||
history->peer->input,
|
||||
MTP_string(textWithTags.text),
|
||||
entities
|
||||
)).done([=](const MTPBool &result, const MTP::Response &response) {
|
||||
history->finishSavingCloudDraft(
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
|
||||
const auto requestId = response.requestId;
|
||||
if (const auto cloudDraft = history->cloudDraft()) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
|
||||
if (cloudDraft->saveRequestId == requestId) {
|
||||
cloudDraft->saveRequestId = 0;
|
||||
history->draftSavedToCloud();
|
||||
history->draftSavedToCloud(topicRootId);
|
||||
}
|
||||
}
|
||||
auto i = _draftsSaveRequestIds.find(history);
|
||||
const auto i = _draftsSaveRequestIds.find(weak);
|
||||
if (i != _draftsSaveRequestIds.cend()
|
||||
&& i->second == requestId) {
|
||||
_draftsSaveRequestIds.erase(history);
|
||||
_draftsSaveRequestIds.erase(i);
|
||||
checkQuitPreventFinished();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error, const MTP::Response &response) {
|
||||
history->finishSavingCloudDraft(
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
|
||||
const auto requestId = response.requestId;
|
||||
if (const auto cloudDraft = history->cloudDraft()) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
|
||||
if (cloudDraft->saveRequestId == requestId) {
|
||||
history->clearCloudDraft();
|
||||
history->clearCloudDraft(topicRootId);
|
||||
}
|
||||
}
|
||||
auto i = _draftsSaveRequestIds.find(history);
|
||||
const auto i = _draftsSaveRequestIds.find(weak);
|
||||
if (i != _draftsSaveRequestIds.cend()
|
||||
&& i->second == requestId) {
|
||||
_draftsSaveRequestIds.erase(history);
|
||||
_draftsSaveRequestIds.erase(i);
|
||||
checkQuitPreventFinished();
|
||||
}
|
||||
}).send();
|
||||
|
@ -3420,6 +3437,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
if (action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
|
||||
if (action.topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
MTPMessageMedia media = MTP_messageMediaEmpty();
|
||||
|
@ -3446,10 +3466,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
|
||||
}
|
||||
const auto clearCloudDraft = action.clearDraft;
|
||||
const auto topicRootId = action.topicRootId;
|
||||
if (clearCloudDraft) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
|
||||
history->clearCloudDraft();
|
||||
history->startSavingCloudDraft();
|
||||
history->clearCloudDraft(topicRootId);
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
}
|
||||
const auto sendAs = action.options.sendAs;
|
||||
const auto messageFromId = sendAs
|
||||
|
@ -3468,12 +3489,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
|
||||
}
|
||||
const auto viaBotId = UserId();
|
||||
const auto replyTo = action.replyTo;
|
||||
lastMessage = history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
replyTo,
|
||||
action.replyTo,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
|
@ -3482,12 +3502,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
HistoryMessageMarkupData());
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
action.replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMessage>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
msgText,
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
|
@ -3497,6 +3519,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
|
@ -3507,6 +3530,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
}
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
});
|
||||
|
@ -3573,12 +3597,16 @@ void ApiWrap::sendInlineResult(
|
|||
? (*localMessageId)
|
||||
: _session->data().nextLocalMessageId());
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
const auto topicRootId = action.replyTo ? action.topicRootId : 0;
|
||||
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
|
||||
if (action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
|
||||
if (topicRootId) {
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
|
@ -3618,19 +3646,20 @@ void ApiWrap::sendInlineResult(
|
|||
action.replyTo,
|
||||
messagePostAuthor);
|
||||
|
||||
history->clearCloudDraft();
|
||||
history->startSavingCloudDraft();
|
||||
history->clearCloudDraft(topicRootId);
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
const auto replyTo = action.replyTo;
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
action.replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendInlineBotResult>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
MTP_long(randomId),
|
||||
MTP_long(data->getQueryId()),
|
||||
MTP_string(data->getId()),
|
||||
|
@ -3638,10 +3667,12 @@ void ApiWrap::sendInlineResult(
|
|||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
sendMessageFail(error, peer, randomId, newId);
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
});
|
||||
finishForwarding(action);
|
||||
|
@ -3740,6 +3771,7 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
uint64 randomId) {
|
||||
const auto history = item->history();
|
||||
const auto replyTo = item->replyToId();
|
||||
const auto topicRootId = item->topicRootId();
|
||||
|
||||
auto caption = item->originalText();
|
||||
TextUtilities::Trim(caption);
|
||||
|
@ -3750,22 +3782,16 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
|
||||
const auto updateRecentStickers = Api::HasAttachedStickers(media);
|
||||
|
||||
const auto flags = MTPmessages_SendMedia::Flags(0)
|
||||
| (replyTo
|
||||
? MTPmessages_SendMedia::Flag::f_reply_to_msg_id
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
using Flag = MTPmessages_SendMedia::Flag;
|
||||
const auto flags = Flag(0)
|
||||
| (replyTo ? Flag::f_reply_to_msg_id : Flag(0))
|
||||
| (topicRootId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (ShouldSendSilent(history->peer, options)
|
||||
? MTPmessages_SendMedia::Flag::f_silent
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (!sentEntities.v.isEmpty()
|
||||
? MTPmessages_SendMedia::Flag::f_entities
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (options.scheduled
|
||||
? MTPmessages_SendMedia::Flag::f_schedule_date
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (options.sendAs
|
||||
? MTPmessages_SendMedia::Flag::f_send_as
|
||||
: MTPmessages_SendMedia::Flag(0));
|
||||
? Flag::f_silent
|
||||
: 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));
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
|
@ -3773,11 +3799,13 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(flags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
media,
|
||||
MTP_string(caption.text),
|
||||
MTP_long(randomId),
|
||||
|
@ -3859,30 +3887,29 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
}
|
||||
const auto history = sample->history();
|
||||
const auto replyTo = sample->replyToId();
|
||||
const auto topicRootId = sample->topicRootId();
|
||||
const auto sendAs = album->options.sendAs;
|
||||
const auto flags = MTPmessages_SendMultiMedia::Flags(0)
|
||||
| (replyTo
|
||||
? MTPmessages_SendMultiMedia::Flag::f_reply_to_msg_id
|
||||
: MTPmessages_SendMultiMedia::Flag(0))
|
||||
using Flag = MTPmessages_SendMultiMedia::Flag;
|
||||
const auto flags = Flag(0)
|
||||
| (replyTo ? Flag::f_reply_to_msg_id : Flag(0))
|
||||
| (topicRootId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (ShouldSendSilent(history->peer, album->options)
|
||||
? MTPmessages_SendMultiMedia::Flag::f_silent
|
||||
: MTPmessages_SendMultiMedia::Flag(0))
|
||||
| (album->options.scheduled
|
||||
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
|
||||
: MTPmessages_SendMultiMedia::Flag(0))
|
||||
| (sendAs
|
||||
? MTPmessages_SendMultiMedia::Flag::f_send_as
|
||||
: MTPmessages_SendMultiMedia::Flag(0));
|
||||
? Flag::f_silent
|
||||
: Flag(0))
|
||||
| (album->options.scheduled ? Flag::f_schedule_date : Flag(0))
|
||||
| (sendAs ? Flag::f_send_as : Flag(0));
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
topicRootId,
|
||||
uint64(0), // randomId
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMultiMedia>(
|
||||
MTP_flags(flags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
MTP_vector<MTPInputSingleMedia>(medias),
|
||||
MTP_int(album->options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
|
@ -3905,6 +3932,7 @@ FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
|
|||
peer->id,
|
||||
action.options,
|
||||
action.replyTo,
|
||||
action.topicRootId,
|
||||
action.replaceMediaOf);
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ public:
|
|||
void updateNotifySettingsDelayed(not_null<const Data::Thread*> thread);
|
||||
void updateNotifySettingsDelayed(not_null<const PeerData*> peer);
|
||||
void updateNotifySettingsDelayed(Data::DefaultNotify type);
|
||||
void saveDraftToCloudDelayed(not_null<History*> history);
|
||||
void saveDraftToCloudDelayed(not_null<Data::Thread*> thread);
|
||||
|
||||
static int OnlineTillFromStatus(
|
||||
const MTPUserStatus &status,
|
||||
|
@ -563,7 +563,9 @@ private:
|
|||
};
|
||||
base::flat_map<NotifySettingsKey, mtpRequestId> _notifySettingRequests;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::flat_map<
|
||||
base::weak_ptr<Data::Thread>,
|
||||
mtpRequestId> _draftsSaveRequestIds;
|
||||
base::Timer _draftsSaveTimer;
|
||||
|
||||
base::flat_set<mtpRequestId> _stickerSetDisenableRequests;
|
||||
|
|
|
@ -65,14 +65,17 @@ void ShareBotGame(
|
|||
auto &histories = history->owner().histories();
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
const auto replyTo = 0;
|
||||
const auto topicRootId = 0;
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(0),
|
||||
chat->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
MTP_inputMediaGame(
|
||||
MTP_inputGameShortName(
|
||||
bot->inputUser,
|
||||
|
|
|
@ -127,7 +127,7 @@ void DicePack::generateLocal(int index, const QString &name) {
|
|||
QByteArray(),
|
||||
nullptr,
|
||||
SendMediaType::File,
|
||||
FileLoadTo(0, {}, 0, 0),
|
||||
FileLoadTo(0, {}, 0, 0, 0),
|
||||
{});
|
||||
task.process({ .generateGoodThumbnail = false });
|
||||
const auto result = task.peekResult();
|
||||
|
|
|
@ -151,8 +151,9 @@ struct TopicUpdate {
|
|||
Notifications = (1U << 4),
|
||||
Title = (1U << 5),
|
||||
Icon = (1U << 6),
|
||||
CloudDraft = (1U << 7),
|
||||
|
||||
LastUsedBit = (1U << 4),
|
||||
LastUsedBit = (1U << 7),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
|
|
@ -19,29 +19,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace Data {
|
||||
|
||||
DraftKey DraftKey::FromSerializedOld(int32 value) {
|
||||
return !value
|
||||
? DraftKey::None()
|
||||
: (value == kLocalDraftIndex + kEditDraftShiftOld)
|
||||
? DraftKey::LocalEdit()
|
||||
: (value == kScheduledDraftIndex + kEditDraftShiftOld)
|
||||
? DraftKey::ScheduledEdit()
|
||||
: (value > 0 && value < 0x4000'0000)
|
||||
? DraftKey::Replies(int64(value))
|
||||
: (value > kEditDraftShiftOld
|
||||
&& value < kEditDraftShiftOld + 0x4000'000)
|
||||
? DraftKey::RepliesEdit(int64(value - kEditDraftShiftOld))
|
||||
: DraftKey::None();
|
||||
}
|
||||
|
||||
Draft::Draft(
|
||||
const TextWithTags &textWithTags,
|
||||
MsgId msgId,
|
||||
MsgId topicRootId,
|
||||
const MessageCursor &cursor,
|
||||
PreviewState previewState,
|
||||
mtpRequestId saveRequestId)
|
||||
: textWithTags(textWithTags)
|
||||
, msgId(msgId)
|
||||
, topicRootId(topicRootId)
|
||||
, cursor(cursor)
|
||||
, previewState(previewState)
|
||||
, saveRequestId(saveRequestId) {
|
||||
|
@ -50,10 +37,12 @@ Draft::Draft(
|
|||
Draft::Draft(
|
||||
not_null<const Ui::InputField*> field,
|
||||
MsgId msgId,
|
||||
MsgId topicRootId,
|
||||
PreviewState previewState,
|
||||
mtpRequestId saveRequestId)
|
||||
: textWithTags(field->getTextWithTags())
|
||||
, msgId(msgId)
|
||||
, topicRootId(topicRootId)
|
||||
, cursor(field)
|
||||
, previewState(previewState) {
|
||||
}
|
||||
|
@ -61,10 +50,11 @@ Draft::Draft(
|
|||
void ApplyPeerCloudDraft(
|
||||
not_null<Main::Session*> session,
|
||||
PeerId peerId,
|
||||
MsgId topicRootId,
|
||||
const MTPDdraftMessage &draft) {
|
||||
const auto history = session->data().history(peerId);
|
||||
const auto date = draft.vdate().v;
|
||||
if (history->skipCloudDraftUpdate(date)) {
|
||||
if (history->skipCloudDraftUpdate(topicRootId, date)) {
|
||||
return;
|
||||
}
|
||||
const auto textWithTags = TextWithTags{
|
||||
|
@ -78,6 +68,7 @@ void ApplyPeerCloudDraft(
|
|||
auto cloudDraft = std::make_unique<Draft>(
|
||||
textWithTags,
|
||||
replyTo,
|
||||
topicRootId,
|
||||
MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX),
|
||||
(draft.is_no_webpage()
|
||||
? Data::PreviewState::Cancelled
|
||||
|
@ -85,20 +76,21 @@ void ApplyPeerCloudDraft(
|
|||
cloudDraft->date = date;
|
||||
|
||||
history->setCloudDraft(std::move(cloudDraft));
|
||||
history->applyCloudDraft();
|
||||
history->applyCloudDraft(topicRootId);
|
||||
}
|
||||
|
||||
void ClearPeerCloudDraft(
|
||||
not_null<Main::Session*> session,
|
||||
PeerId peerId,
|
||||
MsgId topicRootId,
|
||||
TimeId date) {
|
||||
const auto history = session->data().history(peerId);
|
||||
if (history->skipCloudDraftUpdate(date)) {
|
||||
if (history->skipCloudDraftUpdate(topicRootId, date)) {
|
||||
return;
|
||||
}
|
||||
|
||||
history->clearCloudDraft();
|
||||
history->applyCloudDraft();
|
||||
history->clearCloudDraft(topicRootId);
|
||||
history->applyCloudDraft(topicRootId);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -20,10 +20,12 @@ namespace Data {
|
|||
void ApplyPeerCloudDraft(
|
||||
not_null<Main::Session*> session,
|
||||
PeerId peerId,
|
||||
MsgId topicRootId,
|
||||
const MTPDdraftMessage &draft);
|
||||
void ClearPeerCloudDraft(
|
||||
not_null<Main::Session*> session,
|
||||
PeerId peerId,
|
||||
MsgId topicRootId,
|
||||
TimeId date);
|
||||
|
||||
enum class PreviewState : char {
|
||||
|
@ -37,18 +39,21 @@ struct Draft {
|
|||
Draft(
|
||||
const TextWithTags &textWithTags,
|
||||
MsgId msgId,
|
||||
MsgId topicRootId,
|
||||
const MessageCursor &cursor,
|
||||
PreviewState previewState,
|
||||
mtpRequestId saveRequestId = 0);
|
||||
Draft(
|
||||
not_null<const Ui::InputField*> field,
|
||||
MsgId msgId,
|
||||
MsgId topicRootId,
|
||||
PreviewState previewState,
|
||||
mtpRequestId saveRequestId = 0);
|
||||
|
||||
TimeId date = 0;
|
||||
TextWithTags textWithTags;
|
||||
MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft
|
||||
MsgId topicRootId = 0;
|
||||
MessageCursor cursor;
|
||||
PreviewState previewState = PreviewState::Allowed;
|
||||
mtpRequestId saveRequestId = 0;
|
||||
|
@ -56,70 +61,93 @@ struct Draft {
|
|||
|
||||
class DraftKey {
|
||||
public:
|
||||
[[nodiscard]] static DraftKey None() {
|
||||
[[nodiscard]] static constexpr DraftKey None() {
|
||||
return 0;
|
||||
}
|
||||
[[nodiscard]] static DraftKey Local() {
|
||||
return kLocalDraftIndex;
|
||||
[[nodiscard]] static constexpr DraftKey Local(MsgId topicRootId) {
|
||||
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
|
||||
? None()
|
||||
: (topicRootId ? topicRootId.bare : kLocalDraftIndex);
|
||||
}
|
||||
[[nodiscard]] static DraftKey LocalEdit() {
|
||||
return kLocalDraftIndex + kEditDraftShift;
|
||||
[[nodiscard]] static constexpr DraftKey LocalEdit(MsgId topicRootId) {
|
||||
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
|
||||
? None()
|
||||
: ((topicRootId ? topicRootId.bare : kLocalDraftIndex)
|
||||
+ kEditDraftShift);
|
||||
}
|
||||
[[nodiscard]] static DraftKey Cloud() {
|
||||
return kCloudDraftIndex;
|
||||
[[nodiscard]] static constexpr DraftKey Cloud(MsgId topicRootId) {
|
||||
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
|
||||
? None()
|
||||
: topicRootId
|
||||
? (kCloudDraftShift + topicRootId.bare)
|
||||
: kCloudDraftIndex;
|
||||
}
|
||||
[[nodiscard]] static DraftKey Scheduled() {
|
||||
[[nodiscard]] static constexpr DraftKey Scheduled() {
|
||||
return kScheduledDraftIndex;
|
||||
}
|
||||
[[nodiscard]] static DraftKey ScheduledEdit() {
|
||||
[[nodiscard]] static constexpr DraftKey ScheduledEdit() {
|
||||
return kScheduledDraftIndex + kEditDraftShift;
|
||||
}
|
||||
[[nodiscard]] static DraftKey Replies(MsgId rootId) {
|
||||
return rootId.bare;
|
||||
}
|
||||
[[nodiscard]] static DraftKey RepliesEdit(MsgId rootId) {
|
||||
return rootId.bare + kEditDraftShift;
|
||||
}
|
||||
|
||||
[[nodiscard]] static DraftKey FromSerialized(qint64 value) {
|
||||
[[nodiscard]] static constexpr DraftKey FromSerialized(qint64 value) {
|
||||
return value;
|
||||
}
|
||||
[[nodiscard]] qint64 serialize() const {
|
||||
[[nodiscard]] constexpr qint64 serialize() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
[[nodiscard]] static DraftKey FromSerializedOld(int32 value);
|
||||
[[nodiscard]] static constexpr DraftKey FromSerializedOld(int32 value) {
|
||||
return !value
|
||||
? None()
|
||||
: (value == kLocalDraftIndex + kEditDraftShiftOld)
|
||||
? LocalEdit(0)
|
||||
: (value == kScheduledDraftIndex + kEditDraftShiftOld)
|
||||
? ScheduledEdit()
|
||||
: (value > 0 && value < 0x4000'0000)
|
||||
? Local(MsgId(value))
|
||||
: (value > kEditDraftShiftOld
|
||||
&& value < kEditDraftShiftOld + 0x4000'000)
|
||||
? LocalEdit(int64(value - kEditDraftShiftOld))
|
||||
: None();
|
||||
}
|
||||
[[nodiscard]] constexpr bool isLocal() const {
|
||||
return (_value == kLocalDraftIndex)
|
||||
|| (_value > 0 && _value < ServerMaxMsgId.bare);
|
||||
}
|
||||
[[nodiscard]] constexpr bool isCloud() const {
|
||||
return (_value == kCloudDraftIndex)
|
||||
|| (_value > kCloudDraftShift
|
||||
&& _value < kCloudDraftShift + ServerMaxMsgId.bare);
|
||||
}
|
||||
|
||||
inline bool operator<(const DraftKey &other) const {
|
||||
return _value < other._value;
|
||||
}
|
||||
inline bool operator==(const DraftKey &other) const {
|
||||
return _value == other._value;
|
||||
}
|
||||
inline bool operator>(const DraftKey &other) const {
|
||||
return (other < *this);
|
||||
}
|
||||
inline bool operator<=(const DraftKey &other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
inline bool operator>=(const DraftKey &other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
inline bool operator!=(const DraftKey &other) const {
|
||||
return !(*this == other);
|
||||
[[nodiscard]] constexpr MsgId topicRootId() const {
|
||||
const auto max = ServerMaxMsgId.bare;
|
||||
if (_value > kCloudDraftShift && _value < kCloudDraftShift + max) {
|
||||
return (_value - kCloudDraftShift);
|
||||
} else if (_value > kEditDraftShift && _value < kEditDraftShift + max) {
|
||||
return (_value - kEditDraftShift);
|
||||
} else if (_value > 0 && _value < max) {
|
||||
return _value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
friend inline constexpr auto operator<=>(DraftKey, DraftKey) = default;
|
||||
|
||||
inline explicit operator bool() const {
|
||||
return _value != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
DraftKey(int64 value) : _value(value) {
|
||||
constexpr DraftKey(int64 value) : _value(value) {
|
||||
}
|
||||
|
||||
static constexpr auto kLocalDraftIndex = -1;
|
||||
static constexpr auto kCloudDraftIndex = -2;
|
||||
static constexpr auto kScheduledDraftIndex = -3;
|
||||
static constexpr auto kEditDraftShift = ServerMaxMsgId.bare;
|
||||
static constexpr auto kCloudDraftShift = 2 * ServerMaxMsgId.bare;
|
||||
static constexpr auto kEditDraftShiftOld = 0x3FFF'FFFF;
|
||||
|
||||
int64 _value = 0;
|
||||
|
@ -128,7 +156,7 @@ private:
|
|||
|
||||
using HistoryDrafts = base::flat_map<DraftKey, std::unique_ptr<Draft>>;
|
||||
|
||||
inline bool draftStringIsEmpty(const QString &text) {
|
||||
[[nodiscard]] inline bool DraftStringIsEmpty(const QString &text) {
|
||||
for (const auto &ch : text) {
|
||||
if (!ch.isSpace()) {
|
||||
return false;
|
||||
|
@ -137,20 +165,19 @@ inline bool draftStringIsEmpty(const QString &text) {
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool draftIsNull(const Draft *draft) {
|
||||
[[nodiscard]] inline bool DraftIsNull(const Draft *draft) {
|
||||
return !draft
|
||||
|| (draftStringIsEmpty(draft->textWithTags.text) && !draft->msgId);
|
||||
|| (!draft->msgId && DraftStringIsEmpty(draft->textWithTags.text));
|
||||
}
|
||||
|
||||
inline bool draftsAreEqual(const Draft *a, const Draft *b) {
|
||||
bool aIsNull = draftIsNull(a);
|
||||
bool bIsNull = draftIsNull(b);
|
||||
[[nodiscard]] inline bool DraftsAreEqual(const Draft *a, const Draft *b) {
|
||||
const auto aIsNull = DraftIsNull(a);
|
||||
const auto bIsNull = DraftIsNull(b);
|
||||
if (aIsNull) {
|
||||
return bIsNull;
|
||||
} else if (bIsNull) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (a->textWithTags == b->textWithTags)
|
||||
&& (a->msgId == b->msgId)
|
||||
&& (a->previewState == b->previewState);
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_forum.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
|
@ -142,10 +143,15 @@ ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
|||
, _forum(forum)
|
||||
, _list(_forum->topicsList())
|
||||
, _replies(std::make_shared<RepliesList>(history(), rootId))
|
||||
, _sendActionPainter(owner().sendActionManager().repliesPainter(
|
||||
history(),
|
||||
rootId))
|
||||
, _rootId(rootId)
|
||||
, _lastKnownServerMessageId(rootId) {
|
||||
Thread::setMuted(owner().notifySettings().isMuted(this));
|
||||
|
||||
_sendActionPainter->setTopic(this);
|
||||
|
||||
_replies->unreadCountValue(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::filter([=] {
|
||||
|
@ -161,6 +167,7 @@ ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
|||
}
|
||||
|
||||
ForumTopic::~ForumTopic() {
|
||||
_sendActionPainter->setTopic(nullptr);
|
||||
session().api().unreadThings().cancelRequests(this);
|
||||
}
|
||||
|
||||
|
@ -191,11 +198,24 @@ MsgId ForumTopic::rootId() const {
|
|||
return _rootId;
|
||||
}
|
||||
|
||||
bool ForumTopic::creating() const {
|
||||
return _forum->creating(_rootId);
|
||||
}
|
||||
|
||||
void ForumTopic::discard() {
|
||||
Expects(creating());
|
||||
|
||||
_forum->discardCreatingId(_rootId);
|
||||
}
|
||||
|
||||
void ForumTopic::setRealRootId(MsgId realId) {
|
||||
if (_rootId != realId) {
|
||||
_rootId = realId;
|
||||
_lastKnownServerMessageId = realId;
|
||||
_replies = std::make_shared<RepliesList>(history(), _rootId);
|
||||
_sendActionPainter = owner().sendActionManager().repliesPainter(
|
||||
history(),
|
||||
_rootId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,6 +241,15 @@ void ForumTopic::applyTopic(const MTPDforumTopic &data) {
|
|||
|
||||
owner().notifySettings().apply(this, data.vnotify_settings());
|
||||
|
||||
const auto draft = data.vdraft();
|
||||
if (draft && draft->type() == mtpc_draftMessage) {
|
||||
Data::ApplyPeerCloudDraft(
|
||||
&session(),
|
||||
channel()->id,
|
||||
_rootId,
|
||||
draft->c_draftMessage());
|
||||
}
|
||||
|
||||
_replies->setInboxReadTill(
|
||||
data.vread_inbox_max_id().v,
|
||||
data.vunread_count().v);
|
||||
|
@ -390,13 +419,11 @@ void ForumTopic::requestChatListMessage() {
|
|||
|
||||
TimeId ForumTopic::adjustedChatListTimeId() const {
|
||||
const auto result = chatListTimeId();
|
||||
#if 0 // #TODO forum draft
|
||||
if (const auto draft = cloudDraft()) {
|
||||
if (!Data::draftIsNull(draft) && !session().supportMode()) {
|
||||
if (const auto draft = history()->cloudDraft(_rootId)) {
|
||||
if (!Data::DraftIsNull(draft) && !session().supportMode()) {
|
||||
return std::max(result, draft->date);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -480,6 +507,17 @@ void ForumTopic::applyItemAdded(not_null<HistoryItem*> item) {
|
|||
setLastMessage(item);
|
||||
}
|
||||
|
||||
void ForumTopic::maybeSetLastMessage(not_null<HistoryItem*> item) {
|
||||
Expects(item->topicRootId() == _rootId);
|
||||
|
||||
if (!_lastMessage
|
||||
|| ((*_lastMessage)->date() < item->date())
|
||||
|| ((*_lastMessage)->date() == item->date()
|
||||
&& (*_lastMessage)->id < item->id)) {
|
||||
setLastMessage(item);
|
||||
}
|
||||
}
|
||||
|
||||
void ForumTopic::applyItemRemoved(MsgId id) {
|
||||
if (const auto lastItem = lastMessage()) {
|
||||
if (lastItem->id == id) {
|
||||
|
@ -499,6 +537,11 @@ void ForumTopic::applyItemRemoved(MsgId id) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ForumTopic::isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return _replies->isServerSideUnread(item);
|
||||
}
|
||||
|
||||
int ForumTopic::unreadCount() const {
|
||||
return _replies->unreadCountCurrent();
|
||||
}
|
||||
|
@ -544,6 +587,10 @@ void ForumTopic::setUnreadMark(bool unread) {
|
|||
Thread::setUnreadMark(unread);
|
||||
}
|
||||
|
||||
not_null<HistoryView::SendActionPainter*> ForumTopic::sendActionPainter() {
|
||||
return _sendActionPainter.get();
|
||||
}
|
||||
|
||||
int ForumTopic::chatListUnreadCount() const {
|
||||
const auto state = chatListUnreadState();
|
||||
return state.marks
|
||||
|
|
|
@ -25,6 +25,10 @@ namespace Main {
|
|||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace HistoryView {
|
||||
class SendActionPainter;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Data {
|
||||
|
||||
class RepliesList;
|
||||
|
@ -57,6 +61,9 @@ public:
|
|||
[[nodiscard]] rpl::producer<> destroyed() const;
|
||||
[[nodiscard]] MsgId rootId() const;
|
||||
|
||||
[[nodiscard]] bool creating() const;
|
||||
void discard();
|
||||
|
||||
void setRealRootId(MsgId realId);
|
||||
|
||||
void applyTopic(const MTPDforumTopic &data);
|
||||
|
@ -91,6 +98,7 @@ public:
|
|||
void applyColorId(int32 colorId);
|
||||
void applyItemAdded(not_null<HistoryItem*> item);
|
||||
void applyItemRemoved(MsgId id);
|
||||
void maybeSetLastMessage(not_null<HistoryItem*> item);
|
||||
|
||||
[[nodiscard]] PeerNotifySettings ¬ify() {
|
||||
return _notify;
|
||||
|
@ -105,6 +113,9 @@ public:
|
|||
std::shared_ptr<CloudImageView> &view,
|
||||
const Dialogs::Ui::PaintContext &context) const override;
|
||||
|
||||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
|
||||
[[nodiscard]] int unreadCount() const;
|
||||
[[nodiscard]] bool unreadCountKnown() const;
|
||||
|
||||
|
@ -113,6 +124,9 @@ public:
|
|||
void setMuted(bool muted) override;
|
||||
void setUnreadMark(bool unread) override;
|
||||
|
||||
[[nodiscard]] auto sendActionPainter()
|
||||
->not_null<HistoryView::SendActionPainter*> override;
|
||||
|
||||
private:
|
||||
void indexTitleParts();
|
||||
void validateDefaultIcon() const;
|
||||
|
@ -132,6 +146,7 @@ private:
|
|||
const not_null<Forum*> _forum;
|
||||
const not_null<Dialogs::MainList*> _list;
|
||||
std::shared_ptr<RepliesList> _replies;
|
||||
std::shared_ptr<HistoryView::SendActionPainter> _sendActionPainter;
|
||||
MsgId _rootId = 0;
|
||||
MsgId _lastKnownServerMessageId = 0;
|
||||
|
||||
|
|
|
@ -247,7 +247,7 @@ void Histories::readInboxOnNewMessage(not_null<HistoryItem*> item) {
|
|||
}
|
||||
|
||||
void Histories::readClientSideMessage(not_null<HistoryItem*> item) {
|
||||
if (item->out() || !item->unread()) {
|
||||
if (item->out() || !item->unread(item->history())) {
|
||||
return;
|
||||
}
|
||||
const auto history = item->history();
|
||||
|
@ -886,20 +886,22 @@ bool Histories::isCreatingTopic(
|
|||
int Histories::sendPreparedMessage(
|
||||
not_null<History*> history,
|
||||
MsgId replyTo,
|
||||
MsgId topicRootId,
|
||||
uint64 randomId,
|
||||
Fn<PreparedMessage(MsgId replyTo)> message,
|
||||
Fn<PreparedMessage(MsgId replyTo, MsgId topicRootId)> message,
|
||||
Fn<void(const MTPUpdates&, const MTP::Response&)> done,
|
||||
Fn<void(const MTP::Error&, const MTP::Response&)> fail) {
|
||||
if (isCreatingTopic(history, replyTo)) {
|
||||
if (isCreatingTopic(history, topicRootId)) {
|
||||
const auto id = ++_requestAutoincrement;
|
||||
const auto creatingId = FullMsgId(history->peer->id, replyTo);
|
||||
const auto creatingId = FullMsgId(history->peer->id, topicRootId);
|
||||
auto i = _creatingTopics.find(creatingId);
|
||||
if (i == end(_creatingTopics)) {
|
||||
sendCreateTopicRequest(history, replyTo);
|
||||
sendCreateTopicRequest(history, topicRootId);
|
||||
i = _creatingTopics.emplace(creatingId).first;
|
||||
}
|
||||
i->second.push_back({
|
||||
.randomId = randomId,
|
||||
.replyTo = replyTo,
|
||||
.message = std::move(message),
|
||||
.done = std::move(done),
|
||||
.fail = std::move(fail),
|
||||
|
@ -908,8 +910,9 @@ int Histories::sendPreparedMessage(
|
|||
_creatingTopicRequests.emplace(id);
|
||||
return id;
|
||||
}
|
||||
const auto realTo = convertTopicReplyTo(history, replyTo);
|
||||
return v::match(message(realTo), [&](const auto &request) {
|
||||
const auto realReply = convertTopicReplyTo(history, replyTo);
|
||||
const auto realRoot = convertTopicReplyTo(history, topicRootId);
|
||||
return v::match(message(realReply, realRoot), [&](const auto &request) {
|
||||
const auto type = RequestType::Send;
|
||||
return sendRequest(history, type, [=](Fn<void()> finish) {
|
||||
const auto session = &_owner->session();
|
||||
|
@ -935,38 +938,38 @@ int Histories::sendPreparedMessage(
|
|||
});
|
||||
}
|
||||
|
||||
void Histories::checkTopicCreated(FullMsgId rootId, MsgId realId) {
|
||||
void Histories::checkTopicCreated(FullMsgId rootId, MsgId realRoot) {
|
||||
const auto i = _creatingTopics.find(rootId);
|
||||
if (i != end(_creatingTopics)) {
|
||||
auto scheduled = base::take(i->second);
|
||||
_creatingTopics.erase(i);
|
||||
|
||||
_createdTopicIds.emplace(rootId, realId);
|
||||
_createdTopicIds.emplace(rootId, realRoot);
|
||||
|
||||
if (const auto forum = _owner->peer(rootId.peer)->forum()) {
|
||||
forum->created(rootId.msg, realId);
|
||||
forum->created(rootId.msg, realRoot);
|
||||
}
|
||||
|
||||
const auto history = _owner->history(rootId.peer);
|
||||
for (auto &entry : scheduled) {
|
||||
_creatingTopicRequests.erase(entry.requestId);
|
||||
AssertIsDebug();
|
||||
//sendPreparedMessage(
|
||||
// history,
|
||||
// realId,
|
||||
// entry.randomId,
|
||||
// std::move(entry.message),
|
||||
// std::move(entry.done),
|
||||
// std::move(entry.fail));
|
||||
sendPreparedMessage(
|
||||
history,
|
||||
entry.replyTo,
|
||||
realRoot,
|
||||
entry.randomId,
|
||||
std::move(entry.message),
|
||||
std::move(entry.done),
|
||||
std::move(entry.fail));
|
||||
}
|
||||
for (const auto &item : history->clientSideMessages()) {
|
||||
const auto replace = [&](MsgId nowId) {
|
||||
return (nowId == rootId.msg) ? realId : nowId;
|
||||
return (nowId == rootId.msg) ? realRoot : nowId;
|
||||
};
|
||||
if (item->replyToTop() == rootId.msg) {
|
||||
if (item->topicRootId() == rootId.msg) {
|
||||
item->setReplyFields(
|
||||
replace(item->replyToId()),
|
||||
realId,
|
||||
realRoot,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,22 +103,25 @@ public:
|
|||
int sendPreparedMessage(
|
||||
not_null<History*> history,
|
||||
MsgId replyTo,
|
||||
MsgId topicRootId,
|
||||
uint64 randomId,
|
||||
Fn<PreparedMessage(MsgId replyTo)> message,
|
||||
Fn<PreparedMessage(MsgId replyTo, MsgId topicRootId)> message,
|
||||
Fn<void(const MTPUpdates&, const MTP::Response&)> done,
|
||||
Fn<void(const MTP::Error&, const MTP::Response&)> fail);
|
||||
|
||||
struct ReplyToPlaceholder {
|
||||
};
|
||||
struct TopicRootPlaceholder {
|
||||
};
|
||||
template <typename RequestType, typename ...Args>
|
||||
static Fn<Histories::PreparedMessage(MsgId)> PrepareMessage(
|
||||
static Fn<Histories::PreparedMessage(MsgId, MsgId)> PrepareMessage(
|
||||
const Args &...args) {
|
||||
return [=](MsgId replyTo) {
|
||||
return RequestType(ReplaceReplyTo(args, replyTo)...);
|
||||
return [=](MsgId replyTo, MsgId topicRootId) -> RequestType {
|
||||
return { ReplaceReplyIds(args, replyTo, topicRootId)... };
|
||||
};
|
||||
}
|
||||
|
||||
void checkTopicCreated(FullMsgId rootId, MsgId realId);
|
||||
void checkTopicCreated(FullMsgId rootId, MsgId realRoot);
|
||||
[[nodiscard]] MsgId convertTopicReplyTo(
|
||||
not_null<History*> history,
|
||||
MsgId replyTo) const;
|
||||
|
@ -147,7 +150,8 @@ private:
|
|||
};
|
||||
struct DelayedByTopicMessage {
|
||||
uint64 randomId = 0;
|
||||
Fn<PreparedMessage(MsgId replyTo)> message;
|
||||
MsgId replyTo = 0;
|
||||
Fn<PreparedMessage(MsgId replyTo, MsgId topicRootId)> message;
|
||||
Fn<void(const MTPUpdates&, const MTP::Response&)> done;
|
||||
Fn<void(const MTP::Error&, const MTP::Response&)> fail;
|
||||
int requestId = 0;
|
||||
|
@ -162,9 +166,11 @@ private:
|
|||
};
|
||||
|
||||
template <typename Arg>
|
||||
static auto ReplaceReplyTo(Arg arg, MsgId replyTo) {
|
||||
static auto ReplaceReplyIds(Arg arg, MsgId replyTo, MsgId topicRootId) {
|
||||
if constexpr (std::is_same_v<Arg, ReplyToPlaceholder>) {
|
||||
return MTP_int(replyTo);
|
||||
} else if constexpr (std::is_same_v<Arg, TopicRootPlaceholder>) {
|
||||
return MTP_int(topicRootId);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
|
|
|
@ -765,6 +765,14 @@ void RepliesList::setUnreadCount(std::optional<int> count) {
|
|||
}
|
||||
}
|
||||
|
||||
bool RepliesList::isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
const auto till = item->out()
|
||||
? computeOutboxReadTillFull()
|
||||
: computeInboxReadTillFull();
|
||||
return (item->id > till);
|
||||
}
|
||||
|
||||
void RepliesList::checkReadTillEnd() {
|
||||
if (_unreadCount.current() != 0
|
||||
&& _skippedAfter == 0
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
void setOutboxReadTill(MsgId readTillId);
|
||||
[[nodiscard]] MsgId computeOutboxReadTillFull() const;
|
||||
|
||||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
|
||||
[[nodiscard]] std::optional<int> computeUnreadCountLocally(
|
||||
MsgId afterId) const;
|
||||
void requestUnreadCount();
|
||||
|
|
|
@ -75,7 +75,7 @@ auto SendActionManager::repliesPainter(
|
|||
if (auto strong = weak.lock()) {
|
||||
return strong;
|
||||
}
|
||||
auto result = std::make_shared<SendActionPainter>(history);
|
||||
auto result = std::make_shared<SendActionPainter>(history, rootId);
|
||||
weak = result;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ class SendActionPainter;
|
|||
|
||||
namespace Data {
|
||||
|
||||
class Thread;
|
||||
|
||||
class SendActionManager final {
|
||||
public:
|
||||
struct AnimationUpdate {
|
||||
not_null<History*> history;
|
||||
not_null<Thread*> thread;
|
||||
int left = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
|
|
@ -3820,7 +3820,7 @@ void Session::refreshChatListEntry(Dialogs::Key key) {
|
|||
const auto mainList = chatsListFor(entry);
|
||||
auto event = ChatListEntryRefresh{ .key = key };
|
||||
const auto creating = event.existenceChanged = !entry->inChatList();
|
||||
if (creating && topic && topic->forum()->creating(topic->rootId())) {
|
||||
if (creating && topic && topic->creating()) {
|
||||
return;
|
||||
} else if (event.existenceChanged) {
|
||||
const auto mainRow = entry->addToChatList(0, mainList);
|
||||
|
|
|
@ -17,6 +17,13 @@ namespace Data {
|
|||
|
||||
Thread::~Thread() = default;
|
||||
|
||||
MsgId Thread::topicRootId() const {
|
||||
if (const auto topic = asTopic()) {
|
||||
return topic->rootId();
|
||||
}
|
||||
return MsgId();
|
||||
}
|
||||
|
||||
not_null<PeerData*> Thread::peer() const {
|
||||
return owningHistory()->peer;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ class Proxy;
|
|||
class ConstProxy;
|
||||
} // namespace HistoryUnreadThings
|
||||
|
||||
namespace HistoryView {
|
||||
class SendActionPainter;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace st {
|
||||
extern const int &dialogsTextWidthMin;
|
||||
} // namespace st
|
||||
|
@ -57,6 +61,7 @@ public:
|
|||
[[nodiscard]] not_null<const History*> owningHistory() const {
|
||||
return const_cast<Thread*>(this)->owningHistory();
|
||||
}
|
||||
[[nodiscard]] MsgId topicRootId() const;
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
[[nodiscard]] PeerNotifySettings ¬ify();
|
||||
[[nodiscard]] const PeerNotifySettings ¬ify() const;
|
||||
|
@ -87,6 +92,9 @@ public:
|
|||
}
|
||||
virtual void setUnreadMark(bool unread);
|
||||
|
||||
[[nodiscard]] virtual bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const = 0;
|
||||
|
||||
[[nodiscard]] const base::flat_set<MsgId> &unreadMentionsIds() const;
|
||||
[[nodiscard]] const base::flat_set<MsgId> &unreadReactionsIds() const;
|
||||
|
||||
|
@ -97,6 +105,9 @@ public:
|
|||
return _lastItemDialogsView;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto sendActionPainter()
|
||||
-> not_null<HistoryView::SendActionPainter*> = 0;
|
||||
|
||||
private:
|
||||
enum class Flag : uchar {
|
||||
UnreadMark = (1 << 0),
|
||||
|
|
|
@ -185,7 +185,7 @@ InnerWidget::InnerWidget(
|
|||
width(),
|
||||
update.textUpdated);
|
||||
updateDialogRow(
|
||||
RowDescriptor(update.history, FullMsgId()),
|
||||
RowDescriptor(update.thread, FullMsgId()),
|
||||
updateRect,
|
||||
UpdateRowSection::Default | UpdateRowSection::Filtered);
|
||||
}, lifetime());
|
||||
|
@ -2394,8 +2394,10 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
const auto data = &session().data();
|
||||
const auto state = !shownDialogs()->empty()
|
||||
? EmptyState::None
|
||||
: (_openedForum && _openedForum->topicsList()->loaded())
|
||||
? EmptyState::EmptyForum
|
||||
: _openedForum
|
||||
? (_openedForum->topicsList()->loaded()
|
||||
? EmptyState::EmptyForum
|
||||
: EmptyState::Loading)
|
||||
: (!_filterId && data->contactsLoaded().current())
|
||||
? EmptyState::NoContacts
|
||||
: (_filterId > 0) && data->chatsList()->loaded()
|
||||
|
|
|
@ -19,12 +19,18 @@ Key::Key(History *history) : _value(history) {
|
|||
Key::Key(Data::Folder *folder) : _value(folder) {
|
||||
}
|
||||
|
||||
Key::Key(Data::Thread *thread) : _value(thread) {
|
||||
}
|
||||
|
||||
Key::Key(Data::ForumTopic *topic) : _value(topic) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<History*> history) : _value(history) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<Data::Thread*> thread) : _value(thread) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<Data::Folder*> folder) : _value(folder) {
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,12 @@ public:
|
|||
}
|
||||
Key(History *history);
|
||||
Key(Data::Folder *folder);
|
||||
Key(Data::Thread *thread);
|
||||
Key(Data::ForumTopic *topic);
|
||||
Key(not_null<Entry*> entry) : _value(entry) {
|
||||
}
|
||||
Key(not_null<History*> history);
|
||||
Key(not_null<Data::Thread*> thread);
|
||||
Key(not_null<Data::Folder*> folder);
|
||||
Key(not_null<Data::ForumTopic*> topic);
|
||||
|
||||
|
|
|
@ -48,7 +48,8 @@ const auto kPsaBadgePrefix = "cloud_lng_badge_psa_";
|
|||
return user->isBot() && !user->isSupport() && !user->isRepliesChat();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ShowSendActionInDialogs(History *history) {
|
||||
[[nodiscard]] bool ShowSendActionInDialogs(Data::Thread *thread) {
|
||||
const auto history = thread ? thread->owningHistory().get() : nullptr;
|
||||
return history
|
||||
&& (!history->peer->isUser()
|
||||
|| history->peer->asUser()->onlineTill > 0);
|
||||
|
@ -465,8 +466,8 @@ void PaintRow(
|
|||
: context.selected
|
||||
? st::dialogsTextFgServiceOver
|
||||
: st::dialogsTextFgService;
|
||||
if (!ShowSendActionInDialogs(history)
|
||||
|| !history->sendActionPainter()->paint(
|
||||
if (!ShowSendActionInDialogs(thread)
|
||||
|| !thread->sendActionPainter()->paint(
|
||||
p,
|
||||
nameleft,
|
||||
texttop,
|
||||
|
@ -474,7 +475,8 @@ void PaintRow(
|
|||
context.width,
|
||||
color,
|
||||
context.paused)) {
|
||||
if (history->cloudDraftTextCache().isEmpty()) {
|
||||
auto &cache = thread->cloudDraftTextCache();
|
||||
if (cache.isEmpty()) {
|
||||
using namespace TextUtilities;
|
||||
auto draftWrapped = Text::PlainLink(
|
||||
tr::lng_dialogs_text_from_wrapped(
|
||||
|
@ -496,10 +498,10 @@ void PaintRow(
|
|||
}),
|
||||
Text::WithEntities);
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history->session(),
|
||||
.session = &thread->session(),
|
||||
.customEmojiRepaint = customEmojiRepaint,
|
||||
};
|
||||
history->cloudDraftTextCache().setMarkedText(
|
||||
cache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
draftText,
|
||||
DialogTextOptions(),
|
||||
|
@ -510,7 +512,7 @@ void PaintRow(
|
|||
: context.selected
|
||||
? st::dialogsTextFgOver
|
||||
: st::dialogsTextFg);
|
||||
history->cloudDraftTextCache().draw(p, {
|
||||
cache.draw(p, {
|
||||
.position = { nameleft, texttop },
|
||||
.availableWidth = availableWidth,
|
||||
.palette = &(supportMode
|
||||
|
@ -549,8 +551,8 @@ void PaintRow(
|
|||
? st::dialogsTextFgServiceOver
|
||||
: st::dialogsTextFgService;
|
||||
p.setFont(st::dialogsTextFont);
|
||||
if (!ShowSendActionInDialogs(history)
|
||||
|| !history->sendActionPainter()->paint(
|
||||
if (!ShowSendActionInDialogs(thread)
|
||||
|| !thread->sendActionPainter()->paint(
|
||||
p,
|
||||
nameleft,
|
||||
texttop,
|
||||
|
@ -575,8 +577,10 @@ void PaintRow(
|
|||
: st::dialogsPinnedIcon;
|
||||
icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
|
||||
}
|
||||
auto sendStateIcon = [&]() -> const style::icon* {
|
||||
if (draft) {
|
||||
const auto sendStateIcon = [&]() -> const style::icon* {
|
||||
if (!thread) {
|
||||
return nullptr;
|
||||
} else if (draft) {
|
||||
if (draft->saveRequestId) {
|
||||
return &(context.active
|
||||
? st::dialogsSendingIconActive
|
||||
|
@ -586,7 +590,7 @@ void PaintRow(
|
|||
}
|
||||
} else if (item && !item->isEmpty() && item->needCheck()) {
|
||||
if (!item->isSending() && !item->hasFailed()) {
|
||||
if (item->unread()) {
|
||||
if (item->unread(thread)) {
|
||||
return &(context.active
|
||||
? st::dialogsSentIconActive
|
||||
: context.selected
|
||||
|
@ -607,7 +611,7 @@ void PaintRow(
|
|||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (sendStateIcon && history) {
|
||||
if (sendStateIcon) {
|
||||
rectForName.setWidth(rectForName.width() - st::dialogsSendStateSkip);
|
||||
sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), context.width);
|
||||
}
|
||||
|
@ -892,12 +896,12 @@ void RowPainter::Paint(
|
|||
const auto unreadMuted = entry->chatListMutedBadge();
|
||||
const auto item = entry->chatListMessage();
|
||||
const auto cloudDraft = [&]() -> const Data::Draft*{
|
||||
if (history && (!item || (!unreadCount && !unreadMark))) {
|
||||
if (thread && (!item || (!unreadCount && !unreadMark))) {
|
||||
// Draw item, if there are unread messages.
|
||||
if (const auto draft = history->cloudDraft()) {
|
||||
if (!Data::draftIsNull(draft)) {
|
||||
return draft;
|
||||
}
|
||||
const auto draft = thread->owningHistory()->cloudDraft(
|
||||
thread->topicRootId());
|
||||
if (!Data::DraftIsNull(draft)) {
|
||||
return draft;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -977,8 +981,8 @@ void RowPainter::Paint(
|
|||
texttop,
|
||||
availableWidth,
|
||||
st::dialogsTextFont->height);
|
||||
const auto actionWasPainted = ShowSendActionInDialogs(history)
|
||||
? history->sendActionPainter()->paint(
|
||||
const auto actionWasPainted = ShowSendActionInDialogs(thread)
|
||||
? thread->sendActionPainter()->paint(
|
||||
p,
|
||||
rect.x(),
|
||||
rect.y(),
|
||||
|
|
|
@ -654,7 +654,7 @@ bool InnerWidget::elementHideReply(not_null<const Element*> view) {
|
|||
}
|
||||
|
||||
bool InnerWidget::elementShownUnread(not_null<const Element*> view) {
|
||||
return view->data()->unread();
|
||||
return false;
|
||||
}
|
||||
|
||||
void InnerWidget::elementSendBotCommand(
|
||||
|
|
|
@ -695,9 +695,14 @@ void GenerateItems(
|
|||
using LogJoinByRequest =
|
||||
MTPDchannelAdminLogEventActionParticipantJoinByRequest;
|
||||
using LogNoForwards = MTPDchannelAdminLogEventActionToggleNoForwards;
|
||||
using LogActionSendMessage = MTPDchannelAdminLogEventActionSendMessage;
|
||||
using LogEventActionChangeAvailableReactions = MTPDchannelAdminLogEventActionChangeAvailableReactions;
|
||||
using LogEventActionChangeUsernames = MTPDchannelAdminLogEventActionChangeUsernames;
|
||||
using LogSendMessage = MTPDchannelAdminLogEventActionSendMessage;
|
||||
using LogChangeAvailableReactions = MTPDchannelAdminLogEventActionChangeAvailableReactions;
|
||||
using LogChangeUsernames = MTPDchannelAdminLogEventActionChangeUsernames;
|
||||
using LogToggleForum = MTPDchannelAdminLogEventActionToggleForum;
|
||||
using LogCreateTopic = MTPDchannelAdminLogEventActionCreateTopic;
|
||||
using LogEditTopic = MTPDchannelAdminLogEventActionEditTopic;
|
||||
using LogDeleteTopic = MTPDchannelAdminLogEventActionDeleteTopic;
|
||||
using LogPinTopic = MTPDchannelAdminLogEventActionPinTopic;
|
||||
|
||||
const auto session = &history->session();
|
||||
const auto id = event.vid().v;
|
||||
|
@ -972,7 +977,7 @@ void GenerateItems(
|
|||
ExtractSentDate(action.vmessage()));
|
||||
};
|
||||
|
||||
const auto createParticipantJoin = [&]() {
|
||||
const auto createParticipantJoin = [&](const LogJoin&) {
|
||||
const auto text = (channel->isMegagroup()
|
||||
? tr::lng_admin_log_participant_joined
|
||||
: tr::lng_admin_log_participant_joined_channel)(
|
||||
|
@ -983,7 +988,7 @@ void GenerateItems(
|
|||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
const auto createParticipantLeave = [&]() {
|
||||
const auto createParticipantLeave = [&](const LogLeave&) {
|
||||
const auto text = (channel->isMegagroup()
|
||||
? tr::lng_admin_log_participant_left
|
||||
: tr::lng_admin_log_participant_left_channel)(
|
||||
|
@ -1457,7 +1462,7 @@ void GenerateItems(
|
|||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
const auto createSendMessage = [&](const LogActionSendMessage &data) {
|
||||
const auto createSendMessage = [&](const LogSendMessage &data) {
|
||||
const auto text = tr::lng_admin_log_sent_message(
|
||||
tr::now,
|
||||
lt_from,
|
||||
|
@ -1476,7 +1481,7 @@ void GenerateItems(
|
|||
};
|
||||
|
||||
const auto createChangeAvailableReactions = [&](
|
||||
const LogEventActionChangeAvailableReactions &data) {
|
||||
const LogChangeAvailableReactions &data) {
|
||||
const auto text = data.vnew_value().match([&](
|
||||
const MTPDchatReactionsNone&) {
|
||||
return tr::lng_admin_log_reactions_disabled(
|
||||
|
@ -1514,87 +1519,74 @@ void GenerateItems(
|
|||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
const auto createChangeUsernames = [&](
|
||||
const LogEventActionChangeUsernames &data) {
|
||||
const auto createChangeUsernames = [&](const LogChangeUsernames &data) {
|
||||
// #TODO usernames
|
||||
addSimpleServiceMessage({ "changed usernames" });
|
||||
};
|
||||
|
||||
action.match([&](const LogTitle &data) {
|
||||
createChangeTitle(data);
|
||||
}, [&](const LogAbout &data) {
|
||||
createChangeAbout(data);
|
||||
}, [&](const LogUsername &data) {
|
||||
createChangeUsername(data);
|
||||
}, [&](const LogPhoto &data) {
|
||||
createChangePhoto(data);
|
||||
}, [&](const LogInvites &data) {
|
||||
createToggleInvites(data);
|
||||
}, [&](const LogSign &data) {
|
||||
createToggleSignatures(data);
|
||||
}, [&](const LogPin &data) {
|
||||
createUpdatePinned(data);
|
||||
}, [&](const LogEdit &data) {
|
||||
createEditMessage(data);
|
||||
}, [&](const LogDelete &data) {
|
||||
createDeleteMessage(data);
|
||||
}, [&](const LogJoin &) {
|
||||
createParticipantJoin();
|
||||
}, [&](const LogLeave &) {
|
||||
createParticipantLeave();
|
||||
}, [&](const LogInvite &data) {
|
||||
createParticipantInvite(data);
|
||||
}, [&](const LogBan &data) {
|
||||
createParticipantToggleBan(data);
|
||||
}, [&](const LogPromote &data) {
|
||||
createParticipantToggleAdmin(data);
|
||||
}, [&](const LogSticker &data) {
|
||||
createChangeStickerSet(data);
|
||||
}, [&](const LogPreHistory &data) {
|
||||
createTogglePreHistoryHidden(data);
|
||||
}, [&](const LogPermissions &data) {
|
||||
createDefaultBannedRights(data);
|
||||
}, [&](const LogPoll &data) {
|
||||
createStopPoll(data);
|
||||
}, [&](const LogDiscussion &data) {
|
||||
createChangeLinkedChat(data);
|
||||
}, [&](const LogLocation &data) {
|
||||
createChangeLocation(data);
|
||||
}, [&](const LogSlowMode &data) {
|
||||
createToggleSlowMode(data);
|
||||
}, [&](const LogStartCall &data) {
|
||||
createStartGroupCall(data);
|
||||
}, [&](const LogDiscardCall &data) {
|
||||
createDiscardGroupCall(data);
|
||||
}, [&](const LogMute &data) {
|
||||
createParticipantMute(data);
|
||||
}, [&](const LogUnmute &data) {
|
||||
createParticipantUnmute(data);
|
||||
}, [&](const LogCallSetting &data) {
|
||||
createToggleGroupCallSetting(data);
|
||||
}, [&](const LogJoinByInvite &data) {
|
||||
createParticipantJoinByInvite(data);
|
||||
}, [&](const LogInviteDelete &data) {
|
||||
createExportedInviteDelete(data);
|
||||
}, [&](const LogInviteRevoke &data) {
|
||||
createExportedInviteRevoke(data);
|
||||
}, [&](const LogInviteEdit &data) {
|
||||
createExportedInviteEdit(data);
|
||||
}, [&](const LogVolume &data) {
|
||||
createParticipantVolume(data);
|
||||
}, [&](const LogTTL &data) {
|
||||
createChangeHistoryTTL(data);
|
||||
}, [&](const LogJoinByRequest &data) {
|
||||
createParticipantJoinByRequest(data);
|
||||
}, [&](const LogNoForwards &data) {
|
||||
createToggleNoForwards(data);
|
||||
}, [&](const LogActionSendMessage &data) {
|
||||
createSendMessage(data);
|
||||
}, [&](const LogEventActionChangeAvailableReactions &data) {
|
||||
createChangeAvailableReactions(data);
|
||||
}, [&](const LogEventActionChangeUsernames &data) {
|
||||
createChangeUsernames(data);
|
||||
});
|
||||
const auto createToggleForum = [&](const LogToggleForum &data) {
|
||||
|
||||
};
|
||||
|
||||
const auto createCreateTopic = [&](const LogCreateTopic &data) {
|
||||
|
||||
};
|
||||
|
||||
const auto createEditTopic = [&](const LogEditTopic &data) {
|
||||
|
||||
};
|
||||
|
||||
const auto createDeleteTopic = [&](const LogDeleteTopic &data) {
|
||||
|
||||
};
|
||||
|
||||
const auto createPinTopic = [&](const LogPinTopic &data) {
|
||||
|
||||
};
|
||||
|
||||
action.match(
|
||||
createChangeTitle,
|
||||
createChangeAbout,
|
||||
createChangeUsername,
|
||||
createChangePhoto,
|
||||
createToggleInvites,
|
||||
createToggleSignatures,
|
||||
createUpdatePinned,
|
||||
createEditMessage,
|
||||
createDeleteMessage,
|
||||
createParticipantJoin,
|
||||
createParticipantLeave,
|
||||
createParticipantInvite,
|
||||
createParticipantToggleBan,
|
||||
createParticipantToggleAdmin,
|
||||
createChangeStickerSet,
|
||||
createTogglePreHistoryHidden,
|
||||
createDefaultBannedRights,
|
||||
createStopPoll,
|
||||
createChangeLinkedChat,
|
||||
createChangeLocation,
|
||||
createToggleSlowMode,
|
||||
createStartGroupCall,
|
||||
createDiscardGroupCall,
|
||||
createParticipantMute,
|
||||
createParticipantUnmute,
|
||||
createToggleGroupCallSetting,
|
||||
createParticipantJoinByInvite,
|
||||
createExportedInviteDelete,
|
||||
createExportedInviteRevoke,
|
||||
createExportedInviteEdit,
|
||||
createParticipantVolume,
|
||||
createChangeHistoryTTL,
|
||||
createParticipantJoinByRequest,
|
||||
createToggleNoForwards,
|
||||
createSendMessage,
|
||||
createChangeAvailableReactions,
|
||||
createChangeUsernames,
|
||||
createToggleForum,
|
||||
createCreateTopic,
|
||||
createEditTopic,
|
||||
createDeleteTopic,
|
||||
createPinTopic);
|
||||
}
|
||||
|
||||
} // namespace AdminLog
|
||||
|
|
|
@ -157,53 +157,56 @@ void History::itemVanished(not_null<HistoryItem*> item) {
|
|||
clearLastKeyboard();
|
||||
}
|
||||
if ((!item->out() || item->isPost())
|
||||
&& item->unread()
|
||||
&& item->unread(this)
|
||||
&& unreadCount() > 0) {
|
||||
setUnreadCount(unreadCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void History::takeLocalDraft(not_null<History*> from) {
|
||||
const auto i = from->_drafts.find(Data::DraftKey::Local());
|
||||
const auto topicRootId = MsgId(0);
|
||||
const auto i = from->_drafts.find(Data::DraftKey::Local(topicRootId));
|
||||
if (i == end(from->_drafts)) {
|
||||
return;
|
||||
}
|
||||
auto &draft = i->second;
|
||||
if (!draft->textWithTags.text.isEmpty()
|
||||
&& !_drafts.contains(Data::DraftKey::Local())) {
|
||||
&& !_drafts.contains(Data::DraftKey::Local(topicRootId))) {
|
||||
// Edit and reply to drafts can't migrate.
|
||||
// Cloud drafts do not migrate automatically.
|
||||
draft->msgId = 0;
|
||||
|
||||
setLocalDraft(std::move(draft));
|
||||
}
|
||||
from->clearLocalDraft();
|
||||
from->clearLocalDraft(topicRootId);
|
||||
session().api().saveDraftToCloudDelayed(from);
|
||||
}
|
||||
|
||||
void History::createLocalDraftFromCloud() {
|
||||
const auto draft = cloudDraft();
|
||||
void History::createLocalDraftFromCloud(MsgId topicRootId) {
|
||||
const auto draft = cloudDraft(topicRootId);
|
||||
if (!draft) {
|
||||
clearLocalDraft();
|
||||
clearLocalDraft(topicRootId);
|
||||
return;
|
||||
} else if (Data::draftIsNull(draft) || !draft->date) {
|
||||
} else if (Data::DraftIsNull(draft) || !draft->date) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto existing = localDraft();
|
||||
if (Data::draftIsNull(existing)
|
||||
auto existing = localDraft(topicRootId);
|
||||
if (Data::DraftIsNull(existing)
|
||||
|| !existing->date
|
||||
|| draft->date >= existing->date) {
|
||||
if (!existing) {
|
||||
setLocalDraft(std::make_unique<Data::Draft>(
|
||||
draft->textWithTags,
|
||||
draft->msgId,
|
||||
topicRootId,
|
||||
draft->cursor,
|
||||
draft->previewState));
|
||||
existing = localDraft();
|
||||
existing = localDraft(topicRootId);
|
||||
} else if (existing != draft) {
|
||||
existing->textWithTags = draft->textWithTags;
|
||||
existing->msgId = draft->msgId;
|
||||
existing->topicRootId = draft->topicRootId;
|
||||
existing->cursor = draft->cursor;
|
||||
existing->previewState = draft->previewState;
|
||||
}
|
||||
|
@ -219,18 +222,22 @@ Data::Draft *History::draft(Data::DraftKey key) const {
|
|||
return (i != _drafts.end()) ? i->second.get() : nullptr;
|
||||
}
|
||||
|
||||
void History::setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft) {
|
||||
void History::setDraft(
|
||||
Data::DraftKey key,
|
||||
std::unique_ptr<Data::Draft> &&draft) {
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
const auto changingCloudDraft = (key == Data::DraftKey::Cloud());
|
||||
if (changingCloudDraft) {
|
||||
cloudDraftTextCache().clear();
|
||||
const auto cloudThread = key.isCloud()
|
||||
? threadFor(key.topicRootId())
|
||||
: nullptr;
|
||||
if (cloudThread) {
|
||||
cloudThread->cloudDraftTextCache().clear();
|
||||
}
|
||||
if (draft) {
|
||||
_drafts[key] = std::move(draft);
|
||||
} else if (_drafts.remove(key) && changingCloudDraft) {
|
||||
updateChatListSortPosition();
|
||||
} else if (_drafts.remove(key) && cloudThread) {
|
||||
cloudThread->updateChatListSortPosition();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,31 +257,38 @@ void History::clearDraft(Data::DraftKey key) {
|
|||
}
|
||||
|
||||
void History::clearDrafts() {
|
||||
const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud());
|
||||
_drafts.clear();
|
||||
if (changingCloudDraft) {
|
||||
cloudDraftTextCache().clear();
|
||||
updateChatListSortPosition();
|
||||
for (auto &[key, draft] : base::take(_drafts)) {
|
||||
const auto cloudThread = key.isCloud()
|
||||
? threadFor(key.topicRootId())
|
||||
: nullptr;
|
||||
if (cloudThread) {
|
||||
cloudThread->cloudDraftTextCache().clear();
|
||||
cloudThread->updateChatListSortPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
|
||||
if (Data::draftIsNull(fromDraft)) {
|
||||
Data::Draft *History::createCloudDraft(
|
||||
MsgId topicRootId,
|
||||
const Data::Draft *fromDraft) {
|
||||
if (Data::DraftIsNull(fromDraft)) {
|
||||
setCloudDraft(std::make_unique<Data::Draft>(
|
||||
TextWithTags(),
|
||||
0,
|
||||
topicRootId,
|
||||
MessageCursor(),
|
||||
Data::PreviewState::Allowed));
|
||||
cloudDraft()->date = TimeId(0);
|
||||
cloudDraft(topicRootId)->date = TimeId(0);
|
||||
} else {
|
||||
auto existing = cloudDraft();
|
||||
auto existing = cloudDraft(topicRootId);
|
||||
if (!existing) {
|
||||
setCloudDraft(std::make_unique<Data::Draft>(
|
||||
fromDraft->textWithTags,
|
||||
fromDraft->msgId,
|
||||
topicRootId,
|
||||
fromDraft->cursor,
|
||||
fromDraft->previewState));
|
||||
existing = cloudDraft();
|
||||
existing = cloudDraft(topicRootId);
|
||||
} else if (existing != fromDraft) {
|
||||
existing->textWithTags = fromDraft->textWithTags;
|
||||
existing->msgId = fromDraft->msgId;
|
||||
|
@ -284,42 +298,60 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
|
|||
existing->date = base::unixtime::now();
|
||||
}
|
||||
|
||||
cloudDraftTextCache().clear();
|
||||
updateChatListSortPosition();
|
||||
|
||||
return cloudDraft();
|
||||
}
|
||||
|
||||
bool History::skipCloudDraftUpdate(TimeId date) const {
|
||||
return (_savingCloudDraftRequests > 0)
|
||||
|| (date < _acceptCloudDraftsAfter);
|
||||
}
|
||||
|
||||
void History::startSavingCloudDraft() {
|
||||
++_savingCloudDraftRequests;
|
||||
}
|
||||
|
||||
void History::finishSavingCloudDraft(TimeId savedAt) {
|
||||
if (_savingCloudDraftRequests > 0) {
|
||||
--_savingCloudDraftRequests;
|
||||
if (const auto thread = threadFor(topicRootId)) {
|
||||
thread->cloudDraftTextCache().clear();
|
||||
thread->updateChatListSortPosition();
|
||||
}
|
||||
const auto acceptAfter = savedAt + kSkipCloudDraftsFor;
|
||||
_acceptCloudDraftsAfter = std::max(_acceptCloudDraftsAfter, acceptAfter);
|
||||
|
||||
return cloudDraft(topicRootId);
|
||||
}
|
||||
|
||||
void History::applyCloudDraft() {
|
||||
if (session().supportMode()) {
|
||||
bool History::skipCloudDraftUpdate(MsgId topicRootId, TimeId date) const {
|
||||
const auto i = _acceptCloudDraftsAfter.find(topicRootId);
|
||||
return _savingCloudDraftRequests.contains(topicRootId)
|
||||
|| (i != _acceptCloudDraftsAfter.end() && date < i->second);
|
||||
}
|
||||
|
||||
void History::startSavingCloudDraft(MsgId topicRootId) {
|
||||
++_savingCloudDraftRequests[topicRootId];
|
||||
}
|
||||
|
||||
void History::finishSavingCloudDraft(MsgId topicRootId, TimeId savedAt) {
|
||||
const auto i = _savingCloudDraftRequests.find(topicRootId);
|
||||
if (i != _savingCloudDraftRequests.end()) {
|
||||
if (--i->second <= 0) {
|
||||
_savingCloudDraftRequests.erase(i);
|
||||
}
|
||||
}
|
||||
auto &after = _acceptCloudDraftsAfter[topicRootId];
|
||||
after = std::max(after, savedAt + kSkipCloudDraftsFor);
|
||||
}
|
||||
|
||||
void History::applyCloudDraft(MsgId topicRootId) {
|
||||
if (!topicRootId && session().supportMode()) {
|
||||
updateChatListEntry();
|
||||
session().supportHelper().cloudDraftChanged(this);
|
||||
} else {
|
||||
createLocalDraftFromCloud();
|
||||
updateChatListSortPosition();
|
||||
session().changes().historyUpdated(this, UpdateFlag::CloudDraft);
|
||||
createLocalDraftFromCloud(topicRootId);
|
||||
if (const auto thread = threadFor(topicRootId)) {
|
||||
thread->updateChatListSortPosition();
|
||||
if (!topicRootId) {
|
||||
session().changes().historyUpdated(
|
||||
this,
|
||||
UpdateFlag::CloudDraft);
|
||||
} else {
|
||||
session().changes().topicUpdated(
|
||||
thread->asTopic(),
|
||||
Data::TopicUpdate::Flag::CloudDraft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void History::draftSavedToCloud() {
|
||||
updateChatListEntry();
|
||||
void History::draftSavedToCloud(MsgId topicRootId) {
|
||||
if (const auto thread = threadFor(topicRootId)) {
|
||||
thread->updateChatListEntry();
|
||||
}
|
||||
session().local().writeDrafts(this);
|
||||
}
|
||||
|
||||
|
@ -1143,20 +1175,22 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
const auto stillShow = item->showNotification(); // Could be read already.
|
||||
if (stillShow) {
|
||||
Core::App().notifications().schedule(notification);
|
||||
if (!item->out() && item->unread()) {
|
||||
}
|
||||
if (item->out()) {
|
||||
destroyUnreadBar();
|
||||
if (!item->unread(this)) {
|
||||
outboxRead(item);
|
||||
}
|
||||
} else {
|
||||
if (item->unread(this)) {
|
||||
if (unreadCountKnown()) {
|
||||
setUnreadCount(unreadCount() + 1);
|
||||
} else {
|
||||
owner().histories().requestDialogEntry(this);
|
||||
}
|
||||
} else {
|
||||
inboxRead(item);
|
||||
}
|
||||
} else if (item->out()) {
|
||||
destroyUnreadBar();
|
||||
} else if (!item->unread()) {
|
||||
inboxRead(item);
|
||||
}
|
||||
if (item->out() && !item->unread()) {
|
||||
outboxRead(item);
|
||||
}
|
||||
item->incrementReplyToTopCounter();
|
||||
if (!folderKnown()) {
|
||||
|
@ -1879,8 +1913,8 @@ void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
|||
|
||||
TimeId History::adjustedChatListTimeId() const {
|
||||
const auto result = chatListTimeId();
|
||||
if (const auto draft = cloudDraft()) {
|
||||
if (!Data::draftIsNull(draft) && !session().supportMode()) {
|
||||
if (const auto draft = cloudDraft(MsgId(0))) {
|
||||
if (!Data::DraftIsNull(draft) && !session().supportMode()) {
|
||||
return std::max(result, draft->date);
|
||||
}
|
||||
}
|
||||
|
@ -2601,6 +2635,7 @@ void History::applyDialog(
|
|||
Data::ApplyPeerCloudDraft(
|
||||
&session(),
|
||||
peer->id,
|
||||
MsgId(0), // topicRootId
|
||||
draft->c_draftMessage());
|
||||
}
|
||||
owner().histories().dialogEntryApplied(this);
|
||||
|
@ -2823,6 +2858,16 @@ void History::forceFullResize() {
|
|||
_flags |= Flag::HasPendingResizedItems;
|
||||
}
|
||||
|
||||
Data::Thread *History::threadFor(MsgId topicRootId) {
|
||||
return topicRootId
|
||||
? peer->forumTopicFor(topicRootId)
|
||||
: static_cast<Data::Thread*>(this);
|
||||
}
|
||||
|
||||
const Data::Thread *History::threadFor(MsgId topicRootId) const {
|
||||
return const_cast<History*>(this)->threadFor(topicRootId);
|
||||
}
|
||||
|
||||
not_null<History*> History::migrateToOrMe() const {
|
||||
if (const auto to = peer->migrateTo()) {
|
||||
return owner().history(to);
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
not_null<History*> owningHistory() override {
|
||||
return this;
|
||||
}
|
||||
[[nodiscard]] Data::Thread *threadFor(MsgId topicRootId);
|
||||
[[nodiscard]] const Data::Thread *threadFor(MsgId topicRootId) const;
|
||||
|
||||
[[nodiscard]] auto delegateMixin() const
|
||||
-> not_null<HistoryMainElementDelegateMixin*> {
|
||||
|
@ -229,12 +231,13 @@ public:
|
|||
void inboxRead(not_null<const HistoryItem*> wasRead);
|
||||
void outboxRead(MsgId upTo);
|
||||
void outboxRead(not_null<const HistoryItem*> wasRead);
|
||||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
[[nodiscard]] MsgId loadAroundId() const;
|
||||
[[nodiscard]] MsgId inboxReadTillId() const;
|
||||
[[nodiscard]] MsgId outboxReadTillId() const;
|
||||
|
||||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
|
||||
[[nodiscard]] bool trackUnreadMessages() const;
|
||||
[[nodiscard]] int unreadCount() const;
|
||||
[[nodiscard]] bool unreadCountKnown() const;
|
||||
|
@ -301,7 +304,7 @@ public:
|
|||
void setHasPendingResizedItems();
|
||||
|
||||
[[nodiscard]] auto sendActionPainter()
|
||||
-> not_null<HistoryView::SendActionPainter*> {
|
||||
-> not_null<HistoryView::SendActionPainter*> override {
|
||||
return &_sendActionPainter;
|
||||
}
|
||||
|
||||
|
@ -316,41 +319,51 @@ public:
|
|||
[[nodiscard]] const Data::HistoryDrafts &draftsMap() const;
|
||||
void setDraftsMap(Data::HistoryDrafts &&map);
|
||||
|
||||
Data::Draft *localDraft() const {
|
||||
return draft(Data::DraftKey::Local());
|
||||
Data::Draft *localDraft(MsgId topicRootId) const {
|
||||
return draft(Data::DraftKey::Local(topicRootId));
|
||||
}
|
||||
Data::Draft *localEditDraft() const {
|
||||
return draft(Data::DraftKey::LocalEdit());
|
||||
Data::Draft *localEditDraft(MsgId topicRootId) const {
|
||||
return draft(Data::DraftKey::LocalEdit(topicRootId));
|
||||
}
|
||||
Data::Draft *cloudDraft() const {
|
||||
return draft(Data::DraftKey::Cloud());
|
||||
Data::Draft *cloudDraft(MsgId topicRootId) const {
|
||||
return draft(Data::DraftKey::Cloud(topicRootId));
|
||||
}
|
||||
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
|
||||
setDraft(Data::DraftKey::Local(), std::move(draft));
|
||||
setDraft(
|
||||
Data::DraftKey::Local(draft->topicRootId),
|
||||
std::move(draft));
|
||||
}
|
||||
void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) {
|
||||
setDraft(Data::DraftKey::LocalEdit(), std::move(draft));
|
||||
setDraft(
|
||||
Data::DraftKey::LocalEdit(draft->topicRootId),
|
||||
std::move(draft));
|
||||
}
|
||||
void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
|
||||
setDraft(Data::DraftKey::Cloud(), std::move(draft));
|
||||
setDraft(
|
||||
Data::DraftKey::Cloud(draft->topicRootId),
|
||||
std::move(draft));
|
||||
}
|
||||
void clearLocalDraft() {
|
||||
clearDraft(Data::DraftKey::Local());
|
||||
void clearLocalDraft(MsgId topicRootId) {
|
||||
clearDraft(Data::DraftKey::Local(topicRootId));
|
||||
}
|
||||
void clearCloudDraft() {
|
||||
clearDraft(Data::DraftKey::Cloud());
|
||||
void clearCloudDraft(MsgId topicRootId) {
|
||||
clearDraft(Data::DraftKey::Cloud(topicRootId));
|
||||
}
|
||||
void clearLocalEditDraft() {
|
||||
clearDraft(Data::DraftKey::LocalEdit());
|
||||
void clearLocalEditDraft(MsgId topicRootId) {
|
||||
clearDraft(Data::DraftKey::LocalEdit(topicRootId));
|
||||
}
|
||||
void clearDrafts();
|
||||
Data::Draft *createCloudDraft(const Data::Draft *fromDraft);
|
||||
bool skipCloudDraftUpdate(TimeId date) const;
|
||||
void startSavingCloudDraft();
|
||||
void finishSavingCloudDraft(TimeId savedAt);
|
||||
Data::Draft *createCloudDraft(
|
||||
MsgId topicRootId,
|
||||
const Data::Draft *fromDraft);
|
||||
[[nodiscard]] bool skipCloudDraftUpdate(
|
||||
MsgId topicRootId,
|
||||
TimeId date) const;
|
||||
void startSavingCloudDraft(MsgId topicRootId);
|
||||
void finishSavingCloudDraft(MsgId topicRootId, TimeId savedAt);
|
||||
void takeLocalDraft(not_null<History*> from);
|
||||
void applyCloudDraft();
|
||||
void draftSavedToCloud();
|
||||
void applyCloudDraft(MsgId topicRootId);
|
||||
void draftSavedToCloud(MsgId topicRootId);
|
||||
|
||||
[[nodiscard]] const Data::ForwardDraft &forwardDraft() const {
|
||||
return _forwardDraft;
|
||||
|
@ -553,7 +566,7 @@ private:
|
|||
|
||||
void viewReplaced(not_null<const Element*> was, Element *now);
|
||||
|
||||
void createLocalDraftFromCloud();
|
||||
void createLocalDraftFromCloud(MsgId topicRootId);
|
||||
|
||||
HistoryService *insertJoinedMessage();
|
||||
void insertMessageToBlocks(not_null<HistoryItem*> item);
|
||||
|
@ -603,8 +616,8 @@ private:
|
|||
std::unique_ptr<BuildingBlock> _buildingFrontBlock;
|
||||
|
||||
Data::HistoryDrafts _drafts;
|
||||
TimeId _acceptCloudDraftsAfter = 0;
|
||||
int _savingCloudDraftRequests = 0;
|
||||
base::flat_map<MsgId, TimeId> _acceptCloudDraftsAfter;
|
||||
base::flat_map<MsgId, int> _savingCloudDraftRequests;
|
||||
Data::ForwardDraft _forwardDraft;
|
||||
|
||||
QString _topPromotedMessage;
|
||||
|
|
|
@ -227,7 +227,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
bool elementShownUnread(not_null<const Element*> view) override {
|
||||
return view->data()->unread();
|
||||
return view->data()->unread(view->data()->history());
|
||||
}
|
||||
void elementSendBotCommand(
|
||||
const QString &command,
|
||||
|
@ -983,7 +983,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
const auto item = view->data();
|
||||
const auto isSponsored = item->isSponsored();
|
||||
const auto isUnread = !item->out()
|
||||
&& item->unread()
|
||||
&& item->unread(_history)
|
||||
&& (item->history() == _history);
|
||||
const auto withReaction = item->hasUnreadReaction();
|
||||
const auto yShown = [&](int y) {
|
||||
|
|
|
@ -1217,22 +1217,22 @@ bool HistoryItem::needCheck() const {
|
|||
return (out() && !isEmpty()) || (!isRegular() && history()->peer->isSelf());
|
||||
}
|
||||
|
||||
bool HistoryItem::unread() const {
|
||||
bool HistoryItem::unread(not_null<Data::Thread*> thread) const {
|
||||
// Messages from myself are always read, unless scheduled.
|
||||
if (history()->peer->isSelf() && !isFromScheduled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out()) {
|
||||
// Outgoing messages in converted chats are always read.
|
||||
if (history()->peer->migrateTo()) {
|
||||
// All messages in converted chats are always read.
|
||||
if (history()->peer->migrateTo()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isRegular()) {
|
||||
if (!thread->isServerSideUnread(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isRegular()) {
|
||||
if (!history()->isServerSideUnread(this)) {
|
||||
return false;
|
||||
}
|
||||
if (out()) {
|
||||
if (const auto user = history()->peer->asUser()) {
|
||||
if (user->isBot() && !user->isSupport()) {
|
||||
return false;
|
||||
|
@ -1246,13 +1246,7 @@ bool HistoryItem::unread() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (isRegular()) {
|
||||
if (!history()->isServerSideUnread(this)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return (_flags & MessageFlag::ClientSideUnread);
|
||||
return out() || (_flags & MessageFlag::ClientSideUnread);
|
||||
}
|
||||
|
||||
bool HistoryItem::showNotification() const {
|
||||
|
@ -1262,7 +1256,7 @@ bool HistoryItem::showNotification() const {
|
|||
}
|
||||
return (out() || _history->peer->isSelf())
|
||||
? isFromScheduled()
|
||||
: unread();
|
||||
: unread(notificationThread());
|
||||
}
|
||||
|
||||
void HistoryItem::markClientSideAsRead() {
|
||||
|
|
|
@ -150,7 +150,7 @@ public:
|
|||
[[nodiscard]] bool isPinned() const {
|
||||
return _flags & MessageFlag::Pinned;
|
||||
}
|
||||
[[nodiscard]] bool unread() const;
|
||||
[[nodiscard]] bool unread(not_null<Data::Thread*> thread) const;
|
||||
[[nodiscard]] bool showNotification() const;
|
||||
void markClientSideAsRead();
|
||||
[[nodiscard]] bool mentionsMe() const;
|
||||
|
|
|
@ -1773,6 +1773,9 @@ void HistoryMessage::setReplyFields(
|
|||
&& !IsServerMsgId(reply->replyToMsgTop)) {
|
||||
reply->replyToMsgTop = replyToTop;
|
||||
changeReplyToTopCounter(reply, 1);
|
||||
if (const auto topic = this->topic()) {
|
||||
topic->maybeSetLastMessage(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1342,10 +1342,19 @@ void HistoryService::setReplyFields(
|
|||
MsgId replyToTop,
|
||||
bool isForumPost) {
|
||||
const auto data = GetDependentData();
|
||||
if (!data || IsServerMsgId(data->topId) || isScheduled()) {
|
||||
if (!data
|
||||
|| (data->topId == replyToTop)
|
||||
|| IsServerMsgId(data->topId)
|
||||
|| isScheduled()) {
|
||||
return;
|
||||
}
|
||||
data->topId = replyToTop;
|
||||
if (isForumPost) {
|
||||
data->topicPost = true;
|
||||
}
|
||||
if (const auto topic = this->topic()) {
|
||||
topic->maybeSetLastMessage(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> HistoryService::createView(
|
||||
|
|
|
@ -948,7 +948,7 @@ void HistoryWidget::initVoiceRecordBar() {
|
|||
});
|
||||
|
||||
const auto applyLocalDraft = [=] {
|
||||
if (_history && _history->localDraft()) {
|
||||
if (_history && _history->localDraft({})) {
|
||||
applyDraft();
|
||||
}
|
||||
};
|
||||
|
@ -1657,10 +1657,12 @@ void HistoryWidget::saveDraft(bool delayed) {
|
|||
void HistoryWidget::saveFieldToHistoryLocalDraft() {
|
||||
if (!_history) return;
|
||||
|
||||
const auto topicRootId = MsgId();
|
||||
if (_editMsgId) {
|
||||
_history->setLocalEditDraft(std::make_unique<Data::Draft>(
|
||||
_field,
|
||||
_editMsgId,
|
||||
topicRootId,
|
||||
_previewState,
|
||||
_saveEditMsgRequestId));
|
||||
} else {
|
||||
|
@ -1668,11 +1670,12 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
|
|||
_history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
_field,
|
||||
_replyToId,
|
||||
topicRootId,
|
||||
_previewState));
|
||||
} else {
|
||||
_history->clearLocalDraft();
|
||||
_history->clearLocalDraft({});
|
||||
}
|
||||
_history->clearLocalEditDraft();
|
||||
_history->clearLocalEditDraft({});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1760,7 +1763,8 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
|
|||
MessageCursor cursor = { int(textWithTags.text.size()), int(textWithTags.text.size()), QFIXED_MAX };
|
||||
_history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
0,
|
||||
0, // replyTo
|
||||
0, // topicRootId
|
||||
cursor,
|
||||
Data::PreviewState::Allowed));
|
||||
applyDraft();
|
||||
|
@ -1782,21 +1786,19 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U
|
|||
auto draft = std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
to.currentReplyToId,
|
||||
to.rootId,
|
||||
cursor,
|
||||
Data::PreviewState::Allowed);
|
||||
|
||||
if (to.section == Section::Replies) {
|
||||
history->setDraft(
|
||||
Data::DraftKey::Replies(to.rootId),
|
||||
std::move(draft));
|
||||
controller()->showRepliesForMessage(history, to.rootId);
|
||||
} else if (to.section == Section::Scheduled) {
|
||||
if (to.section == Section::Scheduled) {
|
||||
history->setDraft(Data::DraftKey::Scheduled(), std::move(draft));
|
||||
controller()->showSection(
|
||||
std::make_shared<HistoryView::ScheduledMemento>(history));
|
||||
} else {
|
||||
history->setLocalDraft(std::move(draft));
|
||||
if (history == _history) {
|
||||
if (to.section == Section::Replies) {
|
||||
controller()->showRepliesForMessage(history, to.rootId);
|
||||
} else if (history == _history) {
|
||||
applyDraft();
|
||||
} else {
|
||||
controller()->showPeerHistory(history->peer);
|
||||
|
@ -1878,13 +1880,14 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto draft = !_history
|
||||
? nullptr
|
||||
: _history->localEditDraft()
|
||||
? _history->localEditDraft()
|
||||
: _history->localDraft();
|
||||
const auto editDraft = _history ? _history->localEditDraft({}) : nullptr;
|
||||
const auto draft = editDraft
|
||||
? editDraft
|
||||
: _history
|
||||
? _history->localDraft({})
|
||||
: nullptr;
|
||||
auto fieldAvailable = canWriteMessage();
|
||||
if (!draft || (!_history->localEditDraft() && !fieldAvailable)) {
|
||||
if (!draft || (!_history->localEditDraft({}) && !fieldAvailable)) {
|
||||
auto fieldWillBeHiddenAfterEdit = (!fieldAvailable && _editMsgId != 0);
|
||||
clearFieldText(0, fieldHistoryAction);
|
||||
_field->setFocus();
|
||||
|
@ -1913,11 +1916,11 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
|||
_previewState = draft->previewState;
|
||||
|
||||
_replyEditMsg = nullptr;
|
||||
if (const auto editDraft = _history->localEditDraft()) {
|
||||
if (const auto editDraft = _history->localEditDraft({})) {
|
||||
setEditMsgId(editDraft->msgId);
|
||||
_replyToId = 0;
|
||||
} else {
|
||||
_replyToId = readyToForward() ? 0 : _history->localDraft()->msgId;
|
||||
_replyToId = readyToForward() ? 0 : _history->localDraft({})->msgId;
|
||||
setEditMsgId(0);
|
||||
}
|
||||
updateCmdStartShown();
|
||||
|
@ -2036,7 +2039,7 @@ void HistoryWidget::showHistory(
|
|||
info->inlineReturnTo = wasDialogsEntryState;
|
||||
}
|
||||
sendBotStartCommand();
|
||||
_history->clearLocalDraft();
|
||||
_history->clearLocalDraft({});
|
||||
applyDraft();
|
||||
_send->finishAnimating();
|
||||
}
|
||||
|
@ -2356,10 +2359,10 @@ void HistoryWidget::unregisterDraftSources() {
|
|||
}
|
||||
session().local().unregisterDraftSource(
|
||||
_history,
|
||||
Data::DraftKey::Local());
|
||||
Data::DraftKey::Local({}));
|
||||
session().local().unregisterDraftSource(
|
||||
_history,
|
||||
Data::DraftKey::LocalEdit());
|
||||
Data::DraftKey::LocalEdit({}));
|
||||
}
|
||||
|
||||
void HistoryWidget::registerDraftSource() {
|
||||
|
@ -2380,7 +2383,9 @@ void HistoryWidget::registerDraftSource() {
|
|||
};
|
||||
session().local().registerDraftSource(
|
||||
_history,
|
||||
editMsgId ? Data::DraftKey::LocalEdit() : Data::DraftKey::Local(),
|
||||
(editMsgId
|
||||
? Data::DraftKey::LocalEdit({})
|
||||
: Data::DraftKey::Local({})),
|
||||
std::move(draftSource));
|
||||
}
|
||||
|
||||
|
@ -3564,16 +3569,16 @@ void HistoryWidget::saveEditMsg() {
|
|||
cancelEdit();
|
||||
}
|
||||
})();
|
||||
if (const auto editDraft = history->localEditDraft()) {
|
||||
if (const auto editDraft = history->localEditDraft({})) {
|
||||
if (editDraft->saveRequestId == requestId) {
|
||||
history->clearLocalEditDraft();
|
||||
history->clearLocalEditDraft({});
|
||||
history->session().local().writeDrafts(history);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto fail = [=](const QString &error, mtpRequestId requestId) {
|
||||
if (const auto editDraft = history->localEditDraft()) {
|
||||
if (const auto editDraft = history->localEditDraft({})) {
|
||||
if (editDraft->saveRequestId == requestId) {
|
||||
editDraft->saveRequestId = 0;
|
||||
}
|
||||
|
@ -3648,6 +3653,7 @@ Api::SendAction HistoryWidget::prepareSendAction(
|
|||
Api::SendOptions options) const {
|
||||
auto result = Api::SendAction(_history, options);
|
||||
result.replyTo = replyToId();
|
||||
result.topicRootId = 0;
|
||||
result.options.sendAs = _sendAs
|
||||
? _history->session().sendAsPeers().resolveChosen(
|
||||
_history->peer).get()
|
||||
|
@ -6650,12 +6656,13 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
|
|||
}
|
||||
|
||||
if (_editMsgId) {
|
||||
if (auto localDraft = _history->localDraft()) {
|
||||
if (const auto localDraft = _history->localDraft({})) {
|
||||
localDraft->msgId = item->id;
|
||||
} else {
|
||||
_history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
TextWithTags(),
|
||||
item->id,
|
||||
MsgId(), // topicRootId
|
||||
MessageCursor(),
|
||||
Data::PreviewState::Allowed));
|
||||
}
|
||||
|
@ -6708,9 +6715,10 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
|||
_history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
_field,
|
||||
_replyToId,
|
||||
MsgId(), // topicRootId
|
||||
_previewState));
|
||||
} else {
|
||||
_history->clearLocalDraft();
|
||||
_history->clearLocalDraft({});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6732,6 +6740,7 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
|||
_history->setLocalEditDraft(std::make_unique<Data::Draft>(
|
||||
editData,
|
||||
item->id,
|
||||
MsgId(), // topicRootId
|
||||
cursor,
|
||||
previewState));
|
||||
applyDraft();
|
||||
|
@ -6811,10 +6820,10 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
|
|||
refreshTopBarActiveChat();
|
||||
updateControlsGeometry();
|
||||
update();
|
||||
} else if (auto localDraft = (_history ? _history->localDraft() : nullptr)) {
|
||||
} else if (const auto localDraft = (_history ? _history->localDraft({}) : nullptr)) {
|
||||
if (localDraft->msgId) {
|
||||
if (localDraft->textWithTags.text.isEmpty()) {
|
||||
_history->clearLocalDraft();
|
||||
_history->clearLocalDraft({});
|
||||
} else {
|
||||
localDraft->msgId = 0;
|
||||
}
|
||||
|
@ -6856,7 +6865,7 @@ void HistoryWidget::cancelEdit() {
|
|||
|
||||
_replyEditMsg = nullptr;
|
||||
setEditMsgId(0);
|
||||
_history->clearLocalEditDraft();
|
||||
_history->clearLocalEditDraft({});
|
||||
applyDraft();
|
||||
|
||||
if (_saveEditMsgRequestId) {
|
||||
|
|
|
@ -1169,16 +1169,18 @@ void ComposeControls::setFieldText(
|
|||
|
||||
void ComposeControls::saveFieldToHistoryLocalDraft() {
|
||||
const auto key = draftKeyCurrent();
|
||||
if (!_history || key == Data::DraftKey::None()) {
|
||||
if (!_history || !key) {
|
||||
return;
|
||||
}
|
||||
const auto id = _header->getDraftMessageId();
|
||||
if (_preview && (id || !_field->empty())) {
|
||||
const auto key = draftKeyCurrent();
|
||||
_history->setDraft(
|
||||
draftKeyCurrent(),
|
||||
key,
|
||||
std::make_unique<Data::Draft>(
|
||||
_field,
|
||||
_header->getDraftMessageId(),
|
||||
key.topicRootId(),
|
||||
_preview->state()));
|
||||
} else {
|
||||
_history->clearDraft(draftKeyCurrent());
|
||||
|
@ -1680,15 +1682,14 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const {
|
|||
|
||||
switch (_currentDialogsEntryState.section) {
|
||||
case Section::History:
|
||||
return (type == DraftType::Edit) ? Key::LocalEdit() : Key::Local();
|
||||
case Section::Replies:
|
||||
return (type == DraftType::Edit)
|
||||
? Key::LocalEdit(_currentDialogsEntryState.rootId)
|
||||
: Key::Local(_currentDialogsEntryState.rootId);
|
||||
case Section::Scheduled:
|
||||
return (type == DraftType::Edit)
|
||||
? Key::ScheduledEdit()
|
||||
: Key::Scheduled();
|
||||
case Section::Replies:
|
||||
return (type == DraftType::Edit)
|
||||
? Key::RepliesEdit(_currentDialogsEntryState.rootId)
|
||||
: Key::Replies(_currentDialogsEntryState.rootId);
|
||||
}
|
||||
return Key::None();
|
||||
}
|
||||
|
@ -2380,11 +2381,13 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
|
|||
const auto previewState = previewPage
|
||||
? Data::PreviewState::Allowed
|
||||
: Data::PreviewState::EmptyOnEdit;
|
||||
const auto key = draftKey(DraftType::Edit);
|
||||
_history->setDraft(
|
||||
draftKey(DraftType::Edit),
|
||||
key,
|
||||
std::make_unique<Data::Draft>(
|
||||
editData,
|
||||
item->id,
|
||||
key.topicRootId(),
|
||||
cursor,
|
||||
previewState));
|
||||
applyDraft();
|
||||
|
@ -2424,6 +2427,7 @@ void ComposeControls::replyToMessage(FullMsgId id) {
|
|||
std::make_unique<Data::Draft>(
|
||||
TextWithTags(),
|
||||
id.msg,
|
||||
key.topicRootId(),
|
||||
MessageCursor(),
|
||||
Data::PreviewState::Allowed));
|
||||
}
|
||||
|
@ -2438,7 +2442,6 @@ void ComposeControls::replyToMessage(FullMsgId id) {
|
|||
|
||||
void ComposeControls::cancelReplyMessage() {
|
||||
Expects(_history != nullptr);
|
||||
Expects(draftKeyCurrent() != Data::DraftKey::None());
|
||||
|
||||
const auto wasReply = replyingToMessage();
|
||||
_header->replyToMessage({});
|
||||
|
|
|
@ -645,7 +645,7 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
|||
if (forwarded && forwarded->imported) {
|
||||
result.flags |= Flag::Imported;
|
||||
}
|
||||
// We don't want to pass and update it in Date for now.
|
||||
// We don't want to pass and update it in Data for now.
|
||||
//if (item->unread()) {
|
||||
// result.flags |= Flag::Unread;
|
||||
//}
|
||||
|
|
|
@ -185,7 +185,7 @@ bool SimpleElementDelegate::elementHideReply(not_null<const Element*> view) {
|
|||
|
||||
bool SimpleElementDelegate::elementShownUnread(
|
||||
not_null<const Element*> view) {
|
||||
return view->data()->unread();
|
||||
return view->data()->unread(view->data()->history());
|
||||
}
|
||||
|
||||
void SimpleElementDelegate::elementSendBotCommand(
|
||||
|
|
|
@ -553,7 +553,7 @@ bool PinnedWidget::listElementHideReply(not_null<const Element*> view) {
|
|||
}
|
||||
|
||||
bool PinnedWidget::listElementShownUnread(not_null<const Element*> view) {
|
||||
return view->data()->unread();
|
||||
return view->data()->unread(view->data()->history());
|
||||
}
|
||||
|
||||
bool PinnedWidget::listIsGoodForAroundPosition(
|
||||
|
|
|
@ -351,8 +351,8 @@ RepliesWidget::RepliesWidget(
|
|||
}
|
||||
|
||||
RepliesWidget::~RepliesWidget() {
|
||||
if (_topic && _topic->forum()->creating(_rootId)) {
|
||||
_topic->forum()->discardCreatingId(_rootId);
|
||||
if (_topic && _topic->creating()) {
|
||||
_topic->discard();
|
||||
_topic = nullptr;
|
||||
}
|
||||
base::take(_sendAction);
|
||||
|
@ -434,15 +434,20 @@ void RepliesWidget::setupRootView() {
|
|||
}
|
||||
|
||||
void RepliesWidget::setupTopicViewer() {
|
||||
_history->owner().itemIdChanged(
|
||||
const auto owner = &_history->owner();
|
||||
owner->itemIdChanged(
|
||||
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
|
||||
if (_rootId == change.oldId) {
|
||||
_rootId = change.newId.msg;
|
||||
_sendAction = owner->sendActionManager().repliesPainter(
|
||||
_history,
|
||||
_rootId);
|
||||
_root = lookupRoot();
|
||||
if (_topic && _topic->rootId() == change.oldId) {
|
||||
setTopic(_topic->forum()->topicFor(change.newId.msg));
|
||||
} else {
|
||||
refreshReplies();
|
||||
refreshTopBarActiveChat();
|
||||
}
|
||||
_inner->update();
|
||||
}
|
||||
|
@ -1007,6 +1012,7 @@ Api::SendAction RepliesWidget::prepareSendAction(
|
|||
Api::SendOptions options) const {
|
||||
auto result = Api::SendAction(_history, options);
|
||||
result.replyTo = replyToId();
|
||||
result.topicRootId = _rootId;
|
||||
result.options.sendAs = _composeControls->sendAsPeer();
|
||||
return result;
|
||||
}
|
||||
|
@ -1440,7 +1446,7 @@ bool RepliesWidget::preventsClose(Fn<void()> &&continueCallback) const {
|
|||
return true;
|
||||
} else if (!_newTopicDiscarded
|
||||
&& _topic
|
||||
&& _topic->forum()->creating(_rootId)) {
|
||||
&& _topic->creating()) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
auto sure = [=](Fn<void()> &&close) {
|
||||
if (const auto strong = weak.data()) {
|
||||
|
@ -1943,14 +1949,7 @@ bool RepliesWidget::listElementHideReply(not_null<const Element*> view) {
|
|||
}
|
||||
|
||||
bool RepliesWidget::listElementShownUnread(not_null<const Element*> view) {
|
||||
if (!_root) {
|
||||
return false;
|
||||
}
|
||||
const auto item = view->data();
|
||||
const auto till = item->out()
|
||||
? _replies->computeOutboxReadTillFull()
|
||||
: _replies->computeInboxReadTillFull();
|
||||
return (item->id > till);
|
||||
return _replies->isServerSideUnread(view->data());
|
||||
}
|
||||
|
||||
bool RepliesWidget::listIsGoodForAroundPosition(
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history.h"
|
||||
|
@ -39,13 +40,20 @@ constexpr auto kStatusShowClientsideSpeaking = 6 * crl::time(1000);
|
|||
|
||||
} // namespace
|
||||
|
||||
SendActionPainter::SendActionPainter(not_null<History*> history)
|
||||
SendActionPainter::SendActionPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId)
|
||||
: _history(history)
|
||||
, _rootId(rootId)
|
||||
, _weak(&_history->session())
|
||||
, _st(st::dialogsTextStyle)
|
||||
, _sendActionText(st::dialogsTextWidthMin) {
|
||||
}
|
||||
|
||||
void SendActionPainter::setTopic(Data::ForumTopic *topic) {
|
||||
_topic = topic;
|
||||
}
|
||||
|
||||
bool SendActionPainter::updateNeedsAnimating(
|
||||
not_null<UserData*> user,
|
||||
const MTPSendMessageAction &action) {
|
||||
|
@ -382,7 +390,7 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) {
|
|||
st::normalFont->height,
|
||||
st::dialogsMiniPreviewTop + st::dialogsMiniPreview);
|
||||
_history->peer->owner().sendActionManager().updateAnimation({
|
||||
_history,
|
||||
_topic ? ((Data::Thread*)_topic) : _history,
|
||||
0,
|
||||
_sendActionAnimation.width() + _animationLeft,
|
||||
height,
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace Main {
|
|||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
class ForumTopic;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
enum class SendProgressType;
|
||||
struct SendProgress;
|
||||
|
@ -25,7 +29,9 @@ namespace HistoryView {
|
|||
|
||||
class SendActionPainter final {
|
||||
public:
|
||||
explicit SendActionPainter(not_null<History*> history);
|
||||
explicit SendActionPainter(not_null<History*> history, MsgId rootId = 0);
|
||||
|
||||
void setTopic(Data::ForumTopic *topic);
|
||||
|
||||
bool paint(
|
||||
Painter &p,
|
||||
|
@ -53,8 +59,10 @@ public:
|
|||
|
||||
private:
|
||||
const not_null<History*> _history;
|
||||
const MsgId _rootId = 0;
|
||||
const base::weak_ptr<Main::Session> _weak;
|
||||
const style::TextStyle &_st;
|
||||
Data::ForumTopic *_topic = nullptr;
|
||||
base::flat_map<not_null<UserData*>, crl::time> _typing;
|
||||
base::flat_map<not_null<UserData*>, crl::time> _speaking;
|
||||
base::flat_map<not_null<UserData*>, Api::SendProgress> _sendActions;
|
||||
|
|
|
@ -152,7 +152,7 @@ TopBarWidget::TopBarWidget(
|
|||
using AnimationUpdate = Data::SendActionManager::AnimationUpdate;
|
||||
session().data().sendActionManager().animationUpdated(
|
||||
) | rpl::filter([=](const AnimationUpdate &update) {
|
||||
return (update.history == _activeChat.key.history());
|
||||
return (update.thread == _activeChat.key.thread());
|
||||
}) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
@ -722,6 +722,7 @@ void TopBarWidget::backClicked() {
|
|||
void TopBarWidget::setActiveChat(
|
||||
ActiveChat activeChat,
|
||||
SendActionPainter *sendAction) {
|
||||
_sendAction = sendAction;
|
||||
if (_activeChat.key == activeChat.key
|
||||
&& _activeChat.section == activeChat.section) {
|
||||
_activeChat = activeChat;
|
||||
|
@ -733,7 +734,6 @@ void TopBarWidget::setActiveChat(
|
|||
!= activeChat.key.history());
|
||||
|
||||
_activeChat = activeChat;
|
||||
_sendAction = sendAction;
|
||||
_titlePeerText.clear();
|
||||
_back->clearState();
|
||||
update();
|
||||
|
|
|
@ -204,29 +204,35 @@ void Controller::setupMigrationViewer() {
|
|||
) | rpl::filter([=] {
|
||||
return peer->migrateTo() || (peer->migrateFrom() != _migrated);
|
||||
}) | rpl::start_with_next([=] {
|
||||
const auto window = parentController();
|
||||
const auto section = _section;
|
||||
auto params = Window::SectionShow(
|
||||
Window::SectionShow::Way::Backward,
|
||||
anim::type::instant,
|
||||
anim::activation::background);
|
||||
if (wrap() == Wrap::Side) {
|
||||
params.thirdColumn = true;
|
||||
}
|
||||
InvokeQueued(_widget, [=] {
|
||||
window->showSection(
|
||||
std::make_shared<Memento>(peer, section),
|
||||
params);
|
||||
});
|
||||
replaceWith(std::make_shared<Memento>(peer, _section));
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void Controller::replaceWith(std::shared_ptr<Memento> memento) {
|
||||
const auto window = parentController();
|
||||
const auto section = _section;
|
||||
auto params = Window::SectionShow(
|
||||
Window::SectionShow::Way::Backward,
|
||||
anim::type::instant,
|
||||
anim::activation::background);
|
||||
if (wrap() == Wrap::Side) {
|
||||
params.thirdColumn = true;
|
||||
}
|
||||
InvokeQueued(_widget, [=, memento = std::move(memento)]() mutable {
|
||||
window->showSection(std::move(memento), params);
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::setupTopicViewer() {
|
||||
session().data().itemIdChanged(
|
||||
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
|
||||
if (const auto topic = _key.topic()) {
|
||||
if (topic->rootId() == change.oldId) {
|
||||
_key = Key(topic->forum()->topicFor(change.newId.msg));
|
||||
if (topic->rootId() == change.oldId
|
||||
|| (topic->peer()->id == change.newId.peer
|
||||
&& topic->rootId() == change.newId.msg)) {
|
||||
const auto now = topic->forum()->topicFor(change.newId.msg);
|
||||
_key = Key(now);
|
||||
replaceWith(std::make_shared<Memento>(now, _section));
|
||||
}
|
||||
}
|
||||
}, _lifetime);
|
||||
|
|
|
@ -240,6 +240,8 @@ private:
|
|||
void setupMigrationViewer();
|
||||
void setupTopicViewer();
|
||||
|
||||
void replaceWith(std::shared_ptr<Memento> memento);
|
||||
|
||||
not_null<WrapWidget*> _widget;
|
||||
Key _key;
|
||||
PeerData *_migrated = nullptr;
|
||||
|
|
|
@ -577,6 +577,8 @@ Ui::MultiSlideTracker DetailsFiller::fillChannelButtons(
|
|||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> DetailsFiller::fill() {
|
||||
Expects(!_topic || !_topic->creating());
|
||||
|
||||
add(object_ptr<Ui::BoxContentDivider>(_wrap));
|
||||
add(CreateSkipWidget(_wrap));
|
||||
add(setupInfo());
|
||||
|
@ -585,6 +587,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::fill() {
|
|||
}
|
||||
setupMainButtons();
|
||||
add(CreateSkipWidget(_wrap));
|
||||
|
||||
return std::move(_wrap);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
|||
}, _cover->lifetime());
|
||||
_cover->setOnlineCount(rpl::single(0));
|
||||
if (_topic) {
|
||||
if (_topic->creating()) {
|
||||
return result;
|
||||
}
|
||||
result->add(SetupDetails(_controller, parent, _topic));
|
||||
} else {
|
||||
result->add(SetupDetails(_controller, parent, _peer));
|
||||
|
|
|
@ -555,12 +555,14 @@ bool MainWidget::shareUrl(
|
|||
QFIXED_MAX
|
||||
};
|
||||
const auto history = peer->owner().history(peer);
|
||||
const auto topicRootId = 0;
|
||||
history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
0,
|
||||
0, // replyTo
|
||||
topicRootId,
|
||||
cursor,
|
||||
Data::PreviewState::Allowed));
|
||||
history->clearLocalEditDraft();
|
||||
history->clearLocalEditDraft(topicRootId);
|
||||
history->session().changes().historyUpdated(
|
||||
history,
|
||||
Data::HistoryUpdate::Flag::LocalDraftSet);
|
||||
|
@ -587,12 +589,14 @@ bool MainWidget::inlineSwitchChosen(
|
|||
int(botAndQuery.size()),
|
||||
QFIXED_MAX
|
||||
};
|
||||
const auto topicRootId = 0;
|
||||
h->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
0,
|
||||
0, // replyTo
|
||||
topicRootId,
|
||||
cursor,
|
||||
Data::PreviewState::Allowed));
|
||||
h->clearLocalEditDraft();
|
||||
h->clearLocalEditDraft(topicRootId);
|
||||
h->session().changes().historyUpdated(
|
||||
h,
|
||||
Data::HistoryUpdate::Flag::LocalDraftSet);
|
||||
|
|
|
@ -1587,7 +1587,7 @@ void FormController::uploadEncryptedFile(
|
|||
auto prepared = std::make_shared<FileLoadResult>(
|
||||
TaskId(),
|
||||
file.uploadData->fileId,
|
||||
FileLoadTo(PeerId(), Api::SendOptions(), MsgId(), MsgId()),
|
||||
FileLoadTo(PeerId(), Api::SendOptions(), MsgId(), MsgId(), MsgId()),
|
||||
TextWithTags(),
|
||||
std::shared_ptr<SendingAlbum>(nullptr));
|
||||
prepared->type = SendMediaType::Secure;
|
||||
|
|
|
@ -200,15 +200,18 @@ struct FileLoadTo {
|
|||
PeerId peer,
|
||||
Api::SendOptions options,
|
||||
MsgId replyTo,
|
||||
MsgId topicRootId,
|
||||
MsgId replaceMediaOf)
|
||||
: peer(peer)
|
||||
, options(options)
|
||||
, replyTo(replyTo)
|
||||
, topicRootId(topicRootId)
|
||||
, replaceMediaOf(replaceMediaOf) {
|
||||
}
|
||||
PeerId peer;
|
||||
Api::SendOptions options;
|
||||
MsgId replyTo;
|
||||
MsgId topicRootId;
|
||||
MsgId replaceMediaOf;
|
||||
};
|
||||
|
||||
|
|
|
@ -1025,17 +1025,20 @@ std::unique_ptr<MTP::Config> Account::readMtpConfig() {
|
|||
template <typename Callback>
|
||||
void EnumerateDrafts(
|
||||
const Data::HistoryDrafts &map,
|
||||
Data::Draft *cloudDraft,
|
||||
bool supportMode,
|
||||
const base::flat_map<Data::DraftKey, MessageDraftSource> &sources,
|
||||
Callback &&callback) {
|
||||
for (const auto &[key, draft] : map) {
|
||||
if (key == Data::DraftKey::Cloud() || sources.contains(key)) {
|
||||
continue;
|
||||
} else if (key == Data::DraftKey::Local()
|
||||
&& !supportMode
|
||||
&& Data::draftsAreEqual(draft.get(), cloudDraft)) {
|
||||
if (key.isCloud() || sources.contains(key)) {
|
||||
continue;
|
||||
} else if (key.isLocal()
|
||||
&& (!supportMode || key.topicRootId())) {
|
||||
const auto i = map.find(
|
||||
Data::DraftKey::Cloud(key.topicRootId()));
|
||||
const auto cloud = (i != end(map)) ? i->second.get() : nullptr;
|
||||
if (Data::DraftsAreEqual(draft.get(), cloud)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
callback(
|
||||
key,
|
||||
|
@ -1085,10 +1088,6 @@ void Account::unregisterDraftSource(
|
|||
void Account::writeDrafts(not_null<History*> history) {
|
||||
const auto peerId = history->peer->id;
|
||||
const auto &map = history->draftsMap();
|
||||
const auto cloudIt = map.find(Data::DraftKey::Cloud());
|
||||
const auto cloudDraft = (cloudIt != end(map))
|
||||
? cloudIt->second.get()
|
||||
: nullptr;
|
||||
const auto supportMode = _owner->session().supportMode();
|
||||
const auto sourcesIt = _draftSources.find(history);
|
||||
const auto &sources = (sourcesIt != _draftSources.end())
|
||||
|
@ -1097,7 +1096,6 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
auto count = 0;
|
||||
EnumerateDrafts(
|
||||
map,
|
||||
cloudDraft,
|
||||
supportMode,
|
||||
sources,
|
||||
[&](auto&&...) { ++count; });
|
||||
|
@ -1133,7 +1131,6 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
};
|
||||
EnumerateDrafts(
|
||||
map,
|
||||
cloudDraft,
|
||||
supportMode,
|
||||
sources,
|
||||
sizeCallback);
|
||||
|
@ -1159,7 +1156,6 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
};
|
||||
EnumerateDrafts(
|
||||
map,
|
||||
cloudDraft,
|
||||
supportMode,
|
||||
sources,
|
||||
writeCallback);
|
||||
|
@ -1173,10 +1169,6 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
void Account::writeDraftCursors(not_null<History*> history) {
|
||||
const auto peerId = history->peer->id;
|
||||
const auto &map = history->draftsMap();
|
||||
const auto cloudIt = map.find(Data::DraftKey::Cloud());
|
||||
const auto cloudDraft = (cloudIt != end(map))
|
||||
? cloudIt->second.get()
|
||||
: nullptr;
|
||||
const auto supportMode = _owner->session().supportMode();
|
||||
const auto sourcesIt = _draftSources.find(history);
|
||||
const auto &sources = (sourcesIt != _draftSources.end())
|
||||
|
@ -1185,7 +1177,6 @@ void Account::writeDraftCursors(not_null<History*> history) {
|
|||
auto count = 0;
|
||||
EnumerateDrafts(
|
||||
map,
|
||||
cloudDraft,
|
||||
supportMode,
|
||||
sources,
|
||||
[&](auto&&...) { ++count; });
|
||||
|
@ -1223,7 +1214,6 @@ void Account::writeDraftCursors(not_null<History*> history) {
|
|||
};
|
||||
EnumerateDrafts(
|
||||
map,
|
||||
cloudDraft,
|
||||
supportMode,
|
||||
sources,
|
||||
writeCallback);
|
||||
|
@ -1282,7 +1272,7 @@ void Account::readDraftCursors(PeerId peerId, Data::HistoryDrafts &map) {
|
|||
? Data::DraftKey::FromSerialized(keyValue)
|
||||
: keysOld
|
||||
? Data::DraftKey::FromSerializedOld(keyValueOld)
|
||||
: Data::DraftKey::Local();
|
||||
: Data::DraftKey::Local(0);
|
||||
qint32 position = 0, anchor = 0, scroll = QFIXED_MAX;
|
||||
draft.stream >> position >> anchor >> scroll;
|
||||
if (const auto i = map.find(key); i != end(map)) {
|
||||
|
@ -1309,13 +1299,14 @@ void Account::readDraftCursorsLegacy(
|
|||
return;
|
||||
}
|
||||
|
||||
if (const auto i = map.find(Data::DraftKey::Local()); i != end(map)) {
|
||||
if (const auto i = map.find(Data::DraftKey::Local({})); i != end(map)) {
|
||||
i->second->cursor = MessageCursor(
|
||||
localPosition,
|
||||
localAnchor,
|
||||
localScroll);
|
||||
}
|
||||
if (const auto i = map.find(Data::DraftKey::LocalEdit()); i != end(map)) {
|
||||
if (const auto i = map.find(Data::DraftKey::LocalEdit({}))
|
||||
; i != end(map)) {
|
||||
i->second->cursor = MessageCursor(
|
||||
editPosition,
|
||||
editAnchor,
|
||||
|
@ -1327,7 +1318,7 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
|
|||
const auto guard = gsl::finally([&] {
|
||||
if (const auto migrated = history->migrateFrom()) {
|
||||
readDraftsWithCursors(migrated);
|
||||
migrated->clearLocalEditDraft();
|
||||
migrated->clearLocalEditDraft({});
|
||||
history->takeLocalDraft(migrated);
|
||||
}
|
||||
});
|
||||
|
@ -1396,10 +1387,11 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
|
|||
const auto key = keysOld
|
||||
? Data::DraftKey::FromSerializedOld(keyValueOld)
|
||||
: Data::DraftKey::FromSerialized(keyValue);
|
||||
if (key && key != Data::DraftKey::Cloud()) {
|
||||
if (key && !key.isCloud()) {
|
||||
map.emplace(key, std::make_unique<Data::Draft>(
|
||||
data,
|
||||
messageId,
|
||||
key.topicRootId(),
|
||||
MessageCursor(),
|
||||
previewState));
|
||||
}
|
||||
|
@ -1457,24 +1449,31 @@ void Account::readDraftsWithCursorsLegacy(
|
|||
editTagsSerialized,
|
||||
editData.text.size());
|
||||
|
||||
const auto topicRootId = MsgId();
|
||||
auto map = base::flat_map<Data::DraftKey, std::unique_ptr<Data::Draft>>();
|
||||
if (!msgData.text.isEmpty() || msgReplyTo) {
|
||||
map.emplace(Data::DraftKey::Local(), std::make_unique<Data::Draft>(
|
||||
msgData,
|
||||
msgReplyTo,
|
||||
MessageCursor(),
|
||||
(msgPreviewCancelled
|
||||
? Data::PreviewState::Cancelled
|
||||
: Data::PreviewState::Allowed)));
|
||||
map.emplace(
|
||||
Data::DraftKey::Local(topicRootId),
|
||||
std::make_unique<Data::Draft>(
|
||||
msgData,
|
||||
msgReplyTo,
|
||||
topicRootId,
|
||||
MessageCursor(),
|
||||
(msgPreviewCancelled
|
||||
? Data::PreviewState::Cancelled
|
||||
: Data::PreviewState::Allowed)));
|
||||
}
|
||||
if (editMsgId) {
|
||||
map.emplace(Data::DraftKey::LocalEdit(), std::make_unique<Data::Draft>(
|
||||
editData,
|
||||
editMsgId,
|
||||
MessageCursor(),
|
||||
(editPreviewCancelled
|
||||
? Data::PreviewState::Cancelled
|
||||
: Data::PreviewState::Allowed)));
|
||||
map.emplace(
|
||||
Data::DraftKey::LocalEdit(topicRootId),
|
||||
std::make_unique<Data::Draft>(
|
||||
editData,
|
||||
editMsgId,
|
||||
topicRootId,
|
||||
MessageCursor(),
|
||||
(editPreviewCancelled
|
||||
? Data::PreviewState::Cancelled
|
||||
: Data::PreviewState::Allowed)));
|
||||
}
|
||||
readDraftCursors(peerId, map);
|
||||
history->setDraftsMap(std::move(map));
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace {
|
|||
constexpr auto kOccupyFor = TimeId(60);
|
||||
constexpr auto kReoccupyEach = 30 * crl::time(1000);
|
||||
constexpr auto kMaxSupportInfoLength = MaxMessageSize * 4;
|
||||
constexpr auto kTopicRootId = MsgId(0);
|
||||
|
||||
class EditInfoBox : public Ui::BoxContent {
|
||||
public:
|
||||
|
@ -157,7 +158,8 @@ Data::Draft OccupiedDraft(const QString &normalizedName) {
|
|||
+ QString::number(OccupationTag())
|
||||
+ ";n:"
|
||||
+ normalizedName },
|
||||
MsgId(0),
|
||||
MsgId(0), // replyTo
|
||||
kTopicRootId,
|
||||
MessageCursor(),
|
||||
Data::PreviewState::Allowed
|
||||
};
|
||||
|
@ -176,7 +178,7 @@ uint32 ParseOccupationTag(History *history) {
|
|||
if (!TrackHistoryOccupation(history)) {
|
||||
return 0;
|
||||
}
|
||||
const auto draft = history->cloudDraft();
|
||||
const auto draft = history->cloudDraft(kTopicRootId);
|
||||
if (!draft) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -202,7 +204,7 @@ QString ParseOccupationName(History *history) {
|
|||
if (!TrackHistoryOccupation(history)) {
|
||||
return QString();
|
||||
}
|
||||
const auto draft = history->cloudDraft();
|
||||
const auto draft = history->cloudDraft(kTopicRootId);
|
||||
if (!draft) {
|
||||
return QString();
|
||||
}
|
||||
|
@ -228,7 +230,7 @@ TimeId OccupiedBySomeoneTill(History *history) {
|
|||
if (!TrackHistoryOccupation(history)) {
|
||||
return 0;
|
||||
}
|
||||
const auto draft = history->cloudDraft();
|
||||
const auto draft = history->cloudDraft(kTopicRootId);
|
||||
if (!draft) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -340,7 +342,7 @@ void Helper::updateOccupiedHistory(
|
|||
not_null<Window::SessionController*> controller,
|
||||
History *history) {
|
||||
if (isOccupiedByMe(_occupiedHistory)) {
|
||||
_occupiedHistory->clearCloudDraft();
|
||||
_occupiedHistory->clearCloudDraft(kTopicRootId);
|
||||
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
|
||||
}
|
||||
_occupiedHistory = history;
|
||||
|
@ -364,7 +366,7 @@ void Helper::occupyInDraft() {
|
|||
&& !isOccupiedBySomeone(_occupiedHistory)
|
||||
&& !_supportName.isEmpty()) {
|
||||
const auto draft = OccupiedDraft(_supportNameNormalized);
|
||||
_occupiedHistory->createCloudDraft(&draft);
|
||||
_occupiedHistory->createCloudDraft(kTopicRootId, &draft);
|
||||
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
|
||||
_reoccupyTimer.callEach(kReoccupyEach);
|
||||
}
|
||||
|
@ -373,7 +375,7 @@ void Helper::occupyInDraft() {
|
|||
void Helper::reoccupy() {
|
||||
if (isOccupiedByMe(_occupiedHistory)) {
|
||||
const auto draft = OccupiedDraft(_supportNameNormalized);
|
||||
_occupiedHistory->createCloudDraft(&draft);
|
||||
_occupiedHistory->createCloudDraft(kTopicRootId, &draft);
|
||||
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -979,6 +979,10 @@ void Manager::notificationActivated(
|
|||
const auto window = session->windows().front();
|
||||
const auto history = session->data().history(
|
||||
id.contextId.peerId);
|
||||
const auto item = history->owner().message(
|
||||
history->peer,
|
||||
id.msgId);
|
||||
const auto topic = item ? item->topic() : nullptr;
|
||||
if (!reply.text.isEmpty()) {
|
||||
// #TODO forum notifications
|
||||
const auto replyToId = (id.msgId > 0
|
||||
|
@ -988,6 +992,7 @@ void Manager::notificationActivated(
|
|||
auto draft = std::make_unique<Data::Draft>(
|
||||
reply,
|
||||
replyToId,
|
||||
(topic ? topic->rootId() : 0),
|
||||
MessageCursor{
|
||||
int(reply.text.size()),
|
||||
int(reply.text.size()),
|
||||
|
@ -1057,16 +1062,18 @@ void Manager::notificationReplied(
|
|||
return;
|
||||
}
|
||||
const auto history = session->data().history(id.contextId.peerId);
|
||||
const auto item = history->owner().message(history->peer, id.msgId);
|
||||
const auto topic = item ? item->topic() : nullptr;
|
||||
|
||||
auto message = Api::MessageToSend(Api::SendAction(history));
|
||||
message.textWithTags = reply;
|
||||
message.action.replyTo = (id.msgId > 0 && !history->peer->isUser())
|
||||
? id.msgId
|
||||
: id.contextId.topicRootId;
|
||||
message.action.topicRootId = topic ? topic->rootId() : 0;
|
||||
message.action.clearDraft = false;
|
||||
history->session().api().sendMessage(std::move(message));
|
||||
|
||||
const auto item = history->owner().message(history->peer, id.msgId);
|
||||
if (item && item->isUnreadMention() && !item->isIncomingUnreadMedia()) {
|
||||
history->session().api().markContentsRead(item);
|
||||
}
|
||||
|
|
|
@ -844,14 +844,16 @@ void Filler::addCreatePoll() {
|
|||
? SendMenu::Type::SilentOnly
|
||||
: SendMenu::Type::Scheduled;
|
||||
const auto flag = PollData::Flags();
|
||||
const auto topicRootId = _request.rootId;
|
||||
const auto replyToId = _request.currentReplyToId
|
||||
? _request.currentReplyToId
|
||||
: _request.rootId;
|
||||
: topicRootId;
|
||||
auto callback = [=] {
|
||||
PeerMenuCreatePoll(
|
||||
controller,
|
||||
peer,
|
||||
replyToId,
|
||||
topicRootId,
|
||||
flag,
|
||||
flag,
|
||||
source,
|
||||
|
@ -1168,6 +1170,7 @@ void PeerMenuCreatePoll(
|
|||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId replyToId,
|
||||
MsgId topicRootId,
|
||||
PollData::Flags chosen,
|
||||
PollData::Flags disabled,
|
||||
Api::SendType sendType,
|
||||
|
@ -1194,8 +1197,9 @@ void PeerMenuCreatePoll(
|
|||
result.options);
|
||||
action.clearDraft = false;
|
||||
action.replyTo = replyToId;
|
||||
if (const auto localDraft = action.history->localDraft()) {
|
||||
action.clearDraft = localDraft->textWithTags.text.isEmpty();
|
||||
action.topicRootId = topicRootId;
|
||||
if (const auto local = action.history->localDraft(topicRootId)) {
|
||||
action.clearDraft = local->textWithTags.text.isEmpty();
|
||||
}
|
||||
const auto api = &peer->session().api();
|
||||
api->polls().create(result.poll, action, crl::guard(weak, [=] {
|
||||
|
|
|
@ -69,6 +69,7 @@ void PeerMenuCreatePoll(
|
|||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId replyToId = 0,
|
||||
MsgId topicRootId = 0,
|
||||
PollData::Flags chosen = PollData::Flags(),
|
||||
PollData::Flags disabled = PollData::Flags(),
|
||||
Api::SendType sendType = Api::SendType::Normal,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 9ab11ccb36b7d03a3a24ebcd18d2f13b03fc3682
|
||||
Subproject commit d8b1f46715e5fcaf781b76ecbc386cbe31492287
|
Loading…
Add table
Reference in a new issue