Support drafts in monoforum sublists.

This commit is contained in:
John Preston 2025-05-20 20:32:24 +04:00
parent b2c01991a6
commit f65556acb7
30 changed files with 488 additions and 208 deletions

View file

@ -47,6 +47,7 @@ void Polls::create(
const auto topicRootId = action.replyTo.messageId
? action.replyTo.topicRootId
: 0;
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (action.replyTo) {
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
@ -54,9 +55,9 @@ void Polls::create(
const auto clearCloudDraft = action.clearDraft;
if (clearCloudDraft) {
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
history->clearLocalDraft(topicRootId);
history->clearCloudDraft(topicRootId);
history->startSavingCloudDraft(topicRootId);
history->clearLocalDraft(topicRootId, monoforumPeerId);
history->clearCloudDraft(topicRootId, monoforumPeerId);
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
}
const auto silentPost = ShouldSendSilent(peer, action.options);
const auto starsPaid = std::min(
@ -106,6 +107,7 @@ void Polls::create(
if (clearCloudDraft) {
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
}
_session->changes().historyUpdated(
@ -118,6 +120,7 @@ void Polls::create(
if (clearCloudDraft) {
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
}
fail();

View file

@ -2687,13 +2687,22 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto &data = update.c_updateDraftMessage();
const auto peerId = peerFromMTP(data.vpeer());
const auto topicRootId = data.vtop_msg_id().value_or_empty();
const auto monoforumPeerId = data.vsaved_peer_id()
? peerFromMTP(*data.vsaved_peer_id())
: PeerId();
data.vdraft().match([&](const MTPDdraftMessage &data) {
Data::ApplyPeerCloudDraft(&session(), peerId, topicRootId, data);
Data::ApplyPeerCloudDraft(
&session(),
peerId,
topicRootId,
monoforumPeerId,
data);
}, [&](const MTPDdraftMessageEmpty &data) {
Data::ClearPeerCloudDraft(
&session(),
peerId,
topicRootId,
monoforumPeerId,
data.vdate().value_or_empty());
});
} break;

View file

@ -2064,8 +2064,13 @@ void ApiWrap::saveCurrentDraftToCloud() {
_session->local().writeDrafts(history);
const auto topicRootId = thread->topicRootId();
const auto localDraft = history->localDraft(topicRootId);
const auto cloudDraft = history->cloudDraft(topicRootId);
const auto monoforumPeerId = thread->monoforumPeerId();
const auto localDraft = history->localDraft(
topicRootId,
monoforumPeerId);
const auto cloudDraft = history->cloudDraft(
topicRootId,
monoforumPeerId);
if (!Data::DraftsAreEqual(localDraft, cloudDraft)
&& !_session->supportMode()) {
saveDraftToCloudDelayed(thread);
@ -2088,15 +2093,22 @@ void ApiWrap::saveDraftsToCloud() {
const auto history = thread->owningHistory();
const auto topicRootId = thread->topicRootId();
auto cloudDraft = history->cloudDraft(topicRootId);
auto localDraft = history->localDraft(topicRootId);
const auto monoforumPeerId = thread->monoforumPeerId();
auto cloudDraft = history->cloudDraft(topicRootId, monoforumPeerId);
auto localDraft = history->localDraft(topicRootId, monoforumPeerId);
if (cloudDraft && cloudDraft->saveRequestId) {
request(base::take(cloudDraft->saveRequestId)).cancel();
}
if (!_session->supportMode()) {
cloudDraft = history->createCloudDraft(topicRootId, localDraft);
cloudDraft = history->createCloudDraft(
topicRootId,
monoforumPeerId,
localDraft);
} else if (!cloudDraft) {
cloudDraft = history->createCloudDraft(topicRootId, nullptr);
cloudDraft = history->createCloudDraft(
topicRootId,
monoforumPeerId,
nullptr);
}
auto flags = MTPmessages_SaveDraft::Flags(0);
@ -2106,7 +2118,9 @@ void ApiWrap::saveDraftsToCloud() {
} else if (!cloudDraft->webpage.url.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_media;
}
if (cloudDraft->reply.messageId || cloudDraft->reply.topicRootId) {
if (cloudDraft->reply.messageId
|| cloudDraft->reply.topicRootId
|| cloudDraft->reply.monoforumPeerId) {
flags |= MTPmessages_SaveDraft::Flag::f_reply_to;
}
if (!textWithTags.tags.isEmpty()) {
@ -2117,7 +2131,7 @@ void ApiWrap::saveDraftsToCloud() {
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
Api::ConvertOption::SkipLocal);
history->startSavingCloudDraft(topicRootId);
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
MTP_flags(flags),
ReplyToForMTP(history, cloudDraft->reply),
@ -2132,11 +2146,15 @@ void ApiWrap::saveDraftsToCloud() {
const auto requestId = response.requestId;
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
const auto cloudDraft = history->cloudDraft(
topicRootId,
monoforumPeerId);
if (cloudDraft) {
if (cloudDraft->saveRequestId == requestId) {
cloudDraft->saveRequestId = 0;
history->draftSavedToCloud(topicRootId);
history->draftSavedToCloud(topicRootId, monoforumPeerId);
}
}
const auto i = _draftsSaveRequestIds.find(weak);
@ -2149,10 +2167,14 @@ void ApiWrap::saveDraftsToCloud() {
const auto requestId = response.requestId;
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
const auto cloudDraft = history->cloudDraft(
topicRootId,
monoforumPeerId);
if (cloudDraft) {
if (cloudDraft->saveRequestId == requestId) {
history->clearCloudDraft(topicRootId);
history->clearCloudDraft(topicRootId, monoforumPeerId);
}
}
const auto i = _draftsSaveRequestIds.find(weak);
@ -3223,7 +3245,10 @@ void ApiWrap::sendAction(const SendAction &action) {
void ApiWrap::finishForwarding(const SendAction &action) {
const auto history = action.history;
const auto topicRootId = action.replyTo.topicRootId;
auto toForward = history->resolveForwardDraft(topicRootId);
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
auto toForward = history->resolveForwardDraft(
topicRootId,
monoforumPeerId);
if (!toForward.items.empty()) {
const auto error = GetErrorForSending(
history->peer,
@ -3236,7 +3261,7 @@ void ApiWrap::finishForwarding(const SendAction &action) {
}
forwardMessages(std::move(toForward), action);
history->setForwardDraft(topicRootId, {});
history->setForwardDraft(topicRootId, monoforumPeerId, {});
}
_session->data().sendHistoryChangeNotifications();
@ -3728,6 +3753,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
const auto clearCloudDraft = action.clearDraft;
const auto draftTopicRootId = action.replyTo.topicRootId;
const auto draftMonoforumPeerId = action.replyTo.monoforumPeerId;
const auto replyTo = action.replyTo.messageId
? peer->owner().message(action.replyTo.messageId)
: nullptr;
@ -3837,8 +3863,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (clearCloudDraft) {
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
mediaFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
history->clearCloudDraft(draftTopicRootId);
history->startSavingCloudDraft(draftTopicRootId);
history->clearCloudDraft(draftTopicRootId, draftMonoforumPeerId);
history->startSavingCloudDraft(
draftTopicRootId,
draftMonoforumPeerId);
}
const auto sendAs = action.options.sendAs;
if (sendAs) {
@ -3884,6 +3912,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (clearCloudDraft) {
history->finishSavingCloudDraft(
draftTopicRootId,
draftMonoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
}
};
@ -3898,6 +3927,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (clearCloudDraft) {
history->finishSavingCloudDraft(
draftTopicRootId,
draftMonoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
}
};
@ -4016,6 +4046,7 @@ void ApiWrap::sendInlineResult(
const auto topicRootId = action.replyTo.messageId
? action.replyTo.topicRootId
: 0;
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
using SendFlag = MTPmessages_SendInlineBotResult::Flag;
auto flags = NewMessageFlags(peer);
@ -4068,8 +4099,8 @@ void ApiWrap::sendInlineResult(
.postAuthor = NewMessagePostAuthor(action),
});
history->clearCloudDraft(topicRootId);
history->startSavingCloudDraft(topicRootId);
history->clearCloudDraft(topicRootId, monoforumPeerId);
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
auto &histories = history->owner().histories();
histories.sendPreparedMessage(
@ -4090,6 +4121,7 @@ void ApiWrap::sendInlineResult(
), [=](const MTPUpdates &result, const MTP::Response &response) {
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(true);
@ -4098,6 +4130,7 @@ void ApiWrap::sendInlineResult(
sendMessageFail(error, peer, randomId, newId);
history->finishSavingCloudDraft(
topicRootId,
monoforumPeerId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(false);

View file

@ -70,10 +70,11 @@ void ApplyPeerCloudDraft(
not_null<Main::Session*> session,
PeerId peerId,
MsgId topicRootId,
PeerId monoforumPeerId,
const MTPDdraftMessage &draft) {
const auto history = session->data().history(peerId);
const auto date = draft.vdate().v;
if (history->skipCloudDraftUpdate(topicRootId, date)) {
if (history->skipCloudDraftUpdate(topicRootId, monoforumPeerId, date)) {
return;
}
const auto textWithTags = TextWithTags{
@ -87,6 +88,7 @@ void ApplyPeerCloudDraft(
? ReplyToFromMTP(history, *draft.vreply_to())
: FullReplyTo();
replyTo.topicRootId = topicRootId;
replyTo.monoforumPeerId = monoforumPeerId;
auto webpage = WebPageDraft{
.invert = draft.is_invert_media(),
.removed = draft.is_no_webpage(),
@ -112,21 +114,22 @@ void ApplyPeerCloudDraft(
cloudDraft->date = date;
history->setCloudDraft(std::move(cloudDraft));
history->applyCloudDraft(topicRootId);
history->applyCloudDraft(topicRootId, monoforumPeerId);
}
void ClearPeerCloudDraft(
not_null<Main::Session*> session,
PeerId peerId,
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId date) {
const auto history = session->data().history(peerId);
if (history->skipCloudDraftUpdate(topicRootId, date)) {
if (history->skipCloudDraftUpdate(topicRootId, monoforumPeerId, date)) {
return;
}
history->clearCloudDraft(topicRootId);
history->applyCloudDraft(topicRootId);
history->clearCloudDraft(topicRootId, monoforumPeerId);
history->applyCloudDraft(topicRootId, monoforumPeerId);
}
void SetChatLinkDraft(not_null<PeerData*> peer, TextWithEntities draft) {
@ -146,12 +149,16 @@ void SetChatLinkDraft(not_null<PeerData*> peer, TextWithEntities draft) {
};
const auto history = peer->owner().history(peer->id);
const auto topicRootId = MsgId();
const auto monoforumPeerId = PeerId();
history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags,
FullReplyTo{ .topicRootId = topicRootId },
FullReplyTo{
.topicRootId = topicRootId,
.monoforumPeerId = monoforumPeerId,
},
cursor,
Data::WebPageDraft()));
history->clearLocalEditDraft(topicRootId);
history->clearLocalEditDraft(topicRootId, monoforumPeerId);
history->session().changes().entryUpdated(
history,
Data::EntryUpdate::Flag::LocalDraftSet);

View file

@ -23,11 +23,13 @@ void ApplyPeerCloudDraft(
not_null<Main::Session*> session,
PeerId peerId,
MsgId topicRootId,
PeerId monoforumPeerId,
const MTPDdraftMessage &draft);
void ClearPeerCloudDraft(
not_null<Main::Session*> session,
PeerId peerId,
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId date);
struct WebPageDraft {
@ -72,22 +74,38 @@ public:
[[nodiscard]] static constexpr DraftKey None() {
return 0;
}
[[nodiscard]] static constexpr DraftKey Local(MsgId topicRootId) {
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
[[nodiscard]] static constexpr DraftKey Local(
MsgId topicRootId,
PeerId monoforumPeerId) {
return Invalid(topicRootId, monoforumPeerId)
? None()
: (topicRootId ? topicRootId.bare : kLocalDraftIndex);
: (topicRootId
? topicRootId.bare
: monoforumPeerId
? (monoforumPeerId.value + kMonoforumDraftBit)
: kLocalDraftIndex);
}
[[nodiscard]] static constexpr DraftKey LocalEdit(MsgId topicRootId) {
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
[[nodiscard]] static constexpr DraftKey LocalEdit(
MsgId topicRootId,
PeerId monoforumPeerId) {
return Invalid(topicRootId, monoforumPeerId)
? None()
: ((topicRootId ? topicRootId.bare : kLocalDraftIndex)
+ kEditDraftShift);
: (kEditDraftShift
+ (topicRootId
? topicRootId.bare
: monoforumPeerId
? (monoforumPeerId.value + kMonoforumDraftBit)
: kLocalDraftIndex));
}
[[nodiscard]] static constexpr DraftKey Cloud(MsgId topicRootId) {
return (topicRootId < 0 || topicRootId >= ServerMaxMsgId)
[[nodiscard]] static constexpr DraftKey Cloud(
MsgId topicRootId,
PeerId monoforumPeerId) {
return Invalid(topicRootId, monoforumPeerId)
? None()
: topicRootId
? (kCloudDraftShift + topicRootId.bare)
: monoforumPeerId
? (kCloudDraftShift + monoforumPeerId.value + kMonoforumDraftBit)
: kCloudDraftIndex;
}
[[nodiscard]] static constexpr DraftKey Scheduled() {
@ -120,40 +138,62 @@ public:
return !value
? None()
: (value == kLocalDraftIndex + kEditDraftShiftOld)
? LocalEdit(0)
? LocalEdit(MsgId(), PeerId())
: (value == kScheduledDraftIndex + kEditDraftShiftOld)
? ScheduledEdit()
: (value > 0 && value < 0x4000'0000)
? Local(MsgId(value))
? Local(MsgId(value), PeerId())
: (value > kEditDraftShiftOld
&& value < kEditDraftShiftOld + 0x4000'000)
? LocalEdit(int64(value - kEditDraftShiftOld))
? LocalEdit(MsgId(int64(value - kEditDraftShiftOld)), PeerId())
: None();
}
[[nodiscard]] constexpr bool isLocal() const {
return (_value == kLocalDraftIndex)
|| (_value > 0 && _value < ServerMaxMsgId.bare);
|| (_value > 0
&& (_value & kMonoforumDraftMask) < ServerMaxMsgId.bare);
}
[[nodiscard]] constexpr bool isCloud() const {
return (_value == kCloudDraftIndex)
|| (_value > kCloudDraftShift
&& _value < kCloudDraftShift + ServerMaxMsgId.bare);
|| ((_value & kMonoforumDraftMask) > kCloudDraftShift
&& ((_value & kMonoforumDraftMask)
< kCloudDraftShift + ServerMaxMsgId.bare));
}
[[nodiscard]] constexpr MsgId topicRootId() const {
const auto max = ServerMaxMsgId.bare;
if (_value > kCloudDraftShift && _value < kCloudDraftShift + max) {
if (_value & kMonoforumDraftBit) {
return 0;
} else if ((_value > kCloudDraftShift)
&& (_value < kCloudDraftShift + max)) {
return (_value - kCloudDraftShift);
} else if (_value > kEditDraftShift && _value < kEditDraftShift + max) {
} else if ((_value > kEditDraftShift)
&& (_value < kEditDraftShift + max)) {
return (_value - kEditDraftShift);
} else if (_value > 0 && _value < max) {
return _value;
}
return 0;
}
[[nodiscard]] constexpr PeerId monoforumPeerId() const {
const auto max = ServerMaxMsgId.bare;
const auto value = _value & kMonoforumDraftMask;
if (!(_value & kMonoforumDraftBit)) {
return 0;
} else if ((value > kCloudDraftShift)
&& (value < kCloudDraftShift + max)) {
return PeerId(UserId(value - kCloudDraftShift));
} else if ((value > kEditDraftShift)
&& (value < kEditDraftShift + max)) {
return PeerId(UserId(value - kEditDraftShift));
} else if (value > 0 && value < max) {
return PeerId(UserId(value));
}
return 0;
}
friend inline constexpr auto operator<=>(DraftKey, DraftKey) = default;
friend inline constexpr bool operator==(DraftKey, DraftKey) = default;
inline explicit operator bool() const {
return _value != 0;
@ -163,9 +203,20 @@ private:
constexpr DraftKey(int64 value) : _value(value) {
}
[[nodiscard]] static constexpr bool Invalid(
MsgId topicRootId,
PeerId monoforumPeerId) {
return (topicRootId < 0)
|| (topicRootId >= ServerMaxMsgId)
|| !peerIsUser(monoforumPeerId)
|| (monoforumPeerId.value >= ServerMaxMsgId);
}
static constexpr auto kLocalDraftIndex = -1;
static constexpr auto kCloudDraftIndex = -2;
static constexpr auto kScheduledDraftIndex = -3;
static constexpr auto kMonoforumDraftBit = (int64(1) << 60);
static constexpr auto kMonoforumDraftMask = (kMonoforumDraftBit - 1);
static constexpr auto kEditDraftShift = ServerMaxMsgId.bare;
static constexpr auto kCloudDraftShift = 2 * ServerMaxMsgId.bare;
static constexpr auto kShortcutDraftShift = 3 * ServerMaxMsgId.bare;

View file

@ -73,7 +73,7 @@ Forum::~Forum() {
const auto peerId = _history->peer->id;
for (const auto &[rootId, topic] : _topics) {
storage.unload(Storage::SharedMediaUnloadThread(peerId, rootId));
_history->setForwardDraft(rootId, {});
_history->setForwardDraft(rootId, PeerId(), {});
const auto raw = topic.get();
changes.topicRemoved(raw);
@ -197,7 +197,7 @@ void Forum::applyTopicDeleted(MsgId rootId) {
session().storage().unload(Storage::SharedMediaUnloadThread(
_history->peer->id,
rootId));
_history->setForwardDraft(rootId, {});
_history->setForwardDraft(rootId, PeerId(), {});
}
}

View file

@ -406,6 +406,7 @@ void ForumTopic::applyTopic(const MTPDforumTopic &data) {
&session(),
channel()->id,
_rootId,
PeerId(),
data);
}, [](const MTPDdraftMessageEmpty&) {});
}
@ -709,7 +710,7 @@ void ForumTopic::requestChatListMessage() {
TimeId ForumTopic::adjustedChatListTimeId() const {
const auto result = chatListTimeId();
if (const auto draft = history()->cloudDraft(_rootId)) {
if (const auto draft = history()->cloudDraft(_rootId, PeerId())) {
if (!Data::DraftIsNull(draft) && !session().supportMode()) {
return std::max(result, draft->date);
}

View file

@ -180,11 +180,11 @@ struct FullReplyTo {
PeerId monoforumPeerId = 0;
int quoteOffset = 0;
[[nodiscard]] bool valid() const {
return messageId || (storyId && storyId.peer) || monoforumPeerId;
[[nodiscard]] bool replying() const {
return messageId || (storyId && storyId.peer);
}
explicit operator bool() const {
return valid();
return replying() || monoforumPeerId;
}
friend inline auto operator<=>(FullReplyTo, FullReplyTo) = default;
friend inline bool operator==(FullReplyTo, FullReplyTo) = default;

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_saved_messages.h"
#include "apiwrap.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_user.h"
#include "data/data_saved_sublist.h"
@ -47,7 +48,15 @@ SavedMessages::SavedMessages(
}
}
SavedMessages::~SavedMessages() = default;
SavedMessages::~SavedMessages() {
auto &changes = session().changes();
for (const auto &[peer, sublist] : _sublists) {
_owningHistory->setForwardDraft(MsgId(), peer->id, {});
const auto raw = sublist.get();
changes.entryRemoved(raw);
}
}
bool SavedMessages::supported() const {
return !_unsupported;

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_drafts.h"
#include "data/data_histories.h"
#include "data/data_messages.h"
#include "data/data_peer.h"
@ -695,15 +696,18 @@ void SavedSublist::subscribeToUnreadChanges() {
void SavedSublist::applyMonoforumDialog(
const MTPDmonoForumDialog &data,
not_null<HistoryItem*> topItem) {
//if (const auto draft = data.vdraft()) { // #TODO monoforum
// draft->match([&](const MTPDdraftMessage &data) {
// Data::ApplyPeerCloudDraft(
// &session(),
// channel()->id,
// _rootId,
// data);
// }, [](const MTPDdraftMessageEmpty&) {});
//}
if (const auto parent = parentChat()) {
if (const auto draft = data.vdraft()) {
draft->match([&](const MTPDdraftMessage &data) {
Data::ApplyPeerCloudDraft(
&session(),
parent->id,
MsgId(),
sublistPeer()->id,
data);
}, [](const MTPDdraftMessageEmpty&) {});
}
}
setInboxReadTill(
data.vread_inbox_max_id().v,
@ -712,6 +716,18 @@ void SavedSublist::applyMonoforumDialog(
applyMaybeLast(topItem);
}
TimeId SavedSublist::adjustedChatListTimeId() const {
const auto result = chatListTimeId();
const auto monoforumPeerId = sublistPeer()->id;
const auto history = _parent->owningHistory();
if (const auto draft = history->cloudDraft(MsgId(), monoforumPeerId)) {
if (!Data::DraftIsNull(draft) && !session().supportMode()) {
return std::max(result, draft->date);
}
}
return result;
}
rpl::producer<> SavedSublist::changes() const {
return _listChanges.events();
}

View file

@ -72,6 +72,8 @@ public:
void readTillEnd();
void requestChatListMessage();
TimeId adjustedChatListTimeId() const override;
int fixedOnTopIndex() const override;
bool shouldBeInChatList() const override;
Dialogs::UnreadState chatListUnreadState() const override;

View file

@ -32,6 +32,13 @@ MsgId Thread::topicRootId() const {
return MsgId();
}
PeerId Thread::monoforumPeerId() const {
if (const auto sublist = asSublist()) {
return sublist->sublistPeer()->id;
}
return PeerId();
}
PeerData *Thread::maybeSublistPeer() const {
if (const auto sublist = asSublist()) {
return sublist->sublistPeer();

View file

@ -67,6 +67,7 @@ public:
return const_cast<Thread*>(this)->owningHistory();
}
[[nodiscard]] MsgId topicRootId() const;
[[nodiscard]] PeerId monoforumPeerId() const;
[[nodiscard]] PeerData *maybeSublistPeer() const;
[[nodiscard]] not_null<PeerData*> peer() const;
[[nodiscard]] PeerNotifySettings &notify();

View file

@ -382,8 +382,6 @@ struct ForwardDraft {
const ForwardDraft&) = default;
};
using ForwardDrafts = base::flat_map<MsgId, ForwardDraft>;
struct ResolvedForwardDraft {
HistoryItemsList items;
ForwardOptions options = ForwardOptions::PreserveInfo;

View file

@ -962,10 +962,12 @@ void RowPainter::Paint(
if (!thread) {
return nullptr;
}
if ((!peer || !peer->isForum()) && (!item || !badgesState.unread)) {
if ((!peer || (!peer->isForum() && !peer->amMonoforumAdmin()))
&& (!item || !badgesState.unread)) {
// Draw item, if there are unread messages.
const auto draft = thread->owningHistory()->cloudDraft(
thread->topicRootId());
thread->topicRootId(),
thread->monoforumPeerId());
if (!Data::DraftIsNull(draft)) {
return draft;
}

View file

@ -206,33 +206,39 @@ void History::itemVanished(not_null<HistoryItem*> item) {
void History::takeLocalDraft(not_null<History*> from) {
const auto topicRootId = MsgId(0);
const auto i = from->_drafts.find(Data::DraftKey::Local(topicRootId));
const auto monoforumPeerId = PeerId(0);
const auto i = from->_drafts.find(
Data::DraftKey::Local(topicRootId, monoforumPeerId));
if (i == end(from->_drafts)) {
return;
}
auto &draft = i->second;
if (!draft->textWithTags.text.isEmpty()
&& !_drafts.contains(Data::DraftKey::Local(topicRootId))) {
&& !_drafts.contains(
Data::DraftKey::Local(topicRootId, monoforumPeerId))) {
// Edit and reply to drafts can't migrate.
// Cloud drafts do not migrate automatically.
draft->reply = FullReplyTo();
setLocalDraft(std::move(draft));
}
from->clearLocalDraft(topicRootId);
from->clearLocalDraft(topicRootId, monoforumPeerId);
session().api().saveDraftToCloudDelayed(from);
}
void History::createLocalDraftFromCloud(MsgId topicRootId) {
const auto draft = cloudDraft(topicRootId);
void History::createLocalDraftFromCloud(
MsgId topicRootId,
PeerId monoforumPeerId) {
const auto draft = cloudDraft(topicRootId, monoforumPeerId);
if (!draft) {
clearLocalDraft(topicRootId);
clearLocalDraft(topicRootId, monoforumPeerId);
return;
} else if (Data::DraftIsNull(draft) || !draft->date) {
return;
}
draft->reply.topicRootId = topicRootId;
auto existing = localDraft(topicRootId);
draft->reply.monoforumPeerId = monoforumPeerId;
auto existing = localDraft(topicRootId, monoforumPeerId);
if (Data::DraftIsNull(existing)
|| !existing->date
|| draft->date >= existing->date) {
@ -242,7 +248,7 @@ void History::createLocalDraftFromCloud(MsgId topicRootId) {
draft->reply,
draft->cursor,
draft->webpage));
existing = localDraft(topicRootId);
existing = localDraft(topicRootId, monoforumPeerId);
} else if (existing != draft) {
existing->textWithTags = draft->textWithTags;
existing->reply = draft->reply;
@ -268,7 +274,7 @@ void History::setDraft(
return;
}
const auto cloudThread = key.isCloud()
? threadFor(key.topicRootId())
? threadFor(key.topicRootId(), key.monoforumPeerId())
: nullptr;
if (cloudThread) {
cloudThread->cloudDraftTextCache().clear();
@ -298,7 +304,7 @@ void History::clearDraft(Data::DraftKey key) {
void History::clearDrafts() {
for (auto &[key, draft] : base::take(_drafts)) {
const auto cloudThread = key.isCloud()
? threadFor(key.topicRootId())
? threadFor(key.topicRootId(), key.monoforumPeerId())
: nullptr;
if (cloudThread) {
cloudThread->cloudDraftTextCache().clear();
@ -309,25 +315,30 @@ void History::clearDrafts() {
Data::Draft *History::createCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
const Data::Draft *fromDraft) {
if (Data::DraftIsNull(fromDraft)) {
setCloudDraft(std::make_unique<Data::Draft>(
TextWithTags(),
FullReplyTo{ .topicRootId = topicRootId },
FullReplyTo{
.topicRootId = topicRootId,
.monoforumPeerId = monoforumPeerId,
},
MessageCursor(),
Data::WebPageDraft()));
cloudDraft(topicRootId)->date = TimeId(0);
cloudDraft(topicRootId, monoforumPeerId)->date = TimeId(0);
} else {
auto existing = cloudDraft(topicRootId);
auto existing = cloudDraft(topicRootId, monoforumPeerId);
if (!existing) {
auto reply = fromDraft->reply;
reply.topicRootId = topicRootId;
reply.monoforumPeerId = monoforumPeerId;
setCloudDraft(std::make_unique<Data::Draft>(
fromDraft->textWithTags,
reply,
fromDraft->cursor,
fromDraft->webpage));
existing = cloudDraft(topicRootId);
existing = cloudDraft(topicRootId, monoforumPeerId);
} else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags;
existing->reply = fromDraft->reply;
@ -336,44 +347,56 @@ Data::Draft *History::createCloudDraft(
}
existing->date = base::unixtime::now();
existing->reply.topicRootId = topicRootId;
existing->reply.monoforumPeerId = monoforumPeerId;
}
if (const auto thread = threadFor(topicRootId)) {
if (const auto thread = threadFor(topicRootId, monoforumPeerId)) {
thread->cloudDraftTextCache().clear();
thread->updateChatListSortPosition();
}
return cloudDraft(topicRootId);
return cloudDraft(topicRootId, monoforumPeerId);
}
bool History::skipCloudDraftUpdate(MsgId topicRootId, TimeId date) const {
const auto i = _acceptCloudDraftsAfter.find(topicRootId);
return _savingCloudDraftRequests.contains(topicRootId)
bool History::skipCloudDraftUpdate(
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId date) const {
const auto key = Data::DraftKey::Local(topicRootId, monoforumPeerId);
const auto i = _acceptCloudDraftsAfter.find(key);
return _savingCloudDraftRequests.contains(key)
|| (i != _acceptCloudDraftsAfter.end() && date < i->second);
}
void History::startSavingCloudDraft(MsgId topicRootId) {
++_savingCloudDraftRequests[topicRootId];
void History::startSavingCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId) {
const auto key = Data::DraftKey::Local(topicRootId, monoforumPeerId);
++_savingCloudDraftRequests[key];
}
void History::finishSavingCloudDraft(MsgId topicRootId, TimeId savedAt) {
const auto i = _savingCloudDraftRequests.find(topicRootId);
void History::finishSavingCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId savedAt) {
const auto key = Data::DraftKey::Local(topicRootId, monoforumPeerId);
const auto i = _savingCloudDraftRequests.find(key);
if (i != _savingCloudDraftRequests.end()) {
if (--i->second <= 0) {
_savingCloudDraftRequests.erase(i);
}
}
auto &after = _acceptCloudDraftsAfter[topicRootId];
auto &after = _acceptCloudDraftsAfter[key];
after = std::max(after, savedAt + kSkipCloudDraftsFor);
}
void History::applyCloudDraft(MsgId topicRootId) {
void History::applyCloudDraft(MsgId topicRootId, PeerId monoforumPeerId) {
if (!topicRootId && session().supportMode()) {
updateChatListEntry();
session().supportHelper().cloudDraftChanged(this);
} else {
createLocalDraftFromCloud(topicRootId);
if (const auto thread = threadFor(topicRootId)) {
createLocalDraftFromCloud(topicRootId, monoforumPeerId);
if (const auto thread = threadFor(topicRootId, monoforumPeerId)) {
thread->updateChatListSortPosition();
if (!topicRootId) {
session().changes().historyUpdated(
@ -388,17 +411,19 @@ void History::applyCloudDraft(MsgId topicRootId) {
}
}
void History::draftSavedToCloud(MsgId topicRootId) {
if (const auto thread = threadFor(topicRootId)) {
void History::draftSavedToCloud(MsgId topicRootId, PeerId monoforumPeerId) {
if (const auto thread = threadFor(topicRootId, monoforumPeerId)) {
thread->updateChatListEntry();
}
session().local().writeDrafts(this);
}
const Data::ForwardDraft &History::forwardDraft(
MsgId topicRootId) const {
MsgId topicRootId,
PeerId monoforumPeerId) const {
const auto key = Data::DraftKey::Local(topicRootId, monoforumPeerId);
static const auto kEmpty = Data::ForwardDraft();
const auto i = _forwardDrafts.find(topicRootId);
const auto i = _forwardDrafts.find(key);
return (i != end(_forwardDrafts)) ? i->second : kEmpty;
}
@ -411,11 +436,12 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
}
Data::ResolvedForwardDraft History::resolveForwardDraft(
MsgId topicRootId) {
const auto &draft = forwardDraft(topicRootId);
MsgId topicRootId,
PeerId monoforumPeerId) {
const auto &draft = forwardDraft(topicRootId, monoforumPeerId);
auto result = resolveForwardDraft(draft);
if (result.items.size() != draft.ids.size()) {
setForwardDraft(topicRootId, {
setForwardDraft(topicRootId, monoforumPeerId, {
.ids = owner().itemsToIds(result.items),
.options = result.options,
});
@ -425,24 +451,23 @@ Data::ResolvedForwardDraft History::resolveForwardDraft(
void History::setForwardDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
Data::ForwardDraft &&draft) {
auto changed = false;
const auto key = Data::DraftKey::Local(topicRootId, monoforumPeerId);
if (draft.ids.empty()) {
changed = _forwardDrafts.remove(topicRootId);
changed = _forwardDrafts.remove(key);
} else {
auto &now = _forwardDrafts[topicRootId];
auto &now = _forwardDrafts[key];
if (now != draft) {
now = std::move(draft);
changed = true;
}
}
if (changed) {
const auto entry = topicRootId
? peer->forumTopicFor(topicRootId)
: (Dialogs::Entry*)this;
if (entry) {
if (const auto thread = threadFor(topicRootId, monoforumPeerId)) {
session().changes().entryUpdated(
entry,
thread,
Data::EntryUpdate::Flag::ForwardDraft);
}
}
@ -2081,7 +2106,7 @@ void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
TimeId History::adjustedChatListTimeId() const {
const auto result = chatListTimeId();
if (const auto draft = cloudDraft(MsgId(0))) {
if (const auto draft = cloudDraft(MsgId(), PeerId())) {
if (!peer->forum()
&& !Data::DraftIsNull(draft)
&& !session().supportMode()) {
@ -2871,7 +2896,8 @@ void History::applyDialog(
Data::ApplyPeerCloudDraft(
&session(),
peer->id,
MsgId(0), // topicRootId
MsgId(), // topicRootId
PeerId(), // monoforumPeerId
draft->c_draftMessage());
}
if (const auto ttl = data.vttl_period()) {
@ -3101,14 +3127,22 @@ void History::forceFullResize() {
_flags |= Flag::HasPendingResizedItems;
}
Data::Thread *History::threadFor(MsgId topicRootId) {
Data::Thread *History::threadFor(MsgId topicRootId, PeerId monoforumPeerId) {
return topicRootId
? peer->forumTopicFor(topicRootId)
: static_cast<Data::Thread*>(this);
: !monoforumPeerId
? static_cast<Data::Thread*>(this)
: peer->monoforum()
? peer->monoforum()->sublistLoaded(owner().peer(monoforumPeerId))
: nullptr;
}
const Data::Thread *History::threadFor(MsgId topicRootId) const {
return const_cast<History*>(this)->threadFor(topicRootId);
const Data::Thread *History::threadFor(
MsgId topicRootId,
PeerId monoforumPeerId) const {
return const_cast<History*>(this)->threadFor(
topicRootId,
monoforumPeerId);
}
void History::forumChanged(Data::Forum *old) {
@ -3137,7 +3171,7 @@ void History::forumChanged(Data::Forum *old) {
} else {
_flags &= ~Flag::IsForum;
}
if (cloudDraft(MsgId(0))) {
if (cloudDraft(MsgId(), PeerId())) {
updateChatListSortPosition();
}
_flags |= Flag::PendingAllItemsResize;
@ -3173,7 +3207,7 @@ void History::monoforumChanged(Data::SavedMessages *old) {
} else {
_flags &= ~Flag::IsMonoforumAdmin;
}
if (cloudDraft(MsgId(0))) {
if (cloudDraft(MsgId(), PeerId())) {
updateChatListSortPosition();
}
_flags |= Flag::PendingAllItemsResize;

View file

@ -62,8 +62,12 @@ public:
[[nodiscard]] not_null<History*> owningHistory() override {
return this;
}
[[nodiscard]] Data::Thread *threadFor(MsgId topicRootId);
[[nodiscard]] const Data::Thread *threadFor(MsgId topicRootId) const;
[[nodiscard]] Data::Thread *threadFor(
MsgId topicRootId,
PeerId monoforumPeerId);
[[nodiscard]] const Data::Thread *threadFor(
MsgId topicRootId,
PeerId monoforumPeerId) const;
[[nodiscard]] auto delegateMixin() const
-> not_null<HistoryMainElementDelegateMixin*> {
@ -288,60 +292,89 @@ public:
[[nodiscard]] const Data::HistoryDrafts &draftsMap() const;
void setDraftsMap(Data::HistoryDrafts &&map);
Data::Draft *localDraft(MsgId topicRootId) const {
return draft(Data::DraftKey::Local(topicRootId));
Data::Draft *localDraft(
MsgId topicRootId,
PeerId monoforumPeerId) const {
return draft(Data::DraftKey::Local(topicRootId, monoforumPeerId));
}
Data::Draft *localEditDraft(MsgId topicRootId) const {
return draft(Data::DraftKey::LocalEdit(topicRootId));
Data::Draft *localEditDraft(
MsgId topicRootId,
PeerId monoforumPeerId) const {
return draft(
Data::DraftKey::LocalEdit(topicRootId, monoforumPeerId));
}
Data::Draft *cloudDraft(MsgId topicRootId) const {
return draft(Data::DraftKey::Cloud(topicRootId));
Data::Draft *cloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId) const {
return draft(Data::DraftKey::Cloud(topicRootId, monoforumPeerId));
}
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft(
Data::DraftKey::Local(draft->reply.topicRootId),
Data::DraftKey::Local(
draft->reply.topicRootId,
draft->reply.monoforumPeerId),
std::move(draft));
}
void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft(
Data::DraftKey::LocalEdit(draft->reply.topicRootId),
Data::DraftKey::LocalEdit(
draft->reply.topicRootId,
draft->reply.monoforumPeerId),
std::move(draft));
}
void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft(
Data::DraftKey::Cloud(draft->reply.topicRootId),
Data::DraftKey::Cloud(
draft->reply.topicRootId,
draft->reply.monoforumPeerId),
std::move(draft));
}
void clearLocalDraft(MsgId topicRootId) {
clearDraft(Data::DraftKey::Local(topicRootId));
void clearLocalDraft(
MsgId topicRootId,
PeerId monoforumPeerId) {
clearDraft(Data::DraftKey::Local(topicRootId, monoforumPeerId));
}
void clearCloudDraft(MsgId topicRootId) {
clearDraft(Data::DraftKey::Cloud(topicRootId));
void clearCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId) {
clearDraft(Data::DraftKey::Cloud(topicRootId, monoforumPeerId));
}
void clearLocalEditDraft(MsgId topicRootId) {
clearDraft(Data::DraftKey::LocalEdit(topicRootId));
void clearLocalEditDraft(
MsgId topicRootId,
PeerId monoforumPeerId) {
clearDraft(Data::DraftKey::LocalEdit(topicRootId, monoforumPeerId));
}
void clearDrafts();
Data::Draft *createCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
const Data::Draft *fromDraft);
[[nodiscard]] bool skipCloudDraftUpdate(
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId date) const;
void startSavingCloudDraft(MsgId topicRootId);
void finishSavingCloudDraft(MsgId topicRootId, TimeId savedAt);
void startSavingCloudDraft(MsgId topicRootId, PeerId monoforumPeerId);
void finishSavingCloudDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
TimeId savedAt);
void takeLocalDraft(not_null<History*> from);
void applyCloudDraft(MsgId topicRootId);
void draftSavedToCloud(MsgId topicRootId);
void applyCloudDraft(MsgId topicRootId, PeerId monoforumPeerId);
void draftSavedToCloud(MsgId topicRootId, PeerId monoforumPeerId);
void requestChatListMessage();
[[nodiscard]] const Data::ForwardDraft &forwardDraft(
MsgId topicRootId) const;
MsgId topicRootId,
PeerId monoforumPeerId) const;
[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
const Data::ForwardDraft &draft) const;
[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
MsgId topicRootId);
void setForwardDraft(MsgId topicRootId, Data::ForwardDraft &&draft);
MsgId topicRootId,
PeerId monoforumPeerId);
void setForwardDraft(
MsgId topicRootId,
PeerId monoforumPeerId,
Data::ForwardDraft &&draft);
History *migrateSibling() const;
[[nodiscard]] bool useTopPromotion() const;
@ -548,7 +581,9 @@ private:
void viewReplaced(not_null<const Element*> was, Element *now);
void createLocalDraftFromCloud(MsgId topicRootId);
void createLocalDraftFromCloud(
MsgId topicRootId,
PeerId monoforumPeerId);
HistoryItem *insertJoinedMessage();
void insertMessageToBlocks(not_null<HistoryItem*> item);
@ -606,9 +641,9 @@ private:
std::unique_ptr<HistoryTranslation> _translation;
Data::HistoryDrafts _drafts;
base::flat_map<MsgId, TimeId> _acceptCloudDraftsAfter;
base::flat_map<MsgId, int> _savingCloudDraftRequests;
Data::ForwardDrafts _forwardDrafts;
base::flat_map<Data::DraftKey, TimeId> _acceptCloudDraftsAfter;
base::flat_map<Data::DraftKey, int> _savingCloudDraftRequests;
base::flat_map<Data::DraftKey, Data::ForwardDraft> _forwardDrafts;
QString _topPromotedMessage;
QString _topPromotedType;

View file

@ -1118,7 +1118,7 @@ void HistoryWidget::initVoiceRecordBar() {
});
const auto applyLocalDraft = [=] {
if (_history && _history->localDraft({})) {
if (_history && _history->localDraft(MsgId(), PeerId())) {
applyDraft();
}
};
@ -1874,12 +1874,14 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
}
const auto topicRootId = MsgId();
const auto monoforumPeerId = PeerId();
if (_editMsgId) {
_history->setLocalEditDraft(std::make_unique<Data::Draft>(
_field,
FullReplyTo{
.messageId = FullMsgId(_history->peer->id, _editMsgId),
.topicRootId = topicRootId,
.monoforumPeerId = monoforumPeerId,
},
_preview->draft(),
_saveEditMsgRequestId));
@ -1890,9 +1892,9 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
_replyTo,
_preview->draft()));
} else {
_history->clearLocalDraft(topicRootId);
_history->clearLocalDraft(topicRootId, monoforumPeerId);
}
_history->clearLocalEditDraft(topicRootId);
_history->clearLocalEditDraft(topicRootId, monoforumPeerId);
}
}
@ -2187,11 +2189,13 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
});
const auto editDraft = _history ? _history->localEditDraft({}) : nullptr;
const auto editDraft = _history
? _history->localEditDraft(MsgId(), PeerId())
: nullptr;
const auto draft = editDraft
? editDraft
: _history
? _history->localDraft({})
? _history->localDraft(MsgId(), PeerId())
: nullptr;
auto fieldAvailable = canWriteMessage();
const auto editMsgId = editDraft ? editDraft->reply.messageId.msg : 0;
@ -2241,7 +2245,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
requestMessageData(_editMsgId);
}
} else {
const auto draft = _history->localDraft({});
const auto draft = _history->localDraft(MsgId(), PeerId());
_processingReplyTo = draft ? draft->reply : FullReplyTo();
if (_processingReplyTo) {
_processingReplyItem = session().data().message(
@ -2408,7 +2412,7 @@ void HistoryWidget::showHistory(
info->inlineReturnTo = wasState;
}
sendBotStartCommand();
_history->clearLocalDraft({});
_history->clearLocalDraft(MsgId(), PeerId());
applyDraft();
_send->finishAnimating();
}
@ -2864,10 +2868,10 @@ void HistoryWidget::unregisterDraftSources() {
}
session().local().unregisterDraftSource(
_history,
Data::DraftKey::Local({}));
Data::DraftKey::Local(MsgId(), PeerId()));
session().local().unregisterDraftSource(
_history,
Data::DraftKey::LocalEdit({}));
Data::DraftKey::LocalEdit(MsgId(), PeerId()));
}
void HistoryWidget::registerDraftSource() {
@ -2892,8 +2896,8 @@ void HistoryWidget::registerDraftSource() {
session().local().registerDraftSource(
_history,
(editMsgId
? Data::DraftKey::LocalEdit({})
: Data::DraftKey::Local({})),
? Data::DraftKey::LocalEdit(MsgId(), PeerId())
: Data::DraftKey::Local(MsgId(), PeerId())),
std::move(draftSource));
}
@ -3630,6 +3634,7 @@ void HistoryWidget::unreadCountUpdated() {
});
} else {
const auto hideCounter = _history->isForum()
|| _history->amMonoforumAdmin()
|| !_history->trackUnreadMessages();
_cornerButtons.updateJumpDownVisibility(hideCounter
? 0
@ -4376,16 +4381,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(MsgId(), PeerId());
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;
}
@ -7276,7 +7281,7 @@ void HistoryWidget::editDraftOptions() {
} else {
cancelReply();
}
history->setForwardDraft({}, std::move(forward));
history->setForwardDraft(MsgId(), PeerId(), std::move(forward));
_preview->apply(webpage);
};
const auto replyToId = reply.messageId;
@ -7295,7 +7300,9 @@ void HistoryWidget::editDraftOptions() {
.resolver = _preview->resolver(),
.done = done,
.highlight = highlight,
.clearOldDraft = [=] { ClearDraftReplyTo(history, 0, replyToId); },
.clearOldDraft = [=] {
ClearDraftReplyTo(history, MsgId(), PeerId(), replyToId);
},
});
}
@ -8418,7 +8425,7 @@ void HistoryWidget::setReplyFieldsFromProcessing() {
const auto id = base::take(_processingReplyTo);
const auto item = base::take(_processingReplyItem);
if (_editMsgId) {
if (const auto localDraft = _history->localDraft({})) {
if (const auto localDraft = _history->localDraft({}, {})) {
localDraft->reply = id;
} else {
_history->setLocalDraft(std::make_unique<Data::Draft>(
@ -8470,7 +8477,7 @@ void HistoryWidget::editMessage(
_replyTo,
_preview->draft()));
} else {
_history->clearLocalDraft({});
_history->clearLocalDraft(MsgId(), PeerId());
}
}
@ -8580,10 +8587,10 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
updateControlsGeometry();
update();
} else if (const auto localDraft
= (_history ? _history->localDraft({}) : nullptr)) {
= (_history ? _history->localDraft({}, {}) : nullptr)) {
if (localDraft->reply) {
if (localDraft->textWithTags.text.isEmpty()) {
_history->clearLocalDraft({});
_history->clearLocalDraft(MsgId(), PeerId());
} else {
localDraft->reply = {};
}
@ -8629,7 +8636,7 @@ void HistoryWidget::cancelEdit() {
updateReplaceMediaButton();
_replyEditMsg = nullptr;
setEditMsgId(0);
_history->clearLocalEditDraft({});
_history->clearLocalEditDraft(MsgId(), PeerId());
applyDraft();
if (_saveEditMsgRequestId) {
@ -8671,7 +8678,7 @@ void HistoryWidget::cancelFieldAreaState() {
} else if (_replyTo) {
cancelReply();
} else if (readyToForward()) {
_history->setForwardDraft(MsgId(), {});
_history->setForwardDraft(MsgId(), PeerId(), {});
} else if (_kbReplyTo) {
toggleKeyboard();
}
@ -9039,7 +9046,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) {
void HistoryWidget::updateForwarding() {
_forwardPanel->update(_history, _history
? _history->resolveForwardDraft(MsgId())
? _history->resolveForwardDraft(MsgId(), PeerId())
: Data::ResolvedForwardDraft());
updateControlsVisibility();
updateControlsGeometry();

View file

@ -66,6 +66,7 @@ struct WriteRestriction {
struct SetHistoryArgs {
required<History*> history;
MsgId topicRootId = 0;
PeerId monoforumPeerId = 0;
Fn<bool()> showSlowmodeError;
Fn<Api::SendAction()> sendActionFactory;
rpl::producer<int> slowmodeSecondsLeft;

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_drafts.h"
#include "data/data_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/data_chat.h"
@ -197,6 +198,7 @@ private:
History *_history = nullptr;
MsgId _topicRootId = 0;
PeerId _monoforumPeerId = 0;
Preview _preview;
rpl::event_stream<> _editCancelled;
@ -254,6 +256,7 @@ FieldHeader::FieldHeader(
void FieldHeader::setHistory(const SetHistoryArgs &args) {
_history = *args.history;
_topicRootId = args.topicRootId;
_monoforumPeerId = args.monoforumPeerId;
}
void FieldHeader::updateTopicRootId(MsgId topicRootId) {
@ -282,7 +285,7 @@ void FieldHeader::init() {
st::historyLinkIcon.paint(p, position, width());
} else if (isEditingMessage()) {
st::historyEditIcon.paint(p, position, width());
} else if (const auto reply = replyingToMessage()) {
} else if (const auto reply = replyingToMessage(); reply.replying()) {
if (!reply.quote.empty()) {
st::historyQuoteIcon.paint(p, position, width());
} else {
@ -760,6 +763,7 @@ void FieldHeader::editMessage(FullMsgId id, bool photoEditAllowed) {
}
void FieldHeader::replyToMessage(FullReplyTo id) {
id.monoforumPeerId = 0;
_replyTo = id;
}
@ -956,6 +960,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
unregisterDraftSources();
_history = history;
_topicRootId = args.topicRootId;
_monoforumPeerId = args.monoforumPeerId;
_historyLifetime.destroy();
_header->setHistory(args);
registerDraftSource();
@ -999,6 +1004,7 @@ void ComposeControls::setCurrentDialogsEntryState(
Dialogs::EntryState state) {
unregisterDraftSources();
state.currentReplyTo.topicRootId = _topicRootId;
state.currentReplyTo.monoforumPeerId = _monoforumPeerId;
_currentDialogsEntryState = state;
updateForwarding();
registerDraftSource();
@ -1405,6 +1411,7 @@ void ComposeControls::init() {
) | rpl::start_with_next([=] {
const auto history = _history;
const auto topicRootId = _topicRootId;
const auto monoforumPeerId = _monoforumPeerId;
const auto reply = _header->replyingToMessage();
const auto webpage = _preview->draft();
@ -1417,7 +1424,10 @@ void ComposeControls::init() {
} else {
cancelReplyMessage();
}
history->setForwardDraft(topicRootId, std::move(forward));
history->setForwardDraft(
topicRootId,
monoforumPeerId,
std::move(forward));
_preview->apply(webpage);
_field->setFocus();
};
@ -1440,6 +1450,7 @@ void ComposeControls::init() {
.clearOldDraft = [=] { ClearDraftReplyTo(
history,
topicRootId,
monoforumPeerId,
replyToId); },
});
}, _wrap->lifetime());
@ -1809,8 +1820,8 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const {
case Section::Replies:
case Section::SavedSublist:
return (type == DraftType::Edit)
? Key::LocalEdit(_topicRootId)
: Key::Local(_topicRootId);
? Key::LocalEdit(_topicRootId, _monoforumPeerId)
: Key::Local(_topicRootId, _monoforumPeerId);
case Section::Scheduled:
return (type == DraftType::Edit)
? Key::ScheduledEdit()
@ -2038,7 +2049,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
}
void ComposeControls::cancelForward() {
_history->setForwardDraft(_topicRootId, {});
_history->setForwardDraft(_topicRootId, _monoforumPeerId, {});
updateForwarding();
}
@ -2902,9 +2913,11 @@ void ComposeControls::toggleTabbedSelectorMode() {
&& !_regularWindow->adaptive().isOneColumn()) {
Core::App().settings().setTabbedSelectorSectionEnabled(true);
Core::App().saveSettingsDelayed();
const auto topic = _history->peer->forumTopicFor(_topicRootId);
const auto thread = _history->threadFor(
_topicRootId,
_monoforumPeerId);
pushTabbedSelectorToThirdSection(
(topic ? topic : (Data::Thread*)_history),
thread ? thread : _history,
Window::SectionShow::Way::ClearStack);
} else {
_tabbedPanel->toggleAnimated();
@ -2958,6 +2971,7 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
FullReplyTo{
.messageId = item->fullId(),
.topicRootId = key.topicRootId(),
.monoforumPeerId = key.monoforumPeerId(),
},
cursor,
Data::WebPageDraft::FromItem(item)));
@ -3038,6 +3052,7 @@ void ComposeControls::replyToMessage(FullReplyTo id) {
Expects(draftKeyCurrent() != Data::DraftKey::None());
id.topicRootId = _topicRootId;
id.monoforumPeerId = _monoforumPeerId;
if (!id) {
cancelReplyMessage();
return;
@ -3045,6 +3060,7 @@ void ComposeControls::replyToMessage(FullReplyTo id) {
if (isEditingMessage()) {
const auto key = draftKey(DraftType::Normal);
Assert(key.topicRootId() == id.topicRootId);
Assert(key.monoforumPeerId() == id.monoforumPeerId);
if (const auto localDraft = _history->draft(key)) {
localDraft->reply = id;
} else {
@ -3088,12 +3104,11 @@ void ComposeControls::cancelReplyMessage() {
}
void ComposeControls::updateForwarding() {
const auto rootId = _topicRootId;
const auto thread = (_history && rootId)
? _history->peer->forumTopicFor(rootId)
const auto thread = (_history && (_topicRootId || _monoforumPeerId))
? _history->threadFor(_topicRootId, _monoforumPeerId)
: (Data::Thread*)_history;
_header->updateForwarding(thread, thread
? _history->resolveForwardDraft(rootId)
? _history->resolveForwardDraft(_topicRootId, _monoforumPeerId)
: Data::ResolvedForwardDraft());
updateSendButtonType();
}
@ -3108,7 +3123,7 @@ bool ComposeControls::handleCancelRequest() {
} else if (isEditingMessage()) {
maybeCancelEditMessage();
return true;
} else if (replyingToMessage()) {
} else if (replyingToMessage().replying()) {
cancelReplyMessage();
return true;
} else if (readyToForward()) {
@ -3186,6 +3201,11 @@ void ComposeControls::initForwardProcess() {
&& topic->rootId() == _topicRootId) {
updateForwarding();
}
} else if (const auto sublist = update.entry->asSublist()) {
if (sublist->owningHistory() == _history
&& sublist->sublistPeer()->id == _monoforumPeerId) {
updateForwarding();
}
}
}, _wrap->lifetime());
@ -3209,6 +3229,7 @@ bool ComposeControls::isEditingMessage() const {
FullReplyTo ComposeControls::replyingToMessage() const {
auto result = _header->replyingToMessage();
result.topicRootId = _topicRootId;
result.monoforumPeerId = _monoforumPeerId;
return result;
}

View file

@ -365,6 +365,7 @@ private:
History *_history = nullptr;
MsgId _topicRootId = 0;
PeerId _monoforumPeerId = 0;
BusinessShortcutId _shortcutId = 0;
Fn<bool()> _showSlowmodeError;
Fn<Api::SendAction()> _sendActionFactory;

View file

@ -1375,18 +1375,20 @@ void ShowReplyToChatBox(
auto chosen = [=](not_null<Data::Thread*> thread) mutable {
const auto history = thread->owningHistory();
const auto topicRootId = thread->topicRootId();
const auto draft = history->localDraft(topicRootId);
const auto monoforumPeerId = thread->monoforumPeerId();
const auto draft = history->localDraft(topicRootId, monoforumPeerId);
const auto textWithTags = draft
? draft->textWithTags
: TextWithTags();
const auto cursor = draft ? draft->cursor : MessageCursor();
reply.topicRootId = topicRootId;
reply.monoforumPeerId = monoforumPeerId;
history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags,
reply,
cursor,
Data::WebPageDraft()));
history->clearLocalEditDraft(topicRootId);
history->clearLocalEditDraft(topicRootId, monoforumPeerId);
history->session().changes().entryUpdated(
thread,
Data::EntryUpdate::Flag::LocalDraftSet);

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_helpers.h"
#include "history/history_item_components.h"
#include "history/view/history_view_item_preview.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h"
#include "data/data_media_types.h"
#include "data/data_forum_topic.h"
@ -74,6 +75,11 @@ void ForwardPanel::update(
) | rpl::start_with_next([=] {
update(nullptr, {});
}, _dataLifetime);
} else if (const auto sublist = _to->asSublist()) {
sublist->destroyed(
) | rpl::start_with_next([=] {
update(nullptr, {});
}, _dataLifetime);
}
updateTexts();
@ -231,8 +237,10 @@ void ForwardPanel::applyOptions(Data::ForwardOptions options) {
if (_data.items.empty()) {
return;
} else if (_data.options != options) {
const auto topicRootId = _to->topicRootId();
const auto monoforumPeerId = _to->monoforumPeerId();
_data.options = options;
_to->owningHistory()->setForwardDraft(_to->topicRootId(), {
_to->owningHistory()->setForwardDraft(topicRootId, monoforumPeerId, {
.ids = _to->owner().itemsToIds(_data.items),
.options = options,
});
@ -256,7 +264,9 @@ void ForwardPanel::editToNextOption() {
? Options::NoNamesAndCaptions
: Options::PreserveInfo;
_to->owningHistory()->setForwardDraft(_to->topicRootId(), {
const auto topicRootId = _to->topicRootId();
const auto monoforumPeerId = _to->monoforumPeerId();
_to->owningHistory()->setForwardDraft(topicRootId, monoforumPeerId, {
.ids = _to->owner().itemsToIds(_data.items),
.options = next,
});
@ -332,20 +342,25 @@ void ForwardPanel::paint(
void ClearDraftReplyTo(
not_null<History*> history,
MsgId topicRootId,
PeerId monoforumPeerId,
FullMsgId equalTo) {
const auto local = history->localDraft(topicRootId);
const auto local = history->localDraft(topicRootId, monoforumPeerId);
if (!local || (equalTo && local->reply.messageId != equalTo)) {
return;
}
auto draft = *local;
draft.reply = { .topicRootId = topicRootId };
draft.reply = {
.topicRootId = topicRootId,
.monoforumPeerId = monoforumPeerId,
};
if (Data::DraftIsNull(&draft)) {
history->clearLocalDraft(topicRootId);
history->clearLocalDraft(topicRootId, monoforumPeerId);
} else {
history->setLocalDraft(
std::make_unique<Data::Draft>(std::move(draft)));
}
if (const auto thread = history->threadFor(topicRootId)) {
const auto thread = history->threadFor(topicRootId, monoforumPeerId);
if (thread) {
history->session().api().saveDraftToCloudDelayed(thread);
}
}

View file

@ -76,6 +76,7 @@ private:
void ClearDraftReplyTo(
not_null<History*> history,
MsgId topicRootId,
PeerId monoforumPeerId,
FullMsgId equalTo);
void EditWebPageOptions(

View file

@ -438,8 +438,10 @@ ChatWidget::ChatWidget(
ChatWidget::~ChatWidget() {
base::take(_sendAction);
if (_repliesRootId) {
if (_repliesRootId || _sublist) {
session().api().saveCurrentDraftToCloud();
}
if (_repliesRootId) {
controller()->sendingAnimation().clear();
}
if (_topic) {
@ -747,7 +749,8 @@ void ChatWidget::setupComposeControls() {
_composeControls->setHistory({
.history = _history.get(),
.topicRootId = _topic ? _topic->rootId() : MsgId(0),
.topicRootId = _topic ? _topic->rootId() : MsgId(),
.monoforumPeerId = _sublist ? _sublist->sublistPeer()->id : PeerId(),
.showSlowmodeError = [=] { return showSlowmodeError(); },
.sendActionFactory = [=] { return prepareSendAction({}); },
.slowmodeSecondsLeft = SlowmodeSecondsLeft(_peer),

View file

@ -557,6 +557,7 @@ bool MainWidget::setForwardDraft(
const auto history = thread->owningHistory();
const auto items = session().data().idsToItems(draft.ids);
const auto topicRootId = thread->topicRootId();
const auto monoforumPeerId = thread->monoforumPeerId();
const auto error = GetErrorForSending(
history->peer,
{
@ -569,7 +570,7 @@ bool MainWidget::setForwardDraft(
return false;
}
history->setForwardDraft(topicRootId, std::move(draft));
history->setForwardDraft(topicRootId, monoforumPeerId, std::move(draft));
_controller->showThread(
thread,
ShowAtUnreadMsgId,
@ -596,12 +597,16 @@ bool MainWidget::shareUrl(
};
const auto history = thread->owningHistory();
const auto topicRootId = thread->topicRootId();
const auto monoforumPeerId = thread->monoforumPeerId();
history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags,
FullReplyTo{ .topicRootId = topicRootId },
FullReplyTo{
.topicRootId = topicRootId,
.monoforumPeerId = monoforumPeerId,
},
cursor,
Data::WebPageDraft()));
history->clearLocalEditDraft(topicRootId);
history->clearLocalEditDraft(topicRootId, monoforumPeerId);
history->session().changes().entryUpdated(
thread,
Data::EntryUpdate::Flag::LocalDraftSet);
@ -2044,6 +2049,8 @@ bool MainWidget::showBackFromStack(const SectionShow &params) {
});
return (_dialogs != nullptr);
}
session().api().saveCurrentDraftToCloud();
auto item = std::move(_stack.back());
_stack.pop_back();
if (const auto currentHistoryPeer = _history->peer()) {

View file

@ -1176,7 +1176,9 @@ void EnumerateDrafts(
} else if (key.isLocal()
&& (!supportMode || key.topicRootId())) {
const auto i = map.find(
Data::DraftKey::Cloud(key.topicRootId()));
Data::DraftKey::Cloud(
key.topicRootId(),
key.monoforumPeerId()));
const auto cloud = (i != end(map)) ? i->second.get() : nullptr;
if (Data::DraftsAreEqual(draft.get(), cloud)) {
continue;
@ -1426,7 +1428,7 @@ void Account::readDraftCursors(PeerId peerId, Data::HistoryDrafts &map) {
? Data::DraftKey::FromSerialized(keyValue)
: keysOld
? Data::DraftKey::FromSerializedOld(keyValueOld)
: Data::DraftKey::Local(0);
: Data::DraftKey::Local(MsgId(), PeerId());
qint32 position = 0, anchor = 0, scroll = Ui::kQFixedMax;
draft.stream >> position >> anchor >> scroll;
if (const auto i = map.find(key); i != end(map)) {
@ -1453,13 +1455,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(MsgId(), PeerId()))
; i != end(map)) {
i->second->cursor = MessageCursor(
localPosition,
localAnchor,
localScroll);
}
if (const auto i = map.find(Data::DraftKey::LocalEdit({}))
if (const auto i = map.find(Data::DraftKey::LocalEdit(MsgId(), PeerId()))
; i != end(map)) {
i->second->cursor = MessageCursor(
editPosition,
@ -1472,7 +1475,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(MsgId(), PeerId());
history->takeLocalDraft(migrated);
}
});
@ -1643,10 +1646,11 @@ void Account::readDraftsWithCursorsLegacy(
editData.text.size());
const auto topicRootId = MsgId();
const auto monoforumPeerId = PeerId();
auto map = base::flat_map<Data::DraftKey, std::unique_ptr<Data::Draft>>();
if (!msgData.text.isEmpty() || msgReplyTo) {
map.emplace(
Data::DraftKey::Local(topicRootId),
Data::DraftKey::Local(topicRootId, monoforumPeerId),
std::make_unique<Data::Draft>(
msgData,
FullReplyTo{ FullMsgId(peerId, MsgId(msgReplyTo)) },
@ -1657,7 +1661,7 @@ void Account::readDraftsWithCursorsLegacy(
}
if (editMsgId) {
map.emplace(
Data::DraftKey::LocalEdit(topicRootId),
Data::DraftKey::LocalEdit(topicRootId, monoforumPeerId),
std::make_unique<Data::Draft>(
editData,
FullReplyTo{ FullMsgId(peerId, editMsgId) },

View file

@ -54,6 +54,7 @@ constexpr auto kOccupyFor = TimeId(60);
constexpr auto kReoccupyEach = 30 * crl::time(1000);
constexpr auto kMaxSupportInfoLength = MaxMessageSize * 4;
constexpr auto kTopicRootId = MsgId(0);
constexpr auto kMonoforumPeerId = PeerId(0);
class EditInfoBox : public Ui::BoxContent {
public:
@ -183,7 +184,7 @@ uint32 ParseOccupationTag(History *history) {
if (!TrackHistoryOccupation(history)) {
return 0;
}
const auto draft = history->cloudDraft(kTopicRootId);
const auto draft = history->cloudDraft(kTopicRootId, kMonoforumPeerId);
if (!draft) {
return 0;
}
@ -209,7 +210,7 @@ QString ParseOccupationName(History *history) {
if (!TrackHistoryOccupation(history)) {
return QString();
}
const auto draft = history->cloudDraft(kTopicRootId);
const auto draft = history->cloudDraft(kTopicRootId, kMonoforumPeerId);
if (!draft) {
return QString();
}
@ -235,7 +236,7 @@ TimeId OccupiedBySomeoneTill(History *history) {
if (!TrackHistoryOccupation(history)) {
return 0;
}
const auto draft = history->cloudDraft(kTopicRootId);
const auto draft = history->cloudDraft(kTopicRootId, kMonoforumPeerId);
if (!draft) {
return 0;
}
@ -353,7 +354,7 @@ void Helper::updateOccupiedHistory(
not_null<Window::SessionController*> controller,
History *history) {
if (isOccupiedByMe(_occupiedHistory)) {
_occupiedHistory->clearCloudDraft(kTopicRootId);
_occupiedHistory->clearCloudDraft(kTopicRootId, kMonoforumPeerId);
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
}
_occupiedHistory = history;
@ -377,7 +378,10 @@ void Helper::occupyInDraft() {
&& !isOccupiedBySomeone(_occupiedHistory)
&& !_supportName.isEmpty()) {
const auto draft = OccupiedDraft(_supportNameNormalized);
_occupiedHistory->createCloudDraft(kTopicRootId, &draft);
_occupiedHistory->createCloudDraft(
kTopicRootId,
kMonoforumPeerId,
&draft);
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
_reoccupyTimer.callEach(kReoccupyEach);
}
@ -386,7 +390,10 @@ void Helper::occupyInDraft() {
void Helper::reoccupy() {
if (isOccupiedByMe(_occupiedHistory)) {
const auto draft = OccupiedDraft(_supportNameNormalized);
_occupiedHistory->createCloudDraft(kTopicRootId, &draft);
_occupiedHistory->createCloudDraft(
kTopicRootId,
kMonoforumPeerId,
&draft);
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
}
}

View file

@ -1802,8 +1802,10 @@ void PeerMenuCreatePoll(
peer->owner().history(peer),
result.options);
action.replyTo = replyTo;
const auto topicRootId = replyTo.topicRootId;
if (const auto local = action.history->localDraft(topicRootId)) {
const auto local = action.history->localDraft(
replyTo.topicRootId,
replyTo.monoforumPeerId);
if (local) {
action.clearDraft = local->textWithTags.text.isEmpty();
} else {
action.clearDraft = false;

View file

@ -2150,8 +2150,9 @@ bool SessionController::switchInlineQuery(
params);
} else {
const auto topicRootId = to.currentReplyTo.topicRootId;
const auto monoforumPeerId = to.currentReplyTo.monoforumPeerId;
history->setLocalDraft(std::move(draft));
history->clearLocalEditDraft(topicRootId);
history->clearLocalEditDraft(topicRootId, monoforumPeerId);
if (to.section == Section::Replies) {
const auto commentId = MsgId();
showRepliesForMessage(history, topicRootId, commentId, params);