From 4960e08a24a992b1fab7be27c1026a612dc4cd02 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 15 Nov 2018 19:36:04 +0400 Subject: [PATCH] Display occupied chats using drafts for support. --- Telegram/SourceFiles/apiwrap.cpp | 6 +- Telegram/SourceFiles/auth_session.cpp | 16 +- Telegram/SourceFiles/auth_session.h | 6 +- Telegram/SourceFiles/data/data_drafts.cpp | 27 ++- Telegram/SourceFiles/data/data_drafts.h | 11 +- .../dialogs/dialogs_indexed_list.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 5 +- .../SourceFiles/dialogs/dialogs_layout.cpp | 12 +- Telegram/SourceFiles/history/history.cpp | 10 +- Telegram/SourceFiles/history/history.h | 2 +- .../view/history_view_top_bar_widget.cpp | 53 ++--- .../view/history_view_top_bar_widget.h | 3 +- Telegram/SourceFiles/mainwidget.cpp | 33 ++- Telegram/SourceFiles/observer_peer.h | 39 ++-- .../support/support_autocomplete.cpp | 12 +- .../SourceFiles/support/support_helper.cpp | 220 ++++++++++++++++++ Telegram/SourceFiles/support/support_helper.h | 56 +++++ .../SourceFiles/window/window_controller.cpp | 5 + .../SourceFiles/window/window_main_menu.cpp | 4 +- Telegram/gyp/telegram_sources.txt | 2 + 20 files changed, 429 insertions(+), 95 deletions(-) create mode 100644 Telegram/SourceFiles/support/support_helper.cpp create mode 100644 Telegram/SourceFiles/support/support_helper.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 50dbf919e..9a095ea92 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2193,7 +2193,11 @@ void ApiWrap::saveDraftsToCloud() { if (cloudDraft && cloudDraft->saveRequestId) { 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 &textWithTags = cloudDraft->textWithTags; diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp index a78833585..afd4aaea8 100644 --- a/Telegram/SourceFiles/auth_session.cpp +++ b/Telegram/SourceFiles/auth_session.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/send_files_box.h" #include "ui/widgets/input_fields.h" #include "support/support_common.h" -#include "support/support_templates.h" +#include "support/support_helper.h" #include "observer_peer.h" namespace { @@ -342,9 +342,9 @@ AuthSession::AuthSession(const MTPUser &user) , _notifications(std::make_unique(this)) , _data(std::make_unique(this)) , _changelogs(Core::Changelogs::Create(this)) -, _supportTemplates( +, _supportHelper( (Support::ValidateAccount(user) - ? std::make_unique(this) + ? std::make_unique(this) : nullptr)) { App::feedUser(user); @@ -457,13 +457,17 @@ void AuthSession::checkAutoLockIn(TimeMs time) { } bool AuthSession::supportMode() const { - return (_supportTemplates != nullptr); + return (_supportHelper != nullptr); } -not_null AuthSession::supportTemplates() const { +Support::Helper &AuthSession::supportHelper() const { Expects(supportMode()); - return _supportTemplates.get(); + return *_supportHelper; +} + +Support::Templates& AuthSession::supportTemplates() const { + return supportHelper().templates(); } AuthSession::~AuthSession() = default; diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 1c72c376f..16ab3bd61 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -21,6 +21,7 @@ enum class InputSubmitSettings; namespace Support { enum class SwitchSettings; +class Helper; class Templates; } // namespace Support @@ -301,7 +302,8 @@ public: base::Observable, MsgId>> messageIdChanging; bool supportMode() const; - not_null supportTemplates() const; + Support::Helper &supportHelper() const; + Support::Templates &supportTemplates() const; ~AuthSession(); @@ -328,7 +330,7 @@ private: // _changelogs depends on _data, subscribes on chats loading event. const std::unique_ptr _changelogs; - const std::unique_ptr _supportTemplates; + const std::unique_ptr _supportHelper; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp index 37e4e6296..2e29b86ed 100644 --- a/Telegram/SourceFiles/data/data_drafts.cpp +++ b/Telegram/SourceFiles/data/data_drafts.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_widget.h" #include "mainwidget.h" #include "storage/localstorage.h" +#include "support/support_helper.h" namespace Data { namespace { @@ -44,8 +45,8 @@ Draft::Draft( } void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { - auto history = App::history(peerId); - auto textWithTags = TextWithTags { + const auto history = App::history(peerId); + const auto textWithTags = TextWithTags { qs(draft.vmessage), ConvertEntitiesToTextTags( draft.has_entities() @@ -56,14 +57,23 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { return; } auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0); - auto cloudDraft = std::make_unique(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage()); + auto cloudDraft = std::make_unique( + textWithTags, + replyTo, + MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), + draft.is_no_webpage()); cloudDraft->date = draft.vdate.v; history->setCloudDraft(std::move(cloudDraft)); 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); } } @@ -77,7 +87,12 @@ void clearPeerCloudDraft(PeerId peerId, TimeId date) { history->clearCloudDraft(); history->clearLocalDraft(); - history->updateChatListSortPosition(); + if (Auth().supportMode()) { + history->updateChatListEntry(); + Auth().supportHelper().cloudDraftChanged(history); + } else { + history->updateChatListSortPosition(); + } if (auto main = App::main()) { main->applyCloudDraft(history); diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h index 865e81506..c3ccdc3f8 100644 --- a/Telegram/SourceFiles/data/data_drafts.h +++ b/Telegram/SourceFiles/data/data_drafts.h @@ -47,11 +47,12 @@ inline bool draftStringIsEmpty(const QString &text) { return true; } -inline bool draftIsNull(Draft *draft) { - return !draft || (draftStringIsEmpty(draft->textWithTags.text) && !draft->msgId); +inline bool draftIsNull(const Draft *draft) { + 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 bIsNull = draftIsNull(b); if (aIsNull) { @@ -60,7 +61,9 @@ inline bool draftsAreEqual(Draft *a, Draft *b) { 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 diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp index 784b80e49..b6a85cb8a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -55,7 +55,7 @@ Row *IndexedList::addByName(Key key) { } void IndexedList::adjustByPos(const RowsByLetter &links) { - for (auto [ch, row] : links) { + for (const auto [ch, row] : links) { if (ch == QChar(0)) { _list.adjustByPos(row); } else { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index ce0f705cd..9fa1ae662 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -124,7 +124,8 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro auto changes = UpdateFlag::ChatPinnedChanged | UpdateFlag::NameChanged | UpdateFlag::PhotoChanged - | UpdateFlag::UserIsContact; + | UpdateFlag::UserIsContact + | UpdateFlag::OccupiedChanged; subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(changes, [this](const Notify::PeerUpdate &update) { if (update.flags & UpdateFlag::ChatPinnedChanged) { stopReorderPinned(); @@ -132,7 +133,7 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro if (update.flags & UpdateFlag::NameChanged) { handlePeerNameChange(update.peer, update.oldNameFirstLetters); } - if (update.flags & UpdateFlag::PhotoChanged) { + if (update.flags & (UpdateFlag::PhotoChanged | UpdateFlag::OccupiedChanged)) { this->update(); emit App::main()->dialogsUpdated(); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index e89768168..0d9a07fd0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/empty_userpic.h" #include "ui/text_options.h" #include "lang/lang_keys.h" +#include "support/support_helper.h" #include "history/history_item.h" #include "history/history.h" @@ -173,6 +174,11 @@ void paintRow( TimeMs ms, PaintItemCallback &&paintItemCallback, PaintCounterCallback &&paintCounterCallback) { + const auto supportMode = Auth().supportMode(); + if (supportMode) { + draft = nullptr; + } + auto active = (flags & Flag::Active); auto selected = (flags & Flag::Selected); auto fullRect = QRect(0, 0, fullWidth, st::dialogsRowHeight); @@ -250,7 +256,7 @@ void paintRow( auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; - if (draft) { + if (draft || (supportMode && Support::IsOccupiedBySomeone(history))) { if (!promoted) { 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->cloudDraftTextCache.isEmpty()) { 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()); } p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index ce64c3e94..f7f113819 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -348,12 +348,14 @@ void History::takeLocalDraft(History *from) { void History::createLocalDraftFromCloud() { auto draft = cloudDraft(); - if (Data::draftIsNull(draft) || !draft->date) { + if (Data::draftIsNull(draft) || !draft->date || Auth().supportMode()) { return; } 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) { setLocalDraft(std::make_unique( draft->textWithTags, @@ -376,7 +378,7 @@ void History::setCloudDraft(std::unique_ptr &&draft) { cloudDraftTextCache.clear(); } -Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) { +Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) { if (Data::draftIsNull(fromDraft)) { setCloudDraft(std::make_unique( TextWithTags(), @@ -1830,7 +1832,7 @@ std::shared_ptr History::adminLogIdManager() { TimeId History::adjustChatListTimeId() const { const auto result = chatsListTimeId(); if (const auto draft = cloudDraft()) { - if (!Data::draftIsNull(draft)) { + if (!Data::draftIsNull(draft) && !Auth().supportMode()) { return std::max(result, draft->date); } } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index e80255eb8..44de34fbc 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -306,7 +306,7 @@ public: void takeLocalDraft(History *from); void createLocalDraftFromCloud(); void setCloudDraft(std::unique_ptr &&draft); - Data::Draft *createCloudDraft(Data::Draft *fromDraft); + Data::Draft *createCloudDraft(const Data::Draft *fromDraft); bool skipCloudDraft(const QString &text, TimeId date) const; void setSentDraftText(const QString &text); void clearSentDraftText(const QString &text); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 2f922edc7..5fc06ced0 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "data/data_peer_values.h" #include "data/data_feed.h" +#include "support/support_helper.h" #include "observer_peer.h" #include "apiwrap.h" #include "styles/style_window.h" @@ -52,6 +53,7 @@ TopBarWidget::TopBarWidget( , _search(this, st::topBarSearch) , _infoToggle(this, st::topBarInfo) , _menuToggle(this, st::topBarMenuToggle) +, _titlePeerText(st::windowMinWidth / 3) , _onlineUpdater([this] { updateOnlineDisplay(); }) { subscribe(Lang::Current().updated(), [this] { refreshLang(); }); setAttribute(Qt::WA_OpaquePaintEvent); @@ -104,7 +106,8 @@ TopBarWidget::TopBarWidget( using UpdateFlag = Notify::PeerUpdate::Flag; auto flags = UpdateFlag::UserHasCalls | UpdateFlag::UserOnlineChanged - | UpdateFlag::MembersChanged; + | UpdateFlag::MembersChanged + | UpdateFlag::OccupiedChanged; subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(flags, [this](const Notify::PeerUpdate &update) { if (update.flags & UpdateFlag::UserHasCalls) { if (update.peer->isUser()) { @@ -331,14 +334,15 @@ void TopBarWidget::paintTopBar(Painter &p, TimeMs ms) { p.setFont(st::dialogsTextFont); if (paintConnectingState(p, nameleft, statustop, width(), ms)) { return; - } else if (history->paintSendAction( - p, - nameleft, - statustop, - namewidth, - width(), - st::historyStatusFgTyping, - ms)) { + } else if (!Support::IsOccupiedBySomeone(history) + && history->paintSendAction( + p, + nameleft, + statustop, + namewidth, + width(), + st::historyStatusFgTyping, + ms)) { return; } else { paintStatus(p, nameleft, statustop, namewidth, width()); @@ -379,25 +383,20 @@ void TopBarWidget::paintStatus( int top, int availableWidth, int outerWidth) { - auto statustext = _titlePeerText; - auto statuswidth = _titlePeerTextWidth; - if (statuswidth > availableWidth) { - statustext = st::dialogsTextFont->elided( - statustext, - availableWidth, - Qt::ElideLeft); - statuswidth = st::dialogsTextFont->width(statustext); - } - p.setPen(_titlePeerTextOnline + const auto occupied = Auth().supportMode() + && Support::IsOccupiedBySomeone(_activeChat.history()); + p.setPen(occupied + ? st::dialogsTextPaletteDraft.linkFg + : _titlePeerTextOnline ? st::historyStatusFgActive : st::historyStatusFg); - p.drawTextLeft(left, top, outerWidth, statustext, statuswidth); + _titlePeerText.drawLeftElided(p, left, top, availableWidth, outerWidth); } QRect TopBarWidget::getMembersShowAreaGeometry() const { int membersTextLeft = _leftTaken; int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height; - int membersTextWidth = _titlePeerTextWidth; + int membersTextWidth = _titlePeerText.maxWidth(); int membersTextHeight = st::topBarHeight - membersTextTop; return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight); @@ -745,7 +744,10 @@ void TopBarWidget::updateOnlineDisplay() { QString text; const auto now = unixtime(); 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); titlePeerTextOnline = Data::OnlineTextActive(user, now); } else if (const auto chat = _activeChat.peer()->asChat()) { @@ -753,7 +755,7 @@ void TopBarWidget::updateOnlineDisplay() { text = lang(lng_chat_status_unaccessible); } else if (chat->participants.empty()) { if (!_titlePeerText.isEmpty()) { - text = _titlePeerText; + text = _titlePeerText.originalText(); } else if (chat->count <= 0) { text = lang(lng_group_status); } else { @@ -810,10 +812,9 @@ void TopBarWidget::updateOnlineDisplay() { text = lang(channel->isMegagroup() ? lng_group_status : lng_channel_status); } } - if (_titlePeerText != text) { - _titlePeerText = text; + if (_titlePeerText.originalText() != text) { + _titlePeerText.setText(st::dialogsTextStyle, text); _titlePeerTextOnline = titlePeerTextOnline; - _titlePeerTextWidth = st::dialogsTextFont->width(_titlePeerText); updateMembersShowArea(); update(); } diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index 8ee034726..25eb17128 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -136,9 +136,8 @@ private: object_ptr _membersShowArea = { nullptr }; rpl::event_stream _membersShowAreaActive; - QString _titlePeerText; + Text _titlePeerText; bool _titlePeerTextOnline = false; - int _titlePeerTextWidth = 0; int _leftTaken = 0; int _rightTaken = 0; bool _animatingMode = false; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index af393c782..ee9b36b6e 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3840,13 +3840,14 @@ void MainWidget::updateOnline(bool gotOtherOffline) { void MainWidget::saveDraftToCloud() { _history->saveFieldToHistoryLocalDraft(); - auto peer = _history->peer(); - if (auto history = App::historyLoaded(peer)) { + const auto peer = _history->peer(); + if (const auto history = App::historyLoaded(peer)) { writeDrafts(history); - auto localDraft = history->localDraft(); - auto cloudDraft = history->cloudDraft(); - if (!Data::draftsAreEqual(localDraft, cloudDraft)) { + const auto localDraft = history->localDraft(); + const auto cloudDraft = history->cloudDraft(); + if (!Data::draftsAreEqual(localDraft, cloudDraft) + && !Auth().supportMode()) { Auth().api().saveDraftToCloudDelayed(history); } } @@ -3859,17 +3860,27 @@ void MainWidget::applyCloudDraft(History *history) { void MainWidget::writeDrafts(History *history) { Local::MessageDraft storedLocalDraft, storedEditDraft; MessageCursor localCursor, editCursor; - if (auto localDraft = history->localDraft()) { - if (!Data::draftsAreEqual(localDraft, history->cloudDraft())) { - storedLocalDraft = Local::MessageDraft(localDraft->msgId, localDraft->textWithTags, localDraft->previewCancelled); + if (const auto localDraft = history->localDraft()) { + if (Auth().supportMode() + || !Data::draftsAreEqual(localDraft, history->cloudDraft())) { + storedLocalDraft = Local::MessageDraft( + localDraft->msgId, + localDraft->textWithTags, + localDraft->previewCancelled); localCursor = localDraft->cursor; } } - if (auto editDraft = history->editDraft()) { - storedEditDraft = Local::MessageDraft(editDraft->msgId, editDraft->textWithTags, editDraft->previewCancelled); + if (const auto editDraft = history->editDraft()) { + storedEditDraft = Local::MessageDraft( + editDraft->msgId, + editDraft->textWithTags, + editDraft->previewCancelled); editCursor = editDraft->cursor; } - Local::writeDrafts(history->peer->id, storedLocalDraft, storedEditDraft); + Local::writeDrafts( + history->peer->id, + storedLocalDraft, + storedEditDraft); Local::writeDraftCursors(history->peer->id, localCursor, editCursor); } diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index bff96e7e9..f416c2c39 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -39,33 +39,34 @@ struct PeerUpdate { RestrictionReasonChanged = (1 << 8), UnreadViewChanged = (1 << 9), PinnedMessageChanged = (1 << 10), + OccupiedChanged = (1 << 11), // For chats and channels - InviteLinkChanged = (1 << 11), - MembersChanged = (1 << 12), - AdminsChanged = (1 << 13), - BannedUsersChanged = (1 << 14), - UnreadMentionsChanged = (1 << 15), + InviteLinkChanged = (1 << 12), + MembersChanged = (1 << 13), + AdminsChanged = (1 << 14), + BannedUsersChanged = (1 << 15), + UnreadMentionsChanged = (1 << 16), // For users - UserCanShareContact = (1 << 16), - UserIsContact = (1 << 17), - UserPhoneChanged = (1 << 18), - UserIsBlocked = (1 << 19), - BotCommandsChanged = (1 << 20), - UserOnlineChanged = (1 << 21), - BotCanAddToGroups = (1 << 22), - UserCommonChatsChanged = (1 << 23), - UserHasCalls = (1 << 24), + UserCanShareContact = (1 << 17), + UserIsContact = (1 << 18), + UserPhoneChanged = (1 << 19), + UserIsBlocked = (1 << 20), + BotCommandsChanged = (1 << 21), + UserOnlineChanged = (1 << 22), + BotCanAddToGroups = (1 << 23), + UserCommonChatsChanged = (1 << 24), + UserHasCalls = (1 << 25), // For chats - ChatCanEdit = (1 << 16), + ChatCanEdit = (1 << 17), // For channels - ChannelAmIn = (1 << 16), - ChannelRightsChanged = (1 << 17), - ChannelStickersChanged = (1 << 18), - ChannelPromotedChanged = (1 << 19), + ChannelAmIn = (1 << 17), + ChannelRightsChanged = (1 << 18), + ChannelStickersChanged = (1 << 19), + ChannelPromotedChanged = (1 << 20), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp index fcf7c66d6..7b5d2e591 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.cpp +++ b/Telegram/SourceFiles/support/support_autocomplete.cpp @@ -328,11 +328,11 @@ Autocomplete::Autocomplete(QWidget *parent, not_null session) } void Autocomplete::activate(not_null field) { - if (Auth().settings().supportTemplatesAutocomplete()) { + if (_session->settings().supportTemplatesAutocomplete()) { _activate(); } else { - const auto templates = Auth().supportTemplates(); - const auto max = templates->maxKeyLength(); + const auto &templates = _session->supportTemplates(); + const auto max = templates.maxKeyLength(); auto cursor = field->textCursor(); const auto position = cursor.position(); const auto anchor = cursor.anchor(); @@ -344,8 +344,8 @@ void Autocomplete::activate(not_null field) { std::max(position - max, 0), position); const auto result = (position != anchor) - ? templates->matchExact(text.text) - : templates->matchFromEnd(text.text); + ? templates.matchExact(text.text) + : templates.matchFromEnd(text.text); if (result) { const auto till = std::max(position, anchor); const auto from = till - result->key.size(); @@ -410,7 +410,7 @@ void Autocomplete::setupContent() { const auto refresh = [=] { inner->showRows( - _session->supportTemplates()->query(input->getLastText())); + _session->supportTemplates().query(input->getLastText())); scroll->scrollToY(0); }; diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp new file mode 100644 index 000000000..bca761df4 --- /dev/null +++ b/Telegram/SourceFiles/support/support_helper.cpp @@ -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 session) +: _session(session) +, _templates(_session) +, _reoccupyTimer([=] { reoccupy(); }) +, _checkOccupiedTimer([=] { checkOccupiedChats(); }) { +} + +void Helper::registerWindow(not_null 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) { + chatOccupiedUpdated(history); + if (history != _occupiedHistory) { + return; + } + if (!IsOccupiedByMe(_occupiedHistory)) { + occupyInDraft(); + } +} + +void Helper::chatOccupiedUpdated(not_null 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 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 diff --git a/Telegram/SourceFiles/support/support_helper.h b/Telegram/SourceFiles/support/support_helper.h new file mode 100644 index 000000000..89f29b4db --- /dev/null +++ b/Telegram/SourceFiles/support/support_helper.h @@ -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 session); + + void registerWindow(not_null controller); + void cloudDraftChanged(not_null history); + + void chatOccupiedUpdated(not_null history); + + Templates &templates(); + +private: + void checkOccupiedChats(); + void updateOccupiedHistory( + not_null controller, + History *history); + void occupyInDraft(); + void reoccupy(); + + not_null _session; + Templates _templates; + + History *_occupiedHistory = nullptr; + base::Timer _reoccupyTimer; + base::Timer _checkOccupiedTimer; + base::flat_map, TimeId> _occupiedChats; + + rpl::lifetime _lifetime; + +}; + +bool IsOccupiedByMe(History *history); +bool IsOccupiedBySomeone(History *history); +QString ChatOccupiedString(); + +} // namespace Support diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index f142e7ce1..42f5cfdcf 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "auth_session.h" #include "apiwrap.h" +#include "support/support_helper.h" #include "styles/style_window.h" #include "styles/style_dialogs.h" @@ -50,6 +51,10 @@ Controller::Controller(not_null window) startRoundVideo(item); } }, lifetime()); + + if (Auth().supportMode()) { + Auth().supportHelper().registerWindow(this); + } } void Controller::setActiveChatEntry(Dialogs::RowDescriptor row) { diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 43d350411..415a0d987 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -214,13 +214,13 @@ void MainMenu::refreshMenu() { const auto subscription = Ui::AttachAsChild(_menu, rpl::lifetime()); _menu->addAction(qsl("Reload templates"), [=] { - *subscription = Auth().supportTemplates()->errors( + *subscription = Auth().supportTemplates().errors( ) | rpl::start_with_next([=](QStringList errors) { Ui::Toast::Show(errors.isEmpty() ? "Templates reloaded!" : ("Errors:\n\n" + errors.join("\n\n"))); }); - Auth().supportTemplates()->reload(); + Auth().supportTemplates().reload(); }, &st::mainMenuReload, &st::mainMenuReloadOver); } _menu->addAction(lang(lng_menu_settings), [] { diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 096b1f14c..253c827af 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -582,6 +582,8 @@ <(src_loc)/support/support_autocomplete.h <(src_loc)/support/support_common.cpp <(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.h <(src_loc)/ui/effects/cross_animation.cpp