Display occupied chats using drafts for support.

This commit is contained in:
John Preston 2018-11-15 19:36:04 +04:00
parent 60103f7ad6
commit 4960e08a24
20 changed files with 429 additions and 95 deletions

View File

@ -2193,7 +2193,11 @@ void ApiWrap::saveDraftsToCloud() {
if (cloudDraft && cloudDraft->saveRequestId) { if (cloudDraft && cloudDraft->saveRequestId) {
request(base::take(cloudDraft->saveRequestId)).cancel(); request(base::take(cloudDraft->saveRequestId)).cancel();
} }
cloudDraft = history->createCloudDraft(localDraft); if (!Auth().supportMode()) {
cloudDraft = history->createCloudDraft(localDraft);
} else if (!cloudDraft) {
cloudDraft = history->createCloudDraft(nullptr);
}
auto flags = MTPmessages_SaveDraft::Flags(0); auto flags = MTPmessages_SaveDraft::Flags(0);
auto &textWithTags = cloudDraft->textWithTags; auto &textWithTags = cloudDraft->textWithTags;

View File

@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/send_files_box.h" #include "boxes/send_files_box.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "support/support_common.h" #include "support/support_common.h"
#include "support/support_templates.h" #include "support/support_helper.h"
#include "observer_peer.h" #include "observer_peer.h"
namespace { namespace {
@ -342,9 +342,9 @@ AuthSession::AuthSession(const MTPUser &user)
, _notifications(std::make_unique<Window::Notifications::System>(this)) , _notifications(std::make_unique<Window::Notifications::System>(this))
, _data(std::make_unique<Data::Session>(this)) , _data(std::make_unique<Data::Session>(this))
, _changelogs(Core::Changelogs::Create(this)) , _changelogs(Core::Changelogs::Create(this))
, _supportTemplates( , _supportHelper(
(Support::ValidateAccount(user) (Support::ValidateAccount(user)
? std::make_unique<Support::Templates>(this) ? std::make_unique<Support::Helper>(this)
: nullptr)) { : nullptr)) {
App::feedUser(user); App::feedUser(user);
@ -457,13 +457,17 @@ void AuthSession::checkAutoLockIn(TimeMs time) {
} }
bool AuthSession::supportMode() const { bool AuthSession::supportMode() const {
return (_supportTemplates != nullptr); return (_supportHelper != nullptr);
} }
not_null<Support::Templates*> AuthSession::supportTemplates() const { Support::Helper &AuthSession::supportHelper() const {
Expects(supportMode()); Expects(supportMode());
return _supportTemplates.get(); return *_supportHelper;
}
Support::Templates& AuthSession::supportTemplates() const {
return supportHelper().templates();
} }
AuthSession::~AuthSession() = default; AuthSession::~AuthSession() = default;

View File

@ -21,6 +21,7 @@ enum class InputSubmitSettings;
namespace Support { namespace Support {
enum class SwitchSettings; enum class SwitchSettings;
class Helper;
class Templates; class Templates;
} // namespace Support } // namespace Support
@ -301,7 +302,8 @@ public:
base::Observable<std::pair<not_null<HistoryItem*>, MsgId>> messageIdChanging; base::Observable<std::pair<not_null<HistoryItem*>, MsgId>> messageIdChanging;
bool supportMode() const; bool supportMode() const;
not_null<Support::Templates*> supportTemplates() const; Support::Helper &supportHelper() const;
Support::Templates &supportTemplates() const;
~AuthSession(); ~AuthSession();
@ -328,7 +330,7 @@ private:
// _changelogs depends on _data, subscribes on chats loading event. // _changelogs depends on _data, subscribes on chats loading event.
const std::unique_ptr<Core::Changelogs> _changelogs; const std::unique_ptr<Core::Changelogs> _changelogs;
const std::unique_ptr<Support::Templates> _supportTemplates; const std::unique_ptr<Support::Helper> _supportHelper;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_widget.h" #include "history/history_widget.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "support/support_helper.h"
namespace Data { namespace Data {
namespace { namespace {
@ -44,8 +45,8 @@ Draft::Draft(
} }
void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
auto history = App::history(peerId); const auto history = App::history(peerId);
auto textWithTags = TextWithTags { const auto textWithTags = TextWithTags {
qs(draft.vmessage), qs(draft.vmessage),
ConvertEntitiesToTextTags( ConvertEntitiesToTextTags(
draft.has_entities() draft.has_entities()
@ -56,14 +57,23 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
return; return;
} }
auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0); auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage()); auto cloudDraft = std::make_unique<Draft>(
textWithTags,
replyTo,
MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX),
draft.is_no_webpage());
cloudDraft->date = draft.vdate.v; cloudDraft->date = draft.vdate.v;
history->setCloudDraft(std::move(cloudDraft)); history->setCloudDraft(std::move(cloudDraft));
history->createLocalDraftFromCloud(); history->createLocalDraftFromCloud();
history->updateChatListSortPosition(); if (Auth().supportMode()) {
history->updateChatListEntry();
Auth().supportHelper().cloudDraftChanged(history);
} else {
history->updateChatListSortPosition();
}
if (auto main = App::main()) { if (const auto main = App::main()) {
main->applyCloudDraft(history); main->applyCloudDraft(history);
} }
} }
@ -77,7 +87,12 @@ void clearPeerCloudDraft(PeerId peerId, TimeId date) {
history->clearCloudDraft(); history->clearCloudDraft();
history->clearLocalDraft(); history->clearLocalDraft();
history->updateChatListSortPosition(); if (Auth().supportMode()) {
history->updateChatListEntry();
Auth().supportHelper().cloudDraftChanged(history);
} else {
history->updateChatListSortPosition();
}
if (auto main = App::main()) { if (auto main = App::main()) {
main->applyCloudDraft(history); main->applyCloudDraft(history);

View File

@ -47,11 +47,12 @@ inline bool draftStringIsEmpty(const QString &text) {
return true; return true;
} }
inline bool draftIsNull(Draft *draft) { inline bool draftIsNull(const Draft *draft) {
return !draft || (draftStringIsEmpty(draft->textWithTags.text) && !draft->msgId); return !draft
|| (draftStringIsEmpty(draft->textWithTags.text) && !draft->msgId);
} }
inline bool draftsAreEqual(Draft *a, Draft *b) { inline bool draftsAreEqual(const Draft *a, const Draft *b) {
bool aIsNull = draftIsNull(a); bool aIsNull = draftIsNull(a);
bool bIsNull = draftIsNull(b); bool bIsNull = draftIsNull(b);
if (aIsNull) { if (aIsNull) {
@ -60,7 +61,9 @@ inline bool draftsAreEqual(Draft *a, Draft *b) {
return false; return false;
} }
return (a->textWithTags == b->textWithTags) && (a->msgId == b->msgId) && (a->previewCancelled == b->previewCancelled); return (a->textWithTags == b->textWithTags)
&& (a->msgId == b->msgId)
&& (a->previewCancelled == b->previewCancelled);
} }
} // namespace Data } // namespace Data

View File

@ -55,7 +55,7 @@ Row *IndexedList::addByName(Key key) {
} }
void IndexedList::adjustByPos(const RowsByLetter &links) { void IndexedList::adjustByPos(const RowsByLetter &links) {
for (auto [ch, row] : links) { for (const auto [ch, row] : links) {
if (ch == QChar(0)) { if (ch == QChar(0)) {
_list.adjustByPos(row); _list.adjustByPos(row);
} else { } else {

View File

@ -124,7 +124,8 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
auto changes = UpdateFlag::ChatPinnedChanged auto changes = UpdateFlag::ChatPinnedChanged
| UpdateFlag::NameChanged | UpdateFlag::NameChanged
| UpdateFlag::PhotoChanged | UpdateFlag::PhotoChanged
| UpdateFlag::UserIsContact; | UpdateFlag::UserIsContact
| UpdateFlag::OccupiedChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(changes, [this](const Notify::PeerUpdate &update) { subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(changes, [this](const Notify::PeerUpdate &update) {
if (update.flags & UpdateFlag::ChatPinnedChanged) { if (update.flags & UpdateFlag::ChatPinnedChanged) {
stopReorderPinned(); stopReorderPinned();
@ -132,7 +133,7 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
if (update.flags & UpdateFlag::NameChanged) { if (update.flags & UpdateFlag::NameChanged) {
handlePeerNameChange(update.peer, update.oldNameFirstLetters); handlePeerNameChange(update.peer, update.oldNameFirstLetters);
} }
if (update.flags & UpdateFlag::PhotoChanged) { if (update.flags & (UpdateFlag::PhotoChanged | UpdateFlag::OccupiedChanged)) {
this->update(); this->update();
emit App::main()->dialogsUpdated(); emit App::main()->dialogsUpdated();
} }

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "support/support_helper.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history.h" #include "history/history.h"
@ -173,6 +174,11 @@ void paintRow(
TimeMs ms, TimeMs ms,
PaintItemCallback &&paintItemCallback, PaintItemCallback &&paintItemCallback,
PaintCounterCallback &&paintCounterCallback) { PaintCounterCallback &&paintCounterCallback) {
const auto supportMode = Auth().supportMode();
if (supportMode) {
draft = nullptr;
}
auto active = (flags & Flag::Active); auto active = (flags & Flag::Active);
auto selected = (flags & Flag::Selected); auto selected = (flags & Flag::Selected);
auto fullRect = QRect(0, 0, fullWidth, st::dialogsRowHeight); auto fullRect = QRect(0, 0, fullWidth, st::dialogsRowHeight);
@ -250,7 +256,7 @@ void paintRow(
auto texttop = st::dialogsPadding.y() auto texttop = st::dialogsPadding.y()
+ st::msgNameFont->height + st::msgNameFont->height
+ st::dialogsSkip; + st::dialogsSkip;
if (draft) { if (draft || (supportMode && Support::IsOccupiedBySomeone(history))) {
if (!promoted) { if (!promoted) {
paintRowDate(p, date, rectForName, active, selected); paintRowDate(p, date, rectForName, active, selected);
} }
@ -267,7 +273,9 @@ void paintRow(
if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) { if (history && !history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
if (history->cloudDraftTextCache.isEmpty()) { if (history->cloudDraftTextCache.isEmpty()) {
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text)); auto draftText = supportMode
? textcmdLink(1, Support::ChatOccupiedString())
: lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, Ui::DialogTextOptions()); history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, Ui::DialogTextOptions());
} }
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));

View File

@ -348,12 +348,14 @@ void History::takeLocalDraft(History *from) {
void History::createLocalDraftFromCloud() { void History::createLocalDraftFromCloud() {
auto draft = cloudDraft(); auto draft = cloudDraft();
if (Data::draftIsNull(draft) || !draft->date) { if (Data::draftIsNull(draft) || !draft->date || Auth().supportMode()) {
return; return;
} }
auto existing = localDraft(); auto existing = localDraft();
if (Data::draftIsNull(existing) || !existing->date || draft->date >= existing->date) { if (Data::draftIsNull(existing)
|| !existing->date
|| draft->date >= existing->date) {
if (!existing) { if (!existing) {
setLocalDraft(std::make_unique<Data::Draft>( setLocalDraft(std::make_unique<Data::Draft>(
draft->textWithTags, draft->textWithTags,
@ -376,7 +378,7 @@ void History::setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
cloudDraftTextCache.clear(); cloudDraftTextCache.clear();
} }
Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) { Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
if (Data::draftIsNull(fromDraft)) { if (Data::draftIsNull(fromDraft)) {
setCloudDraft(std::make_unique<Data::Draft>( setCloudDraft(std::make_unique<Data::Draft>(
TextWithTags(), TextWithTags(),
@ -1830,7 +1832,7 @@ std::shared_ptr<AdminLog::LocalIdManager> History::adminLogIdManager() {
TimeId History::adjustChatListTimeId() const { TimeId History::adjustChatListTimeId() const {
const auto result = chatsListTimeId(); const auto result = chatsListTimeId();
if (const auto draft = cloudDraft()) { if (const auto draft = cloudDraft()) {
if (!Data::draftIsNull(draft)) { if (!Data::draftIsNull(draft) && !Auth().supportMode()) {
return std::max(result, draft->date); return std::max(result, draft->date);
} }
} }

View File

@ -306,7 +306,7 @@ public:
void takeLocalDraft(History *from); void takeLocalDraft(History *from);
void createLocalDraftFromCloud(); void createLocalDraftFromCloud();
void setCloudDraft(std::unique_ptr<Data::Draft> &&draft); void setCloudDraft(std::unique_ptr<Data::Draft> &&draft);
Data::Draft *createCloudDraft(Data::Draft *fromDraft); Data::Draft *createCloudDraft(const Data::Draft *fromDraft);
bool skipCloudDraft(const QString &text, TimeId date) const; bool skipCloudDraft(const QString &text, TimeId date) const;
void setSentDraftText(const QString &text); void setSentDraftText(const QString &text);
void clearSentDraftText(const QString &text); void clearSentDraftText(const QString &text);

View File

@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_feed.h" #include "data/data_feed.h"
#include "support/support_helper.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_window.h" #include "styles/style_window.h"
@ -52,6 +53,7 @@ TopBarWidget::TopBarWidget(
, _search(this, st::topBarSearch) , _search(this, st::topBarSearch)
, _infoToggle(this, st::topBarInfo) , _infoToggle(this, st::topBarInfo)
, _menuToggle(this, st::topBarMenuToggle) , _menuToggle(this, st::topBarMenuToggle)
, _titlePeerText(st::windowMinWidth / 3)
, _onlineUpdater([this] { updateOnlineDisplay(); }) { , _onlineUpdater([this] { updateOnlineDisplay(); }) {
subscribe(Lang::Current().updated(), [this] { refreshLang(); }); subscribe(Lang::Current().updated(), [this] { refreshLang(); });
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
@ -104,7 +106,8 @@ TopBarWidget::TopBarWidget(
using UpdateFlag = Notify::PeerUpdate::Flag; using UpdateFlag = Notify::PeerUpdate::Flag;
auto flags = UpdateFlag::UserHasCalls auto flags = UpdateFlag::UserHasCalls
| UpdateFlag::UserOnlineChanged | UpdateFlag::UserOnlineChanged
| UpdateFlag::MembersChanged; | UpdateFlag::MembersChanged
| UpdateFlag::OccupiedChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(flags, [this](const Notify::PeerUpdate &update) { subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(flags, [this](const Notify::PeerUpdate &update) {
if (update.flags & UpdateFlag::UserHasCalls) { if (update.flags & UpdateFlag::UserHasCalls) {
if (update.peer->isUser()) { if (update.peer->isUser()) {
@ -331,14 +334,15 @@ void TopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
p.setFont(st::dialogsTextFont); p.setFont(st::dialogsTextFont);
if (paintConnectingState(p, nameleft, statustop, width(), ms)) { if (paintConnectingState(p, nameleft, statustop, width(), ms)) {
return; return;
} else if (history->paintSendAction( } else if (!Support::IsOccupiedBySomeone(history)
p, && history->paintSendAction(
nameleft, p,
statustop, nameleft,
namewidth, statustop,
width(), namewidth,
st::historyStatusFgTyping, width(),
ms)) { st::historyStatusFgTyping,
ms)) {
return; return;
} else { } else {
paintStatus(p, nameleft, statustop, namewidth, width()); paintStatus(p, nameleft, statustop, namewidth, width());
@ -379,25 +383,20 @@ void TopBarWidget::paintStatus(
int top, int top,
int availableWidth, int availableWidth,
int outerWidth) { int outerWidth) {
auto statustext = _titlePeerText; const auto occupied = Auth().supportMode()
auto statuswidth = _titlePeerTextWidth; && Support::IsOccupiedBySomeone(_activeChat.history());
if (statuswidth > availableWidth) { p.setPen(occupied
statustext = st::dialogsTextFont->elided( ? st::dialogsTextPaletteDraft.linkFg
statustext, : _titlePeerTextOnline
availableWidth,
Qt::ElideLeft);
statuswidth = st::dialogsTextFont->width(statustext);
}
p.setPen(_titlePeerTextOnline
? st::historyStatusFgActive ? st::historyStatusFgActive
: st::historyStatusFg); : st::historyStatusFg);
p.drawTextLeft(left, top, outerWidth, statustext, statuswidth); _titlePeerText.drawLeftElided(p, left, top, availableWidth, outerWidth);
} }
QRect TopBarWidget::getMembersShowAreaGeometry() const { QRect TopBarWidget::getMembersShowAreaGeometry() const {
int membersTextLeft = _leftTaken; int membersTextLeft = _leftTaken;
int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height; int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
int membersTextWidth = _titlePeerTextWidth; int membersTextWidth = _titlePeerText.maxWidth();
int membersTextHeight = st::topBarHeight - membersTextTop; int membersTextHeight = st::topBarHeight - membersTextTop;
return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight); return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
@ -745,7 +744,10 @@ void TopBarWidget::updateOnlineDisplay() {
QString text; QString text;
const auto now = unixtime(); const auto now = unixtime();
bool titlePeerTextOnline = false; bool titlePeerTextOnline = false;
if (const auto user = _activeChat.peer()->asUser()) { if (Auth().supportMode()
&& Support::IsOccupiedBySomeone(_activeChat.history())) {
text = Support::ChatOccupiedString();
} else if (const auto user = _activeChat.peer()->asUser()) {
text = Data::OnlineText(user, now); text = Data::OnlineText(user, now);
titlePeerTextOnline = Data::OnlineTextActive(user, now); titlePeerTextOnline = Data::OnlineTextActive(user, now);
} else if (const auto chat = _activeChat.peer()->asChat()) { } else if (const auto chat = _activeChat.peer()->asChat()) {
@ -753,7 +755,7 @@ void TopBarWidget::updateOnlineDisplay() {
text = lang(lng_chat_status_unaccessible); text = lang(lng_chat_status_unaccessible);
} else if (chat->participants.empty()) { } else if (chat->participants.empty()) {
if (!_titlePeerText.isEmpty()) { if (!_titlePeerText.isEmpty()) {
text = _titlePeerText; text = _titlePeerText.originalText();
} else if (chat->count <= 0) { } else if (chat->count <= 0) {
text = lang(lng_group_status); text = lang(lng_group_status);
} else { } else {
@ -810,10 +812,9 @@ void TopBarWidget::updateOnlineDisplay() {
text = lang(channel->isMegagroup() ? lng_group_status : lng_channel_status); text = lang(channel->isMegagroup() ? lng_group_status : lng_channel_status);
} }
} }
if (_titlePeerText != text) { if (_titlePeerText.originalText() != text) {
_titlePeerText = text; _titlePeerText.setText(st::dialogsTextStyle, text);
_titlePeerTextOnline = titlePeerTextOnline; _titlePeerTextOnline = titlePeerTextOnline;
_titlePeerTextWidth = st::dialogsTextFont->width(_titlePeerText);
updateMembersShowArea(); updateMembersShowArea();
update(); update();
} }

View File

@ -136,9 +136,8 @@ private:
object_ptr<TWidget> _membersShowArea = { nullptr }; object_ptr<TWidget> _membersShowArea = { nullptr };
rpl::event_stream<bool> _membersShowAreaActive; rpl::event_stream<bool> _membersShowAreaActive;
QString _titlePeerText; Text _titlePeerText;
bool _titlePeerTextOnline = false; bool _titlePeerTextOnline = false;
int _titlePeerTextWidth = 0;
int _leftTaken = 0; int _leftTaken = 0;
int _rightTaken = 0; int _rightTaken = 0;
bool _animatingMode = false; bool _animatingMode = false;

View File

@ -3840,13 +3840,14 @@ void MainWidget::updateOnline(bool gotOtherOffline) {
void MainWidget::saveDraftToCloud() { void MainWidget::saveDraftToCloud() {
_history->saveFieldToHistoryLocalDraft(); _history->saveFieldToHistoryLocalDraft();
auto peer = _history->peer(); const auto peer = _history->peer();
if (auto history = App::historyLoaded(peer)) { if (const auto history = App::historyLoaded(peer)) {
writeDrafts(history); writeDrafts(history);
auto localDraft = history->localDraft(); const auto localDraft = history->localDraft();
auto cloudDraft = history->cloudDraft(); const auto cloudDraft = history->cloudDraft();
if (!Data::draftsAreEqual(localDraft, cloudDraft)) { if (!Data::draftsAreEqual(localDraft, cloudDraft)
&& !Auth().supportMode()) {
Auth().api().saveDraftToCloudDelayed(history); Auth().api().saveDraftToCloudDelayed(history);
} }
} }
@ -3859,17 +3860,27 @@ void MainWidget::applyCloudDraft(History *history) {
void MainWidget::writeDrafts(History *history) { void MainWidget::writeDrafts(History *history) {
Local::MessageDraft storedLocalDraft, storedEditDraft; Local::MessageDraft storedLocalDraft, storedEditDraft;
MessageCursor localCursor, editCursor; MessageCursor localCursor, editCursor;
if (auto localDraft = history->localDraft()) { if (const auto localDraft = history->localDraft()) {
if (!Data::draftsAreEqual(localDraft, history->cloudDraft())) { if (Auth().supportMode()
storedLocalDraft = Local::MessageDraft(localDraft->msgId, localDraft->textWithTags, localDraft->previewCancelled); || !Data::draftsAreEqual(localDraft, history->cloudDraft())) {
storedLocalDraft = Local::MessageDraft(
localDraft->msgId,
localDraft->textWithTags,
localDraft->previewCancelled);
localCursor = localDraft->cursor; localCursor = localDraft->cursor;
} }
} }
if (auto editDraft = history->editDraft()) { if (const auto editDraft = history->editDraft()) {
storedEditDraft = Local::MessageDraft(editDraft->msgId, editDraft->textWithTags, editDraft->previewCancelled); storedEditDraft = Local::MessageDraft(
editDraft->msgId,
editDraft->textWithTags,
editDraft->previewCancelled);
editCursor = editDraft->cursor; editCursor = editDraft->cursor;
} }
Local::writeDrafts(history->peer->id, storedLocalDraft, storedEditDraft); Local::writeDrafts(
history->peer->id,
storedLocalDraft,
storedEditDraft);
Local::writeDraftCursors(history->peer->id, localCursor, editCursor); Local::writeDraftCursors(history->peer->id, localCursor, editCursor);
} }

View File

@ -39,33 +39,34 @@ struct PeerUpdate {
RestrictionReasonChanged = (1 << 8), RestrictionReasonChanged = (1 << 8),
UnreadViewChanged = (1 << 9), UnreadViewChanged = (1 << 9),
PinnedMessageChanged = (1 << 10), PinnedMessageChanged = (1 << 10),
OccupiedChanged = (1 << 11),
// For chats and channels // For chats and channels
InviteLinkChanged = (1 << 11), InviteLinkChanged = (1 << 12),
MembersChanged = (1 << 12), MembersChanged = (1 << 13),
AdminsChanged = (1 << 13), AdminsChanged = (1 << 14),
BannedUsersChanged = (1 << 14), BannedUsersChanged = (1 << 15),
UnreadMentionsChanged = (1 << 15), UnreadMentionsChanged = (1 << 16),
// For users // For users
UserCanShareContact = (1 << 16), UserCanShareContact = (1 << 17),
UserIsContact = (1 << 17), UserIsContact = (1 << 18),
UserPhoneChanged = (1 << 18), UserPhoneChanged = (1 << 19),
UserIsBlocked = (1 << 19), UserIsBlocked = (1 << 20),
BotCommandsChanged = (1 << 20), BotCommandsChanged = (1 << 21),
UserOnlineChanged = (1 << 21), UserOnlineChanged = (1 << 22),
BotCanAddToGroups = (1 << 22), BotCanAddToGroups = (1 << 23),
UserCommonChatsChanged = (1 << 23), UserCommonChatsChanged = (1 << 24),
UserHasCalls = (1 << 24), UserHasCalls = (1 << 25),
// For chats // For chats
ChatCanEdit = (1 << 16), ChatCanEdit = (1 << 17),
// For channels // For channels
ChannelAmIn = (1 << 16), ChannelAmIn = (1 << 17),
ChannelRightsChanged = (1 << 17), ChannelRightsChanged = (1 << 18),
ChannelStickersChanged = (1 << 18), ChannelStickersChanged = (1 << 19),
ChannelPromotedChanged = (1 << 19), ChannelPromotedChanged = (1 << 20),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View File

@ -328,11 +328,11 @@ Autocomplete::Autocomplete(QWidget *parent, not_null<AuthSession*> session)
} }
void Autocomplete::activate(not_null<Ui::InputField*> field) { void Autocomplete::activate(not_null<Ui::InputField*> field) {
if (Auth().settings().supportTemplatesAutocomplete()) { if (_session->settings().supportTemplatesAutocomplete()) {
_activate(); _activate();
} else { } else {
const auto templates = Auth().supportTemplates(); const auto &templates = _session->supportTemplates();
const auto max = templates->maxKeyLength(); const auto max = templates.maxKeyLength();
auto cursor = field->textCursor(); auto cursor = field->textCursor();
const auto position = cursor.position(); const auto position = cursor.position();
const auto anchor = cursor.anchor(); const auto anchor = cursor.anchor();
@ -344,8 +344,8 @@ void Autocomplete::activate(not_null<Ui::InputField*> field) {
std::max(position - max, 0), std::max(position - max, 0),
position); position);
const auto result = (position != anchor) const auto result = (position != anchor)
? templates->matchExact(text.text) ? templates.matchExact(text.text)
: templates->matchFromEnd(text.text); : templates.matchFromEnd(text.text);
if (result) { if (result) {
const auto till = std::max(position, anchor); const auto till = std::max(position, anchor);
const auto from = till - result->key.size(); const auto from = till - result->key.size();
@ -410,7 +410,7 @@ void Autocomplete::setupContent() {
const auto refresh = [=] { const auto refresh = [=] {
inner->showRows( inner->showRows(
_session->supportTemplates()->query(input->getLastText())); _session->supportTemplates().query(input->getLastText()));
scroll->scrollToY(0); scroll->scrollToY(0);
}; };

View File

@ -0,0 +1,220 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "support/support_helper.h"
#include "dialogs/dialogs_key.h"
#include "data/data_drafts.h"
#include "history/history.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "observer_peer.h"
#include "apiwrap.h"
namespace Support {
namespace {
constexpr auto kOccupyFor = TimeId(60);
constexpr auto kReoccupyEach = 30 * TimeMs(1000);
uint32 OccupationTag() {
return uint32(Sandbox::UserTag() & 0xFFFFFFFFU);
}
Data::Draft OccupiedDraft() {
const auto now = unixtime(), till = now + kOccupyFor;
return {
TextWithTags{ "t:"
+ QString::number(till)
+ ";u:"
+ QString::number(OccupationTag()) },
MsgId(0),
MessageCursor(),
false
};
}
uint32 ParseOccupationTag(History *history) {
if (!history) {
return 0;
}
const auto draft = history->cloudDraft();
if (!draft) {
return 0;
}
const auto &text = draft->textWithTags.text;
#ifndef OS_MAC_OLD
const auto parts = text.splitRef(';');
#else // OS_MAC_OLD
const auto parts = text.split(';');
#endif // OS_MAC_OLD
auto valid = false;
auto result = uint32();
for (const auto &part : parts) {
if (part.startsWith(qstr("t:"))) {
if (part.mid(2).toInt() >= unixtime()) {
valid = true;
} else {
return 0;
}
} else if (part.startsWith(qstr("u:"))) {
result = part.mid(2).toUInt();
}
}
return valid ? result : 0;
}
TimeId OccupiedBySomeoneTill(History *history) {
if (!history) {
return 0;
}
const auto draft = history->cloudDraft();
if (!draft) {
return 0;
}
const auto &text = draft->textWithTags.text;
#ifndef OS_MAC_OLD
const auto parts = text.splitRef(';');
#else // OS_MAC_OLD
const auto parts = text.split(';');
#endif // OS_MAC_OLD
auto valid = false;
auto result = TimeId();
for (const auto &part : parts) {
if (part.startsWith(qstr("t:"))) {
if (part.mid(2).toInt() >= unixtime()) {
result = part.mid(2).toInt();
} else {
return 0;
}
} else if (part.startsWith(qstr("u:"))) {
if (part.mid(2).toUInt() != OccupationTag()) {
valid = true;
} else {
return 0;
}
}
}
return valid ? result : 0;
}
} // namespace
Helper::Helper(not_null<AuthSession*> session)
: _session(session)
, _templates(_session)
, _reoccupyTimer([=] { reoccupy(); })
, _checkOccupiedTimer([=] { checkOccupiedChats(); }) {
}
void Helper::registerWindow(not_null<Window::Controller*> controller) {
controller->activeChatValue(
) | rpl::map([](Dialogs::Key key) {
return key.history();
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](History *history) {
updateOccupiedHistory(controller, history);
}, controller->lifetime());
}
void Helper::cloudDraftChanged(not_null<History*> history) {
chatOccupiedUpdated(history);
if (history != _occupiedHistory) {
return;
}
if (!IsOccupiedByMe(_occupiedHistory)) {
occupyInDraft();
}
}
void Helper::chatOccupiedUpdated(not_null<History*> history) {
if (const auto till = OccupiedBySomeoneTill(history)) {
_occupiedChats[history] = till + 2;
Notify::peerUpdatedDelayed(
history->peer,
Notify::PeerUpdate::Flag::OccupiedChanged);
checkOccupiedChats();
} else if (_occupiedChats.take(history)) {
Notify::peerUpdatedDelayed(
history->peer,
Notify::PeerUpdate::Flag::OccupiedChanged);
}
}
void Helper::checkOccupiedChats() {
const auto now = unixtime();
while (!_occupiedChats.empty()) {
const auto nearest = ranges::min_element(
_occupiedChats,
std::less<>(),
[](const auto &pair) { return pair.second; });
if (nearest->second <= now) {
const auto history = nearest->first;
_occupiedChats.erase(nearest);
Notify::peerUpdatedDelayed(
history->peer,
Notify::PeerUpdate::Flag::OccupiedChanged);
} else {
_checkOccupiedTimer.callOnce(
(nearest->second - now) * TimeMs(1000));
return;
}
}
_checkOccupiedTimer.cancel();
}
void Helper::updateOccupiedHistory(
not_null<Window::Controller*> controller,
History *history) {
if (IsOccupiedByMe(_occupiedHistory)) {
_occupiedHistory->clearCloudDraft();
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
}
_occupiedHistory = history;
occupyInDraft();
}
void Helper::occupyInDraft() {
if (_occupiedHistory && !IsOccupiedBySomeone(_occupiedHistory)) {
const auto draft = OccupiedDraft();
_occupiedHistory->createCloudDraft(&draft);
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
_reoccupyTimer.callEach(kReoccupyEach);
}
}
void Helper::reoccupy() {
if (IsOccupiedByMe(_occupiedHistory)) {
const auto draft = OccupiedDraft();
_occupiedHistory->createCloudDraft(&draft);
_session->api().saveDraftToCloudDelayed(_occupiedHistory);
}
}
Templates &Helper::templates() {
return _templates;
}
bool IsOccupiedByMe(History *history) {
if (const auto tag = ParseOccupationTag(history)) {
return (tag == OccupationTag());
}
return false;
}
bool IsOccupiedBySomeone(History *history) {
if (const auto tag = ParseOccupationTag(history)) {
return (tag != OccupationTag());
}
return false;
}
QString ChatOccupiedString() {
return QString::fromUtf8("\xe2\x9c\x8b\xef\xb8\x8f chat taken");
}
} // namespace Support

View File

@ -0,0 +1,56 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/timer.h"
#include "support/support_templates.h"
class AuthSession;
namespace Window {
class Controller;
} // namespace Window
namespace Support {
class Helper {
public:
explicit Helper(not_null<AuthSession*> session);
void registerWindow(not_null<Window::Controller*> controller);
void cloudDraftChanged(not_null<History*> history);
void chatOccupiedUpdated(not_null<History*> history);
Templates &templates();
private:
void checkOccupiedChats();
void updateOccupiedHistory(
not_null<Window::Controller*> controller,
History *history);
void occupyInDraft();
void reoccupy();
not_null<AuthSession*> _session;
Templates _templates;
History *_occupiedHistory = nullptr;
base::Timer _reoccupyTimer;
base::Timer _checkOccupiedTimer;
base::flat_map<not_null<History*>, TimeId> _occupiedChats;
rpl::lifetime _lifetime;
};
bool IsOccupiedByMe(History *history);
bool IsOccupiedBySomeone(History *history);
QString ChatOccupiedString();
} // namespace Support

View File

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h" #include "mainwindow.h"
#include "auth_session.h" #include "auth_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "support/support_helper.h"
#include "styles/style_window.h" #include "styles/style_window.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
@ -50,6 +51,10 @@ Controller::Controller(not_null<MainWindow*> window)
startRoundVideo(item); startRoundVideo(item);
} }
}, lifetime()); }, lifetime());
if (Auth().supportMode()) {
Auth().supportHelper().registerWindow(this);
}
} }
void Controller::setActiveChatEntry(Dialogs::RowDescriptor row) { void Controller::setActiveChatEntry(Dialogs::RowDescriptor row) {

View File

@ -214,13 +214,13 @@ void MainMenu::refreshMenu() {
const auto subscription = Ui::AttachAsChild(_menu, rpl::lifetime()); const auto subscription = Ui::AttachAsChild(_menu, rpl::lifetime());
_menu->addAction(qsl("Reload templates"), [=] { _menu->addAction(qsl("Reload templates"), [=] {
*subscription = Auth().supportTemplates()->errors( *subscription = Auth().supportTemplates().errors(
) | rpl::start_with_next([=](QStringList errors) { ) | rpl::start_with_next([=](QStringList errors) {
Ui::Toast::Show(errors.isEmpty() Ui::Toast::Show(errors.isEmpty()
? "Templates reloaded!" ? "Templates reloaded!"
: ("Errors:\n\n" + errors.join("\n\n"))); : ("Errors:\n\n" + errors.join("\n\n")));
}); });
Auth().supportTemplates()->reload(); Auth().supportTemplates().reload();
}, &st::mainMenuReload, &st::mainMenuReloadOver); }, &st::mainMenuReload, &st::mainMenuReloadOver);
} }
_menu->addAction(lang(lng_menu_settings), [] { _menu->addAction(lang(lng_menu_settings), [] {

View File

@ -582,6 +582,8 @@
<(src_loc)/support/support_autocomplete.h <(src_loc)/support/support_autocomplete.h
<(src_loc)/support/support_common.cpp <(src_loc)/support/support_common.cpp
<(src_loc)/support/support_common.h <(src_loc)/support/support_common.h
<(src_loc)/support/support_helper.cpp
<(src_loc)/support/support_helper.h
<(src_loc)/support/support_templates.cpp <(src_loc)/support/support_templates.cpp
<(src_loc)/support/support_templates.h <(src_loc)/support/support_templates.h
<(src_loc)/ui/effects/cross_animation.cpp <(src_loc)/ui/effects/cross_animation.cpp