From aede42b0b60e6532abf5d719a5af9db5b55a6107 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 24 Dec 2020 16:55:50 +0400 Subject: [PATCH 001/396] Update submodules. --- Telegram/ThirdParty/tgcalls | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 178983f72..a38a00077 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 178983f72312ca8bd422bc73810fd63f1a89bd9d +Subproject commit a38a00077470e57c931bd1c64fb1ad09707a763f diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 1e2799245..bab4cfa5a 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 1e2799245cf2720a329ecb5cf5644fded669cce6 +Subproject commit bab4cfa5aaac7fb50c13809d37eb51f76538bb67 From bcd2560e8f425d841726c7eb0eda7ec7cbc28158 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 14:10:03 +0400 Subject: [PATCH 002/396] Reuse the code for userpics in Calls::TopBar. --- Telegram/SourceFiles/calls/calls.style | 10 +- Telegram/SourceFiles/calls/calls_top_bar.cpp | 94 ++-- Telegram/SourceFiles/calls/calls_top_bar.h | 9 +- .../view/history_view_group_call_tracker.cpp | 38 +- .../view/history_view_group_call_tracker.h | 14 +- .../history/view/history_view_message.cpp | 19 +- Telegram/SourceFiles/ui/chat/chat.style | 25 +- .../SourceFiles/ui/chat/group_call_bar.cpp | 387 +--------------- Telegram/SourceFiles/ui/chat/group_call_bar.h | 34 +- .../ui/chat/group_call_userpics.cpp | 416 ++++++++++++++++++ .../SourceFiles/ui/chat/group_call_userpics.h | 73 +++ Telegram/cmake/td_ui.cmake | 2 + 12 files changed, 612 insertions(+), 509 deletions(-) create mode 100644 Telegram/SourceFiles/ui/chat/group_call_userpics.cpp create mode 100644 Telegram/SourceFiles/ui/chat/group_call_userpics.h diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index d1921073f..adbf8543d 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -9,6 +9,7 @@ using "ui/basic.style"; using "ui/widgets/widgets.style"; using "ui/layers/layers.style"; +using "ui/chat/chat.style"; // GroupCallUserpics using "window/window.style"; CallSignalBars { @@ -605,9 +606,12 @@ groupCallButtonSkip: 43px; groupCallButtonBottomSkip: 145px; groupCallMuteBottomSkip: 160px; -groupCallTopBarUserpicSize: 28px; -groupCallTopBarUserpicShift: 8px; -groupCallTopBarUserpicStroke: 2px; +groupCallTopBarUserpics: GroupCallUserpics { + size: 28px; + shift: 8px; + stroke: 2px; + align: align(left); +} groupCallTopBarJoin: RoundButton(defaultActiveButton) { width: -26px; height: 26px; diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index ae8cf508f..e54ec8e5e 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/paint/blobs_linear.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" +#include "ui/chat/group_call_userpics.h" // Ui::GroupCallUser. #include "ui/chat/group_call_bar.h" // Ui::GroupCallBarContent. #include "ui/layers/generic_box.h" #include "ui/wrap/padding_wrap.h" @@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "app.h" #include "styles/style_calls.h" +#include "styles/style_chat.h" // style::GroupCallUserpics #include "styles/style_layers.h" namespace Calls { @@ -165,7 +167,7 @@ void DebugInfoBox::updateText() { } // namespace struct TopBar::User { - Ui::GroupCallBarContent::User data; + Ui::GroupCallUser data; }; class Mute final : public Ui::IconButton { @@ -238,6 +240,12 @@ TopBar::TopBar( : RpWidget(parent) , _call(call) , _groupCall(groupCall) +, _userpics(call + ? nullptr + : std::make_unique( + st::groupCallTopBarUserpics, + rpl::single(true), + [=] { updateUserpics(); })) , _durationLabel(_call ? object_ptr(this, st::callBarLabel) : object_ptr(nullptr)) @@ -551,35 +559,31 @@ void TopBar::subscribeToMembersChanges(not_null call) { ) | rpl::map([=](not_null real) { return HistoryView::GroupCallTracker::ContentByCall( real, - HistoryView::UserpicsInRowStyle{ - .size = st::groupCallTopBarUserpicSize, - .shift = st::groupCallTopBarUserpicShift, - .stroke = st::groupCallTopBarUserpicStroke, - }); + st::groupCallTopBarUserpics.size); }) | rpl::flatten_latest( ) | rpl::filter([=](const Ui::GroupCallBarContent &content) { if (_users.size() != content.users.size()) { return true; } for (auto i = 0, count = int(_users.size()); i != count; ++i) { - if (_users[i].data.userpicKey != content.users[i].userpicKey - || _users[i].data.id != content.users[i].id) { + if (_users[i].userpicKey != content.users[i].userpicKey + || _users[i].id != content.users[i].id) { return true; } } return false; }) | rpl::start_with_next([=](const Ui::GroupCallBarContent &content) { - const auto sizeChanged = (_users.size() != content.users.size()); - _users = ranges::view::all( - content.users - ) | ranges::view::transform([](const auto &user) { - return User{ user }; - }) | ranges::to_vector; - generateUserpicsInRow(); - if (sizeChanged) { - updateControlsGeometry(); + _users = content.users; + for (auto &user : _users) { + user.speaking = false; } - update(); + _userpics->update(_users, !isHidden()); + }, lifetime()); + + _userpics->widthValue( + ) | rpl::start_with_next([=](int width) { + _userpicsWidth = width; + updateControlsGeometry(); }, lifetime()); call->peer()->session().changes().peerUpdates( @@ -591,41 +595,10 @@ void TopBar::subscribeToMembersChanges(not_null call) { }) | rpl::start_with_next([=] { updateInfoLabels(); }, lifetime()); - } -void TopBar::generateUserpicsInRow() { - const auto count = int(_users.size()); - if (!count) { - _userpics = QImage(); - return; - } - const auto limit = std::min(count, kMaxUsersInBar); - const auto single = st::groupCallTopBarUserpicSize; - const auto shift = st::groupCallTopBarUserpicShift; - const auto width = single + (limit - 1) * (single - shift); - if (_userpics.width() != width * cIntRetinaFactor()) { - _userpics = QImage( - QSize(width, single) * cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied); - } - _userpics.fill(Qt::transparent); - _userpics.setDevicePixelRatio(cRetinaFactor()); - - auto q = Painter(&_userpics); - auto hq = PainterHighQualityEnabler(q); - auto pen = QPen(Qt::transparent); - pen.setWidth(st::groupCallTopBarUserpicStroke); - auto x = (count - 1) * (single - shift); - for (auto i = count; i != 0;) { - q.setCompositionMode(QPainter::CompositionMode_SourceOver); - q.drawImage(x, 0, _users[--i].data.userpic); - q.setCompositionMode(QPainter::CompositionMode_Source); - q.setBrush(Qt::NoBrush); - q.setPen(pen); - q.drawEllipse(x, 0, single, single); - x -= single - shift; - } +void TopBar::updateUserpics() { + update(_mute->width(), 0, _userpics->maxWidth(), height()); } void TopBar::updateInfoLabels() { @@ -688,9 +661,13 @@ void TopBar::updateControlsGeometry() { _durationLabel->moveToLeft(left, st::callBarLabelTop); left += _durationLabel->width() + st::callBarSkip; } - if (!_userpics.isNull()) { - left += (_userpics.width() / _userpics.devicePixelRatio()) - + st::callBarSkip; + if (_userpicsWidth) { + const auto single = st::groupCallTopBarUserpics.size; + const auto skip = anim::interpolate( + 0, + st::callBarSkip, + std::min(_userpicsWidth, single) / float64(single)); + left += _userpicsWidth + skip; } if (_signalBars) { _signalBars->moveToLeft(left, (height() - _signalBars->height()) / 2); @@ -742,11 +719,10 @@ void TopBar::paintEvent(QPaintEvent *e) { : (_muted ? st::callBarBgMuted : st::callBarBg); p.fillRect(e->rect(), std::move(brush)); - if (!_userpics.isNull()) { - const auto imageSize = _userpics.size() - / _userpics.devicePixelRatio(); - const auto top = (height() - imageSize.height()) / 2; - p.drawImage(_mute->width(), top, _userpics); + if (_userpicsWidth) { + const auto size = st::groupCallTopBarUserpics.size; + const auto top = (height() - size) / 2; + _userpics->paint(p, _mute->width(), top, size); } } diff --git a/Telegram/SourceFiles/calls/calls_top_bar.h b/Telegram/SourceFiles/calls/calls_top_bar.h index 2f89032de..79f65830c 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.h +++ b/Telegram/SourceFiles/calls/calls_top_bar.h @@ -20,6 +20,8 @@ class IconButton; class AbstractButton; class LabelSimple; class FlatLabel; +struct GroupCallUser; +class GroupCallUserpics; } // namespace Ui namespace Main { @@ -66,14 +68,15 @@ private: void setMuted(bool mute); void subscribeToMembersChanges(not_null call); - void generateUserpicsInRow(); + void updateUserpics(); const base::weak_ptr _call; const base::weak_ptr _groupCall; bool _muted = false; - std::vector _users; - QImage _userpics; + std::vector _users; + std::unique_ptr _userpics; + int _userpicsWidth = 0; object_ptr _durationLabel; object_ptr _signalBars; object_ptr _fullInfoLabel; diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp index 0dd370922..5297a71ec 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "main/main_session.h" #include "ui/chat/group_call_bar.h" +#include "ui/chat/group_call_userpics.h" #include "ui/painter.h" #include "calls/calls_group_call.h" #include "calls/calls_instance.h" @@ -24,7 +25,7 @@ namespace HistoryView { void GenerateUserpicsInRow( QImage &result, const std::vector &list, - const UserpicsInRowStyle &st, + const style::GroupCallUserpics &st, int maxElements) { const auto count = int(list.size()); if (!count) { @@ -67,7 +68,7 @@ GroupCallTracker::GroupCallTracker(not_null peer) rpl::producer GroupCallTracker::ContentByCall( not_null call, - const UserpicsInRowStyle &st) { + int userpicSize) { struct State { std::vector userpics; Ui::GroupCallBarContent current; @@ -130,7 +131,7 @@ rpl::producer GroupCallTracker::ContentByCall( static const auto RegenerateUserpics = []( not_null state, not_null call, - const UserpicsInRowStyle &st, + int userpicSize, bool force = false) { const auto result = FillMissingUserpics(state, call) || force; if (!result) { @@ -141,7 +142,9 @@ rpl::producer GroupCallTracker::ContentByCall( state->someUserpicsNotLoaded = false; for (auto &userpic : state->userpics) { userpic.peer->loadUserpic(); - const auto pic = userpic.peer->genUserpic(userpic.view, st.size); + const auto pic = userpic.peer->genUserpic( + userpic.view, + userpicSize); userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view); state->current.users.push_back({ .userpic = pic.toImage(), @@ -161,7 +164,7 @@ rpl::producer GroupCallTracker::ContentByCall( not_null state, not_null call, not_null user, - const UserpicsInRowStyle &st) { + int userpicSize) { const auto i = ranges::find( state->userpics, user, @@ -170,7 +173,7 @@ rpl::producer GroupCallTracker::ContentByCall( return false; } state->userpics.erase(i); - RegenerateUserpics(state, call, st, true); + RegenerateUserpics(state, call, userpicSize, true); return true; }; @@ -178,7 +181,7 @@ rpl::producer GroupCallTracker::ContentByCall( not_null state, not_null call, not_null user, - const UserpicsInRowStyle &st) { + int userpicSize) { Expects(state->userpics.size() <= kLimit); const auto &participants = call->participants(); @@ -237,7 +240,7 @@ rpl::producer GroupCallTracker::ContentByCall( } Assert(state->userpics.size() <= kLimit); } - RegenerateUserpics(state, call, st, true); + RegenerateUserpics(state, call, userpicSize, true); return true; }; @@ -262,12 +265,12 @@ rpl::producer GroupCallTracker::ContentByCall( ) | rpl::start_with_next([=](const ParticipantUpdate &update) { const auto user = update.now ? update.now->user : update.was->user; if (!update.now) { - if (RemoveUserpic(state, call, user, st)) { + if (RemoveUserpic(state, call, user, userpicSize)) { pushNext(); } } else if (update.now->speaking && (!update.was || !update.was->speaking)) { - if (CheckPushToFront(state, call, user, st)) { + if (CheckPushToFront(state, call, user, userpicSize)) { pushNext(); } } else { @@ -287,7 +290,7 @@ rpl::producer GroupCallTracker::ContentByCall( updateSpeakingState = false; } } - if (RegenerateUserpics(state, call, st) + if (RegenerateUserpics(state, call, userpicSize) || updateSpeakingState) { pushNext(); } @@ -296,7 +299,7 @@ rpl::producer GroupCallTracker::ContentByCall( call->participantsSliceAdded( ) | rpl::filter([=] { - return RegenerateUserpics(state, call, st); + return RegenerateUserpics(state, call, userpicSize); }) | rpl::start_with_next(pushNext, lifetime); call->peer()->session().downloaderTaskFinished( @@ -306,14 +309,14 @@ rpl::producer GroupCallTracker::ContentByCall( for (const auto &userpic : state->userpics) { if (userpic.peer->userpicUniqueKey(userpic.view) != userpic.uniqueKey) { - RegenerateUserpics(state, call, st, true); + RegenerateUserpics(state, call, userpicSize, true); pushNext(); return; } } }, lifetime); - RegenerateUserpics(state, call, st); + RegenerateUserpics(state, call, userpicSize); call->fullCountValue( ) | rpl::start_with_next([=](int count) { @@ -345,12 +348,7 @@ rpl::producer GroupCallTracker::content() const { } else if (!call->fullCount() && !call->participantsLoaded()) { call->reload(); } - const auto st = UserpicsInRowStyle{ - .size = st::historyGroupCallUserpicSize, - .shift = st::historyGroupCallUserpicShift, - .stroke = st::historyGroupCallUserpicStroke, - }; - return ContentByCall(call, st); + return ContentByCall(call, st::historyGroupCallUserpics.size); }) | rpl::flatten_latest(); } diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.h b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.h index b1bed1790..c7b2ad1f0 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.h +++ b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.h @@ -18,6 +18,10 @@ class GroupCall; class CloudImageView; } // namespace Data +namespace style { +struct GroupCallUserpics; +} // namespace style + namespace HistoryView { struct UserpicInRow { @@ -27,16 +31,10 @@ struct UserpicInRow { mutable InMemoryKey uniqueKey; }; -struct UserpicsInRowStyle { - int size = 0; - int shift = 0; - int stroke = 0; -}; - void GenerateUserpicsInRow( QImage &result, const std::vector &list, - const UserpicsInRowStyle &st, + const style::GroupCallUserpics &st, int maxElements = 0); class GroupCallTracker final { @@ -48,7 +46,7 @@ public: [[nodiscard]] static rpl::producer ContentByCall( not_null call, - const UserpicsInRowStyle &st); + int userpicSize); private: const not_null _peer; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 71ac89589..9e81e2405 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -795,8 +795,8 @@ void Message::paintCommentsButton( auto &list = _comments->userpics; const auto limit = HistoryMessageViews::kMaxRecentRepliers; const auto count = std::min(int(views->recentRepliers.size()), limit); - const auto single = st::historyCommentsUserpicSize; - const auto shift = st::historyCommentsUserpicOverlap; + const auto single = st::historyCommentsUserpics.size; + const auto shift = st::historyCommentsUserpics.shift; const auto regenerate = [&] { if (list.size() != count) { return true; @@ -828,12 +828,11 @@ void Message::paintCommentsButton( while (list.size() > count) { list.pop_back(); } - const auto st = UserpicsInRowStyle{ - .size = single, - .shift = shift, - .stroke = st::historyCommentsUserpicStroke, - }; - GenerateUserpicsInRow(_comments->cachedUserpics, list, st, limit); + GenerateUserpicsInRow( + _comments->cachedUserpics, + list, + st::historyCommentsUserpics, + limit); } p.drawImage( left, @@ -2135,8 +2134,8 @@ int Message::minWidthForMedia() const { const auto views = data()->Get(); if (data()->repliesAreComments() && !views->replies.text.isEmpty()) { const auto limit = HistoryMessageViews::kMaxRecentRepliers; - const auto single = st::historyCommentsUserpicSize; - const auto shift = st::historyCommentsUserpicOverlap; + const auto single = st::historyCommentsUserpics.size; + const auto shift = st::historyCommentsUserpics.shift; const auto added = single + (limit - 1) * (single - shift) + st::historyCommentsSkipLeft diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index c8f7b4842..fdc66f585 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -18,6 +18,13 @@ MessageBar { duration: int; } +GroupCallUserpics { + size: pixels; + shift: pixels; + stroke: pixels; + align: align; +} + defaultMessageBar: MessageBar { title: semiboldTextStyle; titleFg: windowActiveTextFg; @@ -739,10 +746,13 @@ historyPollInChosenSelected: icon {{ "poll_select_check", historyFileInIconFgSel historyCommentsButtonHeight: 40px; historyCommentsSkipLeft: 9px; historyCommentsSkipText: 10px; -historyCommentsUserpicSize: 25px; -historyCommentsUserpicStroke: 2px; -historyCommentsUserpicOverlap: 6px; historyCommentsSkipRight: 8px; +historyCommentsUserpics: GroupCallUserpics { + size: 25px; + shift: 6px; + stroke: 2px; + align: align(left); +} boxAttachEmoji: IconButton(historyAttachEmoji) { width: 30px; @@ -798,9 +808,12 @@ historyCommentsOpenOutSelected: icon {{ "history_comments_open", msgFileThumbLin historySlowmodeCounterMargins: margins(0px, 0px, 10px, 0px); -historyGroupCallUserpicSize: 32px; -historyGroupCallUserpicShift: 12px; -historyGroupCallUserpicStroke: 4px; +historyGroupCallUserpics: GroupCallUserpics { + size: 32px; + shift: 12px; + stroke: 4px; + align: align(top); +} historyGroupCallBlobMinRadius: 23px; historyGroupCallBlobMaxRadius: 25px; diff --git a/Telegram/SourceFiles/ui/chat/group_call_bar.cpp b/Telegram/SourceFiles/ui/chat/group_call_bar.cpp index 04723a0ae..b74142371 100644 --- a/Telegram/SourceFiles/ui/chat/group_call_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/group_call_bar.cpp @@ -7,12 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/chat/group_call_bar.h" -#include "ui/chat/message_bar.h" +#include "ui/chat/group_call_userpics.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" -#include "ui/paint/blobs.h" #include "lang/lang_keys.h" -#include "base/openssl_help.h" #include "styles/style_chat.h" #include "styles/style_calls.h" #include "styles/style_info.h" // st::topBarArrowPadding, like TopBarWidget. @@ -21,71 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Ui { -namespace { - -constexpr auto kDuration = 160; -constexpr auto kMaxUserpics = 4; -constexpr auto kWideScale = 5; - -constexpr auto kBlobsEnterDuration = crl::time(250); -constexpr auto kLevelDuration = 100. + 500. * 0.23; -constexpr auto kBlobScale = 0.605; -constexpr auto kMinorBlobFactor = 0.9f; -constexpr auto kUserpicMinScale = 0.8; -constexpr auto kMaxLevel = 1.; -constexpr auto kSendRandomLevelInterval = crl::time(100); - -auto Blobs()->std::array { - return { { - { - .segmentsCount = 6, - .minScale = kBlobScale * kMinorBlobFactor, - .minRadius = st::historyGroupCallBlobMinRadius * kMinorBlobFactor, - .maxRadius = st::historyGroupCallBlobMaxRadius * kMinorBlobFactor, - .speedScale = 1., - .alpha = .5, - }, - { - .segmentsCount = 8, - .minScale = kBlobScale, - .minRadius = (float)st::historyGroupCallBlobMinRadius, - .maxRadius = (float)st::historyGroupCallBlobMaxRadius, - .speedScale = 1., - .alpha = .2, - }, - } }; -} - -} // namespace - -struct GroupCallBar::BlobsAnimation { - BlobsAnimation( - std::vector blobDatas, - float levelDuration, - float maxLevel) - : blobs(std::move(blobDatas), levelDuration, maxLevel) { - } - - Ui::Paint::Blobs blobs; - crl::time lastTime = 0; - crl::time lastSpeakingUpdateTime = 0; - float64 enter = 0.; -}; - -struct GroupCallBar::Userpic { - User data; - std::pair cacheKey; - crl::time speakingStarted = 0; - QImage cache; - Animations::Simple leftAnimation; - Animations::Simple shownAnimation; - std::unique_ptr blobsAnimation; - int left = 0; - bool positionInited = false; - bool topMost = false; - bool hiding = false; - bool cacheMasked = false; -}; GroupCallBar::GroupCallBar( not_null parent, @@ -98,16 +31,13 @@ GroupCallBar::GroupCallBar( tr::lng_group_call_join(), st::groupCallTopBarJoin)) , _shadow(std::make_unique(_wrap.parentWidget())) -, _randomSpeakingTimer([=] { sendRandomLevels(); }) { +, _userpics(std::make_unique( + st::historyGroupCallUserpics, + std::move(hideBlobs), + [=] { updateUserpics(); })) { _wrap.hide(anim::type::instant); _shadow->hide(); - const auto limit = kMaxUserpics; - const auto single = st::historyGroupCallUserpicSize; - const auto shift = st::historyGroupCallUserpicShift; - // + 1 * single for the blobs. - _maxUserpicsWidth = 2 * single + (limit - 1) * (single - shift); - _wrap.entity()->paintRequest( ) | rpl::start_with_next([=](QRect clip) { QPainter(_wrap.entity()).fillRect(clip, st::historyPinnedBg); @@ -122,7 +52,7 @@ GroupCallBar::GroupCallBar( copy ) | rpl::start_with_next([=](GroupCallBarContent &&content) { _content = content; - updateUserpicsFromContent(); + _userpics->update(_content.users, !_wrap.isHidden()); _inner->update(); }, lifetime()); @@ -140,53 +70,10 @@ GroupCallBar::GroupCallBar( _wrap.toggle(false, anim::type::normal); }, lifetime()); - style::PaletteChanged( - ) | rpl::start_with_next([=] { - for (auto &userpic : _userpics) { - userpic.cache = QImage(); - } - }, lifetime()); - - _speakingAnimation.init([=](crl::time now) { - if (const auto &last = _speakingAnimationHideLastTime; (last > 0) - && (now - last >= kBlobsEnterDuration)) { - _speakingAnimation.stop(); - } - for (auto &userpic : _userpics) { - if (const auto blobs = userpic.blobsAnimation.get()) { - blobs->blobs.updateLevel(now - blobs->lastTime); - blobs->lastTime = now; - } - } - updateUserpics(); - }); - - rpl::combine( - rpl::single(anim::Disabled()) | rpl::then(anim::Disables()), - std::move(hideBlobs) - ) | rpl::start_with_next([=](bool animDisabled, bool deactivated) { - const auto hide = animDisabled || deactivated; - - if (!(hide && _speakingAnimationHideLastTime)) { - _speakingAnimationHideLastTime = hide ? crl::now() : 0; - } - _skipLevelUpdate = hide; - for (auto &userpic : _userpics) { - if (const auto blobs = userpic.blobsAnimation.get()) { - blobs->blobs.setLevel(0.); - } - } - if (!hide && !_speakingAnimation.animating()) { - _speakingAnimation.start(); - } - _skipLevelUpdate = hide; - }, lifetime()); - setupInner(); } -GroupCallBar::~GroupCallBar() { -} +GroupCallBar::~GroupCallBar() = default; void GroupCallBar::setupInner() { _inner->resize(0, st::historyReplyHeight); @@ -252,142 +139,11 @@ void GroupCallBar::paint(Painter &p) { ? tr::lng_group_call_members(tr::now, lt_count, _content.count) : tr::lng_group_call_no_members(tr::now))); + const auto size = st::historyGroupCallUserpics.size; // Skip shadow of the bar above. - paintUserpics(p); -} - -void GroupCallBar::paintUserpics(Painter &p) { - const auto top = (st::historyReplyHeight - - st::lineWidth - - st::historyGroupCallUserpicSize) / 2 + st::lineWidth; - const auto middle = _inner->width() / 2; - const auto size = st::historyGroupCallUserpicSize; - const auto factor = style::DevicePixelRatio(); - const auto &minScale = kUserpicMinScale; - for (auto &userpic : ranges::view::reverse(_userpics)) { - const auto shown = userpic.shownAnimation.value( - userpic.hiding ? 0. : 1.); - if (shown == 0.) { - continue; - } - validateUserpicCache(userpic); - p.setOpacity(shown); - const auto left = middle + userpic.leftAnimation.value(userpic.left); - const auto blobs = userpic.blobsAnimation.get(); - const auto shownScale = 0.5 + shown / 2.; - const auto scale = shownScale * (!blobs - ? 1. - : (minScale - + (1. - minScale) * (_speakingAnimationHideLastTime - ? (1. - blobs->blobs.currentLevel()) - : blobs->blobs.currentLevel()))); - if (blobs) { - auto hq = PainterHighQualityEnabler(p); - - const auto shift = QPointF(left + size / 2., top + size / 2.); - p.translate(shift); - blobs->blobs.paint(p, st::windowActiveTextFg); - p.translate(-shift); - p.setOpacity(1.); - } - if (std::abs(scale - 1.) < 0.001) { - const auto skip = ((kWideScale - 1) / 2) * size * factor; - p.drawImage( - QRect(left, top, size, size), - userpic.cache, - QRect(skip, skip, size * factor, size * factor)); - } else { - auto hq = PainterHighQualityEnabler(p); - - auto target = QRect( - left + (1 - kWideScale) / 2 * size, - top + (1 - kWideScale) / 2 * size, - kWideScale * size, - kWideScale * size); - auto shrink = anim::interpolate( - (1 - kWideScale) / 2 * size, - 0, - scale); - auto margins = QMargins(shrink, shrink, shrink, shrink); - p.drawImage(target.marginsAdded(margins), userpic.cache); - } - } - p.setOpacity(1.); - - const auto hidden = [](const Userpic &userpic) { - return userpic.hiding && !userpic.shownAnimation.animating(); - }; - _userpics.erase(ranges::remove_if(_userpics, hidden), end(_userpics)); -} - -bool GroupCallBar::needUserpicCacheRefresh(Userpic &userpic) { - if (userpic.cache.isNull()) { - return true; - } else if (userpic.hiding) { - return false; - } else if (userpic.cacheKey != userpic.data.userpicKey) { - return true; - } - const auto shouldBeMasked = !userpic.topMost; - if (userpic.cacheMasked == shouldBeMasked || !shouldBeMasked) { - return true; - } - return !userpic.leftAnimation.animating(); -} - -void GroupCallBar::ensureBlobsAnimation(Userpic &userpic) { - if (userpic.blobsAnimation) { - return; - } - userpic.blobsAnimation = std::make_unique( - Blobs() | ranges::to_vector, - kLevelDuration, - kMaxLevel); - userpic.blobsAnimation->lastTime = crl::now(); -} - -void GroupCallBar::sendRandomLevels() { - if (_skipLevelUpdate) { - return; - } - for (auto &userpic : _userpics) { - if (const auto blobs = userpic.blobsAnimation.get()) { - const auto value = 30 + (openssl::RandomValue() % 70); - userpic.blobsAnimation->blobs.setLevel(float64(value) / 100.); - } - } -} - -void GroupCallBar::validateUserpicCache(Userpic &userpic) { - if (!needUserpicCacheRefresh(userpic)) { - return; - } - const auto factor = style::DevicePixelRatio(); - const auto size = st::historyGroupCallUserpicSize; - const auto shift = st::historyGroupCallUserpicShift; - const auto full = QSize(size, size) * kWideScale * factor; - if (userpic.cache.isNull()) { - userpic.cache = QImage(full, QImage::Format_ARGB32_Premultiplied); - userpic.cache.setDevicePixelRatio(factor); - } - userpic.cacheKey = userpic.data.userpicKey; - userpic.cacheMasked = !userpic.topMost; - userpic.cache.fill(Qt::transparent); - { - Painter p(&userpic.cache); - const auto skip = (kWideScale - 1) / 2 * size; - p.drawImage(skip, skip, userpic.data.userpic); - - if (userpic.cacheMasked) { - auto hq = PainterHighQualityEnabler(p); - auto pen = QPen(Qt::transparent); - pen.setWidth(st::historyGroupCallUserpicStroke); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.setBrush(Qt::transparent); - p.setPen(pen); - p.drawEllipse(skip - size + shift, skip, size, size); - } - } + const auto top = (st::historyReplyHeight - st::lineWidth - size) / 2 + + st::lineWidth; + _userpics->paint(p, _inner->width() / 2, top, size); } void GroupCallBar::updateControlsGeometry(QRect wrapGeometry) { @@ -413,127 +169,14 @@ void GroupCallBar::updateShadowGeometry(QRect wrapGeometry) { : regular); } -void GroupCallBar::updateUserpicsFromContent() { - const auto idFromUserpic = [](const Userpic &userpic) { - return userpic.data.id; - }; - - // Use "topMost" as "willBeHidden" flag. - for (auto &userpic : _userpics) { - userpic.topMost = true; - } - for (const auto &user : _content.users) { - const auto i = ranges::find(_userpics, user.id, idFromUserpic); - if (i == end(_userpics)) { - _userpics.push_back(Userpic{ user }); - toggleUserpic(_userpics.back(), true); - continue; - } - i->topMost = false; - - if (i->hiding) { - toggleUserpic(*i, true); - } - i->data = user; - - // Put this one after the last we are not hiding. - for (auto j = end(_userpics) - 1; j != i; --j) { - if (!j->topMost) { - ranges::rotate(i, i + 1, j + 1); - break; - } - } - } - - // Hide the ones that "willBeHidden" (currently having "topMost" flag). - // Set correct real values of "topMost" flag. - const auto userpicsBegin = begin(_userpics); - const auto userpicsEnd = end(_userpics); - auto markedTopMost = userpicsEnd; - auto hasBlobs = false; - for (auto i = userpicsBegin; i != userpicsEnd; ++i) { - auto &userpic = *i; - if (userpic.data.speaking) { - ensureBlobsAnimation(userpic); - hasBlobs = true; - } else { - userpic.blobsAnimation = nullptr; - } - if (userpic.topMost) { - toggleUserpic(userpic, false); - userpic.topMost = false; - } else if (markedTopMost == userpicsEnd) { - userpic.topMost = true; - markedTopMost = i; - } - } - if (markedTopMost != userpicsEnd && markedTopMost != userpicsBegin) { - // Bring the topMost userpic to the very beginning, above all hiding. - std::rotate(userpicsBegin, markedTopMost, markedTopMost + 1); - } - updateUserpicsPositions(); - - if (!hasBlobs) { - _randomSpeakingTimer.cancel(); - _speakingAnimation.stop(); - } else if (!_randomSpeakingTimer.isActive()) { - _randomSpeakingTimer.callEach(kSendRandomLevelInterval); - _speakingAnimation.start(); - } - - if (_wrap.isHidden()) { - for (auto &userpic : _userpics) { - userpic.shownAnimation.stop(); - userpic.leftAnimation.stop(); - } - } -} - -void GroupCallBar::toggleUserpic(Userpic &userpic, bool shown) { - userpic.hiding = !shown; - userpic.shownAnimation.start( - [=] { updateUserpics(); }, - shown ? 0. : 1., - shown ? 1. : 0., - kDuration); -} - -void GroupCallBar::updateUserpicsPositions() { - const auto shownCount = ranges::count(_userpics, false, &Userpic::hiding); - if (!shownCount) { - return; - } - const auto single = st::historyGroupCallUserpicSize; - const auto shift = st::historyGroupCallUserpicShift; - // + 1 * single for the blobs. - const auto fullWidth = single + (shownCount - 1) * (single - shift); - auto left = (-fullWidth / 2); - for (auto &userpic : _userpics) { - if (userpic.hiding) { - continue; - } - if (!userpic.positionInited) { - userpic.positionInited = true; - userpic.left = left; - } else if (userpic.left != left) { - userpic.leftAnimation.start( - [=] { updateUserpics(); }, - userpic.left, - left, - kDuration); - userpic.left = left; - } - left += (single - shift); - } -} - void GroupCallBar::updateUserpics() { const auto widget = _wrap.entity(); const auto middle = widget->width() / 2; - _wrap.entity()->update( - (middle - _maxUserpicsWidth / 2), + const auto width = _userpics->maxWidth(); + widget->update( + (middle - width / 2), 0, - _maxUserpicsWidth, + width, widget->height()); } diff --git a/Telegram/SourceFiles/ui/chat/group_call_bar.h b/Telegram/SourceFiles/ui/chat/group_call_bar.h index 2641ef586..f085a2d04 100644 --- a/Telegram/SourceFiles/ui/chat/group_call_bar.h +++ b/Telegram/SourceFiles/ui/chat/group_call_bar.h @@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/effects/animations.h" #include "base/object_ptr.h" -#include "base/timer.h" class Painter; @@ -18,17 +17,13 @@ namespace Ui { class PlainShadow; class RoundButton; +struct GroupCallUser; +class GroupCallUserpics; struct GroupCallBarContent { - struct User { - QImage userpic; - std::pair userpicKey = {}; - int32 id = 0; - bool speaking = false; - }; int count = 0; bool shown = false; - std::vector users; + std::vector users; }; class GroupCallBar final { @@ -58,24 +53,13 @@ public: } private: - using User = GroupCallBarContent::User; - struct BlobsAnimation; - struct Userpic; + using User = GroupCallUser; void updateShadowGeometry(QRect wrapGeometry); void updateControlsGeometry(QRect wrapGeometry); - void updateUserpicsFromContent(); + void updateUserpics(); void setupInner(); void paint(Painter &p); - void paintUserpics(Painter &p); - - void toggleUserpic(Userpic &userpic, bool shown); - void updateUserpics(); - void updateUserpicsPositions(); - void validateUserpicCache(Userpic &userpic); - [[nodiscard]] bool needUserpicCacheRefresh(Userpic &userpic); - void ensureBlobsAnimation(Userpic &userpic); - void sendRandomLevels(); SlideWrap<> _wrap; not_null _inner; @@ -83,17 +67,11 @@ private: std::unique_ptr _shadow; rpl::event_stream<> _barClicks; Fn _shadowGeometryPostprocess; - std::vector _userpics; - base::Timer _randomSpeakingTimer; - Ui::Animations::Basic _speakingAnimation; - int _maxUserpicsWidth = 0; bool _shouldBeShown = false; bool _forceHidden = false; - bool _skipLevelUpdate = false; - crl::time _speakingAnimationHideLastTime = 0; - GroupCallBarContent _content; + std::unique_ptr _userpics; }; diff --git a/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp new file mode 100644 index 000000000..68fab5f16 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp @@ -0,0 +1,416 @@ +/* +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 "ui/chat/group_call_userpics.h" + +#include "ui/paint/blobs.h" +#include "base/openssl_help.h" +#include "styles/style_chat.h" + +namespace Ui { +namespace { + +constexpr auto kDuration = 160; +constexpr auto kMaxUserpics = 4; +constexpr auto kWideScale = 5; + +constexpr auto kBlobsEnterDuration = crl::time(250); +constexpr auto kLevelDuration = 100. + 500. * 0.23; +constexpr auto kBlobScale = 0.605; +constexpr auto kMinorBlobFactor = 0.9f; +constexpr auto kUserpicMinScale = 0.8; +constexpr auto kMaxLevel = 1.; +constexpr auto kSendRandomLevelInterval = crl::time(100); + +auto Blobs()->std::array { + return { { + { + .segmentsCount = 6, + .minScale = kBlobScale * kMinorBlobFactor, + .minRadius = st::historyGroupCallBlobMinRadius * kMinorBlobFactor, + .maxRadius = st::historyGroupCallBlobMaxRadius * kMinorBlobFactor, + .speedScale = 1., + .alpha = .5, + }, + { + .segmentsCount = 8, + .minScale = kBlobScale, + .minRadius = (float)st::historyGroupCallBlobMinRadius, + .maxRadius = (float)st::historyGroupCallBlobMaxRadius, + .speedScale = 1., + .alpha = .2, + }, + } }; +} + +} // namespace + +struct GroupCallUserpics::BlobsAnimation { + BlobsAnimation( + std::vector blobDatas, + float levelDuration, + float maxLevel) + : blobs(std::move(blobDatas), levelDuration, maxLevel) { + } + + Ui::Paint::Blobs blobs; + crl::time lastTime = 0; + crl::time lastSpeakingUpdateTime = 0; + float64 enter = 0.; +}; + +struct GroupCallUserpics::Userpic { + User data; + std::pair cacheKey; + crl::time speakingStarted = 0; + QImage cache; + Animations::Simple leftAnimation; + Animations::Simple shownAnimation; + std::unique_ptr blobsAnimation; + int left = 0; + bool positionInited = false; + bool topMost = false; + bool hiding = false; + bool cacheMasked = false; +}; + +GroupCallUserpics::GroupCallUserpics( + const style::GroupCallUserpics &st, + rpl::producer &&hideBlobs, + Fn repaint) +: _st(st) +, _randomSpeakingTimer([=] { sendRandomLevels(); }) +, _repaint(std::move(repaint)) { + const auto limit = kMaxUserpics; + const auto single = _st.size; + const auto shift = _st.shift; + // + 1 * single for the blobs. + _maxWidth = 2 * single + (limit - 1) * (single - shift); + + style::PaletteChanged( + ) | rpl::start_with_next([=] { + for (auto &userpic : _list) { + userpic.cache = QImage(); + } + }, lifetime()); + + _speakingAnimation.init([=](crl::time now) { + if (const auto &last = _speakingAnimationHideLastTime; (last > 0) + && (now - last >= kBlobsEnterDuration)) { + _speakingAnimation.stop(); + } + for (auto &userpic : _list) { + if (const auto blobs = userpic.blobsAnimation.get()) { + blobs->blobs.updateLevel(now - blobs->lastTime); + blobs->lastTime = now; + } + } + if (const auto onstack = _repaint) { + onstack(); + } + }); + + rpl::combine( + rpl::single(anim::Disabled()) | rpl::then(anim::Disables()), + std::move(hideBlobs) + ) | rpl::start_with_next([=](bool animDisabled, bool deactivated) { + const auto hide = animDisabled || deactivated; + + if (!(hide && _speakingAnimationHideLastTime)) { + _speakingAnimationHideLastTime = hide ? crl::now() : 0; + } + _skipLevelUpdate = hide; + for (auto &userpic : _list) { + if (const auto blobs = userpic.blobsAnimation.get()) { + blobs->blobs.setLevel(0.); + } + } + if (!hide && !_speakingAnimation.animating()) { + _speakingAnimation.start(); + } + _skipLevelUpdate = hide; + }, lifetime()); +} + +GroupCallUserpics::~GroupCallUserpics() = default; + +void GroupCallUserpics::paint(Painter &p, int x, int y, int size) { + const auto factor = style::DevicePixelRatio(); + const auto &minScale = kUserpicMinScale; + for (auto &userpic : ranges::view::reverse(_list)) { + const auto shown = userpic.shownAnimation.value( + userpic.hiding ? 0. : 1.); + if (shown == 0.) { + continue; + } + validateCache(userpic); + p.setOpacity(shown); + const auto left = x + userpic.leftAnimation.value(userpic.left); + const auto blobs = userpic.blobsAnimation.get(); + const auto shownScale = 0.5 + shown / 2.; + const auto scale = shownScale * (!blobs + ? 1. + : (minScale + + (1. - minScale) * (_speakingAnimationHideLastTime + ? (1. - blobs->blobs.currentLevel()) + : blobs->blobs.currentLevel()))); + if (blobs) { + auto hq = PainterHighQualityEnabler(p); + + const auto shift = QPointF(left + size / 2., y + size / 2.); + p.translate(shift); + blobs->blobs.paint(p, st::windowActiveTextFg); + p.translate(-shift); + p.setOpacity(1.); + } + if (std::abs(scale - 1.) < 0.001) { + const auto skip = ((kWideScale - 1) / 2) * size * factor; + p.drawImage( + QRect(left, y, size, size), + userpic.cache, + QRect(skip, skip, size * factor, size * factor)); + } else { + auto hq = PainterHighQualityEnabler(p); + + auto target = QRect( + left + (1 - kWideScale) / 2 * size, + y + (1 - kWideScale) / 2 * size, + kWideScale * size, + kWideScale * size); + auto shrink = anim::interpolate( + (1 - kWideScale) / 2 * size, + 0, + scale); + auto margins = QMargins(shrink, shrink, shrink, shrink); + p.drawImage(target.marginsAdded(margins), userpic.cache); + } + } + p.setOpacity(1.); + + const auto hidden = [](const Userpic &userpic) { + return userpic.hiding && !userpic.shownAnimation.animating(); + }; + _list.erase(ranges::remove_if(_list, hidden), end(_list)); +} + +int GroupCallUserpics::maxWidth() const { + return _maxWidth; +} + +rpl::producer GroupCallUserpics::widthValue() const { + return _width.value(); +} + +bool GroupCallUserpics::needCacheRefresh(Userpic &userpic) { + if (userpic.cache.isNull()) { + return true; + } else if (userpic.hiding) { + return false; + } else if (userpic.cacheKey != userpic.data.userpicKey) { + return true; + } + const auto shouldBeMasked = !userpic.topMost; + if (userpic.cacheMasked == shouldBeMasked || !shouldBeMasked) { + return true; + } + return !userpic.leftAnimation.animating(); +} + +void GroupCallUserpics::ensureBlobsAnimation(Userpic &userpic) { + if (userpic.blobsAnimation) { + return; + } + userpic.blobsAnimation = std::make_unique( + Blobs() | ranges::to_vector, + kLevelDuration, + kMaxLevel); + userpic.blobsAnimation->lastTime = crl::now(); +} + +void GroupCallUserpics::sendRandomLevels() { + if (_skipLevelUpdate) { + return; + } + for (auto &userpic : _list) { + if (const auto blobs = userpic.blobsAnimation.get()) { + const auto value = 30 + (openssl::RandomValue() % 70); + userpic.blobsAnimation->blobs.setLevel(float64(value) / 100.); + } + } +} + +void GroupCallUserpics::validateCache(Userpic &userpic) { + if (!needCacheRefresh(userpic)) { + return; + } + const auto factor = style::DevicePixelRatio(); + const auto size = _st.size; + const auto shift = _st.shift; + const auto full = QSize(size, size) * kWideScale * factor; + if (userpic.cache.isNull()) { + userpic.cache = QImage(full, QImage::Format_ARGB32_Premultiplied); + userpic.cache.setDevicePixelRatio(factor); + } + userpic.cacheKey = userpic.data.userpicKey; + userpic.cacheMasked = !userpic.topMost; + userpic.cache.fill(Qt::transparent); + { + Painter p(&userpic.cache); + const auto skip = (kWideScale - 1) / 2 * size; + p.drawImage(skip, skip, userpic.data.userpic); + + if (userpic.cacheMasked) { + auto hq = PainterHighQualityEnabler(p); + auto pen = QPen(Qt::transparent); + pen.setWidth(_st.stroke); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.setBrush(Qt::transparent); + p.setPen(pen); + p.drawEllipse(skip - size + shift, skip, size, size); + } + } +} + +void GroupCallUserpics::update( + const std::vector &users, + bool visible) { + const auto idFromUserpic = [](const Userpic &userpic) { + return userpic.data.id; + }; + + // Use "topMost" as "willBeHidden" flag. + for (auto &userpic : _list) { + userpic.topMost = true; + } + for (const auto &user : users) { + const auto i = ranges::find(_list, user.id, idFromUserpic); + if (i == end(_list)) { + _list.push_back(Userpic{ user }); + toggle(_list.back(), true); + continue; + } + i->topMost = false; + + if (i->hiding) { + toggle(*i, true); + } + i->data = user; + + // Put this one after the last we are not hiding. + for (auto j = end(_list) - 1; j != i; --j) { + if (!j->topMost) { + ranges::rotate(i, i + 1, j + 1); + break; + } + } + } + + // Hide the ones that "willBeHidden" (currently having "topMost" flag). + // Set correct real values of "topMost" flag. + const auto userpicsBegin = begin(_list); + const auto userpicsEnd = end(_list); + auto markedTopMost = userpicsEnd; + auto hasBlobs = false; + for (auto i = userpicsBegin; i != userpicsEnd; ++i) { + auto &userpic = *i; + if (userpic.data.speaking) { + ensureBlobsAnimation(userpic); + hasBlobs = true; + } else { + userpic.blobsAnimation = nullptr; + } + if (userpic.topMost) { + toggle(userpic, false); + userpic.topMost = false; + } else if (markedTopMost == userpicsEnd) { + userpic.topMost = true; + markedTopMost = i; + } + } + if (markedTopMost != userpicsEnd && markedTopMost != userpicsBegin) { + // Bring the topMost userpic to the very beginning, above all hiding. + std::rotate(userpicsBegin, markedTopMost, markedTopMost + 1); + } + updatePositions(); + + if (!hasBlobs) { + _randomSpeakingTimer.cancel(); + _speakingAnimation.stop(); + } else if (!_randomSpeakingTimer.isActive()) { + _randomSpeakingTimer.callEach(kSendRandomLevelInterval); + _speakingAnimation.start(); + } + + if (!visible) { + for (auto &userpic : _list) { + userpic.shownAnimation.stop(); + userpic.leftAnimation.stop(); + } + } + recountAndRepaint(); +} + +void GroupCallUserpics::toggle(Userpic &userpic, bool shown) { + userpic.hiding = !shown; + userpic.shownAnimation.start( + [=] { recountAndRepaint(); }, + shown ? 0. : 1., + shown ? 1. : 0., + kDuration); +} + +void GroupCallUserpics::updatePositions() { + const auto shownCount = ranges::count(_list, false, &Userpic::hiding); + if (!shownCount) { + return; + } + const auto single = _st.size; + const auto shift = _st.shift; + // + 1 * single for the blobs. + const auto fullWidth = single + (shownCount - 1) * (single - shift); + auto left = (_st.align & Qt::AlignLeft) + ? 0 + : (_st.align & Qt::AlignHCenter) + ? (-fullWidth / 2) + : -fullWidth; + for (auto &userpic : _list) { + if (userpic.hiding) { + continue; + } + if (!userpic.positionInited) { + userpic.positionInited = true; + userpic.left = left; + } else if (userpic.left != left) { + userpic.leftAnimation.start( + _repaint, + userpic.left, + left, + kDuration); + userpic.left = left; + } + left += (single - shift); + } +} + +void GroupCallUserpics::recountAndRepaint() { + auto width = 0; + auto maxShown = 0.; + for (const auto &userpic : _list) { + const auto shown = userpic.shownAnimation.value( + userpic.hiding ? 0. : 1.); + if (shown > maxShown) { + maxShown = shown; + } + width += anim::interpolate(0, _st.size - _st.shift, shown); + } + _width = width + anim::interpolate(0, _st.shift, maxShown); + if (_repaint) { + _repaint(); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/group_call_userpics.h b/Telegram/SourceFiles/ui/chat/group_call_userpics.h new file mode 100644 index 000000000..af1da3dba --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/group_call_userpics.h @@ -0,0 +1,73 @@ +/* +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" + +namespace style { +struct GroupCallUserpics; +} // namespace style + +namespace Ui { + +struct GroupCallUser { + QImage userpic; + std::pair userpicKey = {}; + int32 id = 0; + bool speaking = false; +}; + +class GroupCallUserpics final { +public: + GroupCallUserpics( + const style::GroupCallUserpics &st, + rpl::producer &&hideBlobs, + Fn repaint); + ~GroupCallUserpics(); + + void update( + const std::vector &users, + bool visible); + void paint(Painter &p, int x, int y, int size); + + [[nodiscard]] int maxWidth() const; + [[nodiscard]] rpl::producer widthValue() const; + + [[nodiscard]] rpl::lifetime &lifetime() { + return _lifetime; + } + +private: + using User = GroupCallUser; + struct BlobsAnimation; + struct Userpic; + + void toggle(Userpic &userpic, bool shown); + void updatePositions(); + void validateCache(Userpic &userpic); + [[nodiscard]] bool needCacheRefresh(Userpic &userpic); + void ensureBlobsAnimation(Userpic &userpic); + void sendRandomLevels(); + void recountAndRepaint(); + + const style::GroupCallUserpics &_st; + std::vector _list; + base::Timer _randomSpeakingTimer; + Fn _repaint; + Ui::Animations::Basic _speakingAnimation; + int _maxWidth = 0; + bool _skipLevelUpdate = false; + crl::time _speakingAnimationHideLastTime = 0; + + rpl::variable _width; + + rpl::lifetime _lifetime; + +}; + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 016c02a6d..bcdee7aae 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -80,6 +80,8 @@ PRIVATE ui/chat/attach/attach_single_media_preview.h ui/chat/group_call_bar.cpp ui/chat/group_call_bar.h + ui/chat/group_call_userpics.cpp + ui/chat/group_call_userpics.h ui/chat/message_bar.cpp ui/chat/message_bar.h ui/chat/pinned_bar.cpp From a576025d4f6a2790aab8996eec1a6063386524fb Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 15:44:17 +0400 Subject: [PATCH 003/396] Always show invited at the end of voice chat. --- Telegram/SourceFiles/calls/calls_group_members.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index a68dcd754..9d4b1eb9c 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -750,7 +750,21 @@ void MembersController::updateRow( if (row->speaking()) { delegate()->peerListPrependRow(std::move(row)); } else { + static constexpr auto kInvited = Row::State::Invited; + const auto reorder = [&] { + const auto count = delegate()->peerListFullRowsCount(); + if (!count) { + return false; + } + const auto row = delegate()->peerListRowAt(count - 1).get(); + return (static_cast(row)->state() == kInvited); + }(); delegate()->peerListAppendRow(std::move(row)); + if (reorder) { + delegate()->peerListPartitionRows([](const PeerListRow &row) { + return static_cast(row).state() != kInvited; + }); + } } delegate()->peerListRefreshRows(); } From 930e9718812f3907553d2f3d622ae87c20653ee0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 16:11:51 +0400 Subject: [PATCH 004/396] Use more modern audio backend in calls on Windows. --- Telegram/ThirdParty/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index a38a00077..cba2d0daa 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit a38a00077470e57c931bd1c64fb1ad09707a763f +Subproject commit cba2d0daa7bf585069c55773846c4cb10af03920 From 2c0ef9c4e957110173bff19569bdabc3d4808052 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 16:37:46 +0400 Subject: [PATCH 005/396] Improve connecting animation in voice chats. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index bab4cfa5a..17eb0f22b 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit bab4cfa5aaac7fb50c13809d37eb51f76538bb67 +Subproject commit 17eb0f22b4df542e99648b7407ca1d139ba39b78 From 71151f6bf6664d4ba4b7847270aef82709755eac Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 16:45:18 +0400 Subject: [PATCH 006/396] Beta version 2.5.2. - Fix possible crash in video calls. - Fix possible crash in connecting to voice chats. - Use different audio module code on Windows in calls. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 10 +++++----- changelog.txt | 6 ++++++ 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 93884509d..8d9900a94 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.5.2.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 599fa1a7e..73071e47b 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,1,0 - PRODUCTVERSION 2,5,1,0 + FILEVERSION 2,5,2,0 + PRODUCTVERSION 2,5,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.1.0" + VALUE "FileVersion", "2.5.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.1.0" + VALUE "ProductVersion", "2.5.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index d91d26525..21a6324c6 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,1,0 - PRODUCTVERSION 2,5,1,0 + FILEVERSION 2,5,2,0 + PRODUCTVERSION 2,5,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.1.0" + VALUE "FileVersion", "2.5.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.1.0" + VALUE "ProductVersion", "2.5.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 6876564ac..c27a5e770 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005001; -constexpr auto AppVersionStr = "2.5.1"; -constexpr auto AppBetaVersion = false; +constexpr auto AppVersion = 2005002; +constexpr auto AppVersionStr = "2.5.2"; +constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 565c3be2f..6e9452f50 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005001 +AppVersion 2005002 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.1 -AppVersionStr 2.5.1 -BetaChannel 0 +AppVersionStrSmall 2.5.2 +AppVersionStr 2.5.2 +BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 2.5.1 +AppVersionOriginal 2.5.2.beta diff --git a/changelog.txt b/changelog.txt index 27e6ccda7..4e7a7481d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +2.5.2 beta (25.12.20) + +- Fix possible crash in video calls. +- Fix possible crash in connecting to voice chats. +- Use different audio module code on Windows in calls. + 2.5.1 (23.12.20) - Fix crash in voice calls. From f3614d640244227857e9df5446379726c692d7a3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 18:30:41 +0400 Subject: [PATCH 007/396] Beta version 2.5.2: Add in-app changelog. --- Telegram/SourceFiles/core/changelogs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 7bf982d01..865b42603 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -75,6 +75,14 @@ std::map BetaLogs() { "- Fix freeze on secondary screen disconnect.\n" }, + { + 2005002, + "- Fix possible crash in video calls.\n" + + "- Fix possible crash in connecting to voice chats.\n" + + "- Use different audio module code on Windows in calls.\n" + } }; }; From 486424af4f14cba7950831cde11e50a1715ca827 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 25 Dec 2020 18:57:21 +0400 Subject: [PATCH 008/396] Beta version 2.5.2: Update cmake_helpers. --- cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake b/cmake index a81345a28..220835876 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit a81345a28d407fb5acd5267ec6afa1864f4e8d5b +Subproject commit 220835876580f1df072c469e6c4dd746131a2293 From 28f857f7637aa3140c0e93e26abc20a2bc4c0e79 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 27 Dec 2020 15:30:19 +0400 Subject: [PATCH 009/396] Add support for G-S-D's media-keys extension This fixes media keys handling on (but not limited to, probably): * GNOME * Cinnamon * MATE * Budgie * Pantheon (elementaryOS) * Unity --- Telegram/CMakeLists.txt | 9 + .../platform/linux/linux_gsd_media_keys.cpp | 182 ++++++++++++++++++ .../platform/linux/linux_gsd_media_keys.h | 36 ++++ .../platform/linux/main_window_linux.cpp | 16 ++ .../platform/linux/main_window_linux.h | 7 + 5 files changed, 250 insertions(+) create mode 100644 Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bff3d3880..391e91daa 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -812,6 +812,8 @@ PRIVATE platform/linux/linux_desktop_environment.h platform/linux/linux_gdk_helper.cpp platform/linux/linux_gdk_helper.h + platform/linux/linux_gsd_media_keys.cpp + platform/linux/linux_gsd_media_keys.h platform/linux/linux_libs.cpp platform/linux/linux_libs.h platform/linux/linux_wayland_integration.cpp @@ -1101,6 +1103,13 @@ if (NOT LINUX) ) endif() +if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) + remove_target_sources(Telegram ${src_loc} + platform/linux/linux_gsd_media_keys.cpp + platform/linux/linux_gsd_media_keys.h + ) +endif() + if (LINUX AND DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_wayland_integration.cpp) nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp) diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp new file mode 100644 index 000000000..95c72047c --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp @@ -0,0 +1,182 @@ +/* +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 "platform/linux/linux_gsd_media_keys.h" + +#include "core/sandbox.h" +#include "media/player/media_player_instance.h" + +#include +#include + +extern "C" { +#undef signals +#include +#define signals public +} // extern "C" + +namespace Platform { +namespace internal { +namespace { + +constexpr auto kDBusTimeout = 30000; +constexpr auto kService = "org.gnome.SettingsDaemon.MediaKeys"_cs; +constexpr auto kOldService = "org.gnome.SettingsDaemon"_cs; +constexpr auto kMATEService = "org.mate.SettingsDaemon"_cs; +constexpr auto kObjectPath = "/org/gnome/SettingsDaemon/MediaKeys"_cs; +constexpr auto kMATEObjectPath = "/org/mate/SettingsDaemon/MediaKeys"_cs; +constexpr auto kInterface = kService; +constexpr auto kMATEInterface = "org.mate.SettingsDaemon.MediaKeys"_cs; + +void KeyPressed( + GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { + gchar *appUtf8; + gchar *keyUtf8; + g_variant_get(parameters, "(ss)", &appUtf8, &keyUtf8); + const auto app = QString::fromUtf8(appUtf8); + const auto key = QString::fromUtf8(keyUtf8); + g_free(keyUtf8); + g_free(appUtf8); + + if (app != QCoreApplication::applicationName()) { + return; + } + + Core::Sandbox::Instance().customEnterFromEventLoop([&] { + if (key == qstr("Play")) { + Media::Player::instance()->playPause(); + } else if (key == qstr("Stop")) { + Media::Player::instance()->stop(); + } else if (key == qstr("Next")) { + Media::Player::instance()->next(); + } else if (key == qstr("Previous")) { + Media::Player::instance()->previous(); + } + }); +} + +} // namespace + +GSDMediaKeys::GSDMediaKeys() { + GError *error = nullptr; + const auto interface = QDBusConnection::sessionBus().interface(); + + if (!interface) { + return; + } + + if (interface->isServiceRegistered(kService.utf16())) { + _service = kService.utf16(); + _objectPath = kObjectPath.utf16(); + _interface = kInterface.utf16(); + } else if (interface->isServiceRegistered(kOldService.utf16())) { + _service = kOldService.utf16(); + _objectPath = kObjectPath.utf16(); + _interface = kInterface.utf16(); + } else if (interface->isServiceRegistered(kMATEService.utf16())) { + _service = kMATEService.utf16(); + _objectPath = kMATEObjectPath.utf16(); + _interface = kMATEInterface.utf16(); + } else { + return; + } + + _dbusConnection = g_bus_get_sync( + G_BUS_TYPE_SESSION, + nullptr, + &error); + + if (error) { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + return; + } + + auto reply = g_dbus_connection_call_sync( + _dbusConnection, + _service.toUtf8(), + _objectPath.toUtf8(), + _interface.toUtf8(), + "GrabMediaPlayerKeys", + g_variant_new( + "(su)", + QCoreApplication::applicationName().toUtf8().constData(), + 0), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + kDBusTimeout, + nullptr, + &error); + + if (!error) { + _grabbed = true; + g_variant_unref(reply); + } else { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + } + + _signalId = g_dbus_connection_signal_subscribe( + _dbusConnection, + _service.toUtf8(), + _interface.toUtf8(), + "MediaPlayerKeyPressed", + _objectPath.toUtf8(), + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + KeyPressed, + nullptr, + nullptr); +} + +GSDMediaKeys::~GSDMediaKeys() { + GError *error = nullptr; + + if (_signalId != 0) { + g_dbus_connection_signal_unsubscribe( + _dbusConnection, + _signalId); + } + + if (_grabbed) { + auto reply = g_dbus_connection_call_sync( + _dbusConnection, + _service.toUtf8(), + _objectPath.toUtf8(), + _interface.toUtf8(), + "ReleaseMediaPlayerKeys", + g_variant_new( + "(s)", + QCoreApplication::applicationName().toUtf8().constData()), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + kDBusTimeout, + nullptr, + &error); + + if (!error) { + _grabbed = false; + g_variant_unref(reply); + } else { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + } + } + + if (_dbusConnection) { + g_object_unref(_dbusConnection); + } +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h new file mode 100644 index 000000000..f3a87ba81 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h @@ -0,0 +1,36 @@ +/* +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 + +typedef struct _GDBusConnection GDBusConnection; + +namespace Platform { +namespace internal { + +class GSDMediaKeys { +public: + GSDMediaKeys(); + + GSDMediaKeys(const GSDMediaKeys &other) = delete; + GSDMediaKeys &operator=(const GSDMediaKeys &other) = delete; + GSDMediaKeys(GSDMediaKeys &&other) = delete; + GSDMediaKeys &operator=(GSDMediaKeys &&other) = delete; + + ~GSDMediaKeys(); + +private: + GDBusConnection *_dbusConnection = nullptr; + QString _service; + QString _objectPath; + QString _interface; + uint _signalId = 0; + bool _grabbed = false; +}; + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 503c84f28..beafa089c 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -23,8 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "window/window_controller.h" #include "window/window_session_controller.h" +#include "media/player/media_player_instance.h" +#include "media/audio/media_audio.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_gsd_media_keys.h" +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "base/call_delayed.h" #include "ui/widgets/input_fields.h" #include "facades.h" @@ -572,6 +577,17 @@ void MainWindow::initHook() { } else { LOG(("Not using Unity launcher counter.")); } + + Media::Player::instance()->updatedNotifier( + ) | rpl::start_with_next([=](const Media::Player::TrackState &state) { + if (!Media::Player::IsStoppedOrStopping(state.state)) { + if (!_gsdMediaKeys) { + _gsdMediaKeys = std::make_unique(); + } + } else if (_gsdMediaKeys) { + _gsdMediaKeys = nullptr; + } + }, lifetime()); #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION updateWaylandDecorationColors(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 73934b4dc..1e4c6eb0f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -24,6 +24,11 @@ typedef struct _GDBusProxy GDBusProxy; #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION namespace Platform { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +namespace internal { +class GSDMediaKeys; +} // namespace internal +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION class MainWindow : public Window::MainWindow { public: @@ -88,6 +93,8 @@ private: DBusMenuExporter *_mainMenuExporter = nullptr; QDBusObjectPath _mainMenuPath; + std::unique_ptr _gsdMediaKeys; + QMenu *psMainMenu = nullptr; QAction *psLogout = nullptr; QAction *psUndo = nullptr; From e594b75f4c3856880c9dd1bcee499da4b294992a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 27 Dec 2020 21:59:57 +0400 Subject: [PATCH 010/396] Use more forward declarations in main_window_linux --- .../platform/linux/main_window_linux.cpp | 5 ++++- .../SourceFiles/platform/linux/main_window_linux.h | 13 ++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index beafa089c..91257d1b5 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gsd_media_keys.h" #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "base/call_delayed.h" +#include "ui/widgets/popup_menu.h" #include "ui/widgets/input_fields.h" #include "facades.h" #include "app.h" @@ -39,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include #include #include #include @@ -48,7 +50,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include -#include +#include +#include extern "C" { #undef signals diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 1e4c6eb0f..863542820 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -9,13 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_main_window.h" -#include "ui/widgets/popup_menu.h" +namespace Ui { +class PopupMenu; +} // namespace Ui #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include "statusnotifieritem.h" -#include #include -#include + +class QTemporaryFile; +class DBusMenuExporter; +class StatusNotifierItem; typedef void* gpointer; typedef char gchar; @@ -87,7 +90,7 @@ private: #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION StatusNotifierItem *_sniTrayIcon = nullptr; GDBusProxy *_sniDBusProxy = nullptr; - std::unique_ptr _trayIconFile = nullptr; + std::unique_ptr _trayIconFile; bool _appMenuSupported = false; DBusMenuExporter *_mainMenuExporter = nullptr; From e247be7e33485d171c2a25c34a3c377993d90503 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 27 Dec 2020 22:09:51 +0400 Subject: [PATCH 011/396] Operate with QString instead of QDBusObjectPath --- .../platform/linux/main_window_linux.cpp | 13 +++++++------ .../SourceFiles/platform/linux/main_window_linux.h | 4 +--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 91257d1b5..145847c50 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include #include #include @@ -451,7 +452,7 @@ bool IsAppMenuSupported() { return interface->isServiceRegistered(kAppMenuService.utf16()); } -void RegisterAppMenu(uint winId, const QDBusObjectPath &menuPath) { +void RegisterAppMenu(uint winId, const QString &menuPath) { auto message = QDBusMessage::createMethodCall( kAppMenuService.utf16(), kAppMenuObjectPath.utf16(), @@ -460,7 +461,7 @@ void RegisterAppMenu(uint winId, const QDBusObjectPath &menuPath) { message.setArguments({ winId, - QVariant::fromValue(menuPath) + QVariant::fromValue(QDBusObjectPath(menuPath)) }); QDBusConnection::sessionBus().send(message); @@ -759,7 +760,7 @@ void MainWindow::handleAppMenuOwnerChanged( LOG(("Not using D-Bus global menu.")); } - if (_appMenuSupported && !_mainMenuPath.path().isEmpty()) { + if (_appMenuSupported && !_mainMenuPath.isEmpty()) { RegisterAppMenu(winId(), _mainMenuPath); } else { UnregisterAppMenu(winId()); @@ -1075,10 +1076,10 @@ void MainWindow::createGlobalMenu() { about->setMenuRole(QAction::AboutQtRole); - _mainMenuPath.setPath(qsl("/MenuBar")); + _mainMenuPath = qsl("/MenuBar"); _mainMenuExporter = new DBusMenuExporter( - _mainMenuPath.path(), + _mainMenuPath, psMainMenu); if (_appMenuSupported) { @@ -1214,7 +1215,7 @@ void MainWindow::handleVisibleChangedHook(bool visible) { } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - if (_appMenuSupported && !_mainMenuPath.path().isEmpty()) { + if (_appMenuSupported && !_mainMenuPath.isEmpty()) { if (visible) { RegisterAppMenu(winId(), _mainMenuPath); } else { diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 863542820..3132b7acb 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -14,8 +14,6 @@ class PopupMenu; } // namespace Ui #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include - class QTemporaryFile; class DBusMenuExporter; class StatusNotifierItem; @@ -94,7 +92,7 @@ private: bool _appMenuSupported = false; DBusMenuExporter *_mainMenuExporter = nullptr; - QDBusObjectPath _mainMenuPath; + QString _mainMenuPath; std::unique_ptr _gsdMediaKeys; From 8142e8339593294a14bf85007a25c4a253d4ba1a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 27 Dec 2020 22:45:30 +0400 Subject: [PATCH 012/396] Fix connection to QSystemTrayIcon::messageClicked in main_window_win --- Telegram/SourceFiles/platform/win/main_window_win.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 63c43fe43..61ce7f500 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -217,7 +217,11 @@ void MainWindow::psSetupTrayIcon() { auto icon = QIcon(App::pixmapFromImageInPlace(Core::App().logoNoMargin())); trayIcon->setIcon(icon); - connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); + connect( + trayIcon, + &QSystemTrayIcon::messageClicked, + this, + [=] { App::wnd()->showFromTray(); }); attachToTrayIcon(trayIcon); } updateIconCounters(); From 28f29b51c0fbf409e127db93db01789efccd3381 Mon Sep 17 00:00:00 2001 From: Stepan Skryabin <20664125+zurg3@users.noreply.github.com> Date: Sun, 27 Dec 2020 19:52:28 +0300 Subject: [PATCH 013/396] Update supported operating systems --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5bb2235fc..4af5dcaae 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,17 @@ The latest version is available for * [Windows 7 and above](https://telegram.org/dl/desktop/win) ([portable](https://telegram.org/dl/desktop/win_portable)) * [macOS 10.12 and above](https://telegram.org/dl/desktop/mac) -* [OS X 10.10 and 10.11](https://telegram.org/dl/desktop/osx) -* [Linux static build for 64 bit](https://telegram.org/dl/desktop/linux) ([32 bit](https://telegram.org/dl/desktop/linux32)) +* [Linux static build for 64 bit](https://telegram.org/dl/desktop/linux) * [Snap](https://snapcraft.io/telegram-desktop) * [Flatpak](https://flathub.org/apps/details/org.telegram.desktop) ## Old system versions +Version **2.4.4** was the last that supports older systems + +* [OS X 10.10 and 10.11](https://updates.tdesktop.com/tosx/tsetup-osx.2.4.4.dmg) +* [Linux static build for 32 bit](https://updates.tdesktop.com/tlinux32/tsetup32.2.4.4.tar.xz) + Version **1.8.15** was the last that supports older systems * [Windows XP and Vista](https://updates.tdesktop.com/tsetup/tsetup.1.8.15.exe) ([portable](https://updates.tdesktop.com/tsetup/tportable.1.8.15.zip)) From 0c8033414ef5b331b2cf09bafd3639b9f9686358 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 28 Dec 2020 17:06:08 +0400 Subject: [PATCH 014/396] Allow using mouse buttons in global shortcuts. --- Telegram/lib_base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index 81df0d0b7..b0b13dc60 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 81df0d0b7842be2b6c88f93dfa136b8efea4c9ad +Subproject commit b0b13dc6098f62fcf58087e2a0987de3af55ae3f From d648d294ca1d761dcdea19521b388a1cab2bcf2b Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 28 Dec 2020 18:29:09 +0400 Subject: [PATCH 015/396] Fix layout in intro Settings. --- Telegram/SourceFiles/settings/settings_intro.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 7f7424433..9e2695bcc 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -73,10 +73,18 @@ object_ptr CreateIntroSettings( SetupUpdate(result); AddSkip(result); } - AddDivider(result); - AddSkip(result); - SetupSystemIntegrationContent(result); - AddSkip(result); + { + auto wrap = object_ptr(result); + SetupSystemIntegrationContent(wrap.data()); + if (wrap->count() > 0) { + AddDivider(result); + AddSkip(result); + result->add(object_ptr( + result, + std::move(wrap))); + AddSkip(result); + } + } AddDivider(result); AddSkip(result); SetupInterfaceScale(result, false); From f66cfb5684932a5da51a40f3c586dec315c8a1ea Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 28 Dec 2020 05:58:42 +0400 Subject: [PATCH 016/396] Use new IsSupportedByWM XCB API from lib_base --- .../platform/linux/specific_linux.cpp | 48 ++----------------- Telegram/lib_base | 2 +- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index e4146ab14..5ffe8a1e0 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -451,25 +451,6 @@ bool ShowXCBWindowMenu(QWindow *window) { return true; } -bool XCBFrameExtentsSupported() { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto frameExtentsAtom = base::Platform::XCB::GetAtom( - connection, - kXCBFrameExtentsAtomName.utf16()); - - if (!frameExtentsAtom.has_value()) { - return false; - } - - return ranges::contains( - base::Platform::XCB::GetWMSupported(connection), - *frameExtentsAtom); -} - bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) { const auto connection = base::Platform::XCB::GetConnectionFromQt(); if (!connection) { @@ -526,25 +507,6 @@ bool UnsetXCBFrameExtents(QWindow *window) { return true; } -bool XCBSkipTaskbarSupported() { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto skipTaskbarAtom = base::Platform::XCB::GetAtom( - connection, - "_NET_WM_STATE_SKIP_TASKBAR"); - - if (!skipTaskbarAtom.has_value()) { - return false; - } - - return ranges::contains( - base::Platform::XCB::GetWMSupported(connection), - *skipTaskbarAtom); -} - Window::Control GtkKeywordToWindowControl(const QString &keyword) { if (keyword == qstr("minimize")) { return Window::Control::Minimize; @@ -804,7 +766,8 @@ bool TrayIconSupported() { } bool SkipTaskbarSupported() { - return !IsWayland() && XCBSkipTaskbarSupported(); + return !IsWayland() + && base::Platform::XCB::IsSupportedByWM("_NET_WM_STATE_SKIP_TASKBAR"); } bool StartSystemMove(QWindow *window) { @@ -848,11 +811,8 @@ bool UnsetWindowExtents(QWindow *window) { } bool WindowsNeedShadow() { - if (!IsWayland() && XCBFrameExtentsSupported()) { - return true; - } - - return false; + return !IsWayland() + && base::Platform::XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16()); } Window::ControlsLayout WindowControlsLayout() { diff --git a/Telegram/lib_base b/Telegram/lib_base index b0b13dc60..dc89f34be 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit b0b13dc6098f62fcf58087e2a0987de3af55ae3f +Subproject commit dc89f34be5387bef731c59aeca3ca201e042ecc3 From 8da33113a298399746821cd34215489436e532e3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 29 Dec 2020 11:17:43 +0400 Subject: [PATCH 017/396] Use DeviceModelPretty/SystemVersionPretty directly This allows using methods that require a running QGuiApplication instance to detect system --- Telegram/SourceFiles/core/launcher.cpp | 16 ++-------------- Telegram/SourceFiles/core/launcher.h | 9 +-------- Telegram/SourceFiles/main/main_account.cpp | 10 +++++----- .../platform/linux/launcher_linux.cpp | 3 +-- .../SourceFiles/platform/mac/launcher_mac.mm | 3 +-- .../SourceFiles/platform/win/launcher_win.cpp | 3 +-- 6 files changed, 11 insertions(+), 33 deletions(-) diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index aa50e19a2..4f9eb8ed8 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -270,14 +270,10 @@ std::unique_ptr Launcher::Create(int argc, char *argv[]) { Launcher::Launcher( int argc, - char *argv[], - const QString &deviceModel, - const QString &systemVersion) + char *argv[]) : _argc(argc) , _argv(argv) -, _baseIntegration(_argc, _argv) -, _deviceModel(deviceModel) -, _systemVersion(systemVersion) { +, _baseIntegration(_argc, _argv) { base::Integration::Set(&_baseIntegration); } @@ -419,14 +415,6 @@ void Launcher::prepareSettings() { processArguments(); } -QString Launcher::deviceModel() const { - return _deviceModel; -} - -QString Launcher::systemVersion() const { - return _systemVersion; -} - uint64 Launcher::installationTag() const { return InstallationTag; } diff --git a/Telegram/SourceFiles/core/launcher.h b/Telegram/SourceFiles/core/launcher.h index 05648bdb1..8847fec81 100644 --- a/Telegram/SourceFiles/core/launcher.h +++ b/Telegram/SourceFiles/core/launcher.h @@ -15,9 +15,7 @@ class Launcher { public: Launcher( int argc, - char *argv[], - const QString &deviceModel, - const QString &systemVersion); + char *argv[]); static std::unique_ptr Create(int argc, char *argv[]); @@ -26,8 +24,6 @@ public: QString argumentsString() const; bool customWorkingDir() const; - QString deviceModel() const; - QString systemVersion() const; uint64 installationTag() const; bool checkPortableVersionFolder(); @@ -67,9 +63,6 @@ private: QStringList _arguments; BaseIntegration _baseIntegration; - const QString _deviceModel; - const QString _systemVersion; - bool _customWorkingDir = false; }; diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index a4b2eb8fb..3fe0abb4e 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "main/main_account.h" +#include "base/platform/base_platform_info.h" #include "core/application.h" -#include "core/launcher.h" #include "core/shortcuts.h" #include "storage/storage_account.h" #include "storage/storage_domain.h" // Storage::StartResult. @@ -386,8 +386,8 @@ void Account::startMtp(std::unique_ptr config) { auto fields = base::take(_mtpFields); fields.config = std::move(config); - fields.deviceModel = Core::App().launcher()->deviceModel(); - fields.systemVersion = Core::App().launcher()->systemVersion(); + fields.deviceModel = Platform::DeviceModelPretty(); + fields.systemVersion = Platform::SystemVersionPretty(); _mtp = std::make_unique( MTP::Instance::Mode::Normal, std::move(fields)); @@ -534,8 +534,8 @@ void Account::destroyMtpKeys(MTP::AuthKeysList &&keys) { destroyFields.mainDcId = MTP::Instance::Fields::kNoneMainDc; destroyFields.config = std::make_unique(_mtp->config()); destroyFields.keys = std::move(keys); - destroyFields.deviceModel = Core::App().launcher()->deviceModel(); - destroyFields.systemVersion = Core::App().launcher()->systemVersion(); + destroyFields.deviceModel = Platform::DeviceModelPretty(); + destroyFields.systemVersion = Platform::SystemVersionPretty(); _mtpForKeysDestroy = std::make_unique( MTP::Instance::Mode::KeysDestroyer, std::move(destroyFields)); diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp index f35c13be2..18840ea96 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/launcher_linux.h" -#include "base/platform/base_platform_info.h" #include "platform/linux/specific_linux.h" #include "core/crash_reports.h" #include "core/update_checker.h" @@ -46,7 +45,7 @@ private: } // namespace Launcher::Launcher(int argc, char *argv[]) -: Core::Launcher(argc, argv, DeviceModelPretty(), SystemVersionPretty()) { +: Core::Launcher(argc, argv) { } void Launcher::initHook() { diff --git a/Telegram/SourceFiles/platform/mac/launcher_mac.mm b/Telegram/SourceFiles/platform/mac/launcher_mac.mm index 94fa4d5e5..e68bb0b2c 100644 --- a/Telegram/SourceFiles/platform/mac/launcher_mac.mm +++ b/Telegram/SourceFiles/platform/mac/launcher_mac.mm @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" -#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_file_utilities.h" #include "base/platform/mac/base_utilities_mac.h" @@ -20,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { Launcher::Launcher(int argc, char *argv[]) -: Core::Launcher(argc, argv, DeviceModelPretty(), SystemVersionPretty()) { +: Core::Launcher(argc, argv) { } void Launcher::initHook() { diff --git a/Telegram/SourceFiles/platform/win/launcher_win.cpp b/Telegram/SourceFiles/platform/win/launcher_win.cpp index 44821f1ea..199c06a42 100644 --- a/Telegram/SourceFiles/platform/win/launcher_win.cpp +++ b/Telegram/SourceFiles/platform/win/launcher_win.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "core/update_checker.h" -#include "base/platform/base_platform_info.h" #include "base/platform/win/base_windows_h.h" #include @@ -18,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { Launcher::Launcher(int argc, char *argv[]) -: Core::Launcher(argc, argv, DeviceModelPretty(), SystemVersionPretty()) { +: Core::Launcher(argc, argv) { } std::optional Launcher::readArgumentsHook( From 3940d57c3da62125c74f9bfec16d5bdbcbce0939 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 26 Dec 2020 09:29:40 +0300 Subject: [PATCH 018/396] Disabled message editing while voice recording. --- Telegram/SourceFiles/history/history_widget.cpp | 2 +- .../history/view/controls/history_view_compose_controls.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 70fa132e7..b67e0fc31 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5652,7 +5652,7 @@ void HistoryWidget::editMessage(FullMsgId itemId) { } void HistoryWidget::editMessage(not_null item) { - if (_voiceRecordBar->isListenState()) { + if (_voiceRecordBar->isActive()) { Ui::show(Box(tr::lng_edit_caption_voice(tr::now))); return; } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index b836c68c8..54f972d45 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1814,7 +1814,7 @@ void ComposeControls::editMessage(not_null item) { Expects(_history != nullptr); Expects(draftKeyCurrent() != Data::DraftKey::None()); - if (_voiceRecordBar && _voiceRecordBar->isListenState()) { + if (_voiceRecordBar->isActive()) { Ui::show(Box(tr::lng_edit_caption_voice(tr::now))); return; } From f24b0c623775e0a078effcd49a7aa1c20c85522f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 26 Dec 2020 09:46:26 +0300 Subject: [PATCH 019/396] Fixed hiding cancel button in state of listen to recorded voice data. --- .../view/controls/history_view_voice_record_bar.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp index d94d8cc92..13c8eeb1e 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp @@ -924,12 +924,9 @@ CancelButton::CancelButton(not_null parent, int height) void CancelButton::init() { _showProgress.value( - ) | rpl::start_with_next([=](float64 progress) { - const auto hasProgress = (progress > 0.); - if (isHidden() == !hasProgress) { - setVisible(hasProgress); - } - update(); + ) | rpl::map(rpl::mappers::_1 > 0.) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](bool hasProgress) { + setVisible(hasProgress); }, lifetime()); paintRequest( @@ -960,6 +957,7 @@ QPoint CancelButton::prepareRippleStartPosition() const { void CancelButton::requestPaintProgress(float64 progress) { _showProgress = progress; + update(); } VoiceRecordBar::VoiceRecordBar( From fe85a8256a77477d89d15cd803fc3075445a564f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 28 Dec 2020 20:47:37 +0300 Subject: [PATCH 020/396] Added Github Action that updates copyright year. --- .github/workflows/copyright_year_updater.yml | 16 +++ .github/workflows/user_agent_updater.yml | 144 +------------------ 2 files changed, 19 insertions(+), 141 deletions(-) create mode 100644 .github/workflows/copyright_year_updater.yml diff --git a/.github/workflows/copyright_year_updater.yml b/.github/workflows/copyright_year_updater.yml new file mode 100644 index 000000000..7cb129e36 --- /dev/null +++ b/.github/workflows/copyright_year_updater.yml @@ -0,0 +1,16 @@ +name: Copyright year updater. + +on: + repository_dispatch: + types: ["Restart copyright_year_updater workflow."] + schedule: + # At 03:00 on January 1. + - cron: "0 3 1 1 *" + +jobs: + Copyright-year: + runs-on: ubuntu-latest + steps: + - uses: desktop-app/action_code_updater@master + with: + type: "license-year" diff --git a/.github/workflows/user_agent_updater.yml b/.github/workflows/user_agent_updater.yml index a19b3271f..c1713b357 100644 --- a/.github/workflows/user_agent_updater.yml +++ b/.github/workflows/user_agent_updater.yml @@ -5,152 +5,14 @@ on: types: ["Restart user_agent_updater workflow."] schedule: # At 00:00 on day-of-month 1. - - cron: '0 0 1 * *' + - cron: "0 0 1 * *" pull_request_target: types: [closed] jobs: User-agent: runs-on: ubuntu-latest - - env: - codeFile: "Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp" - headBranchPrefix: "chrome_" - baseBranch: "dev" - isPull: "0" - steps: - - name: Set env. - if: startsWith(github.event_name, 'pull_request') - run: | - echo "isPull=1" >> $GITHUB_ENV - - - name: Clone. - uses: actions/checkout@v2 - - - name: Set up git. - run: | - token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }} - if [ -z "${token}" ]; then - echo "Token is unset. Nothing to do." - exit 1 - fi - - url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY - - git config --global user.email "action@github.com" - git config --global user.name "GitHub Action" - - git remote set-url origin $url - - - name: Delete branch. - env: - ref: ${{ github.event.pull_request.head.ref }} - if: | - env.isPull == '1' - && github.event.action == 'closed' - && startsWith(env.ref, env.headBranchPrefix) - run: | - git push origin --delete $ref - - - name: Write a new version of Google Chrome to the user-agent for DNS. - if: env.isPull == '0' - shell: python - run: | - import subprocess, os, re; - - regExpVersion = "[0-9]+.[0-9]+.[0-9]+.[0-9]+"; - chrome = "Chrome/"; - - def newVersion(): - output = subprocess.check_output(["google-chrome", "--version"]); - version = re.search(regExpVersion, output); - if not version: - print("Can't find a Chrome version."); - exit(); - return version.group(0); - - newChromeVersion = newVersion(); - print(newChromeVersion); - - def setEnv(value): - open(os.environ['GITHUB_ENV'], "a").write(value); - - def writeUserAgent(): - p = os.environ['codeFile']; - w = open(p, "r"); - content = w.read(); - w.close(); - - regExpChrome = chrome + regExpVersion; - - version = re.search(regExpChrome, content); - if not version: - print("Can't find an user-agent in the code."); - exit(); - content = re.sub(regExpChrome, chrome + newChromeVersion, content); - - w = open(p, "w"); - w.write(content); - - setEnv("ChromeVersion=" + newChromeVersion); - - writeUserAgent(); - - - name: Push to a new branch. - if: env.isPull == '0' && env.ChromeVersion != '' - run: | - git diff > git_diff.txt - if [[ ! -s git_diff.txt ]]; then - echo "Nothing to commit." - exit 0 - fi - - git checkout -b $headBranchPrefix$ChromeVersion - git add $codeFile - git commit -m "Update User-Agent for DNS to Chrome $ChromeVersion." - - git push origin $headBranchPrefix$ChromeVersion - echo "Done!" - - - name: Close previous pull requests. - if: env.isPull == '0' && env.ChromeVersion != '' - uses: actions/github-script@0.4.0 + - uses: desktop-app/action_code_updater@master with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const common = { - owner: context.repo.owner, - repo: context.repo.repo, - }; - - github.pulls.list(common).then(response => { - response.data.forEach((item, _) => { - if (item.head.ref.startsWith(process.env.headBranchPrefix)) { - console.log(`Close ${item.title} #${item.number}.`); - github.pulls.update({ - pull_number: item.number, - state: "closed", - ...common - }); - } - }); - }); - - - name: Create a new pull request. - if: env.isPull == '0' && env.ChromeVersion != '' - uses: actions/github-script@0.4.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const version = process.env.ChromeVersion; - const title = `Update User-Agent for DNS to Chrome ${version}.`; - - github.pulls.create({ - title: title, - body: "", - head: `${process.env.headBranchPrefix}${version}`, - base: process.env.baseBranch, - owner: context.repo.owner, - repo: context.repo.repo, - }); + type: "user-agent" From e864aa2ff2d14c56c277dbd3af81006c394c80fe Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Dec 2020 16:39:38 +0400 Subject: [PATCH 021/396] Create audio device module uniformely. --- Telegram/ThirdParty/tgcalls | 2 +- Telegram/cmake/lib_tgcalls.cmake | 2 ++ Telegram/lib_webrtc | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index cba2d0daa..6e8b3f7e5 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit cba2d0daa7bf585069c55773846c4cb10af03920 +Subproject commit 6e8b3f7e5f14321cb9e77f18c0a82089bf7f31c6 diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index 6e837df05..cde4f6144 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -28,6 +28,8 @@ PRIVATE if (NOT DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION) nice_target_sources(lib_tgcalls ${tgcalls_loc} PRIVATE + AudioDeviceHelper.cpp + AudioDeviceHelper.h CodecSelectHelper.cpp CodecSelectHelper.h CryptoHelper.cpp diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index 4bc51d6f6..d9e8307af 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit 4bc51d6f6d5740159fdb51cb1593e80ce149ed4e +Subproject commit d9e8307af6aa30e9b5e1e5376035641cfd063b7b From 19a5dcbffcab99c791780f070171736a723f90d1 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 29 Dec 2020 15:25:44 +0400 Subject: [PATCH 022/396] Make OpenAL debugging easier --- Telegram/SourceFiles/core/launcher.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 4f9eb8ed8..3ab874538 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -324,6 +324,23 @@ int Launcher::exec() { // Must be started before Platform is started. Logs::start(this); + if (Logs::DebugEnabled()) { + const auto openalLogPath = QDir::toNativeSeparators( + cWorkingDir() + qsl("DebugLogs/last_openal_log.txt")); + + qputenv("ALSOFT_LOGLEVEL", "3"); + +#ifdef Q_OS_WIN + _wputenv_s( + L"ALSOFT_LOGFILE", + openalLogPath.toStdWString().c_str()); +#else // Q_OS_WIN + qputenv( + "ALSOFT_LOGFILE", + QFile::encodeName(openalLogPath)); +#endif // !Q_OS_WIN + } + // Must be started before Sandbox is created. Platform::start(); Ui::DisableCustomScaling(); From 39f91477903a22094a81b9cdcad2af96d5d14347 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 29 Dec 2020 16:13:53 +0400 Subject: [PATCH 023/396] Check for dbus menu exporter instead of menu path --- .../platform/linux/main_window_linux.cpp | 17 +++++++++-------- .../platform/linux/main_window_linux.h | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 145847c50..00a48742d 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -67,6 +67,7 @@ namespace { constexpr auto kPanelTrayIconName = "telegram-panel"_cs; constexpr auto kMutePanelTrayIconName = "telegram-mute-panel"_cs; constexpr auto kAttentionPanelTrayIconName = "telegram-attention-panel"_cs; + constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; constexpr auto kTrayIconFilename = "tdesktop-trayicon-XXXXXX.png"_cs; @@ -78,6 +79,8 @@ constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs; constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs; constexpr auto kAppMenuInterface = kAppMenuService; +constexpr auto kMainMenuObjectPath = "/MenuBar"_cs; + bool TrayIconMuted = true; int32 TrayIconCount = 0; base::flat_map TrayIconImageBack; @@ -760,8 +763,8 @@ void MainWindow::handleAppMenuOwnerChanged( LOG(("Not using D-Bus global menu.")); } - if (_appMenuSupported && !_mainMenuPath.isEmpty()) { - RegisterAppMenu(winId(), _mainMenuPath); + if (_appMenuSupported && _mainMenuExporter) { + RegisterAppMenu(winId(), kMainMenuObjectPath.utf16()); } else { UnregisterAppMenu(winId()); } @@ -1076,14 +1079,12 @@ void MainWindow::createGlobalMenu() { about->setMenuRole(QAction::AboutQtRole); - _mainMenuPath = qsl("/MenuBar"); - _mainMenuExporter = new DBusMenuExporter( - _mainMenuPath, + kMainMenuObjectPath.utf16(), psMainMenu); if (_appMenuSupported) { - RegisterAppMenu(winId(), _mainMenuPath); + RegisterAppMenu(winId(), kMainMenuObjectPath.utf16()); } updateGlobalMenu(); @@ -1215,9 +1216,9 @@ void MainWindow::handleVisibleChangedHook(bool visible) { } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - if (_appMenuSupported && !_mainMenuPath.isEmpty()) { + if (_appMenuSupported && _mainMenuExporter) { if (visible) { - RegisterAppMenu(winId(), _mainMenuPath); + RegisterAppMenu(winId(), kMainMenuObjectPath.utf16()); } else { UnregisterAppMenu(winId()); } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 3132b7acb..5e06aadbe 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -92,7 +92,6 @@ private: bool _appMenuSupported = false; DBusMenuExporter *_mainMenuExporter = nullptr; - QString _mainMenuPath; std::unique_ptr _gsdMediaKeys; From 3a3488148853862c1ca74bbd42c41350af1a1254 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 12:55:11 +0400 Subject: [PATCH 024/396] Highlight album part that had a reply clicked. --- .../admin_log/history_admin_log_inner.cpp | 2 +- .../admin_log/history_admin_log_inner.h | 2 +- .../history/history_inner_widget.cpp | 10 +-- .../history/history_inner_widget.h | 2 +- .../SourceFiles/history/history_widget.cpp | 12 ++-- .../history/view/history_view_element.cpp | 37 +++++++---- .../history/view/history_view_element.h | 11 +++- .../history/view/history_view_list_widget.cpp | 22 +++++-- .../history/view/history_view_list_widget.h | 3 +- .../history/view/history_view_message.cpp | 63 ++++++++++--------- .../view/media/history_view_document.cpp | 1 + .../view/media/history_view_document.h | 1 + .../history/view/media/history_view_gif.cpp | 11 +++- .../history/view/media/history_view_gif.h | 1 + .../history/view/media/history_view_media.h | 6 ++ .../view/media/history_view_media_grouped.cpp | 16 +++++ .../view/media/history_view_media_grouped.h | 4 ++ .../history/view/media/history_view_photo.cpp | 12 +++- .../history/view/media/history_view_photo.h | 1 + 19 files changed, 154 insertions(+), 63 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index bf383ddec..923b8d316 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -556,7 +556,7 @@ bool InnerWidget::elementUnderCursor( } crl::time InnerWidget::elementHighlightTime( - not_null element) { + not_null item) { return crl::time(0); } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 32a6fcf42..ca8e50066 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -97,7 +97,7 @@ public: bool elementUnderCursor( not_null view) override; crl::time elementHighlightTime( - not_null element) override; + not_null item) override; bool elementInSelectionMode() override; bool elementIntersectsRange( not_null view, diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index cc24aca93..7af0eaabf 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2520,9 +2520,9 @@ void HistoryInner::elementStartStickerLoop( _animatedStickersPlayed.emplace(view->data()); } -crl::time HistoryInner::elementHighlightTime(not_null view) { - const auto fullAnimMs = _controller->content()->highlightStartTime( - view->data()); +crl::time HistoryInner::elementHighlightTime( + not_null item) { + const auto fullAnimMs = _controller->content()->highlightStartTime(item); if (fullAnimMs > 0) { const auto now = crl::now(); if (fullAnimMs < now) { @@ -3421,8 +3421,8 @@ not_null HistoryInner::ElementDelegate() { return (App::hoveredItem() == view); } crl::time elementHighlightTime( - not_null view) override { - return Instance ? Instance->elementHighlightTime(view) : 0; + not_null item) override { + return Instance ? Instance->elementHighlightTime(item) : 0; } bool elementInSelectionMode() override { return Instance ? Instance->inSelectionMode() : false; diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 8e5f5d6f3..09b22bf1e 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -84,7 +84,7 @@ public: int till) const; void elementStartStickerLoop(not_null view); [[nodiscard]] crl::time elementHighlightTime( - not_null view); + not_null item); void elementShowPollResults( not_null poll, FullMsgId context); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index b67e0fc31..7208241d8 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1045,11 +1045,6 @@ void HistoryWidget::scrollToAnimationCallback( void HistoryWidget::enqueueMessageHighlight( not_null view) { - if (const auto group = session().data().groups().find(view->data())) { - if (const auto leader = group->items.front()->mainView()) { - view = leader; - } - } auto enqueueMessageId = [this](MsgId universalId) { if (_highlightQueue.empty() && !_highlightTimer.isActive()) { highlightMessage(universalId); @@ -1096,7 +1091,7 @@ void HistoryWidget::checkNextHighlight() { void HistoryWidget::updateHighlightedMessage() { const auto item = getItemFromHistoryOrMigrated(_highlightedMessageId); - const auto view = item ? item->mainView() : nullptr; + auto view = item ? item->mainView() : nullptr; if (!view) { return stopMessageHighlight(); } @@ -1105,6 +1100,11 @@ void HistoryWidget::updateHighlightedMessage() { return stopMessageHighlight(); } + if (const auto group = session().data().groups().find(view->data())) { + if (const auto leader = group->items.front()->mainView()) { + view = leader; + } + } session().data().requestViewRepaint(view); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 97d128ba0..eb533b848 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -79,7 +79,7 @@ bool SimpleElementDelegate::elementUnderCursor( } crl::time SimpleElementDelegate::elementHighlightTime( - not_null element) { + not_null item) { return crl::time(0); } @@ -280,29 +280,44 @@ void Element::refreshDataIdHook() { void Element::paintHighlight( Painter &p, int geometryHeight) const { - const auto animms = delegate()->elementHighlightTime(this); - if (!animms - || animms >= st::activeFadeInDuration + st::activeFadeOutDuration) { - return; - } - const auto top = marginTop(); const auto bottom = marginBottom(); const auto fill = qMin(top, bottom); const auto skiptop = top - fill; const auto fillheight = fill + geometryHeight + fill; - const auto dt = (animms > st::activeFadeInDuration) + paintCustomHighlight(p, skiptop, fillheight, data()); +} + +float64 Element::highlightOpacity(not_null item) const { + const auto animms = delegate()->elementHighlightTime(item); + if (!animms + || animms >= st::activeFadeInDuration + st::activeFadeOutDuration) { + return 0.; + } + + return (animms > st::activeFadeInDuration) ? (1. - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration)); +} + +void Element::paintCustomHighlight( + Painter &p, + int y, + int height, + not_null item) const { + const auto opacity = highlightOpacity(item); + if (opacity == 0.) { + return; + } const auto o = p.opacity(); - p.setOpacity(o * dt); + p.setOpacity(o * opacity); p.fillRect( 0, - skiptop, + y, width(), - fillheight, + height, st::defaultTextPalette.selectOverlay); p.setOpacity(o); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index fe64bdf3e..82c610eee 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -51,7 +51,7 @@ public: Element *replacing = nullptr) = 0; virtual bool elementUnderCursor(not_null view) = 0; virtual crl::time elementHighlightTime( - not_null element) = 0; + not_null item) = 0; virtual bool elementInSelectionMode() = 0; virtual bool elementIntersectsRange( not_null view, @@ -87,7 +87,7 @@ public: Element *replacing = nullptr) override; bool elementUnderCursor(not_null view) override; crl::time elementHighlightTime( - not_null element) override; + not_null item) override; bool elementInSelectionMode() override; bool elementIntersectsRange( not_null view, @@ -301,6 +301,13 @@ public: virtual void unloadHeavyPart(); void checkHeavyPart(); + void paintCustomHighlight( + Painter &p, + int y, + int height, + not_null item) const; + float64 highlightOpacity(not_null item) const; + // Legacy blocks structure. HistoryBlock *block(); const HistoryBlock *block() const; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 15f02c0af..dc07f3a48 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -482,7 +482,7 @@ void ListWidget::highlightMessage(FullMsgId itemId) { _highlightedMessageId = itemId; _highlightTimer.callEach(AnimationTimerDelta); - repaintItem(view); + repaintHighlightedItem(view); } } } @@ -496,10 +496,24 @@ void ListWidget::showAroundPosition( refreshViewer(); } +void ListWidget::repaintHighlightedItem(not_null view) { + if (view->isHiddenByGroup()) { + if (const auto group = session().data().groups().find(view->data())) { + if (const auto leader = viewForItem(group->items.front())) { + if (!leader->isHiddenByGroup()) { + repaintItem(leader); + return; + } + } + } + } + repaintItem(view); +} + void ListWidget::updateHighlightedMessage() { if (const auto item = session().data().message(_highlightedMessageId)) { if (const auto view = viewForItem(item)) { - repaintItem(view); + repaintHighlightedItem(view); auto duration = st::activeFadeInDuration + st::activeFadeOutDuration; if (crl::now() - _highlightStart <= duration) { return; @@ -1244,8 +1258,8 @@ bool ListWidget::elementUnderCursor( } crl::time ListWidget::elementHighlightTime( - not_null element) { - if (element->data()->fullId() == _highlightedMessageId) { + not_null item) { + if (item->fullId() == _highlightedMessageId) { if (_highlightTimer.isActive()) { return crl::now() - _highlightStart; } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index dff78abba..90b65f9af 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -221,7 +221,7 @@ public: Element *replacing = nullptr) override; bool elementUnderCursor(not_null view) override; crl::time elementHighlightTime( - not_null element) override; + not_null item) override; bool elementInSelectionMode() override; bool elementIntersectsRange( not_null view, @@ -340,6 +340,7 @@ private: int itemTop(not_null view) const; void repaintItem(FullMsgId itemId); void repaintItem(const Element *view); + void repaintHighlightedItem(not_null view); void resizeItem(not_null view); void refreshItem(not_null view); void itemRemoved(not_null item); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 9e81e2405..d5dac0735 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -570,7 +570,40 @@ void Message::draw( return; } - paintHighlight(p, g.height()); + auto entry = logEntryOriginal(); + auto mediaDisplayed = media && media->isDisplayed(); + + // Entry page is always a bubble bottom. + auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); + auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); + + auto mediaSelectionIntervals = (!selected && mediaDisplayed) + ? media->getBubbleSelectionIntervals(selection) + : std::vector(); + auto localMediaTop = 0; + const auto customHighlight = mediaDisplayed && media->customHighlight(); + if (!mediaSelectionIntervals.empty() || customHighlight) { + auto localMediaBottom = g.top() + g.height(); + if (data()->repliesAreComments() || data()->externalReply()) { + localMediaBottom -= st::historyCommentsButtonHeight; + } + if (!mediaOnBottom) { + localMediaBottom -= st::msgPadding.bottom(); + } + if (entry) { + localMediaBottom -= entry->height(); + } + localMediaTop = localMediaBottom - media->height(); + for (auto &[top, height] : mediaSelectionIntervals) { + top += localMediaTop; + } + } + + if (customHighlight) { + media->drawHighlight(p, localMediaTop); + } else { + paintHighlight(p, g.height()); + } const auto roll = media ? media->bubbleRoll() : Media::BubbleRoll(); if (roll) { @@ -602,34 +635,6 @@ void Message::draw( fromNameUpdated(g.width()); } - auto entry = logEntryOriginal(); - auto mediaDisplayed = media && media->isDisplayed(); - - // Entry page is always a bubble bottom. - auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); - auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); - - - auto mediaSelectionIntervals = (!selected && mediaDisplayed) - ? media->getBubbleSelectionIntervals(selection) - : std::vector(); - if (!mediaSelectionIntervals.empty()) { - auto localMediaBottom = g.top() + g.height(); - if (data()->repliesAreComments() || data()->externalReply()) { - localMediaBottom -= st::historyCommentsButtonHeight; - } - if (!mediaOnBottom) { - localMediaBottom -= st::msgPadding.bottom(); - } - if (entry) { - localMediaBottom -= entry->height(); - } - const auto localMediaTop = localMediaBottom - media->height(); - for (auto &[top, height] : mediaSelectionIntervals) { - top += localMediaTop; - } - } - auto skipTail = isAttachedToNext() || (media && media->skipBubbleTail()) || (keyboard != nullptr) diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index da4f6ad6a..df58d562a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -951,6 +951,7 @@ void Document::drawGrouped( const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const { p.translate(geometry.topLeft()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index d50d7e0ed..150c47684 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -72,6 +72,7 @@ public: const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const override; TextState getStateGrouped( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 58b2eec0e..334c6b893 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -901,6 +901,7 @@ void Gif::drawGrouped( const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const { ensureDataMediaCreated(); @@ -989,8 +990,16 @@ void Gif::drawGrouped( p.drawPixmap(geometry, *cache); } - if (selected) { + const auto overlayOpacity = selected + ? (1. - highlightOpacity) + : highlightOpacity; + if (overlayOpacity > 0.) { + p.setOpacity(overlayOpacity); Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); + if (!selected) { + Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); + } + p.setOpacity(1.); } if (radial diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index e5bfcc30c..1aa559b63 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -79,6 +79,7 @@ public: const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const override; TextState getStateGrouped( diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index 82df073bf..d4308af06 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -84,6 +84,8 @@ public: } virtual void refreshParentId(not_null realParent) { } + virtual void drawHighlight(Painter &p, int top) const { + } virtual void draw( Painter &p, const QRect &r, @@ -177,6 +179,7 @@ public: const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const { Unexpected("Grouping method call."); @@ -274,6 +277,9 @@ public: const QRect &bubble, crl::time ms) const { } + [[nodiscard]] virtual bool customHighlight() const { + return false; + } virtual bool hasHeavyPart() const { return false; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index 422f18596..c55c98d16 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -268,6 +268,18 @@ QMargins GroupedMedia::groupedPadding() const { (normal.bottom() - grouped.bottom()) + addToBottom); } +void GroupedMedia::drawHighlight(Painter &p, int top) const { + if (_mode != Mode::Column) { + return; + } + const auto skip = top + groupedPadding().top(); + for (auto i = 0, count = int(_parts.size()); i != count; ++i) { + const auto &part = _parts[i]; + const auto rect = part.geometry.translated(0, skip); + _parent->paintCustomHighlight(p, rect.y(), rect.height(), part.item); + } +} + void GroupedMedia::draw( Painter &p, const QRect &clip, @@ -290,6 +302,9 @@ void GroupedMedia::draw( if (textSelection) { selection = part.content->skipSelection(selection); } + const auto highlightOpacity = (_mode == Mode::Grid) + ? _parent->highlightOpacity(part.item) + : 0.; part.content->drawGrouped( p, clip, @@ -298,6 +313,7 @@ void GroupedMedia::draw( part.geometry.translated(0, groupPadding.top()), part.sides, cornersFromSides(part.sides), + highlightOpacity, &part.cacheKey, &part.cache); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h index ee24c61f8..442f9a5b1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -31,6 +31,7 @@ public: void refreshParentId(not_null realParent) override; + void drawHighlight(Painter &p, int top) const override; void draw( Painter &p, const QRect &clip, @@ -87,6 +88,9 @@ public: bool allowsFastShare() const override { return true; } + bool customHighlight() const override { + return true; + } void stopAnimation() override; void checkAnimation() override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index ebb7fd318..b86d4fdef 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -485,6 +485,7 @@ void Photo::drawGrouped( const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const { ensureDataMediaCreated(); @@ -509,9 +510,18 @@ void Photo::drawGrouped( // App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } p.drawPixmap(geometry.topLeft(), *cache); - if (selected) { + + const auto overlayOpacity = selected + ? (1. - highlightOpacity) + : highlightOpacity; + if (overlayOpacity > 0.) { + p.setOpacity(overlayOpacity); const auto roundRadius = ImageRoundRadius::Large; Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); + if (!selected) { + Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); + } + p.setOpacity(1.); } const auto displayState = radial diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index 234843cf0..b0acad447 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -68,6 +68,7 @@ public: const QRect &geometry, RectParts sides, RectParts corners, + float64 highlightOpacity, not_null cacheKey, not_null cache) const override; TextState getStateGrouped( From 0709bc6d70ae175fee1319f69cc2098f8f99a9fc Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 13:15:46 +0400 Subject: [PATCH 025/396] Fix links in voice chat admin log events. --- .../SourceFiles/history/admin_log/history_admin_log_item.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 7ac1783af..d5647de4b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -838,7 +838,7 @@ void GenerateItems( data.vparticipant().match([&](const MTPDgroupCallParticipant &data) { const auto user = history->owner().user(data.vuser_id().v); const auto userLink = user->createOpenLink(); - const auto userLinkText = textcmdLink(1, user->name); + const auto userLinkText = textcmdLink(2, user->name); auto text = tr::lng_admin_log_muted_participant( tr::now, lt_from, @@ -862,7 +862,7 @@ void GenerateItems( data.vparticipant().match([&](const MTPDgroupCallParticipant &data) { const auto user = history->owner().user(data.vuser_id().v); const auto userLink = user->createOpenLink(); - const auto userLinkText = textcmdLink(1, user->name); + const auto userLinkText = textcmdLink(2, user->name); auto text = tr::lng_admin_log_unmuted_participant( tr::now, lt_from, From 2af63ec48f45e3ad5318ad62400211da3d6c9a1a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 13:28:35 +0400 Subject: [PATCH 026/396] Correctly show legacy groups with no admins. --- Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 3 +++ Telegram/SourceFiles/info/profile/info_profile_values.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 3ce97701f..1e95ac2d7 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -1212,6 +1212,9 @@ void ParticipantsBoxController::rebuildChatAdmins( return true; }(); if (same) { + if (!_allLoaded && !delegate()->peerListFullRowsCount()) { + chatListReady(); + } return; } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 289abaf04..dc31b11d9 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -247,7 +247,7 @@ rpl::producer AdminsCountValue(not_null peer) { ) | rpl::map([=] { return chat->participants.empty() ? 0 - : int(chat->admins.size() + 1); // + creator + : int(chat->admins.size() + (chat->creator ? 1 : 0)); }); } else if (const auto channel = peer->asChannel()) { return peer->session().changes().peerFlagsValue( From 1d7fb6c4ce4c807b036acd91fb785217254c6282 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 15:53:01 +0400 Subject: [PATCH 027/396] Better count voice chat participants count. --- .../SourceFiles/calls/calls_group_members.cpp | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 9d4b1eb9c..e311318e0 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -273,6 +273,8 @@ private: rpl::event_stream _toggleMuteRequests; rpl::event_stream> _kickMemberRequests; rpl::variable _fullCount = 1; + rpl::variable _fullCountMin = 0; + rpl::variable _fullCountMax = std::numeric_limits::max(); not_null _menuParent; base::unique_qptr _menu; @@ -682,9 +684,12 @@ void MembersController::subscribeToChanges(not_null real) { _realCallRawValue = real; _realId = real->id(); - _fullCount = real->fullCountValue( - ) | rpl::map([](int value) { - return std::max(value, 1); + _fullCount = rpl::combine( + real->fullCountValue(), + _fullCountMin.value(), + _fullCountMax.value() + ) | rpl::map([](int value, int min, int max) { + return std::max(std::clamp(value, min, max), 1); }); real->participantsSliceAdded( @@ -741,10 +746,14 @@ void MembersController::appendInvitedUsers() { void MembersController::updateRow( const std::optional &was, const Data::GroupCall::Participant &now) { + auto countChange = 0; if (const auto row = findRow(now.user)) { if (now.speaking && (!was || !was->speaking)) { checkSpeakingRowPosition(row); } + if (row->state() == Row::State::Invited) { + countChange = 1; + } updateRow(row, &now); } else if (auto row = createRow(now)) { if (row->speaking()) { @@ -767,6 +776,14 @@ void MembersController::updateRow( } } delegate()->peerListRefreshRows(); + countChange = 1; + } + if (countChange) { + const auto fullCountMin = _fullCountMin.current() + countChange; + if (_fullCountMax.current() < fullCountMin) { + _fullCountMax = fullCountMin; + } + _fullCountMin = fullCountMin; } } @@ -879,10 +896,11 @@ void MembersController::prepare() { setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); const auto call = _call.get(); - if (const auto real = _peer->groupCall(); - real && call && real->id() == call->id()) { + if (const auto real = _peer->groupCall() + ; real && call && real->id() == call->id()) { prepareRows(real); } else if (auto row = createSelfRow()) { + _fullCountMin = (row->state() == Row::State::Invited) ? 0 : 1; delegate()->peerListAppendRow(std::move(row)); delegate()->peerListRefreshRows(); } @@ -898,6 +916,7 @@ void MembersController::prepareRows(not_null real) { auto foundSelf = false; auto changed = false; const auto &participants = real->participants(); + auto fullCountMin = 0; auto count = delegate()->peerListFullRowsCount(); for (auto i = 0; i != count;) { auto row = delegate()->peerListRowAt(i); @@ -912,6 +931,7 @@ void MembersController::prepareRows(not_null real) { not_null{ user }, &Data::GroupCall::Participant::user); if (contains) { + ++fullCountMin; ++i; } else { changed = true; @@ -927,18 +947,29 @@ void MembersController::prepareRows(not_null real) { &Data::GroupCall::Participant::user); auto row = (i != end(participants)) ? createRow(*i) : createSelfRow(); if (row) { + if (row->state() != Row::State::Invited) { + ++fullCountMin; + } changed = true; delegate()->peerListAppendRow(std::move(row)); } } for (const auto &participant : participants) { if (auto row = createRow(participant)) { + ++fullCountMin; changed = true; delegate()->peerListAppendRow(std::move(row)); } } if (changed) { delegate()->peerListRefreshRows(); + if (_fullCountMax.current() < fullCountMin) { + _fullCountMax = fullCountMin; + } + _fullCountMin = fullCountMin; + if (real->participantsLoaded()) { + _fullCountMax = fullCountMin; + } } } From a6eb241ec10f9759030ec06ab2543be41045ebb0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 16:12:39 +0400 Subject: [PATCH 028/396] Fix blurred thumbnails in Shared Links section. --- .../SourceFiles/overview/overview_layout.cpp | 28 +++++++++++++------ .../SourceFiles/overview/overview_layout.h | 1 + 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 470d68f09..4c68b7d7a 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -1479,9 +1479,8 @@ Link::Link( int32 tw = 0, th = 0; if (_page && _page->photo) { const auto photo = _page->photo; - if (photo->inlineThumbnailBytes().isEmpty() - && (photo->hasExact(Data::PhotoSize::Small) - || photo->hasExact(Data::PhotoSize::Thumbnail))) { + if (photo->hasExact(Data::PhotoSize::Small) + || photo->hasExact(Data::PhotoSize::Thumbnail)) { photo->load(Data::PhotoSize::Small, parent->fullId()); } tw = style::ConvertScale(photo->width()); @@ -1623,7 +1622,7 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P } void Link::validateThumbnail() { - if (!_thumbnail.isNull()) { + if (!_thumbnail.isNull() && !_thumbnailBlurred) { return; } if (_page && _page->photo) { @@ -1631,12 +1630,16 @@ void Link::validateThumbnail() { ensurePhotoMediaCreated(); if (const auto thumbnail = _photoMedia->image(PhotoSize::Thumbnail)) { _thumbnail = thumbnail->pixSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + _thumbnailBlurred = false; } else if (const auto large = _photoMedia->image(PhotoSize::Large)) { _thumbnail = large->pixSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + _thumbnailBlurred = false; } else if (const auto small = _photoMedia->image(PhotoSize::Small)) { _thumbnail = small->pixSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + _thumbnailBlurred = false; } else if (const auto blurred = _photoMedia->thumbnailInline()) { _thumbnail = blurred->pixBlurredSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + return; } else { return; } @@ -1644,14 +1647,20 @@ void Link::validateThumbnail() { delegate()->unregisterHeavyItem(this); } else if (_page && _page->document && _page->document->hasThumbnail()) { ensureDocumentMediaCreated(); + const auto roundRadius = _page->document->isVideoMessage() + ? ImageRoundRadius::Ellipse + : ImageRoundRadius::Small; if (const auto thumbnail = _documentMedia->thumbnail()) { - auto roundRadius = _page->document->isVideoMessage() - ? ImageRoundRadius::Ellipse - : ImageRoundRadius::Small; _thumbnail = thumbnail->pixSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, roundRadius); - _documentMedia = nullptr; - delegate()->unregisterHeavyItem(this); + _thumbnailBlurred = false; + } else if (const auto blurred = _documentMedia->thumbnailInline()) { + _thumbnail = blurred->pixBlurredSingle(_pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, roundRadius); + return; + } else { + return; } + _documentMedia = nullptr; + delegate()->unregisterHeavyItem(this); } else { const auto size = QSize(st::linksPhotoSize, st::linksPhotoSize); _thumbnail = QPixmap(size * cIntRetinaFactor()); @@ -1683,6 +1692,7 @@ void Link::validateThumbnail() { _letter, style::al_center); } + _thumbnailBlurred = false; } } diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index f3f51ca8f..40bc22903 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -368,6 +368,7 @@ private: int _pixh = 0; Ui::Text::String _text = { st::msgMinWidth }; QPixmap _thumbnail; + bool _thumbnailBlurred = true; struct LinkEntry { LinkEntry() : width(0) { From 818624e05110d4339293dccdbb3b748b5c6e9f88 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 16:14:13 +0400 Subject: [PATCH 029/396] Don't allow kicking yourself from legacy group. --- Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 1e95ac2d7..dd4e3587a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -358,7 +358,7 @@ bool ParticipantsAdditionalData::canRemoveUser( if (canRestrictUser(user)) { return true; } else if (const auto chat = _peer->asChat()) { - return chat->invitedByMe.contains(user); + return !user->isSelf() && chat->invitedByMe.contains(user); } return false; } From 3fd62d51aa8d004f528af34a05db7e8d5f756a0b Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 16:27:32 +0400 Subject: [PATCH 030/396] Hide bot command start button when editing message. Fixes #9941. --- Telegram/SourceFiles/history/history_widget.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7208241d8..b59ec772a 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1661,6 +1661,7 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { _editMsgId = 0; _replyToId = readyToForward() ? 0 : _history->localDraft()->msgId; } + updateCmdStartShown(); updateControlsVisibility(); updateControlsGeometry(); refreshTopBarActiveChat(); @@ -2228,11 +2229,7 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); } else { _botKeyboardShow->hide(); - if (_cmdStartShown) { - _botCommandStart->show(); - } else { - _botCommandStart->hide(); - } + _botCommandStart->setVisible(_cmdStartShown); } } _attachToggle->show(); @@ -3738,7 +3735,7 @@ void HistoryWidget::updateSendButtonType() { bool HistoryWidget::updateCmdStartShown() { bool cmdStartShown = false; if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isMegagroup() && _peer->asChannel()->mgInfo->botStatus > 0) || (_peer->isUser() && _peer->asUser()->isBot()))) { - if (!isBotStart() && !isBlocked() && !_keyboard->hasMarkup() && !_keyboard->forceReply()) { + if (!isBotStart() && !isBlocked() && !_keyboard->hasMarkup() && !_keyboard->forceReply() && !_editMsgId) { if (!HasSendText(_field)) { cmdStartShown = true; } @@ -4081,8 +4078,8 @@ void HistoryWidget::checkFieldAutocomplete() { && cRecentInlineBots().isEmpty()) { session().local().readRecentHashtagsAndBots(); } else if (autocomplete.query[0] == '/' - && _peer->isUser() - && !_peer->asUser()->isBot()) { + && ((_peer->isUser() && !_peer->asUser()->isBot()) + || _editMsgId)) { return; } } @@ -4845,7 +4842,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _tabbedSelectorToggle->show(); _botKeyboardHide->hide(); _botKeyboardShow->hide(); - _botCommandStart->show(); + _botCommandStart->setVisible(!_editMsgId); } _field->setMaxHeight(computeMaxFieldHeight()); _kbShown = false; From 0ecd4d3b4067fc2059c650fb0a54d4fabc6cab1d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 17:12:40 +0400 Subject: [PATCH 031/396] Close PiP when opening a loading video. Fixes #9926. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 1f25222be..a32bd82e7 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2443,7 +2443,9 @@ bool OverlayWidget::initStreaming(bool continueStreaming) { void OverlayWidget::startStreamingPlayer() { Expects(_streamed != nullptr); - if (_streamed->instance.player().playing()) { + if (!_streamed->instance.player().paused() + && !_streamed->instance.player().finished() + && !_streamed->instance.player().failed()) { if (!_streamed->withSound) { return; } From 373635a765a6e05e6660132ed8037cbb91365654 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Dec 2020 17:53:37 +0400 Subject: [PATCH 032/396] Beta version 2.5.3. - Allow using mouse buttons in Push-to-Talk shortcut. - Fix blurred thumbnails in Shared Links section. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 10 ++++++++-- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- Telegram/lib_ui | 2 +- changelog.txt | 5 +++++ 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 8d9900a94..b92d22f3a 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.5.3.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 73071e47b..e8e8b802f 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,2,0 - PRODUCTVERSION 2,5,2,0 + FILEVERSION 2,5,3,0 + PRODUCTVERSION 2,5,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.2.0" + VALUE "FileVersion", "2.5.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.2.0" + VALUE "ProductVersion", "2.5.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 21a6324c6..e38f7242f 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,2,0 - PRODUCTVERSION 2,5,2,0 + FILEVERSION 2,5,3,0 + PRODUCTVERSION 2,5,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.2.0" + VALUE "FileVersion", "2.5.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.2.0" + VALUE "ProductVersion", "2.5.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 865b42603..98a7f87a0 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -62,7 +62,7 @@ std::map BetaLogs() { "- Fix sticker pack opening.\n" "- Fix group status display.\n" - + "- Fix group members display.\n" }, { @@ -82,7 +82,13 @@ std::map BetaLogs() { "- Fix possible crash in connecting to voice chats.\n" "- Use different audio module code on Windows in calls.\n" - } + }, + { + 2005003, + "- Allow using mouse buttons in Push-to-Talk shortcut.\n" + + "- Fix blurred thumbnails in Shared Links section.\n" + }, }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index c27a5e770..781d7ff05 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005002; -constexpr auto AppVersionStr = "2.5.2"; +constexpr auto AppVersion = 2005003; +constexpr auto AppVersionStr = "2.5.3"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 6e9452f50..822879515 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005002 +AppVersion 2005003 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.2 -AppVersionStr 2.5.2 +AppVersionStrSmall 2.5.3 +AppVersionStr 2.5.3 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 2.5.2.beta +AppVersionOriginal 2.5.3.beta diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 17eb0f22b..b5fb343d6 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 17eb0f22b4df542e99648b7407ca1d139ba39b78 +Subproject commit b5fb343d6cea52df8cde00e8e3ca1008c05e4589 diff --git a/changelog.txt b/changelog.txt index 4e7a7481d..4c7f2ec62 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +2.5.3 beta (30.12.20) + +- Allow using mouse buttons in Push-to-Talk shortcut. +- Fix blurred thumbnails in Shared Links section. + 2.5.2 beta (25.12.20) - Fix possible crash in video calls. From 5d68d224e59511b52407c438b10a9db6e39d9eb0 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 31 Dec 2020 00:30:38 +0400 Subject: [PATCH 033/396] Use more --depth=1 in Dockerfile --- Telegram/build/docker/centos_env/Dockerfile | 25 +++++++++------------ 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 6be2e6e26..ae13b7df6 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -27,8 +27,8 @@ ENV LibrariesPath /usr/src/Libraries WORKDIR $LibrariesPath FROM builder AS patches -RUN git clone $GIT/desktop-app/patches.git -RUN cd patches && git checkout 6afd91a +ADD https://api.github.com/repos/desktop-app/patches/git/refs/heads/master patches-version.json +RUN git clone --depth=1 $GIT/desktop-app/patches.git FROM builder AS libffi RUN git clone -b v3.3 --depth=1 $GIT/libffi/libffi.git @@ -107,20 +107,18 @@ RUN rm -rf libxcb FROM builder AS xcb-wm -RUN git clone --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-wm.git +RUN git clone -b 0.4.1 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-wm.git WORKDIR libxcb-wm -RUN git checkout 0.4.1 RUN ./autogen.sh --enable-static RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/xcb-wm-cache" install FROM builder AS xcb-util -RUN git clone --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-util.git +RUN git clone -b 0.4.0 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-util.git WORKDIR libxcb-util -RUN git checkout 0.4.0 RUN ./autogen.sh --enable-static RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/xcb-util-cache" install @@ -128,30 +126,27 @@ RUN make DESTDIR="$LibrariesPath/xcb-util-cache" install FROM builder AS xcb-image COPY --from=xcb-util ${LibrariesPath}/xcb-util-cache / -RUN git clone --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-image.git +RUN git clone -b 0.4.0 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-image.git WORKDIR libxcb-image -RUN git checkout 0.4.0 RUN ./autogen.sh --enable-static RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/xcb-image-cache" install FROM builder AS xcb-keysyms -RUN git clone --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms.git +RUN git clone -b 0.4.0 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms.git WORKDIR libxcb-keysyms -RUN git checkout 0.4.0 RUN ./autogen.sh --enable-static RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/xcb-keysyms-cache" install FROM builder AS xcb-render-util -RUN git clone --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-render-util.git +RUN git clone -b 0.3.9 --depth=1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-render-util.git WORKDIR libxcb-render-util -RUN git checkout 0.3.9 RUN ./autogen.sh --enable-static RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/xcb-render-util-cache" install @@ -482,7 +477,7 @@ RUN make DESTDIR="$BreakpadCache" install WORKDIR src RUN rm -rf testing -RUN git clone $GIT/google/googletest.git testing +RUN git clone --depth=1 $GIT/google/googletest.git testing WORKDIR tools RUN sed -i 's/minidump_upload.m/minidump_upload.cc/' linux/tools_linux.gypi @@ -503,10 +498,10 @@ COPY --from=opus ${LibrariesPath}/opus-cache / COPY --from=ffmpeg ${LibrariesPath}/ffmpeg-cache / COPY --from=openssl ${LibrariesPath}/openssl-cache / -RUN git clone --recursive $GIT/desktop-app/tg_owt.git +ADD https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master tg_owt-version.json +RUN git clone --depth=1 --recursive $GIT/desktop-app/tg_owt.git WORKDIR tg_owt -RUN git checkout 75ac669 RUN cmake3 -B out/Release . \ -DCMAKE_BUILD_TYPE=Release \ From 8ce0bd5575dd834da94c976b8dd1b0d518b09ff4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 1 Jan 2021 01:16:41 +0000 Subject: [PATCH 034/396] Update User-Agent for DNS to Chrome 87.0.4280.88. --- .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp index 8d9a56849..734f000aa 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp @@ -65,7 +65,7 @@ QByteArray DnsUserAgent() { static const auto kResult = QByteArray( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/86.0.4240.198 Safari/537.36"); + "Chrome/87.0.4280.88 Safari/537.36"); return kResult; } From b28da30038b89eef5eafd02b07535f5456b486c8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 1 Jan 2021 03:20:58 +0000 Subject: [PATCH 035/396] Update copyright year to 2021. --- LEGAL | 2 +- Telegram/Resources/winrc/Telegram.rc | 2 +- Telegram/Resources/winrc/Updater.rc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGAL b/LEGAL index 34fe8e674..53df2b501 100644 --- a/LEGAL +++ b/LEGAL @@ -1,7 +1,7 @@ This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. -Copyright (c) 2014-2020 The Telegram Desktop Authors. +Copyright (c) 2014-2021 The Telegram Desktop Authors. Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index e8e8b802f..ed2715df0 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -63,7 +63,7 @@ BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" VALUE "FileVersion", "2.5.3.0" - VALUE "LegalCopyright", "Copyright (C) 2014-2020" + VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" VALUE "ProductVersion", "2.5.3.0" END diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index e38f7242f..35c14a7b5 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -54,7 +54,7 @@ BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" VALUE "FileVersion", "2.5.3.0" - VALUE "LegalCopyright", "Copyright (C) 2014-2020" + VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" VALUE "ProductVersion", "2.5.3.0" END From 15a9842b9f7ba6e77356775b6d2695028c3afaa6 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 2 Jan 2021 07:52:06 +0400 Subject: [PATCH 036/396] Make open with dialog modal on Linux --- .../platform/linux/file_utilities_linux.cpp | 97 ++++++++++++------- .../SourceFiles/platform/linux/linux_libs.cpp | 2 - .../SourceFiles/platform/linux/linux_libs.h | 8 -- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 0cc204e63..c5c1d0e72 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -45,45 +45,95 @@ bool ShowOpenWithSupported() { && (Libs::gtk_app_chooser_dialog_new != nullptr) && (Libs::gtk_app_chooser_get_app_info != nullptr) && (Libs::gtk_app_chooser_get_type != nullptr) - && (Libs::gtk_widget_get_type != nullptr) && (Libs::gtk_widget_get_window != nullptr) && (Libs::gtk_widget_realize != nullptr) && (Libs::gtk_widget_show != nullptr) && (Libs::gtk_widget_destroy != nullptr); } -void HandleAppChooserResponse( - GtkDialog *dialog, - int responseId, - GFile *file) { +class OpenWithDialog : public QWindow { +public: + OpenWithDialog(const QString &filepath); + ~OpenWithDialog(); + + bool exec(); + +private: + static void handleResponse(OpenWithDialog *dialog, int responseId); + + GFile *_gfileInstance = nullptr; + GtkWidget *_gtkWidget = nullptr; + QEventLoop _loop; + std::optional _result = std::nullopt; +}; + +OpenWithDialog::OpenWithDialog(const QString &filepath) +: _gfileInstance(g_file_new_for_path(filepath.toUtf8())) +, _gtkWidget(Libs::gtk_app_chooser_dialog_new( + nullptr, + GTK_DIALOG_MODAL, + _gfileInstance)) { + g_signal_connect_swapped( + _gtkWidget, + "response", + G_CALLBACK(handleAppChooserResponse), + this); +} + +OpenWithDialog::~OpenWithDialog() { + Libs::gtk_widget_destroy(_gtkWidget); + g_object_unref(_gfileInstance); +} + +bool OpenWithDialog::exec() { + Libs::gtk_widget_realize(_gtkWidget); + + if (const auto activeWindow = Core::App().activeWindow()) { + Platform::internal::XSetTransientForHint( + Libs::gtk_widget_get_window(_gtkWidget), + activeWindow->widget().get()->windowHandle()->winId()); + } + + QGuiApplicationPrivate::showModalWindow(this); + Libs::gtk_widget_show(_gtkWidget); + + if (!_result.has_value()) { + _loop.exec(); + } + + QGuiApplicationPrivate::hideModalWindow(this); + return *_result; +} + +void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) { GAppInfo *chosenAppInfo = nullptr; + dialog->_result = true; switch (responseId) { case GTK_RESPONSE_OK: chosenAppInfo = Libs::gtk_app_chooser_get_app_info( - Libs::gtk_app_chooser_cast(dialog)); + Libs::gtk_app_chooser_cast(dialog->_gtkWidget)); if (chosenAppInfo) { GList *uris = nullptr; - uris = g_list_prepend(uris, g_file_get_uri(file)); + uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance)); g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr); g_list_free(uris); g_object_unref(chosenAppInfo); } - g_object_unref(file); - Libs::gtk_widget_destroy(Libs::gtk_widget_cast(dialog)); break; case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: - g_object_unref(file); - Libs::gtk_widget_destroy(Libs::gtk_widget_cast(dialog)); break; default: + dialog->_result = false; break; } + + dialog->_loop.quit(); } #endif // !TDESKTOP_DISABLE_GTK_INTEGRATION @@ -141,30 +191,7 @@ bool UnsafeShowOpenWith(const QString &filepath) { } const auto absolutePath = QFileInfo(filepath).absoluteFilePath(); - auto gfileInstance = g_file_new_for_path(absolutePath.toUtf8()); - - auto appChooserDialog = Libs::gtk_app_chooser_dialog_new( - nullptr, - GTK_DIALOG_MODAL, - gfileInstance); - - g_signal_connect( - appChooserDialog, - "response", - G_CALLBACK(HandleAppChooserResponse), - gfileInstance); - - Libs::gtk_widget_realize(appChooserDialog); - - if (const auto activeWindow = Core::App().activeWindow()) { - Platform::internal::XSetTransientForHint( - Libs::gtk_widget_get_window(appChooserDialog), - activeWindow->widget().get()->windowHandle()->winId()); - } - - Libs::gtk_widget_show(appChooserDialog); - - return true; + return OpenWithDialog(absolutePath).exec(); #else // !TDESKTOP_DISABLE_GTK_INTEGRATION return false; #endif // TDESKTOP_DISABLE_GTK_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp index 244c99616..2dbddebb8 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_libs.cpp @@ -74,7 +74,6 @@ bool setupGtkBase(QLibrary &lib_gtk) { if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_get_type", gtk_widget_get_type)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false; @@ -236,7 +235,6 @@ f_gtk_widget_get_window gtk_widget_get_window = nullptr; f_gtk_widget_realize gtk_widget_realize = nullptr; f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete = nullptr; f_gtk_widget_destroy gtk_widget_destroy = nullptr; -f_gtk_widget_get_type gtk_widget_get_type = nullptr; f_gtk_clipboard_get gtk_clipboard_get = nullptr; f_gtk_clipboard_store gtk_clipboard_store = nullptr; f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents = nullptr; diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.h b/Telegram/SourceFiles/platform/linux/linux_libs.h index fa651615d..b9661512b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.h +++ b/Telegram/SourceFiles/platform/linux/linux_libs.h @@ -235,14 +235,6 @@ inline GtkWindow *gtk_window_cast(Object *obj) { return g_type_cic_helper(obj, gtk_window_get_type()); } -typedef GType (*f_gtk_widget_get_type)(void) G_GNUC_CONST; -extern f_gtk_widget_get_type gtk_widget_get_type; - -template -inline GtkWidget *gtk_widget_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_widget_get_type()); -} - typedef GType (*f_gtk_app_chooser_get_type)(void) G_GNUC_CONST; extern f_gtk_app_chooser_get_type gtk_app_chooser_get_type; From 8fb6ece79679d95d5329fc0ad82867fbb12b3091 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 2 Jan 2021 08:34:04 +0400 Subject: [PATCH 037/396] Revert "Use xcb to set transient parent for gtk file dialog" This reverts commit cd3b989e706112f076a9d9dd613be0090ce5b937. --- .../platform/linux/linux_gdk_helper.cpp | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp index 20a98191c..35f3c0c1c 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp @@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_libs.h" -#include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_xcb_utilities_linux.h" extern "C" { #undef signals @@ -33,6 +31,9 @@ GtkLoaded gdk_helper_loaded = GtkLoaded::GtkNone; #define GdkDrawable GdkWindow // Gtk 2 +using f_gdk_x11_drawable_get_xdisplay = Display*(*)(GdkDrawable*); +f_gdk_x11_drawable_get_xdisplay gdk_x11_drawable_get_xdisplay = nullptr; + using f_gdk_x11_drawable_get_xid = XID(*)(GdkDrawable*); f_gdk_x11_drawable_get_xid gdk_x11_drawable_get_xid = nullptr; @@ -46,6 +47,12 @@ inline bool gdk_is_x11_window_check(Object *obj) { return Libs::g_type_cit_helper(obj, gdk_x11_window_get_type()); } +using f_gdk_window_get_display = GdkDisplay*(*)(GdkWindow *window); +f_gdk_window_get_display gdk_window_get_display = nullptr; + +using f_gdk_x11_display_get_xdisplay = Display*(*)(GdkDisplay *display); +f_gdk_x11_display_get_xdisplay gdk_x11_display_get_xdisplay = nullptr; + using f_gdk_x11_window_get_xid = Window(*)(GdkWindow *window); f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr; @@ -53,6 +60,7 @@ bool GdkHelperLoadGtk2(QLibrary &lib) { #if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY return false; #else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY + if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xdisplay", gdk_x11_drawable_get_xdisplay)) return false; if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false; return true; #endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY @@ -60,6 +68,8 @@ bool GdkHelperLoadGtk2(QLibrary &lib) { bool GdkHelperLoadGtk3(QLibrary &lib) { if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false; + if (!LOAD_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false; + if (!LOAD_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false; if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false; return true; } @@ -79,28 +89,14 @@ bool GdkHelperLoaded() { void XSetTransientForHint(GdkWindow *window, quintptr winId) { if (gdk_helper_loaded == GtkLoaded::Gtk2) { - if (!IsWayland()) { - xcb_change_property( - base::Platform::XCB::GetConnectionFromQt(), - XCB_PROP_MODE_REPLACE, - gdk_x11_drawable_get_xid(window), - XCB_ATOM_WM_TRANSIENT_FOR, - XCB_ATOM_WINDOW, - 32, - 1, - &winId); - } + ::XSetTransientForHint(gdk_x11_drawable_get_xdisplay(window), + gdk_x11_drawable_get_xid(window), + winId); } else if (gdk_helper_loaded == GtkLoaded::Gtk3) { - if (!IsWayland() && gdk_is_x11_window_check(window)) { - xcb_change_property( - base::Platform::XCB::GetConnectionFromQt(), - XCB_PROP_MODE_REPLACE, - gdk_x11_window_get_xid(window), - XCB_ATOM_WM_TRANSIENT_FOR, - XCB_ATOM_WINDOW, - 32, - 1, - &winId); + if (gdk_is_x11_window_check(window)) { + ::XSetTransientForHint(gdk_x11_display_get_xdisplay(gdk_window_get_display(window)), + gdk_x11_window_get_xid(window), + winId); } } } From b9acea9cef1021adad94378107a5feb91dc1462c Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 2 Jan 2021 10:26:07 +0400 Subject: [PATCH 038/396] Move GSDMediaKeys initialization to SetWatchingMediaKeys --- .../platform/linux/main_window_linux.cpp | 16 ---------------- .../platform/linux/main_window_linux.h | 7 ------- .../platform/linux/specific_linux.cpp | 16 ++++++++++++++++ .../SourceFiles/platform/linux/specific_linux.h | 3 --- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 00a48742d..3d8826618 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -23,13 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "window/window_controller.h" #include "window/window_session_controller.h" -#include "media/player/media_player_instance.h" -#include "media/audio/media_audio.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include "platform/linux/linux_gsd_media_keys.h" -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "base/call_delayed.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/input_fields.h" @@ -584,17 +579,6 @@ void MainWindow::initHook() { } else { LOG(("Not using Unity launcher counter.")); } - - Media::Player::instance()->updatedNotifier( - ) | rpl::start_with_next([=](const Media::Player::TrackState &state) { - if (!Media::Player::IsStoppedOrStopping(state.state)) { - if (!_gsdMediaKeys) { - _gsdMediaKeys = std::make_unique(); - } - } else if (_gsdMediaKeys) { - _gsdMediaKeys = nullptr; - } - }, lifetime()); #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION updateWaylandDecorationColors(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 5e06aadbe..0460829af 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -25,11 +25,6 @@ typedef struct _GDBusProxy GDBusProxy; #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION namespace Platform { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -namespace internal { -class GSDMediaKeys; -} // namespace internal -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION class MainWindow : public Window::MainWindow { public: @@ -93,8 +88,6 @@ private: bool _appMenuSupported = false; DBusMenuExporter *_mainMenuExporter = nullptr; - std::unique_ptr _gsdMediaKeys; - QMenu *psMainMenu = nullptr; QAction *psLogout = nullptr; QAction *psUndo = nullptr; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 5ffe8a1e0..85800979d 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -24,6 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "core/application.h" +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_gsd_media_keys.h" +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION + #include #include #include @@ -521,6 +525,18 @@ Window::Control GtkKeywordToWindowControl(const QString &keyword) { } // namespace +void SetWatchingMediaKeys(bool watching) { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + static std::unique_ptr Instance; + + if (watching && !Instance) { + Instance = std::make_unique(); + } else if (!watching && Instance) { + Instance = nullptr; + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION +} + void SetApplicationIcon(const QIcon &icon) { QApplication::setWindowIcon(icon); } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 2bf9d9e61..80584a23d 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -17,9 +17,6 @@ class LocationPoint; namespace Platform { -inline void SetWatchingMediaKeys(bool watching) { -} - bool InFlatpak(); bool InSnap(); bool IsStaticBinary(); From 99af2a7058f15d19737d844831b4e175f86991b5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 2 Jan 2021 05:06:11 +0400 Subject: [PATCH 039/396] Check for xdg-decoration protocol support on Wayland --- Telegram/CMakeLists.txt | 7 ++ .../linux/linux_wayland_integration.cpp | 66 ++++++++++++++++- .../linux/linux_wayland_integration.h | 6 ++ .../linux/linux_wayland_integration_dummy.cpp | 12 ++++ .../platform/linux/specific_linux.cpp | 5 ++ .../platform/linux/window_title_linux.cpp | 7 +- Telegram/build/docker/centos_env/Dockerfile | 71 ++++++++++++++++++- cmake | 2 +- snap/snapcraft.yaml | 2 + 9 files changed, 172 insertions(+), 6 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 391e91daa..bd0322afc 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -84,6 +84,13 @@ if (LINUX) ) endif() + if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) + target_link_libraries(Telegram + PRIVATE + desktop-app::external_kwayland + ) + endif() + if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0) diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp index e6af77b70..baefa943b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp @@ -15,11 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +#include +#include + #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) && !defined DESKTOP_APP_QT_PATCHED #include #endif // Qt < 5.13 && !DESKTOP_APP_QT_PATCHED using QtWaylandClient::QWaylandWindow; +using namespace KWayland::Client; namespace Platform { namespace internal { @@ -51,15 +55,75 @@ enum wl_shell_surface_resize WlResizeFromEdges(Qt::Edges edges) { } // namespace -WaylandIntegration::WaylandIntegration() { +class WaylandIntegration::Private : public QObject { +public: + Private(); + + [[nodiscard]] Registry ®istry() { + return _registry; + } + + [[nodiscard]] QEventLoop &interfacesLoop() { + return _interfacesLoop; + } + + [[nodiscard]] bool interfacesAnnounced() const { + return _interfacesAnnounced; + } + +private: + ConnectionThread _connection; + Registry _registry; + QEventLoop _interfacesLoop; + bool _interfacesAnnounced = false; +}; + +WaylandIntegration::Private::Private() { + connect(&_connection, &ConnectionThread::connected, [=] { + LOG(("Successfully connected to Wayland server at socket: %1") + .arg(_connection.socketName())); + + _registry.create(&_connection); + _registry.setup(); + }); + + connect( + &_connection, + &ConnectionThread::connectionDied, + &_registry, + &Registry::destroy); + + connect(&_registry, &Registry::interfacesAnnounced, [=] { + _interfacesAnnounced = true; + _interfacesLoop.quit(); + }); + + _connection.initConnection(); } +WaylandIntegration::WaylandIntegration() +: _private(std::make_unique()) { +} + +WaylandIntegration::~WaylandIntegration() = default; + WaylandIntegration *WaylandIntegration::Instance() { if (!IsWayland()) return nullptr; static WaylandIntegration instance; return &instance; } +void WaylandIntegration::waitForInterfaceAnnounce() { + if (!_private->interfacesAnnounced()) { + _private->interfacesLoop().exec(); + } +} + +bool WaylandIntegration::supportsXdgDecoration() { + return _private->registry().hasInterface( + Registry::Interface::XdgDecorationUnstableV1); +} + bool WaylandIntegration::startMove(QWindow *window) { // There are startSystemMove on Qt 5.15 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) && !defined DESKTOP_APP_QT_PATCHED diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h index 73486fef7..a6e582042 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h @@ -15,12 +15,18 @@ namespace internal { class WaylandIntegration { public: static WaylandIntegration *Instance(); + void waitForInterfaceAnnounce(); + bool supportsXdgDecoration(); bool startMove(QWindow *window); bool startResize(QWindow *window, Qt::Edges edges); bool showWindowMenu(QWindow *window); private: WaylandIntegration(); + ~WaylandIntegration(); + + class Private; + const std::unique_ptr _private; }; } // namespace internal diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp index e79fd2d95..7a5b52e96 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp @@ -12,15 +12,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace internal { +class WaylandIntegration::Private { +}; + WaylandIntegration::WaylandIntegration() { } +WaylandIntegration::~WaylandIntegration() = default; + WaylandIntegration *WaylandIntegration::Instance() { if (!IsWayland()) return nullptr; static WaylandIntegration instance; return &instance; } +void WaylandIntegration::waitForInterfaceAnnounce() { +} + +bool WaylandIntegration::supportsXdgDecoration() { + return false; +} + bool WaylandIntegration::startMove(QWindow *window) { return false; } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 85800979d..5eac9bd28 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -1226,6 +1226,11 @@ void start() { Libs::start(); MainWindow::LibsLoaded(); + + // wait for interface announce to know if native window frame is supported + if (const auto waylandIntegration = WaylandIntegration::Instance()) { + waylandIntegration->waitForInterfaceAnnounce(); + } } void finish() { diff --git a/Telegram/SourceFiles/platform/linux/window_title_linux.cpp b/Telegram/SourceFiles/platform/linux/window_title_linux.cpp index 95e8b6449..bd990755c 100644 --- a/Telegram/SourceFiles/platform/linux/window_title_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/window_title_linux.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/window_title_linux.h" -#include "platform/linux/linux_desktop_environment.h" +#include "platform/linux/linux_wayland_integration.h" #include "base/platform/base_platform_info.h" namespace Platform { @@ -24,9 +24,10 @@ bool SystemMoveResizeSupported() { } // namespace bool AllowNativeWindowFrameToggle() { + const auto waylandIntegration = internal::WaylandIntegration::Instance(); return SystemMoveResizeSupported() - // https://gitlab.gnome.org/GNOME/mutter/-/issues/217 - && !(DesktopEnvironment::IsGnome() && IsWayland()); + && (!waylandIntegration + || waylandIntegration->supportsXdgDecoration()); } object_ptr CreateTitleWidget(QWidget *parent) { diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index ae13b7df6..ef88c6941 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -1,12 +1,14 @@ FROM centos:7 AS builder ENV GIT https://github.com -ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig +ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig:/usr/local/share/pkgconfig ENV QT 5_15_2 ENV QT_TAG v5.15.2 ENV QT_PREFIX /usr/local/desktop-app/Qt-5.15.2 ENV OPENSSL_VER 1_1_1 ENV OPENSSL_PREFIX /usr/local/desktop-app/openssl-1.1.1 +ENV PATH ${PATH}:${QT_PREFIX}/bin +ENV Qt5_DIR ${QT_PREFIX} RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm RUN yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpm @@ -30,6 +32,18 @@ FROM builder AS patches ADD https://api.github.com/repos/desktop-app/patches/git/refs/heads/master patches-version.json RUN git clone --depth=1 $GIT/desktop-app/patches.git +FROM builder AS extra-cmake-modules + +RUN git clone -b v5.77.0 --depth=1 $GIT/KDE/extra-cmake-modules.git + +WORKDIR extra-cmake-modules +RUN cmake3 -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF +RUN cmake3 --build build -j$(nproc) +RUN DESTDIR="$LibrariesPath/extra-cmake-modules-cache" cmake3 --install build + +WORKDIR .. +RUN rm -rf extra-cmake-modules + FROM builder AS libffi RUN git clone -b v3.3 --depth=1 $GIT/libffi/libffi.git @@ -203,6 +217,32 @@ RUN make DESTDIR="$LibrariesPath/wayland-cache" install WORKDIR .. RUN rm -rf wayland +FROM builder AS wayland-protocols +COPY --from=wayland ${LibrariesPath}/wayland-cache / + +RUN git clone -b 1.18 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols.git + +WORKDIR wayland-protocols +RUN ./autogen.sh +RUN make -j$(nproc) +RUN make DESTDIR="$LibrariesPath/wayland-protocols-cache" install + +WORKDIR .. +RUN rm -rf wayland-protocols + +FROM builder AS plasma-wayland-protocols +COPY --from=extra-cmake-modules ${LibrariesPath}/extra-cmake-modules-cache / + +RUN git clone -b v1.1.1 --depth=1 $GIT/KDE/plasma-wayland-protocols.git + +WORKDIR plasma-wayland-protocols +RUN cmake3 -B build . -DCMAKE_BUILD_TYPE=Release +RUN cmake3 --build build -j$(nproc) +RUN DESTDIR="$LibrariesPath/plasma-wayland-protocols-cache" cmake3 --install build + +WORKDIR .. +RUN rm -rf plasma-wayland-protocols + FROM builder AS libva COPY --from=libffi ${LibrariesPath}/libffi-cache / @@ -452,6 +492,34 @@ RUN make INSTALL_ROOT="$LibrariesPath/qt-cache" install WORKDIR .. RUN rm -rf qt_${QT} +FROM builder AS kwayland + +COPY --from=extra-cmake-modules ${LibrariesPath}/extra-cmake-modules-cache / +COPY --from=libffi ${LibrariesPath}/libffi-cache / +COPY --from=mozjpeg ${LibrariesPath}/mozjpeg-cache / +COPY --from=xcb ${LibrariesPath}/xcb-cache / +COPY --from=xcb-wm ${LibrariesPath}/xcb-wm-cache / +COPY --from=xcb-util ${LibrariesPath}/xcb-util-cache / +COPY --from=xcb-image ${LibrariesPath}/xcb-image-cache / +COPY --from=xcb-keysyms ${LibrariesPath}/xcb-keysyms-cache / +COPY --from=xcb-render-util ${LibrariesPath}/xcb-render-util-cache / +COPY --from=wayland ${LibrariesPath}/wayland-cache / +COPY --from=wayland-protocols ${LibrariesPath}/wayland-protocols-cache / +COPY --from=plasma-wayland-protocols ${LibrariesPath}/plasma-wayland-protocols-cache / +COPY --from=openssl ${LibrariesPath}/openssl-cache / +COPY --from=xkbcommon ${LibrariesPath}/xkbcommon-cache / +COPY --from=qt ${LibrariesPath}/qt-cache / + +RUN git clone -b v5.77.0 --depth=1 $GIT/KDE/kwayland.git + +WORKDIR kwayland +RUN cmake3 -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF +RUN cmake3 --build build -j$(nproc) +RUN DESTDIR="$LibrariesPath/kwayland-cache" cmake3 --install build + +WORKDIR .. +RUN rm -rf kwayland + FROM patches AS breakpad RUN git clone https://chromium.googlesource.com/breakpad/breakpad.git @@ -547,6 +615,7 @@ COPY --from=openal ${LibrariesPath}/openal-cache / COPY --from=openssl ${LibrariesPath}/openssl-cache / COPY --from=xkbcommon ${LibrariesPath}/xkbcommon-cache / COPY --from=qt ${LibrariesPath}/qt-cache / +COPY --from=kwayland ${LibrariesPath}/kwayland-cache / COPY --from=breakpad ${LibrariesPath}/breakpad breakpad COPY --from=breakpad ${LibrariesPath}/breakpad-cache / COPY --from=webrtc ${LibrariesPath}/tg_owt tg_owt diff --git a/cmake b/cmake index 220835876..561273a2f 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 220835876580f1df072c469e6c4dd746131a2293 +Subproject commit 561273a2f88306072750283b28d763959cd58652 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f4f447143..67d54c356 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -74,6 +74,7 @@ parts: - libasound2-dev - libglib2.0-dev - libgtk-3-dev + - libkf5wayland-dev - liblzma-dev - libopus-dev - libpulse-dev @@ -90,6 +91,7 @@ parts: - libasound2 - libglib2.0-0 - libgtk-3-0 + - libkf5waylandclient5 - liblzma5 - libopus0 - libpulse0 From 5affb168a27dd5342b5f30a06020ec48e3ce6e03 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 4 Jan 2021 14:21:53 +0400 Subject: [PATCH 040/396] Fix callback function name in open with dialog --- Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index c5c1d0e72..1c09e2edd 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -76,7 +76,7 @@ OpenWithDialog::OpenWithDialog(const QString &filepath) g_signal_connect_swapped( _gtkWidget, "response", - G_CALLBACK(handleAppChooserResponse), + G_CALLBACK(handleResponse), this); } From daa3a2f62ff56fd4d542c56ed02264b7334bf13a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 4 Jan 2021 16:37:38 +0400 Subject: [PATCH 041/396] React to resizeEvent in media viewer --- .../SourceFiles/media/view/media_view_overlay_widget.cpp | 8 +++----- .../SourceFiles/media/view/media_view_overlay_widget.h | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index a32bd82e7..edc46ebb7 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -446,11 +446,7 @@ void OverlayWidget::moveToScreen() { : nullptr; const auto activeWindowScreen = widgetScreen(window); const auto myScreen = widgetScreen(this); - // Wayland doesn't support positioning, but Qt emits screenChanged anyway - // and geometry of the widget become broken - if (activeWindowScreen - && myScreen != activeWindowScreen - && !Platform::IsWayland()) { + if (activeWindowScreen && myScreen != activeWindowScreen) { windowHandle()->setScreen(activeWindowScreen); } updateGeometry(); @@ -465,7 +461,9 @@ void OverlayWidget::updateGeometry() { return; } setGeometry(available); +} +void OverlayWidget::resizeEvent(QResizeEvent *e) { auto navSkip = 2 * st::mediaviewControlMargin + st::mediaviewControlSize; _closeNav = myrtlrect(width() - st::mediaviewControlMargin - st::mediaviewControlSize, st::mediaviewControlMargin, st::mediaviewControlSize, st::mediaviewControlSize); _closeNavIcon = style::centerrect(_closeNav, st::mediaviewClose); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 3879d04ad..9cab8f0f2 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -166,6 +166,7 @@ private: }; void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void wheelEvent(QWheelEvent *e) override; From b3892f49fa8e2d6aec393f1fac861b410b4e1273 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 4 Jan 2021 13:51:35 +0400 Subject: [PATCH 042/396] Fix kSNIWatcherService/kSNIWatcherInterface misusage Even though they're the same, there should be interface specified --- Telegram/SourceFiles/platform/linux/main_window_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 3d8826618..a5ee08551 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -413,7 +413,7 @@ bool IsSNIAvailable() { qsl("Get")); message.setArguments({ - kSNIWatcherService.utf16(), + kSNIWatcherInterface.utf16(), qsl("IsStatusNotifierHostRegistered") }); From d4d688d494f3e91e55e9edb2358ac25344ce23d0 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 4 Jan 2021 14:18:48 +0400 Subject: [PATCH 043/396] Merge two ifndef blocks in main_window_linux --- .../SourceFiles/platform/linux/main_window_linux.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index a5ee08551..e7d7c6938 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -402,10 +402,8 @@ bool UseUnityCounter() { return Result; } -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION bool IsSNIAvailable() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION auto message = QDBusMessage::createMethodCall( kSNIWatcherService.utf16(), kSNIWatcherObjectPath.utf16(), @@ -425,7 +423,6 @@ bool IsSNIAvailable() { } else if (reply.error().type() != QDBusError::ServiceUnknown) { LOG(("SNI Error: %1").arg(reply.error().message())); } -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION return false; } @@ -439,7 +436,6 @@ quint32 djbStringHash(QString string) { return hash; } -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION bool IsAppMenuSupported() { const auto interface = QDBusConnection::sessionBus().interface(); @@ -512,10 +508,9 @@ MainWindow::MainWindow(not_null controller) } void MainWindow::initHook() { - _sniAvailable = IsSNIAvailable(); - LOG(("System tray available: %1").arg(Logs::b(trayAvailable()))); - #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + _sniAvailable = IsSNIAvailable(); + _sniDBusProxy = g_dbus_proxy_new_for_bus_sync( G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, @@ -581,8 +576,9 @@ void MainWindow::initHook() { } #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION - updateWaylandDecorationColors(); + LOG(("System tray available: %1").arg(Logs::b(trayAvailable()))); + updateWaylandDecorationColors(); style::PaletteChanged( ) | rpl::start_with_next([=] { updateWaylandDecorationColors(); From 0fbea454bc567ff4261010de9c3a22289de111a5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 4 Jan 2021 14:39:19 +0400 Subject: [PATCH 044/396] Format unity counter setting --- .../platform/linux/main_window_linux.cpp | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index e7d7c6938..34f410584 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -822,25 +822,28 @@ void MainWindow::updateIconCounters() { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION if (UseUnityCounter()) { const auto launcherUrl = "application://" + GetLauncherFilename(); + // Gnome requires that count is a 64bit integer + const qint64 counterSlice = std::min(counter, 9999); QVariantMap dbusUnityProperties; - if (counter > 0) { - // Gnome requires that count is a 64bit integer - dbusUnityProperties.insert( - "count", - (qint64) ((counter > 9999) - ? 9999 - : counter)); - dbusUnityProperties.insert("count-visible", true); + + if (counterSlice > 0) { + dbusUnityProperties["count"] = counterSlice; + dbusUnityProperties["count-visible"] = true; } else { - dbusUnityProperties.insert("count-visible", false); + dbusUnityProperties["count-visible"] = false; } - QDBusMessage signal = QDBusMessage::createSignal( + + auto signal = QDBusMessage::createSignal( "/com/canonical/unity/launcherentry/" + QString::number(djbStringHash(launcherUrl)), "com.canonical.Unity.LauncherEntry", "Update"); - signal << launcherUrl; - signal << dbusUnityProperties; + + signal.setArguments({ + launcherUrl, + dbusUnityProperties + }); + QDBusConnection::sessionBus().send(signal); } From 0c37990ccd533c4faaade6cc9408d262da6fb00b Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 5 Jan 2021 12:42:58 +0400 Subject: [PATCH 045/396] Fix tg_owt cache in windows & macos actions --- .github/workflows/mac.yml | 4 +++- .github/workflows/win.yml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 3d259f333..5522262ef 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -102,6 +102,8 @@ jobs: cd Libraries/macos echo "LibrariesPath=`pwd`" >> $GITHUB_ENV + curl -o tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master + - name: Patches. run: | echo "Find necessary commit from doc." @@ -475,7 +477,7 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.LibrariesPath }}/tg_owt - key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }} + key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }} - name: WebRTC. if: steps.cache-webrtc.outputs.cache-hit != 'true' run: | diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index f9d997ad9..25072e9b8 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -103,6 +103,7 @@ jobs: - name: Generate cache key. shell: bash run: | + curl -o $LibrariesPath/tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master echo $MANUAL_CACHING >> CACHE_KEY.txt if [ "$AUTO_CACHING" == "1" ]; then thisFile=$REPO_NAME/.github/workflows/win.yml @@ -359,7 +360,7 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.LibrariesPath }}/tg_owt - key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }} + key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }} - name: WebRTC. if: steps.cache-webrtc.outputs.cache-hit != 'true' run: | From 3b7d5d3c803cac95ef8c2f20036486082c0718d4 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 5 Jan 2021 17:02:54 +0400 Subject: [PATCH 046/396] Eliminate ifndefs in notifications_manager_linux --- Telegram/CMakeLists.txt | 6 +++ .../linux/notifications_manager_linux.cpp | 35 ---------------- .../notifications_manager_linux_dummy.cpp | 40 +++++++++++++++++++ .../window/notifications_manager.h | 24 +++++++++++ 4 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bd0322afc..4ebda77fe 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1114,6 +1114,12 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.h + platform/linux/notifications_manager_linux.cpp + ) + + nice_target_sources(Telegram ${src_loc} + PRIVATE + platform/linux/notifications_manager_linux_dummy.cpp ) endif() diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index bacb22379..771db12f6 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "lang/lang_keys.h" -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include #include #include @@ -29,12 +28,9 @@ extern "C" { #include #define signals public } // extern "C" -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION namespace Platform { namespace Notifications { - -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION namespace { constexpr auto kDBusTimeout = 30000; @@ -615,16 +611,13 @@ void NotificationData::notificationReplied(uint id, const QString &text) { } } // namespace -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION bool SkipAudio() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION if (Supported() && GetCapabilities().contains(qsl("inhibitions")) && !InhibitedNotSupported) { return Inhibited(); } -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION return false; } @@ -638,18 +631,12 @@ bool SkipFlashBounce() { } bool Supported() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION return NotificationsSupported; -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION - - return false; } std::unique_ptr Create( Window::Notifications::System *system) { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION GetSupported(); -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION if ((Core::App().settings().nativeNotifications() && Supported()) || IsWayland()) { @@ -659,27 +646,6 @@ std::unique_ptr Create( return nullptr; } -#ifdef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -class Manager::Private { -public: - using Type = Window::Notifications::CachedUserpics::Type; - explicit Private(not_null manager, Type type) {} - - void showNotification( - not_null peer, - std::shared_ptr &userpicView, - MsgId msgId, - const QString &title, - const QString &subtitle, - const QString &msg, - bool hideNameAndPhoto, - bool hideReplyButton) {} - void clearAll() {} - void clearFromHistory(not_null history) {} - void clearFromSession(not_null session) {} - void clearNotification(NotificationId id) {} -}; -#else // DESKTOP_APP_DISABLE_DBUS_INTEGRATION class Manager::Private { public: using Type = Window::Notifications::CachedUserpics::Type; @@ -866,7 +832,6 @@ void Manager::Private::clearNotification(NotificationId id) { Manager::Private::~Private() { clearAll(); } -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION Manager::Manager(not_null system) : NativeManager(system) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp new file mode 100644 index 000000000..44ef96fa1 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -0,0 +1,40 @@ + +/* +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 "platform/linux/notifications_manager_linux.h" + +namespace Platform { +namespace Notifications { + +bool SkipAudio() { + return false; +} + +bool SkipToast() { + return false; +} + +bool SkipFlashBounce() { + return false; +} + +bool Supported() { + return false; +} + +std::unique_ptr Create( + Window::Notifications::System *system) { + if (IsWayland()) { + return std::make_unique(system); + } + + return nullptr; +} + +} // namespace Notifications +} // namespace Platform diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index 13fbb0c44..f3953f95e 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -248,6 +248,30 @@ protected: }; +class DummyManager : public NativeManager { +public: + using NativeManager::NativeManager; + +protected: + void doShowNativeNotification( + not_null peer, + std::shared_ptr &userpicView, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) override { + } + void doClearAllFast() override { + } + void doClearFromHistory(not_null history) override { + } + void doClearFromSession(not_null session) override { + } + +}; + QString WrapFromScheduled(const QString &text); } // namespace Notifications From e81f4e8545d81209da28554d4815f6a5110d11fb Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 5 Jan 2021 18:29:13 +0400 Subject: [PATCH 047/396] Add updateControls to resizeEvent in media viewer --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index edc46ebb7..caebcab93 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -476,6 +476,7 @@ void OverlayWidget::resizeEvent(QResizeEvent *e) { _photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize); resizeContentByScreenSize(); + updateControls(); update(); } From d557e0f2b7b100b30c2c0346341ebf1aecdc7f25 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 5 Jan 2021 21:10:56 +0400 Subject: [PATCH 048/396] Don't set geometry to OverlayWidget (except macOS). --- .../SourceFiles/media/view/media_view_overlay_widget.cpp | 3 +++ .../SourceFiles/media/view/media_view_overlay_widget.h | 8 ++++++-- Telegram/lib_ui | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index caebcab93..b758c3723 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -453,6 +453,9 @@ void OverlayWidget::moveToScreen() { } void OverlayWidget::updateGeometry() { + if (!Platform::IsMac()) { + return; + } const auto screen = windowHandle() && windowHandle()->screen() ? windowHandle()->screen() : QApplication::primaryScreen(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 9cab8f0f2..99c2f8467 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -55,10 +55,14 @@ class Pip; #define USE_OPENGL_OVERLAY_WIDGET #endif // Q_OS_MAC && !OS_MAC_OLD +struct OverlayParentTraits : Ui::RpWidgetDefaultTraits { + static constexpr bool kSetZeroGeometry = false; +}; + #ifdef USE_OPENGL_OVERLAY_WIDGET -using OverlayParent = Ui::RpWidgetWrap; +using OverlayParent = Ui::RpWidgetWrap; #else // USE_OPENGL_OVERLAY_WIDGET -using OverlayParent = Ui::RpWidget; +using OverlayParent = Ui::RpWidgetWrap; #endif // USE_OPENGL_OVERLAY_WIDGET class OverlayWidget final diff --git a/Telegram/lib_ui b/Telegram/lib_ui index b5fb343d6..120a52c14 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit b5fb343d6cea52df8cde00e8e3ca1008c05e4589 +Subproject commit 120a52c143a9efbcb41d2bf109e82728becf7d6c From 0bdb38753bfd51e0e673fc2251bfb1d1b1fff046 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 2 Jan 2021 17:36:14 +0300 Subject: [PATCH 049/396] Added items for polls to context menu in sections. Fixed #10055. --- .../history/history_inner_widget.cpp | 17 +++------- .../view/history_view_context_menu.cpp | 31 +++++++++++++++++++ .../history/view/history_view_context_menu.h | 5 +++ .../history/view/media/history_view_poll.cpp | 4 ++- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 7af0eaabf..e70464ef0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1751,18 +1751,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } if (const auto media = item->media()) { if (const auto poll = media->poll()) { - if (!poll->closed()) { - if (poll->voted() && !poll->quiz()) { - _menu->addAction(tr::lng_polls_retract(tr::now), [=] { - session->api().sendPollVotes(itemId, {}); - }); - } - if (item->canStopPoll()) { - _menu->addAction(tr::lng_polls_stop(tr::now), [=] { - HistoryView::StopPoll(session, itemId); - }); - } - } + HistoryView::AddPollActions( + _menu, + poll, + item, + HistoryView::Context::History); } else if (const auto contact = media->sharedContact()) { const auto phone = contact->phoneNumber; _menu->addAction(tr::lng_profile_copy_phone(tr::now), [=] { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 4eb646699..dcabec08a 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -868,6 +868,9 @@ base::unique_qptr FillContextMenu( const auto document = linkDocument ? linkDocument->document().get() : nullptr; + const auto poll = item + ? (item->media() ? item->media()->poll() : nullptr) + : nullptr; const auto hasSelection = !request.selectedItems.empty() || !request.selectedText.empty(); @@ -897,6 +900,8 @@ base::unique_qptr FillContextMenu( // }); // AddToggleGroupingAction(result, linkPeer->peer()); // } + } else if (poll) { + AddPollActions(result, poll, item, list->elementContext()); } else if (!request.overSelection && view && !hasSelection) { const auto owner = &view->data()->history()->owner(); const auto media = view->media(); @@ -979,4 +984,30 @@ void StopPoll(not_null session, FullMsgId itemId) { stop)); } +void AddPollActions( + not_null menu, + not_null poll, + not_null item, + Context context) { + if ((context != Context::History) + && (context != Context::Replies) + && (context != Context::Pinned)) { + return; + } + if (poll->closed()) { + return; + } + const auto itemId = item->fullId(); + if (poll->voted() && !poll->quiz()) { + menu->addAction(tr::lng_polls_retract(tr::now), [=] { + poll->session().api().sendPollVotes(itemId, {}); + }); + } + if (item->canStopPoll()) { + menu->addAction(tr::lng_polls_stop(tr::now), [=] { + StopPoll(&poll->session(), itemId); + }); + } +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h index e33495dbe..be6f160dc 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.h +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -49,5 +49,10 @@ void CopyPostLink( FullMsgId itemId, Context context); void StopPoll(not_null session, FullMsgId itemId); +void AddPollActions( + not_null menu, + not_null poll, + not_null item, + Context context); } // namespace diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index 2738debf8..3d5b393da 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -765,7 +765,9 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m : nullptr; if (animation) { animation->percent.update(progress, anim::linear); - animation->filling.update(progress, anim::linear); + animation->filling.update( + progress, + showVotes() ? anim::easeOutCirc : anim::linear); animation->opacity.update(progress, anim::linear); } const auto height = paintAnswer( From 613bf98283e8e8f6a878f35fb0c23433ff4c8995 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 5 Jan 2021 20:16:47 +0400 Subject: [PATCH 050/396] Fix media viewer controls geometry updating. --- .../SourceFiles/media/view/media_view_overlay_widget.cpp | 5 +++++ Telegram/SourceFiles/media/view/media_view_overlay_widget.h | 1 + 2 files changed, 6 insertions(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b758c3723..59f8e3f06 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -360,6 +360,7 @@ OverlayWidget::OverlayWidget() setWindowFlags(Qt::FramelessWindowHint); } updateGeometry(); + updateControlsGeometry(); setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_TranslucentBackground, true); setMouseTracking(true); @@ -467,6 +468,10 @@ void OverlayWidget::updateGeometry() { } void OverlayWidget::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void OverlayWidget::updateControlsGeometry() { auto navSkip = 2 * st::mediaviewControlMargin + st::mediaviewControlSize; _closeNav = myrtlrect(width() - st::mediaviewControlMargin - st::mediaviewControlSize, st::mediaviewControlMargin, st::mediaviewControlSize, st::mediaviewControlSize); _closeNavIcon = style::centerrect(_closeNav, st::mediaviewClose); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 99c2f8467..4b680626e 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -275,6 +275,7 @@ private: void updateDocSize(); void updateControls(); void updateActions(); + void updateControlsGeometry(); void resizeCenteredControls(); void resizeContentByScreenSize(); From b6b7f5706fab7b8a461bc3b3983ce165ed704325 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 5 Jan 2021 19:19:49 +0400 Subject: [PATCH 051/396] Show small voice chat button for empty voice chats. --- .../view/history_view_group_call_tracker.cpp | 1 + .../view/history_view_top_bar_widget.cpp | 31 ++++++++++++++++++- .../view/history_view_top_bar_widget.h | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp index 5297a71ec..038dc3da5 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp @@ -321,6 +321,7 @@ rpl::producer GroupCallTracker::ContentByCall( call->fullCountValue( ) | rpl::start_with_next([=](int count) { state->current.count = count; + state->current.shown = (count > 0); consumer.put_next_copy(state->current); }, lifetime); 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 0fec8df2d..a681768bf 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -525,12 +525,36 @@ void TopBarWidget::setActiveChat( _activeChat = activeChat; return; } + const auto peerChanged = (_activeChat.key.history() + != activeChat.key.history()); + _activeChat = activeChat; _sendAction = sendAction; _titlePeerText.clear(); _back->clearState(); update(); + if (peerChanged) { + _activeChatLifetime.destroy(); + if (const auto history = _activeChat.key.history()) { + session().changes().peerFlagsValue( + history->peer, + Data::PeerUpdate::Flag::GroupCall + ) | rpl::map([=] { + return history->peer->groupCall(); + }) | rpl::distinct_until_changed( + ) | rpl::map([](Data::GroupCall *call) { + return call ? call->fullCountValue() : rpl::single(-1); + }) | rpl::flatten_latest( + ) | rpl::map([](int count) { + return (count == 0); + }) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=] { + updateControlsVisibility(); + updateControlsGeometry(); + }, _activeChatLifetime); + } + } updateUnreadBadge(); refreshInfoButton(); if (_menu) { @@ -722,7 +746,12 @@ void TopBarWidget::updateControlsVisibility() { _call->setVisible(historyMode && callsEnabled); const auto groupCallsEnabled = [&] { if (const auto peer = _activeChat.key.peer()) { - return peer->canManageGroupCall(); + if (peer->canManageGroupCall()) { + return true; + } else if (const auto call = peer->groupCall()) { + return (call->fullCount() == 0); + } + return false; } return false; }(); 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 076c56f0d..0cde0340a 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -130,6 +130,7 @@ private: const not_null _controller; ActiveChat _activeChat; QString _customTitleText; + rpl::lifetime _activeChatLifetime; int _selectedCount = 0; bool _canDelete = false; From b23e4fa4910ddeca8274dc0718ff6fb032c3adcd Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 5 Jan 2021 19:02:48 +0400 Subject: [PATCH 052/396] Use OpenAL recording backend for calls on Windows. --- Telegram/SourceFiles/calls/calls_call.cpp | 2 ++ Telegram/SourceFiles/calls/calls_group_call.cpp | 2 ++ Telegram/ThirdParty/libtgvoip | 2 +- Telegram/ThirdParty/tgcalls | 2 +- Telegram/lib_webrtc | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 80712f3b9..f4ed5818f 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_panel.h" #include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_media_devices.h" +#include "webrtc/webrtc_create_adm.h" #include "data/data_user.h" #include "data/data_session.h" #include "facades.h" @@ -779,6 +780,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) { sendSignalingData(bytes); }); }, + .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(), }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 2a68b411e..ee8b3e609 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "base/global_shortcuts.h" #include "webrtc/webrtc_media_devices.h" +#include "webrtc/webrtc_create_adm.h" #include @@ -581,6 +582,7 @@ void GroupCall::createAndStartController() { }, .initialInputDeviceId = _audioInputId.toStdString(), .initialOutputDeviceId = _audioOutputId.toStdString(), + .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(), }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 37d98e984..13a5fcb16 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 37d98e984fd6fa389262307db826d52ab86c8241 +Subproject commit 13a5fcb16b04472d808ce122abd695dbf5d206cd diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 6e8b3f7e5..bac020b46 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 6e8b3f7e5f14321cb9e77f18c0a82089bf7f31c6 +Subproject commit bac020b468d51a014843081ab89a291a74a2a64b diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index d9e8307af..bada95202 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit d9e8307af6aa30e9b5e1e5376035641cfd063b7b +Subproject commit bada95202ae45a650d76d9572b20fc9cb03365ad From e11efe483ed55db29f4903daae97ccebcc37ded4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 Jan 2021 19:27:11 +0400 Subject: [PATCH 053/396] Add ability to choose calls audio backend. --- Telegram/SourceFiles/calls/calls_call.cpp | 3 +- .../SourceFiles/calls/calls_group_call.cpp | 3 +- .../calls/calls_group_settings.cpp | 1 + Telegram/SourceFiles/core/core_settings.cpp | 17 ++- Telegram/SourceFiles/core/core_settings.h | 19 ++- .../SourceFiles/settings/settings_calls.cpp | 120 +++++++++++++----- .../SourceFiles/settings/settings_calls.h | 3 + Telegram/lib_webrtc | 2 +- 8 files changed, 127 insertions(+), 41 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index f4ed5818f..2adc74912 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -780,7 +780,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) { sendSignalingData(bytes); }); }, - .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(), + .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator( + settings.callAudioBackend()), }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index ee8b3e609..d4b55dac3 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -582,7 +582,8 @@ void GroupCall::createAndStartController() { }, .initialInputDeviceId = _audioInputId.toStdString(), .initialOutputDeviceId = _audioOutputId.toStdString(), - .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(), + .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator( + settings.callAudioBackend()), }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 0a9414506..f16dc6de4 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -480,6 +480,7 @@ void GroupCallSettingsBox( // Means we finished showing the box. crl::on_main(box, [=] { state->micTester = std::make_unique( + Core::App().settings().callAudioBackend(), Core::App().settings().callInputDeviceId()); state->levelUpdateTimer.callEach(kMicTestUpdateInterval); }); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index cecd332f0..5eb5bbcd8 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -13,12 +13,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" #include "window/section_widget.h" #include "base/platform/base_platform_info.h" +#include "webrtc/webrtc_create_adm.h" #include "facades.h" namespace Core { Settings::Settings() -: _sendSubmitWay(Ui::InputSubmitSettings::Enter) +: _callAudioBackend(Webrtc::Backend::OpenAL) +, _sendSubmitWay(Ui::InputSubmitSettings::Enter) , _floatPlayerColumn(Window::Column::Second) , _floatPlayerCorner(RectPart::TopRight) , _dialogsWidthRatio(DefaultDialogsWidthRatio()) { @@ -112,7 +114,8 @@ QByteArray Settings::serialize() const { << qint32(_ipRevealWarning ? 1 : 0) << qint32(_groupCallPushToTalk ? 1 : 0) << _groupCallPushToTalkShortcut - << qint64(_groupCallPushToTalkDelay); + << qint64(_groupCallPushToTalkDelay) + << qint32(_callAudioBackend); } return result; } @@ -183,6 +186,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 groupCallPushToTalk = _groupCallPushToTalk ? 1 : 0; QByteArray groupCallPushToTalkShortcut = _groupCallPushToTalkShortcut; qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay; + qint32 callAudioBackend = static_cast(_callAudioBackend); stream >> themesAccentColors; if (!stream.atEnd()) { @@ -275,6 +279,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { >> groupCallPushToTalkShortcut >> groupCallPushToTalkDelay; } + if (!stream.atEnd()) { + stream >> callAudioBackend; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -369,6 +376,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _groupCallPushToTalk = (groupCallPushToTalk == 1); _groupCallPushToTalkShortcut = groupCallPushToTalkShortcut; _groupCallPushToTalkDelay = groupCallPushToTalkDelay; + auto uncheckedBackend = static_cast(callAudioBackend); + switch (uncheckedBackend) { + case Webrtc::Backend::OpenAL: + case Webrtc::Backend::ADM: + case Webrtc::Backend::ADM2: _callAudioBackend = uncheckedBackend; break; + } } bool Settings::chatWide() const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index c6333ba82..a476dde4b 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -21,6 +21,10 @@ namespace Window { enum class Column; } // namespace Window +namespace Webrtc { +enum class Backend; +} // namespace Webrtc + namespace Core { class Settings final { @@ -217,6 +221,12 @@ public: void setCallAudioDuckingEnabled(bool value) { _callAudioDuckingEnabled = value; } + [[nodiscard]] Webrtc::Backend callAudioBackend() const { + return _callAudioBackend; + } + void setCallAudioBackend(Webrtc::Backend backend) { + _callAudioBackend = backend; + } [[nodiscard]] bool groupCallPushToTalk() const { return _groupCallPushToTalk; } @@ -531,13 +541,14 @@ private: int _callOutputVolume = 100; int _callInputVolume = 100; bool _callAudioDuckingEnabled = true; + Webrtc::Backend _callAudioBackend = Webrtc::Backend(); bool _groupCallPushToTalk = false; QByteArray _groupCallPushToTalkShortcut; crl::time _groupCallPushToTalkDelay = 20; Window::Theme::AccentColors _themesAccentColors; bool _lastSeenWarningSeen = false; - Ui::SendFilesWay _sendFilesWay; - Ui::InputSubmitSettings _sendSubmitWay; + Ui::SendFilesWay _sendFilesWay = Ui::SendFilesWay(); + Ui::InputSubmitSettings _sendSubmitWay = Ui::InputSubmitSettings(); base::flat_map _soundOverrides; bool _exeLaunchWarning = true; bool _ipRevealWarning = true; @@ -553,8 +564,8 @@ private: rpl::variable _autoDownloadDictionaries = true; rpl::variable _mainMenuAccountsShown = true; bool _tabbedSelectorSectionEnabled = false; // per-window - Window::Column _floatPlayerColumn; // per-window - RectPart _floatPlayerCorner; // per-window + Window::Column _floatPlayerColumn = Window::Column(); // per-window + RectPart _floatPlayerCorner = RectPart(); // per-window bool _thirdSectionInfoEnabled = true; // per-window rpl::event_stream _thirdSectionInfoEnabledValue; // per-window int _thirdSectionExtendedBy = -1; // per-window diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index 1d4b10c4b..bbc0676b8 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -30,11 +30,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_audio_input_tester.h" +#include "webrtc/webrtc_create_adm.h" // Webrtc::Backend. #include "tgcalls/VideoCaptureInterface.h" #include "facades.h" +#include "app.h" // App::restart(). #include "styles/style_layers.h" namespace Settings { +namespace { + +using namespace Webrtc; + +} // namespace Calls::Calls( QWidget *parent, @@ -58,7 +65,7 @@ void Calls::setupContent() { const auto content = Ui::CreateChild(this); const auto &settings = Core::App().settings(); - const auto cameras = Webrtc::GetVideoInputList(); + const auto cameras = GetVideoInputList(); if (!cameras.empty()) { const auto hasCall = (Core::App().calls().currentCall() != nullptr); @@ -66,16 +73,16 @@ void Calls::setupContent() { const auto capturer = capturerOwner.get(); content->lifetime().add([owner = std::move(capturerOwner)]{}); - const auto track = content->lifetime().make_state( + const auto track = content->lifetime().make_state( (hasCall - ? Webrtc::VideoState::Inactive - : Webrtc::VideoState::Active)); + ? VideoState::Inactive + : VideoState::Active)); const auto currentCameraName = [&] { const auto i = ranges::find( cameras, settings.callVideoInputDeviceId(), - &Webrtc::VideoInput::id); + &VideoInput::id); return (i != end(cameras)) ? i->name : tr::lng_settings_call_device_default(tr::now); @@ -93,15 +100,15 @@ void Calls::setupContent() { ), st::settingsButton )->addClickHandler([=] { - const auto &devices = Webrtc::GetVideoInputList(); + const auto &devices = GetVideoInputList(); const auto options = ranges::view::concat( ranges::view::single(tr::lng_settings_call_device_default(tr::now)), - devices | ranges::view::transform(&Webrtc::VideoInput::name) + devices | ranges::view::transform(&VideoInput::name) ) | ranges::to_vector; const auto i = ranges::find( devices, Core::App().settings().callVideoInputDeviceId(), - &Webrtc::VideoInput::id); + &VideoInput::id); const auto currentOption = (i != end(devices)) ? int(i - begin(devices) + 1) : 0; @@ -159,11 +166,11 @@ void Calls::setupContent() { Core::App().calls().currentCallValue( ) | rpl::start_with_next([=](::Calls::Call *value) { if (value) { - track->setState(Webrtc::VideoState::Inactive); + track->setState(VideoState::Inactive); bubbleWrap->resize(bubbleWrap->width(), 0); } else { capturer->setPreferredAspectRatio(0.); - track->setState(Webrtc::VideoState::Active); + track->setState(VideoState::Active); capturer->setOutput(track->sink()); } }, content->lifetime()); @@ -252,6 +259,22 @@ void Calls::setupContent() { // }, content->lifetime()); //#endif // Q_OS_MAC && !OS_MAC_STORE + const auto backend = [&]() -> QString { + using namespace Webrtc; + switch (settings.callAudioBackend()) { + case Backend::OpenAL: return "OpenAL"; + case Backend::ADM: return "WebRTC ADM"; + case Backend::ADM2: return "WebRTC ADM2"; + } + Unexpected("Value in backend."); + }(); + AddButton( + content, + rpl::single("Call audio backend: " + backend), + st::settingsButton + )->addClickHandler([] { + Ui::show(ChooseAudioBackendBox()); + }); AddButton( content, tr::lng_settings_call_open_system_prefs(), @@ -300,27 +323,30 @@ void Calls::requestPermissionAndStartTestingMicrophone() { void Calls::startTestingMicrophone() { _levelUpdateTimer.callEach(kMicTestUpdateInterval); - _micTester = std::make_unique( + _micTester = std::make_unique( + Core::App().settings().callAudioBackend(), Core::App().settings().callInputDeviceId()); } QString CurrentAudioOutputName() { - const auto list = Webrtc::GetAudioOutputList(); + const auto &settings = Core::App().settings(); + const auto list = GetAudioOutputList(settings.callAudioBackend()); const auto i = ranges::find( list, - Core::App().settings().callOutputDeviceId(), - &Webrtc::AudioOutput::id); + settings.callOutputDeviceId(), + &AudioOutput::id); return (i != end(list)) ? i->name : tr::lng_settings_call_device_default(tr::now); } QString CurrentAudioInputName() { - const auto list = Webrtc::GetAudioInputList(); + const auto &settings = Core::App().settings(); + const auto list = GetAudioInputList(settings.callAudioBackend()); const auto i = ranges::find( list, - Core::App().settings().callInputDeviceId(), - &Webrtc::AudioInput::id); + settings.callInputDeviceId(), + &AudioInput::id); return (i != end(list)) ? i->name : tr::lng_settings_call_device_default(tr::now); @@ -330,21 +356,22 @@ object_ptr ChooseAudioOutputBox( Fn chosen, const style::Checkbox *st, const style::Radio *radioSt) { - const auto &devices = Webrtc::GetAudioOutputList(); + const auto &settings = Core::App().settings(); + const auto list = GetAudioOutputList(settings.callAudioBackend()); const auto options = ranges::view::concat( ranges::view::single(tr::lng_settings_call_device_default(tr::now)), - devices | ranges::view::transform(&Webrtc::AudioOutput::name) + list | ranges::view::transform(&AudioOutput::name) ) | ranges::to_vector; const auto i = ranges::find( - devices, - Core::App().settings().callOutputDeviceId(), - &Webrtc::AudioOutput::id); - const auto currentOption = (i != end(devices)) - ? int(i - begin(devices) + 1) + list, + settings.callOutputDeviceId(), + &AudioOutput::id); + const auto currentOption = (i != end(list)) + ? int(i - begin(list) + 1) : 0; const auto save = [=](int option) { const auto deviceId = option - ? devices[option - 1].id + ? list[option - 1].id : "default"; Core::App().calls().setCurrentAudioDevice(false, deviceId); chosen(deviceId, options[option]); @@ -362,21 +389,22 @@ object_ptr ChooseAudioInputBox( Fn chosen, const style::Checkbox *st, const style::Radio *radioSt) { - const auto devices = Webrtc::GetAudioInputList(); + const auto &settings = Core::App().settings(); + const auto list = GetAudioInputList(settings.callAudioBackend()); const auto options = ranges::view::concat( ranges::view::single(tr::lng_settings_call_device_default(tr::now)), - devices | ranges::view::transform(&Webrtc::AudioInput::name) + list | ranges::view::transform(&AudioInput::name) ) | ranges::to_vector; const auto i = ranges::find( - devices, + list, Core::App().settings().callInputDeviceId(), - &Webrtc::AudioInput::id); - const auto currentOption = (i != end(devices)) - ? int(i - begin(devices) + 1) + &AudioInput::id); + const auto currentOption = (i != end(list)) + ? int(i - begin(list) + 1) : 0; const auto save = [=](int option) { const auto deviceId = option - ? devices[option - 1].id + ? list[option - 1].id : "default"; Core::App().calls().setCurrentAudioDevice(true, deviceId); chosen(deviceId, options[option]); @@ -390,5 +418,33 @@ object_ptr ChooseAudioInputBox( radioSt); } +object_ptr ChooseAudioBackendBox( + const style::Checkbox *st, + const style::Radio *radioSt) { + const auto &settings = Core::App().settings(); + const auto list = GetAudioInputList(settings.callAudioBackend()); + const auto options = std::vector{ + "OpenAL", + "Webrtc ADM", +#ifdef Q_OS_WIN + "Webrtc ADM2", +#endif // Q_OS_WIN + }; + const auto currentOption = static_cast(settings.callAudioBackend()); + const auto save = [=](int option) { + Core::App().settings().setCallAudioBackend( + static_cast(option)); + Core::App().saveSettings(); + App::restart(); + }; + return Box( + rpl::single("Calls audio backend"), + options, + currentOption, + save, + st, + radioSt); +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_calls.h b/Telegram/SourceFiles/settings/settings_calls.h index db298f746..068208242 100644 --- a/Telegram/SourceFiles/settings/settings_calls.h +++ b/Telegram/SourceFiles/settings/settings_calls.h @@ -69,6 +69,9 @@ inline constexpr auto kMicTestAnimationDuration = crl::time(200); Fn chosen, const style::Checkbox *st = nullptr, const style::Radio *radioSt = nullptr); +[[nodiscard]] object_ptr ChooseAudioBackendBox( + const style::Checkbox *st = nullptr, + const style::Radio *radioSt = nullptr); } // namespace Settings diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index bada95202..4dd8c26bd 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit bada95202ae45a650d76d9572b20fc9cb03365ad +Subproject commit 4dd8c26bd1748a677df34785e62e031785e93e23 From 7508980f62fe779f2b71d102e657f113519590e7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 Jan 2021 20:02:21 +0400 Subject: [PATCH 054/396] Beta version 2.5.4. - Implement new audio module code for calls and voice chats. - Allow retracting votes from polls in comments to channel posts. - Show small voice chat button for empty voice chats. - Fix media viewer updating when screen resolution is changed. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 10 ++++++++++ Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 7 +++++++ 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index b92d22f3a..376961eb4 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.5.4.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index ed2715df0..a75cb0dab 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,3,0 - PRODUCTVERSION 2,5,3,0 + FILEVERSION 2,5,4,0 + PRODUCTVERSION 2,5,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.3.0" + VALUE "FileVersion", "2.5.4.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.3.0" + VALUE "ProductVersion", "2.5.4.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 35c14a7b5..a327a25dc 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,3,0 - PRODUCTVERSION 2,5,3,0 + FILEVERSION 2,5,4,0 + PRODUCTVERSION 2,5,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.3.0" + VALUE "FileVersion", "2.5.4.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.3.0" + VALUE "ProductVersion", "2.5.4.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 98a7f87a0..8ce1b8281 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -89,6 +89,16 @@ std::map BetaLogs() { "- Fix blurred thumbnails in Shared Links section.\n" }, + { + 2005004, + "- Implement new audio module code for calls and voice chats.\n" + + "- Allow retracting votes from polls in comments to channel posts.\n" + + "- Show small voice chat button for empty voice chats.\n" + + "- Fix media viewer updating when screen resolution is changed.\n" + }, }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 781d7ff05..23c450813 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005003; -constexpr auto AppVersionStr = "2.5.3"; +constexpr auto AppVersion = 2005004; +constexpr auto AppVersionStr = "2.5.4"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 822879515..ce396b0ee 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005003 +AppVersion 2005004 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.3 -AppVersionStr 2.5.3 +AppVersionStrSmall 2.5.4 +AppVersionStr 2.5.4 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 2.5.3.beta +AppVersionOriginal 2.5.4.beta diff --git a/changelog.txt b/changelog.txt index 4c7f2ec62..458087080 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +2.5.4 beta (07.01.21) + +- Implement new audio module code for calls and voice chats. +- Allow retracting votes from polls in comments to channel posts. +- Show small voice chat button for empty voice chats. +- Fix media viewer updating when screen resolution is changed. + 2.5.3 beta (30.12.20) - Allow using mouse buttons in Push-to-Talk shortcut. From 348dfefbaadc07532b3465b42c2326f90c2b0bd5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 Jan 2021 20:07:06 +0400 Subject: [PATCH 055/396] Beta version 2.5.4: Update lib_ui. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 120a52c14..9ebd51c8f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 120a52c143a9efbcb41d2bf109e82728becf7d6c +Subproject commit 9ebd51c8f89799d5b6125e34028528d52b0b0b8e From 0b85d0f1852ab0697ba64b208f2fffc500e75976 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 Jan 2021 22:02:22 +0400 Subject: [PATCH 056/396] Beta version 2.5.4: Fix build on macOS. --- Telegram/lib_webrtc | 2 +- docs/building-xcode.md | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index 4dd8c26bd..c41c510b2 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit 4dd8c26bd1748a677df34785e62e031785e93e23 +Subproject commit c41c510b29842d16973c5a440454e22a012c39ae diff --git a/docs/building-xcode.md b/docs/building-xcode.md index 91c878b6a..a684fc555 100644 --- a/docs/building-xcode.md +++ b/docs/building-xcode.md @@ -68,17 +68,8 @@ Go to ***BuildPath*** and run cd build CFLAGS="$UNGUARDED" CPPFLAGS="$UNGUARDED" cmake -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.12 -D CMAKE_INSTALL_PREFIX:STRING=/usr/local/macos .. make $MAKE_THREADS_CNT - cd ../.. - - xz_ver=5.2.4 - wget https://tukaani.org/xz/xz-$xz_ver.tar.gz - tar -xvzf xz-$xz_ver.tar.gz - rm xz-$xz_ver.tar.gz - cd xz-$xz_ver - CFLAGS="$MIN_VER" LDFLAGS="$MIN_VER" ./configure --prefix=/usr/local/macos - make $MAKE_THREADS_CNT sudo make install - cd .. + cd ../.. git clone https://github.com/desktop-app/zlib.git cd zlib From 1704cb345a26578b62b60c72b86cf9670d076136 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 Jan 2021 23:17:54 +0400 Subject: [PATCH 057/396] Beta version 2.5.4: Fix build on Linux. --- Telegram/ThirdParty/tgcalls | 2 +- snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index bac020b46..bc56b9108 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit bac020b468d51a014843081ab89a291a74a2a64b +Subproject commit bc56b9108ae66e234c3ea241696039166b391e85 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 67d54c356..94343903e 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -253,7 +253,7 @@ parts: webrtc: source: https://github.com/desktop-app/tg_owt.git source-depth: 1 - source-commit: 75ac66937341d8a9207375aaee79b4bdc500146c + source-commit: d91d618889cc743aafd809151449012de62e5327 plugin: cmake build-packages: - yasm From adc8d6a6d18bece835267b7600c26ff53e78b8fb Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 8 Jan 2021 19:09:45 +0400 Subject: [PATCH 058/396] Fix audio recording in voice chats. --- Telegram/ThirdParty/tgcalls | 2 +- Telegram/lib_webrtc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index bc56b9108..513c63560 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit bc56b9108ae66e234c3ea241696039166b391e85 +Subproject commit 513c635602ae1bb05e17bce1d6af2cb040bf4ae3 diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index c41c510b2..c9691526c 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit c41c510b29842d16973c5a440454e22a012c39ae +Subproject commit c9691526c98fa76f08de295052ca8bd3560ec80b From 8ae1b10b91267935419e57468e76004296f7201b Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 9 Jan 2021 13:55:50 +0400 Subject: [PATCH 059/396] Fix media viewer regression. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 4 ++-- Telegram/ThirdParty/tgcalls | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 59f8e3f06..b9926b702 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -454,7 +454,7 @@ void OverlayWidget::moveToScreen() { } void OverlayWidget::updateGeometry() { - if (!Platform::IsMac()) { + if (Platform::IsLinux()) { return; } const auto screen = windowHandle() && windowHandle()->screen() @@ -483,8 +483,8 @@ void OverlayWidget::updateControlsGeometry() { _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2); _photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize); - resizeContentByScreenSize(); updateControls(); + resizeContentByScreenSize(); update(); } diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 513c63560..b85f5e9a2 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 513c635602ae1bb05e17bce1d6af2cb040bf4ae3 +Subproject commit b85f5e9a25171c08a0e9486c27f07331e26ff1eb From a030911ad549c3747e50c8225eafbcd3a85bc736 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 8 Jan 2021 01:38:09 +0300 Subject: [PATCH 060/396] Fixed filling context menu in TabbedPanel between sections. Fixed #10082. --- .../chat_helpers/tabbed_selector.cpp | 12 ++++++---- .../chat_helpers/tabbed_selector.h | 8 ++----- .../SourceFiles/history/history_widget.cpp | 22 ++++++++++--------- .../history_view_compose_controls.cpp | 5 ++++- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index a3ccef428..cc0330d84 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -877,11 +877,8 @@ void TabbedSelector::scrollToY(int y) { } } -void TabbedSelector::contextMenuEvent(QContextMenuEvent *e) { +void TabbedSelector::showMenuWithType(SendMenu::Type type) { _menu = base::make_unique_q(this); - const auto type = _sendMenuType - ? _sendMenuType() - : SendMenu::Type::Disabled; currentTab()->widget()->fillContextMenu(_menu, type); if (!_menu->actions().empty()) { @@ -889,6 +886,13 @@ void TabbedSelector::contextMenuEvent(QContextMenuEvent *e) { } } +rpl::producer<> TabbedSelector::contextMenuRequested() const { + return events( + ) | rpl::filter([=](not_null e) { + return e->type() == QEvent::ContextMenu; + }) | rpl::to_empty; +} + TabbedSelector::Inner::Inner( QWidget *parent, not_null controller) diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index a73e97e67..0011fecb9 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -82,6 +82,7 @@ public: rpl::producer<> cancelled() const; rpl::producer<> checkForHide() const; rpl::producer<> slideFinished() const; + rpl::producer<> contextMenuRequested() const; void setRoundRadius(int radius); void refreshStickers(); @@ -109,9 +110,7 @@ public: _beforeHidingCallback = std::move(callback); } - void setSendMenuType(Fn &&callback) { - _sendMenuType = std::move(callback); - } + void showMenuWithType(SendMenu::Type type); // Float player interface. bool floatPlayerHandleWheelEvent(QEvent *e); @@ -127,7 +126,6 @@ public: protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; private: class Tab { @@ -228,8 +226,6 @@ private: Fn _afterShownCallback; Fn _beforeHidingCallback; - Fn _sendMenuType; - rpl::event_stream<> _showRequests; rpl::event_stream<> _slideFinished; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index b59ec772a..47f89ecd1 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -846,6 +846,11 @@ void HistoryWidget::initTabbedSelector() { return base::EventFilterResult::Continue; }); + auto filter = rpl::filter([=] { + return !isHidden(); + }); + using Selector = TabbedSelector; + selector->emojiChosen( ) | rpl::filter([=] { return !isHidden() && !_field->isHidden(); @@ -854,27 +859,24 @@ void HistoryWidget::initTabbedSelector() { }, lifetime()); selector->fileChosen( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=](TabbedSelector::FileChosen data) { + ) | filter | rpl::start_with_next([=](Selector::FileChosen data) { sendExistingDocument(data.document, data.options); }, lifetime()); selector->photoChosen( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=](TabbedSelector::PhotoChosen data) { + ) | filter | rpl::start_with_next([=](Selector::PhotoChosen data) { sendExistingPhoto(data.photo, data.options); }, lifetime()); selector->inlineResultChosen( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=](TabbedSelector::InlineChosen data) { + ) | filter | rpl::start_with_next([=](Selector::InlineChosen data) { sendInlineResult(data); }, lifetime()); - selector->setSendMenuType([=] { return sendMenuType(); }); + selector->contextMenuRequested( + ) | filter | rpl::start_with_next([=] { + selector->showMenuWithType(sendMenuType()); + }, lifetime()); } void HistoryWidget::supportInitAutocomplete() { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 54f972d45..da991c175 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1410,7 +1410,10 @@ void ComposeControls::initTabbedSelector() { selector->inlineResultChosen( ) | rpl::start_to_stream(_inlineResultChosen, wrap->lifetime()); - selector->setSendMenuType([=] { return sendMenuType(); }); + selector->contextMenuRequested( + ) | rpl::start_with_next([=] { + selector->showMenuWithType(sendMenuType()); + }, wrap->lifetime()); } void ComposeControls::initSendButton() { From 838a3b23c768beefe73d4ca6b0018e4961e34429 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 10 Jan 2021 12:30:58 +0400 Subject: [PATCH 061/396] Beta version 2.5.5. - Fix recording of audio in voice chats. - Fix media viewer zoom and crashing. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 6 ++++++ Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 5 +++++ 7 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 376961eb4..9dc2b015b 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.5.5.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index a75cb0dab..ae2932ce5 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,4,0 - PRODUCTVERSION 2,5,4,0 + FILEVERSION 2,5,5,0 + PRODUCTVERSION 2,5,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.4.0" + VALUE "FileVersion", "2.5.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.4.0" + VALUE "ProductVersion", "2.5.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index a327a25dc..b60403993 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,4,0 - PRODUCTVERSION 2,5,4,0 + FILEVERSION 2,5,5,0 + PRODUCTVERSION 2,5,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.4.0" + VALUE "FileVersion", "2.5.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.4.0" + VALUE "ProductVersion", "2.5.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 8ce1b8281..7900cf6ed 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -99,6 +99,12 @@ std::map BetaLogs() { "- Fix media viewer updating when screen resolution is changed.\n" }, + { + 2005005, + "- Fix recording of audio in voice chats.\n" + + "- Fix media viewer zoom and crashing.\n" + }, }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 23c450813..809e8a840 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005004; -constexpr auto AppVersionStr = "2.5.4"; +constexpr auto AppVersion = 2005005; +constexpr auto AppVersionStr = "2.5.5"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index ce396b0ee..606bbc64c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005004 +AppVersion 2005005 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.4 -AppVersionStr 2.5.4 +AppVersionStrSmall 2.5.5 +AppVersionStr 2.5.5 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 2.5.4.beta +AppVersionOriginal 2.5.5.beta diff --git a/changelog.txt b/changelog.txt index 458087080..0f490d483 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +2.5.5 beta (10.01.21) + +- Fix recording of audio in voice chats. +- Fix media viewer zoom and crashing. + 2.5.4 beta (07.01.21) - Implement new audio module code for calls and voice chats. From a483eb98a191efd19af6d6689bd42ec0867fde0e Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 11 Jan 2021 12:38:47 +0400 Subject: [PATCH 062/396] Remove not-needed requests for file parts above real size. --- .../storage/download_manager_mtproto.cpp | 31 ++++++++++++++----- .../storage/download_manager_mtproto.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp index 4ba4a9ce9..1af23d665 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp @@ -171,6 +171,10 @@ void DownloadManagerMtproto::checkSendNext(MTP::DcId dcId, Queue &queue) { } } +void DownloadManagerMtproto::checkSendNextAfterSuccess(MTP::DcId dcId) { + checkSendNext(dcId, _queues[dcId]); +} + bool DownloadManagerMtproto::trySendNextPart(MTP::DcId dcId, Queue &queue) { auto &balanceData = _balanceData[dcId]; const auto &sessions = balanceData.sessions; @@ -227,10 +231,6 @@ void DownloadManagerMtproto::requestSucceeded( crl::time timeAtRequestStart) { using namespace rpl::mappers; - const auto guard = gsl::finally([&] { - checkSendNext(dcId, _queues[dcId]); - }); - const auto i = _balanceData.find(dcId); Assert(i != end(_balanceData)); auto &dc = i->second; @@ -606,24 +606,34 @@ void DownloadMtprotoTask::normalPartLoaded( const auto requestData = finishSentRequest( requestId, FinishRequestReason::Success); + const auto owner = _owner; + const auto dcId = this->dcId(); result.match([&](const MTPDupload_fileCdnRedirect &data) { switchToCDN(requestData, data); }, [&](const MTPDupload_file &data) { partLoaded(requestData.offset, data.vbytes().v); }); + + // 'this' may be deleted at this point. + owner->checkSendNextAfterSuccess(dcId); } void DownloadMtprotoTask::webPartLoaded( const MTPupload_WebFile &result, mtpRequestId requestId) { + const auto requestData = finishSentRequest( + requestId, + FinishRequestReason::Success); + const auto owner = _owner; + const auto dcId = this->dcId(); result.match([&](const MTPDupload_webFile &data) { - const auto requestData = finishSentRequest( - requestId, - FinishRequestReason::Success); if (setWebFileSizeHook(data.vsize().v)) { partLoaded(requestData.offset, data.vbytes().v); } }); + + // 'this' may be deleted at this point. + owner->checkSendNextAfterSuccess(dcId); } void DownloadMtprotoTask::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId) { @@ -647,6 +657,13 @@ void DownloadMtprotoTask::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequ const auto requestData = finishSentRequest( requestId, FinishRequestReason::Success); + const auto owner = _owner; + const auto dcId = this->dcId(); + const auto guard = gsl::finally([=] { + // 'this' may be deleted at this point. + owner->checkSendNextAfterSuccess(dcId); + }); + auto key = bytes::make_span(_cdnEncryptionKey); auto iv = bytes::make_span(_cdnEncryptionIV); Expects(key.size() == MTP::CTRState::KeySize); diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.h b/Telegram/SourceFiles/storage/download_manager_mtproto.h index 46d91f3e0..9c467c16f 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.h +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.h @@ -51,6 +51,7 @@ public: int index, int amountAtRequestStart, crl::time timeAtRequestStart); + void checkSendNextAfterSuccess(MTP::DcId dcId); [[nodiscard]] int chooseSessionIndex(MTP::DcId dcId) const; private: From 8fffe7d12811880eab932097d6cdd41f571c195d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 9 Jan 2021 22:52:55 +0300 Subject: [PATCH 063/396] Added ability to show song cover in HistoryView and Overview::Layout. --- Telegram/SourceFiles/data/data_document.cpp | 4 ++ Telegram/SourceFiles/data/data_document.h | 1 + .../view/media/history_view_document.cpp | 60 ++++++++++++++++--- .../view/media/history_view_document.h | 6 ++ .../SourceFiles/overview/overview_layout.cpp | 29 ++++++--- Telegram/SourceFiles/ui/image/image.cpp | 8 ++- Telegram/SourceFiles/ui/image/image.h | 3 +- 7 files changed, 93 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index b7c8d16ca..dc1495791 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1488,6 +1488,10 @@ bool DocumentData::isSong() const { return (type == SongDocument); } +bool DocumentData::isSongWithCover() const { + return isSong() && hasThumbnail(); +} + bool DocumentData::isAudioFile() const { if (isVoiceMessage()) { return false; diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 35b998d12..7ee523c0d 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -139,6 +139,7 @@ public: [[nodiscard]] bool isVoiceMessage() const; [[nodiscard]] bool isVideoMessage() const; [[nodiscard]] bool isSong() const; + [[nodiscard]] bool isSongWithCover() const; [[nodiscard]] bool isAudioFile() const; [[nodiscard]] bool isVideoFile() const; [[nodiscard]] bool isAnimation() const; diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index df58d562a..a28aabf31 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/format_values.h" #include "ui/cached_round_corners.h" +#include "ui/ui_utility.h" #include "layout.h" // FullSelection #include "data/data_session.h" #include "data/data_document.h" @@ -442,14 +443,14 @@ void Document::draw( } } else { p.setPen(Qt::NoPen); - if (selected) { - p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); - } else { - p.setBrush(outbg ? st::msgFileOutBg : st::msgFileInBg); - } - { + const auto coverDrawn = _data->isSongWithCover() + && DrawThumbnailAsSongCover(p, _dataMedia, inner, selected); + if (!coverDrawn) { PainterHighQualityEnabler hq(p); + p.setBrush(selected + ? (outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected) + : (outbg ? st::msgFileOutBg : st::msgFileInBg)); p.drawEllipse(inner); } @@ -581,7 +582,7 @@ void Document::ensureDataMediaCreated() const { return; } _dataMedia = _data->createMediaView(); - if (Get()) { + if (Get() || _data->isSongWithCover()) { _dataMedia->thumbnailWanted(_realParent->fullId()); } history()->owner().registerHeavyViewPart(_parent); @@ -1072,4 +1073,49 @@ Ui::Text::String Document::createCaption() { timestampLinkBase); } +bool DrawThumbnailAsSongCover( + Painter &p, + const std::shared_ptr &dataMedia, + const QRect &rect, + const bool selected) { + if (!dataMedia) { + return false; + } + + QPixmap cover; + + const auto ow = rect.width(); + const auto oh = rect.height(); + const auto r = ImageRoundRadius::Ellipse; + const auto c = RectPart::AllCorners; + const auto color = &st::songCoverOverlayFg; + const auto aspectRatio = Qt::KeepAspectRatioByExpanding; + + const auto scaled = [&](not_null image) -> std::pair { + const auto size = image->size().scaled(ow, oh, aspectRatio); + return { size.width(), size.height() }; + }; + + if (const auto normal = dataMedia->thumbnail()) { + const auto &[w, h] = scaled(normal); + cover = normal->pixSingle(w, h, ow, oh, r, c, color); + } else if (const auto blurred = dataMedia->thumbnailInline()) { + const auto &[w, h] = scaled(blurred); + cover = blurred->pixBlurredSingle(w, h, ow, oh, r, c, color); + } else { + return false; + } + if (selected) { + auto selectedCover = Images::prepareColored( + p.textPalette().selectOverlay, + cover.toImage()); + cover = QPixmap::fromImage( + std::move(selectedCover), + Qt::ColorOnly); + } + p.drawPixmap(rect.topLeft(), cover); + + return true; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index 150c47684..e438d30ec 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -143,4 +143,10 @@ private: }; +bool DrawThumbnailAsSongCover( + Painter &p, + const std::shared_ptr &dataMedia, + const QRect &rect, + const bool selected = false); + } // namespace HistoryView diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 4c68b7d7a..d9ee1a862 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/history_item_components.h" #include "history/view/history_view_cursor_state.h" +#include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover #include "base/unixtime.h" #include "base/qt_adapters.h" #include "ui/effects/round_checkbox.h" @@ -1008,21 +1009,33 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con auto inner = style::rtlrect(_st.songPadding.left(), _st.songPadding.top(), _st.songThumbSize, _st.songThumbSize, _width); if (clip.intersects(inner)) { + const auto isLoading = (!cornerDownload + && (_data->loading() || _data->uploading())); p.setPen(Qt::NoPen); - if (selected) { - p.setBrush(st::msgFileInBgSelected); - } else { - auto over = ClickHandler::showAsActive((!cornerDownload && (_data->loading() || _data->uploading())) ? _cancell : (loaded || _dataMedia->canBePlayed()) ? _openl : _savel); - p.setBrush(anim::brush(_st.songIconBg, _st.songOverBg, _a_iconOver.value(over ? 1. : 0.))); - } - { + using namespace HistoryView; + const auto coverDrawn = _data->isSongWithCover() + && DrawThumbnailAsSongCover(p, _dataMedia, inner, selected); + if (!coverDrawn) { + if (selected) { + p.setBrush(st::msgFileInBgSelected); + } else { + const auto over = ClickHandler::showAsActive(isLoading + ? _cancell + : (loaded || _dataMedia->canBePlayed()) + ? _openl + : _savel); + p.setBrush(anim::brush( + _st.songIconBg, + _st.songOverBg, + _a_iconOver.value(over ? 1. : 0.))); + } PainterHighQualityEnabler hq(p); p.drawEllipse(inner); } const auto icon = [&] { - if (!cornerDownload && (_data->loading() || _data->uploading())) { + if (isLoading) { return &(selected ? _st.songCancelSelected : _st.songCancel); } else if (showPause) { return &(selected ? _st.songPauseSelected : _st.songPause); diff --git a/Telegram/SourceFiles/ui/image/image.cpp b/Telegram/SourceFiles/ui/image/image.cpp index ceeff1125..0dad5e9ed 100644 --- a/Telegram/SourceFiles/ui/image/image.cpp +++ b/Telegram/SourceFiles/ui/image/image.cpp @@ -343,7 +343,8 @@ const QPixmap &Image::pixBlurredSingle( int outerw, int outerh, ImageRoundRadius radius, - RectParts corners) const { + RectParts corners, + const style::color *colored) const { if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); } else { @@ -365,11 +366,14 @@ const QPixmap &Image::pixBlurredSingle( } else if (radius == ImageRoundRadius::Ellipse) { options |= Option::Circled | cornerOptions(corners); } + if (colored) { + options |= Option::Colored; + } auto k = SinglePixKey(options); auto i = _cache.find(k); if (i == _cache.cend() || i->second.width() != (outerw * cIntRetinaFactor()) || i->second.height() != (outerh * cIntRetinaFactor())) { - auto p = pixNoCache(w, h, options, outerw, outerh); + auto p = pixNoCache(w, h, options, outerw, outerh, colored); p.setDevicePixelRatio(cRetinaFactor()); i = _cache.emplace_or_assign(k, p).first; } diff --git a/Telegram/SourceFiles/ui/image/image.h b/Telegram/SourceFiles/ui/image/image.h index 16bba2b1b..07f5a6a4d 100644 --- a/Telegram/SourceFiles/ui/image/image.h +++ b/Telegram/SourceFiles/ui/image/image.h @@ -70,7 +70,8 @@ public: int outerw, int outerh, ImageRoundRadius radius, - RectParts corners = RectPart::AllCorners) const; + RectParts corners = RectPart::AllCorners, + const style::color *colored = nullptr) const; [[nodiscard]] const QPixmap &pixCircled(int w = 0, int h = 0) const; [[nodiscard]] const QPixmap &pixBlurredCircled(int w = 0, int h = 0) const; [[nodiscard]] QPixmap pixNoCache( From cf0cde6e8356c342a6d6286abe62dcac3c72b226 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 10 Jan 2021 02:04:03 +0300 Subject: [PATCH 064/396] Added ability to show song cover in EditCaptionBox and SendFilesBox. --- .../SourceFiles/boxes/edit_caption_box.cpp | 79 ++++++++++++++----- Telegram/SourceFiles/boxes/edit_caption_box.h | 2 + .../ui/chat/attach/attach_prepare.cpp | 18 +++++ .../ui/chat/attach/attach_prepare.h | 2 + .../attach/attach_single_file_preview.cpp | 24 ++++-- .../chat/attach/attach_single_file_preview.h | 2 + 6 files changed, 100 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index edcb57b56..ca9408b30 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_drag_area.h" #include "history/history_item.h" +#include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover #include "platform/platform_specific.h" #include "lang/lang_keys.h" #include "media/streaming/media_streaming_instance.h" @@ -171,7 +172,9 @@ EditCaptionBox::EditCaptionBox( _thumbw = 0; _thumbnailImageLoaded = true; } else { - const auto thumbSize = st::msgFileThumbLayout.thumbSize; + const auto thumbSize = (!media->document()->isSongWithCover() + ? st::msgFileThumbLayout + : st::msgFileLayout).thumbSize; const auto tw = dimensions.width(), th = dimensions.height(); if (tw > th) { _thumbw = (tw * thumbSize) / th; @@ -183,19 +186,31 @@ EditCaptionBox::EditCaptionBox( if (!image) { return; } - const auto options = Images::Option::Smooth - | Images::Option::RoundedSmall - | Images::Option::RoundedTopLeft - | Images::Option::RoundedTopRight - | Images::Option::RoundedBottomLeft - | Images::Option::RoundedBottomRight; - _thumb = App::pixmapFromImageInPlace(Images::prepare( - image->original(), - _thumbw * cIntRetinaFactor(), - 0, - options, - thumbSize, - thumbSize)); + if (media->document()->isSongWithCover()) { + const auto size = QSize(thumbSize, thumbSize); + _thumb = QPixmap(size); + _thumb.fill(Qt::transparent); + Painter p(&_thumb); + + HistoryView::DrawThumbnailAsSongCover( + p, + _documentMedia, + QRect(QPoint(), size)); + } else { + const auto options = Images::Option::Smooth + | Images::Option::RoundedSmall + | Images::Option::RoundedTopLeft + | Images::Option::RoundedTopRight + | Images::Option::RoundedBottomLeft + | Images::Option::RoundedBottomRight; + _thumb = App::pixmapFromImageInPlace(Images::prepare( + image->original(), + _thumbw * cIntRetinaFactor(), + 0, + options, + thumbSize, + thumbSize)); + } _thumbnailImageLoaded = true; }; _refreshThumbnail(); @@ -539,6 +554,14 @@ void EditCaptionBox::updateEditPreview() { song->title, song->performer); _isAudio = true; + + if (auto cover = song->cover; !cover.isNull()) { + _thumb = Ui::PrepareSongCoverForThumbnail( + cover, + st::msgFileLayout.thumbSize); + _thumbw = _thumb.width() / cIntRetinaFactor(); + _thumbh = _thumb.height() / cIntRetinaFactor(); + } } const auto getExt = [&] { @@ -810,15 +833,21 @@ void EditCaptionBox::setupDragArea() { areas.photo->setDroppedCallback(droppedCallback(true)); } +bool EditCaptionBox::isThumbedLayout() const { + return (_thumbw && !_isAudio); +} + void EditCaptionBox::updateBoxSize() { auto newHeight = st::boxPhotoPadding.top() + st::boxPhotoCaptionSkip + _field->height() + errorTopSkip() + st::normalFont->height; if (_photo) { newHeight += _wayWrap->height() / 2; } - const auto &st = _thumbw ? st::msgFileThumbLayout : st::msgFileLayout; + const auto &st = isThumbedLayout() + ? st::msgFileThumbLayout + : st::msgFileLayout; if (_photo || _animated) { newHeight += std::max(_thumbh, _gifh); - } else if (_thumbw || _doc) { + } else if (isThumbedLayout() || _doc) { newHeight += 0 + st.thumbSize + 0; } else { newHeight += st::boxTitleFont->height; @@ -902,7 +931,9 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { icon->paintInCenter(p, inner); } } else if (_doc) { - const auto &st = _thumbw ? st::msgFileThumbLayout : st::msgFileLayout; + const auto &st = isThumbedLayout() + ? st::msgFileThumbLayout + : st::msgFileLayout; const auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); const auto h = 0 + st.thumbSize + 0; const auto nameleft = 0 + st.thumbSize + st.padding.right(); @@ -918,18 +949,24 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { // Ui::FillRoundCorner(p, x, y, w, h, st::msgInBg, Ui::MessageInCorners, &st::msgInShadow); const auto rthumb = style::rtlrect(x + 0, y + 0, st.thumbSize, st.thumbSize, width()); - if (_thumbw) { + if (isThumbedLayout()) { p.drawPixmap(rthumb.topLeft(), _thumb); } else { p.setPen(Qt::NoPen); - p.setBrush(st::msgFileInBg); - { + if (_isAudio && _thumbw) { + p.drawPixmap(rthumb.topLeft(), _thumb); + } else { + p.setBrush(st::msgFileInBg); PainterHighQualityEnabler hq(p); p.drawEllipse(rthumb); } - const auto icon = &(_isAudio ? st::historyFileInPlay : _isImage ? st::historyFileInImage : st::historyFileInDocument); + const auto icon = &(_isAudio + ? st::historyFileInPlay + : _isImage + ? st::historyFileInImage + : st::historyFileInDocument); icon->paintInCenter(p, rthumb); } p.setFont(st::semiboldFont); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 048d260ad..76e80c3c1 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -96,6 +96,8 @@ private: void createEditMediaButton(); bool setPreparedList(Ui::PreparedList &&list); + bool isThumbedLayout() const; + inline QString getNewMediaPath() { return _preparedList.files.empty() ? QString() diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index e24e4107f..8ba397094 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_send_files_way.h" +#include "ui/image/image_prepare.h" +#include "ui/ui_utility.h" #include "core/mime_type.h" namespace Ui { @@ -255,4 +257,20 @@ std::vector DivideByGroups( return result; } +QPixmap PrepareSongCoverForThumbnail(QImage image, int size) { + const auto scaledSize = image.size().scaled( + size, + size, + Qt::KeepAspectRatioByExpanding); + using Option = Images::Option; + return PixmapFromImage(Images::prepare( + std::move(image), + scaledSize.width() * style::DevicePixelRatio(), + scaledSize.height() * style::DevicePixelRatio(), + Option::Circled | Option::Colored | Option::Smooth, + size, + size, + &st::songCoverOverlayFg)); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index 630b01aea..fe7d5251d 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -132,4 +132,6 @@ struct PreparedGroup { [[nodiscard]] int MaxAlbumItems(); [[nodiscard]] bool ValidateThumbDimensions(int width, int height); +[[nodiscard]] QPixmap PrepareSongCoverForThumbnail(QImage image, int size); + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp index 61119b362..c5e0d343e 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -32,7 +32,7 @@ SingleFilePreview::SingleFilePreview( _editMedia->setIconOverride(&st::sendBoxAlbumGroupEditButtonIconFile); _deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile); - const auto &st = _fileThumb.isNull() + const auto &st = !isThumbedLayout() ? st::attachPreviewLayout : st::attachPreviewThumbLayout; resize(width(), st.thumbSize); @@ -106,13 +106,19 @@ void SingleFilePreview::preparePreview(const PreparedFile &file) { songTitle = song->title; songPerformer = song->performer; _fileIsAudio = true; + + if (auto cover = song->cover; !cover.isNull()) { + _fileThumb = Ui::PrepareSongCoverForThumbnail( + cover, + st::attachPreviewLayout.thumbSize); + } } } _name = ComposeNameString(filename, songTitle, songPerformer); _statusText = FormatSizeText(fileinfo.size()); } - const auto &st = _fileThumb.isNull() + const auto &st = !isThumbedLayout() ? st::attachPreviewLayout : st::attachPreviewThumbLayout; const auto nameleft = st.thumbSize + st.padding.right(); @@ -141,7 +147,7 @@ void SingleFilePreview::paintEvent(QPaintEvent *e) { auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); auto h = height(); - const auto &st = _fileThumb.isNull() + const auto &st = !isThumbedLayout() ? st::attachPreviewLayout : st::attachPreviewThumbLayout; const auto nameleft = st.thumbSize + st.padding.right(); @@ -149,12 +155,14 @@ void SingleFilePreview::paintEvent(QPaintEvent *e) { const auto statustop = st.statusTop; const auto x = (width() - w) / 2, y = 0; - if (_fileThumb.isNull()) { + if (!isThumbedLayout()) { QRect inner(style::rtlrect(x, y, st.thumbSize, st.thumbSize, width())); p.setPen(Qt::NoPen); - p.setBrush(st::msgFileInBg); - { + if (_fileIsAudio && !_fileThumb.isNull()) { + p.drawPixmap(inner.topLeft(), _fileThumb); + } else { + p.setBrush(st::msgFileInBg); PainterHighQualityEnabler hq(p); p.drawEllipse(inner); } @@ -188,4 +196,8 @@ void SingleFilePreview::resizeEvent(QResizeEvent *e) { _editMedia->moveToRight(right, top); } +bool SingleFilePreview::isThumbedLayout() const { + return (!_fileThumb.isNull() && !_fileIsAudio); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h index e549cf3f0..630e5e807 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h @@ -32,6 +32,8 @@ private: void preparePreview(const PreparedFile &file); void prepareThumb(const QImage &preview); + bool isThumbedLayout() const; + QPixmap _fileThumb; QString _name; QString _statusText; From 1607752cf9b23f46cf15602b21554fcc337ea771 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 10 Jan 2021 03:48:37 +0300 Subject: [PATCH 065/396] Added ability to show song cover in inline results. --- .../inline_bot_layout_internal.cpp | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 6796114e3..a15bea0d8 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" #include "history/history_location_manager.h" #include "history/view/history_view_cursor_state.h" +#include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover #include "ui/image/image.h" #include "ui/text/format_values.h" #include "ui/cached_round_corners.h" @@ -865,16 +866,21 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con auto inner = style::rtlrect(0, st::inlineRowMargin, st::inlineFileSize, st::inlineFileSize, _width); p.setPen(Qt::NoPen); - if (isThumbAnimation()) { - auto over = _animation->a_thumbOver.value(1.); - p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, over)); - } else { - bool over = ClickHandler::showAsActive(_document->loading() ? _cancel : _open); - p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); - } - { + const auto coverDrawn = _document->isSongWithCover() + && HistoryView::DrawThumbnailAsSongCover(p, _documentMedia, inner); + if (!coverDrawn) { PainterHighQualityEnabler hq(p); + if (isThumbAnimation()) { + const auto over = _animation->a_thumbOver.value(1.); + p.setBrush( + anim::brush(st::msgFileInBg, st::msgFileInBgOver, over)); + } else { + const auto over = ClickHandler::showAsActive(_document->loading() + ? _cancel + : _open); + p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); + } p.drawEllipse(inner); } From 15254599e20c01f52e0120408504a0194e019763 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Jan 2021 00:10:29 +0300 Subject: [PATCH 066/396] Unified checking for editable message. --- .../data/data_scheduled_messages.cpp | 12 +++++++---- .../data/data_scheduled_messages.h | 3 ++- Telegram/SourceFiles/history/history.cpp | 5 +++-- Telegram/SourceFiles/history/history.h | 2 +- Telegram/SourceFiles/history/history_item.cpp | 21 ------------------- Telegram/SourceFiles/history/history_item.h | 2 -- .../SourceFiles/history/history_widget.cpp | 3 +-- .../view/history_view_replies_section.cpp | 2 +- .../view/history_view_scheduled_section.cpp | 5 +++-- 9 files changed, 19 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index c77f95ca3..a8bb8b8cb 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -543,7 +543,8 @@ int32 ScheduledMessages::countListHash(const List &list) const { return HashFinalize(hash); } -HistoryItem *ScheduledMessages::lastSentMessage(not_null history) { +HistoryItem *ScheduledMessages::lastEditableMessage( + not_null history) { const auto i = _data.find(history); if (i == end(_data)) { return nullptr; @@ -552,9 +553,12 @@ HistoryItem *ScheduledMessages::lastSentMessage(not_null history) { sort(list); const auto items = ranges::view::reverse(list.items); - const auto it = ranges::find_if( - items, - &HistoryItem::canBeEditedFromHistory); + + const auto now = base::unixtime::now(); + auto proj = [&](const OwnedItem &item) { + return item->allowsEdit(now); + }; + const auto it = ranges::find_if(items, std::move(proj)); return (it == end(items)) ? nullptr : (*it).get(); } diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.h b/Telegram/SourceFiles/data/data_scheduled_messages.h index 3391cd382..650434c2c 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.h +++ b/Telegram/SourceFiles/data/data_scheduled_messages.h @@ -32,7 +32,8 @@ public: [[nodiscard]] HistoryItem *lookupItem(PeerId peer, MsgId msg) const; [[nodiscard]] HistoryItem *lookupItem(FullMsgId itemId) const; [[nodiscard]] int count(not_null history) const; - [[nodiscard]] HistoryItem *lastSentMessage(not_null history); + [[nodiscard]] HistoryItem *lastEditableMessage( + not_null history); void checkEntitiesAndUpdate(const MTPDmessage &data); void apply(const MTPDupdateNewScheduledMessage &update); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 753b3abfd..907bb0c10 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2704,14 +2704,15 @@ MsgId History::msgIdForRead() const { : result; } -HistoryItem *History::lastSentMessage() const { +HistoryItem *History::lastEditableMessage() const { if (!loadedAtBottom()) { return nullptr; } + const auto now = base::unixtime::now(); for (const auto &block : ranges::view::reverse(blocks)) { for (const auto &message : ranges::view::reverse(block->messages)) { const auto item = message->data(); - if (item->canBeEditedFromHistory()) { + if (item->allowsEdit(now)) { return item; } } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9c9f92055..b715e7733 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -259,7 +259,7 @@ public: MsgId minMsgId() const; MsgId maxMsgId() const; MsgId msgIdForRead() const; - HistoryItem *lastSentMessage() const; + HistoryItem *lastEditableMessage() const; void resizeToWidth(int newWidth); void forceFullResize(); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 517ed9a1c..12b4b2c3b 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -762,27 +762,6 @@ bool HistoryItem::canUpdateDate() const { return isScheduled(); } -bool HistoryItem::canBeEditedFromHistory() const { - // Skip if message is editing media. - if (isEditingMedia()) { - return false; - } - // Skip if message is video message or sticker. - if (const auto m = media()) { - // Skip only if media is not webpage. - if (!m->webpage() && !m->allowsEditCaption()) { - return false; - } - } - if ((IsServerMsgId(id) || isScheduled()) - && !serviceMsg() - && (out() || history()->peer->isSelf()) - && !Has()) { - return true; - } - return false; -} - void HistoryItem::sendFailed() { Expects(_clientFlags & MTPDmessage_ClientFlag::f_sending); Expects(!(_clientFlags & MTPDmessage_ClientFlag::f_failed)); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 280afbed2..008cb72cd 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -390,8 +390,6 @@ public: void updateDate(TimeId newDate); [[nodiscard]] bool canUpdateDate() const; - [[nodiscard]] bool canBeEditedFromHistory() const; - virtual ~HistoryItem(); MsgId id; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 47f89ecd1..8772e78ac 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5022,10 +5022,9 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { _scroll->keyPressEvent(e); } else if (e->key() == Qt::Key_Up && !commonModifiers) { const auto item = _history - ? _history->lastSentMessage() + ? _history->lastEditableMessage() : nullptr; if (item - && item->allowsEdit(base::unixtime::now()) && _field->empty() && !_editMsgId && !_replyToId) { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 89ef5e3d0..d6df6439b 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -494,7 +494,7 @@ void RepliesWidget::setupComposeControls() { if (!_composeControls->isEditingMessage()) { // #TODO replies edit last sent message //auto &messages = session().data().scheduledMessages(); - //if (const auto item = messages.lastSentMessage(_history)) { + //if (const auto item = messages.lastEditableMessage(_history)) { // _inner->editMessageRequestNotify(item->fullId()); //} else { _scroll->keyPressEvent(e); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 9191bd357..9d72215a5 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -235,8 +235,9 @@ void ScheduledWidget::setupComposeControls() { ) | rpl::start_with_next([=](not_null e) { if (e->key() == Qt::Key_Up) { if (!_composeControls->isEditingMessage()) { - auto &messages = session().data().scheduledMessages(); - if (const auto item = messages.lastSentMessage(_history)) { + const auto item = session().data().scheduledMessages() + .lastEditableMessage(_history); + if (item) { _inner->editMessageRequestNotify(item->fullId()); } else { _scroll->keyPressEvent(e); From 3fadf2ee540a07472a5d342d734be2fb810487e9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Jan 2021 00:33:56 +0300 Subject: [PATCH 067/396] Added Up arrow shortcut to edit comments. --- Telegram/SourceFiles/data/data_replies_list.cpp | 17 +++++++++++++++++ Telegram/SourceFiles/data/data_replies_list.h | 2 ++ .../view/history_view_replies_section.cpp | 10 ++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index b2004fa79..4d9f49ccc 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_replies_list.h" +#include "base/unixtime.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_service.h" @@ -626,4 +627,20 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) { return (list.size() == skipped); } +HistoryItem *RepliesList::lastEditableMessage() { + const auto message = [&](MsgId msgId) { + return _history->owner().message(_history->channelId(), msgId); + }; + + const auto now = base::unixtime::now(); + auto proj = [&](MsgId msgId) { + if (const auto item = message(msgId)) { + return item->allowsEdit(now); + } + return false; + }; + const auto it = ranges::find_if(_list, std::move(proj)); + return (it == end(_list)) ? nullptr : message(*it); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_replies_list.h b/Telegram/SourceFiles/data/data_replies_list.h index 36932b82b..945e70da8 100644 --- a/Telegram/SourceFiles/data/data_replies_list.h +++ b/Telegram/SourceFiles/data/data_replies_list.h @@ -31,6 +31,8 @@ public: [[nodiscard]] rpl::producer fullCount() const; + [[nodiscard]] HistoryItem *lastEditableMessage(); + private: struct Viewer; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index d6df6439b..caf5059fe 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -492,13 +492,11 @@ void RepliesWidget::setupComposeControls() { ) | rpl::start_with_next([=](not_null e) { if (e->key() == Qt::Key_Up) { if (!_composeControls->isEditingMessage()) { - // #TODO replies edit last sent message - //auto &messages = session().data().scheduledMessages(); - //if (const auto item = messages.lastEditableMessage(_history)) { - // _inner->editMessageRequestNotify(item->fullId()); - //} else { + if (const auto item = _replies->lastEditableMessage()) { + _inner->editMessageRequestNotify(item->fullId()); + } else { _scroll->keyPressEvent(e); - //} + } } else { _scroll->keyPressEvent(e); } From ad761011d64493d12a0a2e7628936b944de3cbf2 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Jan 2021 16:58:25 +0300 Subject: [PATCH 068/396] Added ability to fetch song cover from external resource. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/data/data_document.cpp | 8 + .../storage/storage_cloud_song_cover.cpp | 151 ++++++++++++++++++ .../storage/storage_cloud_song_cover.h | 16 ++ 4 files changed, 177 insertions(+) create mode 100644 Telegram/SourceFiles/storage/storage_cloud_song_cover.cpp create mode 100644 Telegram/SourceFiles/storage/storage_cloud_song_cover.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 4ebda77fe..9d8dcff78 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -961,6 +961,8 @@ PRIVATE storage/storage_account.h storage/storage_cloud_blob.cpp storage/storage_cloud_blob.h + storage/storage_cloud_song_cover.cpp + storage/storage_cloud_song_cover.h storage/storage_domain.cpp storage/storage_domain.h storage/storage_facade.cpp diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index dc1495791..a615e17f0 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_gif.h" #include "window/window_session_controller.h" #include "storage/cache/storage_cache_database.h" +#include "storage/storage_cloud_song_cover.h" #include "boxes/confirm_box.h" #include "ui/image/image.h" #include "ui/text/text_utilities.h" @@ -540,6 +541,13 @@ void DocumentData::setattributes( songData->duration = data.vduration().v; songData->title = qs(data.vtitle().value_or_empty()); songData->performer = qs(data.vperformer().value_or_empty()); + + if (!hasThumbnail() + && !songData->title.isEmpty() + && !songData->performer.isEmpty()) { + + Storage::CloudSongCover::LoadThumbnailFromExternal(this); + } } }, [&](const MTPDdocumentAttributeFilename &data) { _filename = qs(data.vfile_name()); diff --git a/Telegram/SourceFiles/storage/storage_cloud_song_cover.cpp b/Telegram/SourceFiles/storage/storage_cloud_song_cover.cpp new file mode 100644 index 000000000..b5168e4d2 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_cloud_song_cover.cpp @@ -0,0 +1,151 @@ +/* +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 "storage/storage_cloud_song_cover.h" + +#include "data/data_cloud_file.h" +#include "data/data_document.h" +#include "data/data_file_origin.h" +#include "data/data_session.h" +#include "main/main_session.h" +#include "storage/file_download.h" + +#include +#include +#include +#include + +namespace Storage::CloudSongCover { +namespace { + +constexpr auto kMaxResponseSize = 1024 * 1024; +constexpr auto kDefaultCoverSize = 100; + +struct Responce { + const QString artworkUrl; + const int size; +}; + +auto Location(const QString &url) { + return DownloadLocation{ PlainUrlLocation{ url } }; +} + +auto JsonUrl(not_null song) { + return QString("https://itunes.apple.com/search?term=" \ + "%1 %2&entity=song&limit=4").arg(song->performer).arg(song->title); +} + +// Dummy JSON responce. +// { +// "resultCount": 2, +// "results": [ +// { +// "artworkUrl100": "" +// }, +// { +// "artworkUrl100": "" +// } +// ] +// } + +std::optional ParseResponce(const QByteArray &response) { + if (response.size() >= kMaxResponseSize) { + return std::nullopt; + } + auto error = QJsonParseError{ 0, QJsonParseError::NoError }; + const auto document = QJsonDocument::fromJson(response, &error); + + const auto log = [](const QString &message) { + DEBUG_LOG(("Parse Artwork JSON Error: %1.").arg(message)); + }; + + if (error.error != QJsonParseError::NoError) { + log(error.errorString()); + return std::nullopt; + } else if (!document.isObject()) { + log("not an object received in JSON"); + return std::nullopt; + } + const auto results = document.object().value("results"); + if (!results.isArray()) { + log("'results' field not found"); + return std::nullopt; + } + const auto resultsArray = results.toArray(); + if (resultsArray.empty()) { + return std::nullopt; + } + const auto artworkUrl = resultsArray.first().toObject() + .value("artworkUrl100").toString(); + if (artworkUrl.isEmpty()) { + log("'artworkUrl100' field is empty"); + return std::nullopt; + } + + return Responce{ artworkUrl, kDefaultCoverSize }; +} + +void LoadAndApplyThumbnail( + not_null document, + const Responce &responce) { + const auto size = responce.size; + const auto imageWithLocation = ImageWithLocation{ + .location = ImageLocation(Location(responce.artworkUrl), size, size) + }; + + document->updateThumbnails( + QByteArray(), + imageWithLocation, + ImageWithLocation{ .location = ImageLocation() }); + + document->loadThumbnail(Data::FileOrigin()); +} + +} + +void LoadThumbnailFromExternal(not_null document) { + const auto songData = document->song(); + if (!songData + || songData->performer.isEmpty() + || songData->title.isEmpty()) { + return; + } + + const auto &size = kDefaultCoverSize; + const auto jsonLocation = ImageWithLocation{ + .location = ImageLocation(Location(JsonUrl(songData)), size, size) + }; + + const auto jsonCloudFile = std::make_shared(); + Data::UpdateCloudFile( + *jsonCloudFile, + jsonLocation, + document->owner().cache(), + 0, // Cache tag. + nullptr, + nullptr); + + auto done = [=](const QByteArray &result) { + if (!jsonCloudFile) { + return; + } + if (const auto responce = ParseResponce(result)) { + LoadAndApplyThumbnail(document, *responce); + } + }; + Data::LoadCloudFile( + &document->session(), + *jsonCloudFile, + Data::FileOrigin(), + LoadFromCloudOrLocal, + true, + 0, + [] { return true; }, + std::move(done)); +} + +} // namespace Storage::CloudSongCover diff --git a/Telegram/SourceFiles/storage/storage_cloud_song_cover.h b/Telegram/SourceFiles/storage/storage_cloud_song_cover.h new file mode 100644 index 000000000..8b649f9ea --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_cloud_song_cover.h @@ -0,0 +1,16 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class DocumentData; + +namespace Storage::CloudSongCover { + +void LoadThumbnailFromExternal(not_null document); + +} // namespace Storage::CloudSongCover From eb42a77eb77446d2a78d630167f4399640977b30 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 18 Jan 2021 18:04:03 +0300 Subject: [PATCH 069/396] Changed Up arrow shortcut for albums to edit item with caption. Fixed #10134. --- Telegram/SourceFiles/data/data_groups.cpp | 16 ++++++++++++++++ Telegram/SourceFiles/data/data_groups.h | 2 ++ Telegram/SourceFiles/data/data_replies_list.cpp | 4 +++- .../SourceFiles/data/data_scheduled_messages.cpp | 5 ++++- Telegram/SourceFiles/history/history.cpp | 2 +- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/data/data_groups.cpp b/Telegram/SourceFiles/data/data_groups.cpp index 25968975f..6e0998d4d 100644 --- a/Telegram/SourceFiles/data/data_groups.cpp +++ b/Telegram/SourceFiles/data/data_groups.cpp @@ -145,4 +145,20 @@ void Groups::refreshViews(const HistoryItemsList &items) { } } +not_null Groups::findItemToEdit( + not_null item) const { + const auto group = find(item); + if (!group) { + return item; + } + const auto &list = group->items; + const auto it = ranges::find_if( + list, + ranges::not_fn(&HistoryItem::emptyText)); + if (it == end(list)) { + return list.front(); + } + return (*it); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_groups.h b/Telegram/SourceFiles/data/data_groups.h index 4ab230e98..be628cbaf 100644 --- a/Telegram/SourceFiles/data/data_groups.h +++ b/Telegram/SourceFiles/data/data_groups.h @@ -31,6 +31,8 @@ public: const Group *find(not_null item) const; + not_null findItemToEdit(not_null item) const; + private: HistoryItemsList::const_iterator findPositionForItem( const HistoryItemsList &group, diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index 4d9f49ccc..e27d2d80b 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -640,7 +640,9 @@ HistoryItem *RepliesList::lastEditableMessage() { return false; }; const auto it = ranges::find_if(_list, std::move(proj)); - return (it == end(_list)) ? nullptr : message(*it); + return (it == end(_list)) + ? nullptr + : _history->owner().groups().findItemToEdit(message(*it)); } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index a8bb8b8cb..7e1d2bdd7 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -558,8 +558,11 @@ HistoryItem *ScheduledMessages::lastEditableMessage( auto proj = [&](const OwnedItem &item) { return item->allowsEdit(now); }; + const auto it = ranges::find_if(items, std::move(proj)); - return (it == end(items)) ? nullptr : (*it).get(); + return (it == end(items)) + ? nullptr + : history->owner().groups().findItemToEdit((*it).get()); } } // namespace Data diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 907bb0c10..8e51f250a 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2713,7 +2713,7 @@ HistoryItem *History::lastEditableMessage() const { for (const auto &message : ranges::view::reverse(block->messages)) { const auto item = message->data(); if (item->allowsEdit(now)) { - return item; + return owner().groups().findItemToEdit(item); } } } From b6f17e1ceaaec9e6d96ea93ac3840d283e629c1e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 17 Nov 2020 03:54:53 +0300 Subject: [PATCH 070/396] Replaced QTimer with base::Timer in OverlayWidget. --- .../media/view/media_view_overlay_widget.cpp | 31 ++++++++----------- .../media/view/media_view_overlay_widget.h | 9 +++--- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b9926b702..0bf5ccaa8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -322,8 +322,7 @@ OverlayWidget::OverlayWidget() , _radial([=](crl::time now) { return radialAnimationCallback(now); }) , _lastAction(-st::mediaviewDeltaFromLastAction, -st::mediaviewDeltaFromLastAction) , _stateAnimation([=](crl::time now) { return stateAnimationCallback(now); }) -, _dropdown(this, st::mediaviewDropdownMenu) -, _dropdownShowTimer(this) { +, _dropdown(this, st::mediaviewDropdownMenu) { Lang::Updated( ) | rpl::start_with_next([=] { refreshLang(); @@ -410,23 +409,19 @@ OverlayWidget::OverlayWidget() } }, lifetime()); - _saveMsgUpdater.setSingleShot(true); - connect(&_saveMsgUpdater, SIGNAL(timeout()), this, SLOT(updateImage())); + _saveMsgUpdater.setCallback([=] { updateImage(); }); setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); + _touchTimer.setCallback([=] { onTouchTimer(); }); - _controlsHideTimer.setSingleShot(true); - connect(&_controlsHideTimer, SIGNAL(timeout()), this, SLOT(onHideControls())); + _controlsHideTimer.setCallback([=] { onHideControls(); }); _docDownload->addClickHandler([=] { onDownload(); }); _docSaveAs->addClickHandler([=] { onSaveAs(); }); _docCancel->addClickHandler([=] { onSaveCancel(); }); _dropdown->setHiddenCallback([this] { dropdownHidden(); }); - _dropdownShowTimer->setSingleShot(true); - connect(_dropdownShowTimer, SIGNAL(timeout()), this, SLOT(onDropdown())); + _dropdownShowTimer.setCallback([=] { onDropdown(); }); } void OverlayWidget::refreshLang() { @@ -1240,7 +1235,7 @@ void OverlayWidget::close() { void OverlayWidget::activateControls() { if (!_menu && !_mousePressed) { - _controlsHideTimer.start(int(st::mediaviewWaitHide)); + _controlsHideTimer.callOnce(st::mediaviewWaitHide); } if (_fullScreenVideo) { if (_streamed) { @@ -3113,7 +3108,7 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { } if (!_blurred) { auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); - _saveMsgUpdater.start(nextFrame); + _saveMsgUpdater.callOnce(nextFrame); } } else { _saveMsgStarted = 0; @@ -3968,9 +3963,9 @@ bool OverlayWidget::updateOverState(OverState newState) { bool result = true; if (_over != newState) { if (newState == OverMore && !_ignoringDropdown) { - _dropdownShowTimer->start(0); + _dropdownShowTimer.callOnce(0); } else { - _dropdownShowTimer->stop(); + _dropdownShowTimer.cancel(); } updateOverRect(_over); updateOverRect(newState); @@ -4163,7 +4158,7 @@ void OverlayWidget::touchEvent(QTouchEvent *e) { switch (e->type()) { case QEvent::TouchBegin: { if (_touchPress || e->touchPoints().isEmpty()) return; - _touchTimer.start(QApplication::startDragTime()); + _touchTimer.callOnce(QApplication::startDragTime()); _touchPress = true; _touchMove = _touchRightButton = false; _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); @@ -4203,14 +4198,14 @@ void OverlayWidget::touchEvent(QTouchEvent *e) { } } if (weak) { - _touchTimer.stop(); + _touchTimer.cancel(); _touchPress = _touchMove = _touchRightButton = false; } } break; case QEvent::TouchCancel: { _touchPress = false; - _touchTimer.stop(); + _touchTimer.cancel(); } break; } } @@ -4315,7 +4310,7 @@ void OverlayWidget::setVisibleHook(bool visible) { _preloadPhotos.clear(); _preloadDocuments.clear(); if (_menu) _menu->hideMenu(true); - _controlsHideTimer.stop(); + _controlsHideTimer.cancel(); _controlsState = ControlsShown; _controlsOpacity = anim::value(1, 1); _groupThumbs = nullptr; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 4b680626e..8f9733c93 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/timer.h" #include "ui/rp_widget.h" #include "ui/widgets/dropdown_menu.h" #include "ui/effects/animations.h" @@ -493,13 +494,13 @@ private: }; ControlsState _controlsState = ControlsShown; crl::time _controlsAnimStarted = 0; - QTimer _controlsHideTimer; + base::Timer _controlsHideTimer; anim::value _controlsOpacity; bool _mousePressed = false; Ui::PopupMenu *_menu = nullptr; object_ptr _dropdown; - object_ptr _dropdownShowTimer; + base::Timer _dropdownShowTimer; struct ActionData { QString text; @@ -512,7 +513,7 @@ private: bool _touchPress = false; bool _touchMove = false; bool _touchRightButton = false; - QTimer _touchTimer; + base::Timer _touchTimer; QPoint _touchStart; QPoint _accumScroll; @@ -520,7 +521,7 @@ private: crl::time _saveMsgStarted = 0; anim::value _saveMsgOpacity; QRect _saveMsg; - QTimer _saveMsgUpdater; + base::Timer _saveMsgUpdater; Ui::Text::String _saveMsgText; SavePhotoVideo _savePhotoVideoWhenLoaded = SavePhotoVideo::None; From dd7598a7017664f62f51e0d6c0c8158a305239e0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 17 Nov 2020 03:56:09 +0300 Subject: [PATCH 071/396] Replaced singleShot with InvokeQueued in OverlayWidget. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 0bf5ccaa8..3501df963 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -4099,7 +4099,7 @@ void OverlayWidget::mouseReleaseEvent(QMouseEvent *e) { } else if (_over == OverIcon && _down == OverIcon) { onDocClick(); } else if (_over == OverMore && _down == OverMore) { - QTimer::singleShot(0, this, SLOT(onDropdown())); + InvokeQueued(this, [=] { onDropdown(); }); } else if (_over == OverClose && _down == OverClose) { close(); } else if (_over == OverVideo && _down == OverVideo) { @@ -4339,7 +4339,7 @@ void OverlayWidget::onMenuDestroy(QObject *obj) { activateControls(); } _receiveMouse = false; - QTimer::singleShot(0, this, SLOT(receiveMouse())); + InvokeQueued(this, [=] { receiveMouse(); }); } void OverlayWidget::receiveMouse() { From 2d906bddb24557bc3b1320a1b4799a0ec979a9b0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 17 Nov 2020 04:29:27 +0300 Subject: [PATCH 072/396] Replaced raw PopupMenu pointer with unique_qptr in OverlayWidget. --- .../media/view/media_view_overlay_widget.cpp | 37 +++++++++---------- .../media/view/media_view_overlay_widget.h | 3 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 3501df963..ee574be6f 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1162,8 +1162,6 @@ void OverlayWidget::clearSession() { _animationOpacities.clear(); } clearStreaming(); - delete _menu; - _menu = nullptr; setContext(v::null); _from = nullptr; _fromName = QString(); @@ -1620,7 +1618,9 @@ void OverlayWidget::onDelete() { } void OverlayWidget::onOverview() { - if (_menu) _menu->hideMenu(true); + if (_menu) { + _menu->hideMenu(true); + } update(); if (const auto overviewType = computeOverviewType()) { close(); @@ -3858,7 +3858,9 @@ void OverlayWidget::preloadData(int delta) { void OverlayWidget::mousePressEvent(QMouseEvent *e) { updateOver(e->pos()); - if (_menu || !_receiveMouse) return; + if (_menu || !_receiveMouse) { + return; + } ClickHandler::pressed(); @@ -4138,16 +4140,18 @@ void OverlayWidget::mouseReleaseEvent(QMouseEvent *e) { void OverlayWidget::contextMenuEvent(QContextMenuEvent *e) { if (e->reason() != QContextMenuEvent::Mouse || QRect(_x, _y, _w, _h).contains(e->pos())) { - if (_menu) { - _menu->deleteLater(); - _menu = nullptr; - } - _menu = new Ui::PopupMenu(this, st::mediaviewPopupMenu); + _menu = base::make_unique_q( + this, + st::mediaviewPopupMenu); updateActions(); for_const (auto &action, _actions) { _menu->addAction(action.text, this, action.member); } - connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); + _menu->setDestroyedCallback(crl::guard(this, [=] { + activateControls(); + _receiveMouse = false; + InvokeQueued(this, [=] { receiveMouse(); }); + })); _menu->popup(e->globalPos()); e->accept(); activateControls(); @@ -4309,7 +4313,9 @@ void OverlayWidget::setVisibleHook(bool visible) { assignMediaPointer(nullptr); _preloadPhotos.clear(); _preloadDocuments.clear(); - if (_menu) _menu->hideMenu(true); + if (_menu) { + _menu->hideMenu(true); + } _controlsHideTimer.cancel(); _controlsState = ControlsShown; _controlsOpacity = anim::value(1, 1); @@ -4333,15 +4339,6 @@ void OverlayWidget::setVisibleHook(bool visible) { } } -void OverlayWidget::onMenuDestroy(QObject *obj) { - if (_menu == obj) { - _menu = nullptr; - activateControls(); - } - _receiveMouse = false; - InvokeQueued(this, [=] { receiveMouse(); }); -} - void OverlayWidget::receiveMouse() { _receiveMouse = true; } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 8f9733c93..152669a2e 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -128,7 +128,6 @@ private slots: void onDelete(); void onOverview(); void onCopy(); - void onMenuDestroy(QObject *obj); void receiveMouse(); void onPhotoAttachedStickers(); void onDocumentAttachedStickers(); @@ -498,7 +497,7 @@ private: anim::value _controlsOpacity; bool _mousePressed = false; - Ui::PopupMenu *_menu = nullptr; + base::unique_qptr _menu; object_ptr _dropdown; base::Timer _dropdownShowTimer; From 03a7131a1a867cd99663a0d693f57574d8727543 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 17 Nov 2020 04:48:47 +0300 Subject: [PATCH 073/396] Replaced slots with lambdas to fill context menu in OverlayWidget. --- .../media/view/media_view_overlay_widget.cpp | 55 ++++++++++--------- .../media/view/media_view_overlay_widget.h | 11 ++-- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index ee574be6f..26dba3153 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -793,33 +793,37 @@ void OverlayWidget::refreshCaptionGeometry() { captionHeight); } -void OverlayWidget::updateActions() { - _actions.clear(); - +void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { if (_document && _document->loading()) { - _actions.push_back({ tr::lng_cancel(tr::now), SLOT(onSaveCancel()) }); + addAction(tr::lng_cancel(tr::now), [=] { onSaveCancel(); }); } if (IsServerMsgId(_msgid.msg)) { - _actions.push_back({ tr::lng_context_to_msg(tr::now), SLOT(onToMessage()) }); + addAction(tr::lng_context_to_msg(tr::now), [=] { onToMessage(); }); } if (_document && !_document->filepath(true).isEmpty()) { - _actions.push_back({ Platform::IsMac() ? tr::lng_context_show_in_finder(tr::now) : tr::lng_context_show_in_folder(tr::now), SLOT(onShowInFolder()) }); + const auto text = Platform::IsMac() + ? tr::lng_context_show_in_finder(tr::now) + : tr::lng_context_show_in_folder(tr::now); + addAction(text, [=] { onShowInFolder(); }); } if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { - _actions.push_back({ tr::lng_mediaview_copy(tr::now), SLOT(onCopy()) }); + addAction(tr::lng_mediaview_copy(tr::now), [=] { onCopy(); }); } if ((_photo && _photo->hasAttachedStickers()) || (_document && _document->hasAttachedStickers())) { - auto member = _photo - ? SLOT(onPhotoAttachedStickers()) - : SLOT(onDocumentAttachedStickers()); - _actions.push_back({ + auto callback = [=] { + if (_photo) { + onPhotoAttachedStickers(); + } else if (_document) { + onDocumentAttachedStickers(); + } + }; + addAction( tr::lng_context_attached_stickers(tr::now), - std::move(member) - }); + std::move(callback)); } if (_canForwardItem) { - _actions.push_back({ tr::lng_mediaview_forward(tr::now), SLOT(onForward()) }); + addAction(tr::lng_mediaview_forward(tr::now), [=] { onForward(); }); } const auto canDelete = [&] { if (_canDeleteItem) { @@ -839,12 +843,15 @@ void OverlayWidget::updateActions() { return false; }(); if (canDelete) { - _actions.push_back({ tr::lng_mediaview_delete(tr::now), SLOT(onDelete()) }); + addAction(tr::lng_mediaview_delete(tr::now), [=] { onDelete(); }); } - _actions.push_back({ tr::lng_mediaview_save_as(tr::now), SLOT(onSaveAs()) }); + addAction(tr::lng_mediaview_save_as(tr::now), [=] { onSaveAs(); }); if (const auto overviewType = computeOverviewType()) { - _actions.push_back({ _document ? tr::lng_mediaview_files_all(tr::now) : tr::lng_mediaview_photos_all(tr::now), SLOT(onOverview()) }); + const auto text = _document + ? tr::lng_mediaview_files_all(tr::now) + : tr::lng_mediaview_photos_all(tr::now); + addAction(text, [=] { onOverview(); }); } } @@ -4143,10 +4150,9 @@ void OverlayWidget::contextMenuEvent(QContextMenuEvent *e) { _menu = base::make_unique_q( this, st::mediaviewPopupMenu); - updateActions(); - for_const (auto &action, _actions) { - _menu->addAction(action.text, this, action.member); - } + fillContextMenuActions([&] (const QString &text, Fn handler) { + _menu->addAction(text, std::move(handler)); + }); _menu->setDestroyedCallback(crl::guard(this, [=] { activateControls(); _receiveMouse = false; @@ -4344,11 +4350,10 @@ void OverlayWidget::receiveMouse() { } void OverlayWidget::onDropdown() { - updateActions(); _dropdown->clearActions(); - for_const (auto &action, _actions) { - _dropdown->addAction(action.text, this, action.member); - } + fillContextMenuActions([&] (const QString &text, Fn handler) { + _dropdown->addAction(text, std::move(handler)); + }); _dropdown->moveToRight(0, height() - _dropdown->height()); _dropdown->showAnimated(Ui::PanelAnimation::Origin::BottomRight); _dropdown->setFocus(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 152669a2e..d2fd5d6a9 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -274,8 +274,11 @@ private: void dropdownHidden(); void updateDocSize(); void updateControls(); - void updateActions(); void updateControlsGeometry(); + + using MenuCallback = Fn)>; + void fillContextMenuActions(const MenuCallback &addAction); + void resizeCenteredControls(); void resizeContentByScreenSize(); @@ -501,12 +504,6 @@ private: object_ptr _dropdown; base::Timer _dropdownShowTimer; - struct ActionData { - QString text; - const char *member; - }; - QList _actions; - bool _receiveMouse = true; bool _touchPress = false; From 97e8c0956f6671cb65acdf7dacd5bfb291ba8274 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Jan 2021 01:59:55 +0300 Subject: [PATCH 074/396] Moved all files related to menu to separate namespace. --- Telegram/SourceFiles/window/window_main_menu.cpp | 9 +++++---- Telegram/SourceFiles/window/window_main_menu.h | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 2213c1b84..0c36684e6 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -12,7 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" -#include "ui/widgets/menu.h" +#include "ui/widgets/menu/menu.h" +#include "ui/widgets/menu/menu_common.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" @@ -524,7 +525,7 @@ MainMenu::MainMenu( _inner.get(), object_ptr(_inner.get())))) , _menu(_inner->add( - object_ptr(_inner.get(), st::mainMenu), + object_ptr(_inner.get(), st::mainMenu), { 0, st::mainMenuSkip, 0, 0 })) , _footer(_inner->add(object_ptr(_inner.get()))) , _telegram( @@ -565,8 +566,8 @@ MainMenu::MainMenu( }, _inner->lifetime()); parentResized(); - _menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) { - emit action->triggered(); + _menu->setTriggeredCallback([](const Ui::Menu::CallbackData &data) { + emit data.action->triggered(); }); refreshMenu(); refreshBackground(); diff --git a/Telegram/SourceFiles/window/window_main_menu.h b/Telegram/SourceFiles/window/window_main_menu.h index af8df5bbd..4866d6192 100644 --- a/Telegram/SourceFiles/window/window_main_menu.h +++ b/Telegram/SourceFiles/window/window_main_menu.h @@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class IconButton; class FlatLabel; -class Menu; class UserpicButton; class PopupMenu; class ScrollArea; @@ -25,6 +24,9 @@ class RippleButton; class PlainShadow; template class SlideWrap; +namespace Menu { +class Menu; +} // namespace Menu } // namespace Ui namespace Main { @@ -84,7 +86,7 @@ private: not_null*> _accounts; Ui::SlideWrap *_addAccount = nullptr; not_null*> _shadow; - not_null _menu; + not_null _menu; not_null _footer; not_null _telegram; not_null _version; From 1ccfcc824c244ff084896c0b20b6a5ebbc3aac8f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 13 Jan 2021 04:10:25 +0300 Subject: [PATCH 075/396] Updated code to be consistent with lib_ui. --- .../chat_helpers/field_autocomplete.cpp | 2 +- .../SourceFiles/chat_helpers/tabbed_selector.cpp | 4 ++-- .../SourceFiles/dialogs/dialogs_inner_widget.cpp | 2 +- .../admin_log/history_admin_log_inner.cpp | 2 +- .../SourceFiles/history/history_inner_widget.cpp | 2 +- .../history/view/history_view_list_widget.cpp | 2 +- .../history/view/history_view_top_bar_widget.cpp | 2 +- .../inline_bots/inline_results_inner.cpp | 2 +- Telegram/SourceFiles/window/window_main_menu.cpp | 16 ++++++++++++++-- 9 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 3b939b3d7..36eddd08e 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -1138,7 +1138,7 @@ void FieldAutocomplete::Inner::contextMenuEvent(QContextMenuEvent *e) { SendMenu::DefaultSilentCallback(send), SendMenu::DefaultScheduleCallback(this, type, send)); - if (!_menu->actions().empty()) { + if (!_menu->empty()) { _menu->popup(QCursor::pos()); } } diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index cc0330d84..9b9a92910 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -594,7 +594,7 @@ bool TabbedSelector::preventAutoHide() const { } bool TabbedSelector::hasMenu() const { - return (_menu && !_menu->actions().empty()); + return (_menu && !_menu->empty()); } QImage TabbedSelector::grabForAnimation() { @@ -881,7 +881,7 @@ void TabbedSelector::showMenuWithType(SendMenu::Type type) { _menu = base::make_unique_q(this); currentTab()->widget()->fillContextMenu(_menu, type); - if (!_menu->actions().empty()) { + if (!_menu->empty()) { _menu->popup(QCursor::pos()); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 67adfa4f3..90da5d83f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1841,7 +1841,7 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) { selectByMouse(globalPosition); } }); - if (_menu->actions().empty()) { + if (_menu->empty()) { _menu = nullptr; } else { _menu->popup(e->globalPos()); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 923b8d316..f96a4e476 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1177,7 +1177,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - if (_menu->actions().empty()) { + if (_menu->empty()) { _menu = nullptr; } else { _menu->popup(e->globalPos()); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index e70464ef0..755dd3387 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1851,7 +1851,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - if (_menu->actions().empty()) { + if (_menu->empty()) { _menu = nullptr; } else { _menu->popup(e->globalPos()); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index dc07f3a48..e75789e2a 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1832,7 +1832,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _overState)); _menu = FillContextMenu(this, request); - if (_menu && !_menu->actions().empty()) { + if (_menu && !_menu->empty()) { _menu->popup(e->globalPos()); e->accept(); } else if (_menu) { 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 a681768bf..1a654ee20 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -256,7 +256,7 @@ void TopBarWidget::showMenu() { _controller, _activeChat, addAction); - if (_menu->actions().empty()) { + if (_menu->empty()) { _menu.destroy(); } else { _menu->moveToRight((parentWidget()->width() - width()) + st::topBarMenuPosition.x(), st::topBarMenuPosition.y()); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index 4507e698a..e44caf6fe 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -305,7 +305,7 @@ void Inner::contextMenuEvent(QContextMenuEvent *e) { SendMenu::DefaultSilentCallback(send), SendMenu::DefaultScheduleCallback(this, type, send)); - if (!_menu->actions().empty()) { + if (!_menu->empty()) { _menu->popup(QCursor::pos()); } } diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 0c36684e6..a59bd0342 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/menu/menu.h" #include "ui/widgets/menu/menu_common.h" +#include "ui/widgets/menu/menu_toggle.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" @@ -905,7 +906,8 @@ void MainMenu::refreshMenu() { }, &st::mainMenuSettings, &st::mainMenuSettingsOver); _nightThemeAction = std::make_shared>(); - auto action = _menu->addAction(tr::lng_menu_night_mode(tr::now), [=] { + + auto nightCallback = [=] { if (Window::Theme::Background()->editingTheme()) { Ui::show(Box( tr::lng_theme_editor_cant_change_theme(tr::now))); @@ -924,7 +926,17 @@ void MainMenu::refreshMenu() { Window::Theme::ToggleNightModeWithConfirmation( &_controller->window(), toggle); - }, &st::mainMenuNightMode, &st::mainMenuNightModeOver); + }; + + auto item = base::make_unique_q( + _menu, + st::mainMenu, + tr::lng_menu_night_mode(tr::now), + std::move(nightCallback), + &st::mainMenuNightMode, + &st::mainMenuNightModeOver); + + auto action = _menu->addAction(std::move(item)); *_nightThemeAction = action; action->setCheckable(true); action->setChecked(Window::Theme::IsNightMode()); From 5277080115211ce717f00952444cd362ecbebc89 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 21 Jan 2021 23:33:22 +0300 Subject: [PATCH 076/396] Fixed adding caption to grouped files. Fixed #10192. --- .../ui/chat/attach/attach_prepare.cpp | 19 +++++++++++++++++++ .../ui/chat/attach/attach_prepare.h | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index 8ba397094..a2e3f21ed 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -152,6 +152,25 @@ bool PreparedList::canAddCaption(bool sendingAlbum) const { } else if (!sendingAlbum) { return false; } + + // All music. + { + auto pred = [](const PreparedFile &file) { + return file.type == PreparedFile::Type::Music; + }; + if (ranges::all_of(files, std::move(pred))) { + return true; + } + } + // All files. + { + auto pred = [](const PreparedFile &file) { + return file.type == PreparedFile::Type::File; + }; + if (ranges::all_of(files, std::move(pred))) { + return true; + } + } const auto hasFiles = ranges::contains( files, PreparedFile::Type::File, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index fe7d5251d..3f9ad0453 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -120,7 +120,7 @@ struct PreparedGroup { [[nodiscard]] bool sentWithCaption() const { return (list.files.size() == 1) - || (type == AlbumType::PhotoVideo); + || (type != AlbumType::None); } }; From dc631ef631a4e8e52632d8cc06ec7b3afb7b3f16 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 22 Jan 2021 02:36:22 +0300 Subject: [PATCH 077/396] Added ability to see url of inline button in tooltip. Fixed #5457. --- .../history/history_item_components.cpp | 42 ++++++++++++------- .../history/history_item_components.h | 6 +-- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index a1292c6bb..59ccf1ca5 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -413,23 +413,13 @@ ReplyMarkupClickHandler::ReplyMarkupClickHandler( // Copy to clipboard support. QString ReplyMarkupClickHandler::copyToClipboardText() const { - if (const auto button = getButton()) { - using Type = HistoryMessageMarkupButton::Type; - if (button->type == Type::Url || button->type == Type::Auth) { - return QString::fromUtf8(button->data); - } - } - return QString(); + const auto button = getUrlButton(); + return button ? QString::fromUtf8(button->data) : QString(); } QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const { - if (const auto button = getButton()) { - using Type = HistoryMessageMarkupButton::Type; - if (button->type == Type::Url || button->type == Type::Auth) { - return tr::lng_context_copy_link(tr::now); - } - } - return QString(); + const auto button = getUrlButton(); + return button ? tr::lng_context_copy_link(tr::now) : QString(); } // Finds the corresponding button in the items markup struct. @@ -440,6 +430,17 @@ const HistoryMessageMarkupButton *ReplyMarkupClickHandler::getButton() const { return HistoryMessageMarkupButton::Get(_owner, _itemId, _row, _column); } +auto ReplyMarkupClickHandler::getUrlButton() const +-> const HistoryMessageMarkupButton* { + if (const auto button = getButton()) { + using Type = HistoryMessageMarkupButton::Type; + if (button->type == Type::Url || button->type == Type::Auth) { + return button; + } + } + return nullptr; +} + void ReplyMarkupClickHandler::onClickImpl() const { if (const auto item = _owner->message(_itemId)) { App::activateBotCommand(item, _row, _column); @@ -454,6 +455,19 @@ QString ReplyMarkupClickHandler::buttonText() const { return QString(); } +QString ReplyMarkupClickHandler::tooltip() const { + const auto button = getUrlButton(); + const auto url = button ? QString::fromUtf8(button->data) : QString(); + const auto text = _fullDisplayed ? QString() : buttonText(); + if (!url.isEmpty() && !text.isEmpty()) { + return QString("%1\n\n%2").arg(text).arg(url); + } else if (url.isEmpty() != text.isEmpty()) { + return text + url; + } else { + return QString(); + } +} + ReplyKeyboard::Button::Button() = default; ReplyKeyboard::Button::Button(Button &&other) = default; ReplyKeyboard::Button &ReplyKeyboard::Button::operator=( diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 5e720fb20..36a7fc4cf 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -251,9 +251,7 @@ public: int column, FullMsgId context); - QString tooltip() const override { - return _fullDisplayed ? QString() : buttonText(); - } + QString tooltip() const override; void setFullDisplayed(bool full) { _fullDisplayed = full; @@ -269,6 +267,8 @@ public: // than the one was used when constructing the handler, but not a big deal. const HistoryMessageMarkupButton *getButton() const; + const HistoryMessageMarkupButton *getUrlButton() const; + // We hold only FullMsgId, not HistoryItem*, because all click handlers // are activated async and the item may be already destroyed. void setMessageId(const FullMsgId &msgId) { From 3d1f21bd053f5d17b3790bd4e76a916a3c19a9e4 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 22 Jan 2021 04:24:15 +0300 Subject: [PATCH 078/396] Added sent date info to tooltip of messages in admin log. Fixed #5706. --- Telegram/Resources/langs/lang.strings | 1 + .../admin_log/history_admin_log_inner.cpp | 19 +++++- .../admin_log/history_admin_log_inner.h | 1 + .../admin_log/history_admin_log_item.cpp | 60 +++++++++++++------ .../admin_log/history_admin_log_item.h | 2 +- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 5efaea8d4..dcfb9aead 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1187,6 +1187,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_in_reply_to" = "In reply to"; "lng_edited" = "edited"; "lng_edited_date" = "Edited: {date}"; +"lng_sent_date" = "Sent: {date}"; "lng_admin_badge" = "admin"; "lng_owner_badge" = "owner"; "lng_channel_badge" = "channel"; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index f96a4e476..1fea6e2da 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" #include "boxes/sticker_set_box.h" #include "base/platform/base_platform_info.h" +#include "base/unixtime.h" #include "mainwindow.h" #include "mainwidget.h" #include "core/application.h" @@ -509,8 +510,17 @@ QString InnerWidget::tooltipText() const { if (_mouseCursorState == CursorState::Date && _mouseAction == MouseAction::None) { if (const auto view = App::hoveredItem()) { - auto dateText = view->dateTime().toString( - QLocale::system().dateTimeFormat(QLocale::LongFormat)); + const auto format = QLocale::system().dateTimeFormat( + QLocale::LongFormat); + auto dateText = view->dateTime().toString(format); + + const auto sentIt = _itemDates.find(view->data()); + if (sentIt != end(_itemDates)) { + dateText += '\n' + tr::lng_sent_date( + tr::now, + lt_date, + base::unixtime::parse(sentIt->second).toString(format)); + } return dateText; } } else if (_mouseCursorState == CursorState::Forwarded @@ -722,7 +732,10 @@ void InnerWidget::addEvents(Direction direction, const QVectordata(), sentDate); + } _eventIds.emplace(id); _itemsByData.emplace(item->data(), item.get()); addToItems.push_back(std::move(item)); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index ca8e50066..b3af9bb0b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -242,6 +242,7 @@ private: std::vector _items; std::set _eventIds; std::map, not_null> _itemsByData; + base::flat_map, TimeId> _itemDates; base::flat_set _animatedStickersPlayed; base::flat_map< not_null, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index d5647de4b..ed06eda57 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -45,6 +45,16 @@ TextWithEntities PrepareText(const QString &value, const QString &emptyValue) { return result; } +TimeId ExtractSentDate(const MTPMessage &message) { + return message.match([&](const MTPDmessageEmpty &) { + return 0; + }, [&](const MTPDmessageService &data) { + return data.vdate().v; + }, [&](const MTPDmessage &data) { + return data.vdate().v; + }); +} + MTPMessage PrepareLogMessage( const MTPMessage &message, MsgId newId, @@ -380,7 +390,7 @@ void GenerateItems( not_null delegate, not_null history, const MTPDchannelAdminLogEvent &event, - Fn callback) { + Fn callback) { Expects(history->peer->isChannel()); const auto session = &history->session(); @@ -389,8 +399,10 @@ void GenerateItems( const auto channel = history->peer->asChannel(); const auto &action = event.vaction(); const auto date = event.vdate().v; - const auto addPart = [&](not_null item) { - return callback(OwnedItem(delegate, item)); + const auto addPart = [&]( + not_null item, + TimeId sentDate = 0) { + return callback(OwnedItem(delegate, item), sentDate); }; using Flag = MTPDmessage::Flag; @@ -545,13 +557,15 @@ void GenerateItems( addSimpleServiceMessage(text); auto detachExistingItem = false; - addPart(history->createItem( - PrepareLogMessage( - action.vmessage(), - history->nextNonHistoryEntryId(), - date), - MTPDmessage_ClientFlag::f_admin_log_entry, - detachExistingItem)); + addPart( + history->createItem( + PrepareLogMessage( + action.vmessage(), + history->nextNonHistoryEntryId(), + date), + MTPDmessage_ClientFlag::f_admin_log_entry, + detachExistingItem), + ExtractSentDate(action.vmessage())); }, [&](const auto &) { auto text = tr::lng_admin_log_unpinned_message(tr::now, lt_from, fromLinkText); addSimpleServiceMessage(text); @@ -598,10 +612,15 @@ void GenerateItems( addSimpleServiceMessage(text); auto detachExistingItem = false; - addPart(history->createItem( - PrepareLogMessage(action.vmessage(), history->nextNonHistoryEntryId(), date), - MTPDmessage_ClientFlag::f_admin_log_entry, - detachExistingItem)); + addPart( + history->createItem( + PrepareLogMessage( + action.vmessage(), + history->nextNonHistoryEntryId(), + date), + MTPDmessage_ClientFlag::f_admin_log_entry, + detachExistingItem), + ExtractSentDate(action.vmessage())); }; auto createParticipantJoin = [&]() { @@ -740,10 +759,15 @@ void GenerateItems( addSimpleServiceMessage(text); auto detachExistingItem = false; - addPart(history->createItem( - PrepareLogMessage(action.vmessage(), history->nextNonHistoryEntryId(), date), - MTPDmessage_ClientFlag::f_admin_log_entry, - detachExistingItem)); + addPart( + history->createItem( + PrepareLogMessage( + action.vmessage(), + history->nextNonHistoryEntryId(), + date), + MTPDmessage_ClientFlag::f_admin_log_entry, + detachExistingItem), + ExtractSentDate(action.vmessage())); }; auto createChangeLinkedChat = [&](const MTPDchannelAdminLogEventActionChangeLinkedChat &action) { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h index fa0373780..ceaa50bc3 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h @@ -22,7 +22,7 @@ void GenerateItems( not_null delegate, not_null history, const MTPDchannelAdminLogEvent &event, - Fn callback); + Fn callback); // Smart pointer wrapper for HistoryItem* that destroys the owned item. class OwnedItem { From 26166591166bdda533adc00701451ee4e3703cf7 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 22 Jan 2021 05:05:24 +0300 Subject: [PATCH 079/396] Added missed date detailed info in tooltips to admin log and sections. --- .../admin_log/history_admin_log_inner.cpp | 2 +- .../history/history_inner_widget.cpp | 35 +------------------ .../history/view/history_view_element.cpp | 35 +++++++++++++++++++ .../history/view/history_view_element.h | 2 ++ .../history/view/history_view_list_widget.cpp | 3 +- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 1fea6e2da..c3781e0d2 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -512,7 +512,7 @@ QString InnerWidget::tooltipText() const { if (const auto view = App::hoveredItem()) { const auto format = QLocale::system().dateTimeFormat( QLocale::LongFormat); - auto dateText = view->dateTime().toString(format); + auto dateText = HistoryView::DateTooltipText(view); const auto sentIt = _itemDates.find(view->data()); if (sentIt != end(_itemDates)) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 755dd3387..3ccafebf4 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -3313,40 +3313,7 @@ QString HistoryInner::tooltipText() const { if (_mouseCursorState == CursorState::Date && _mouseAction == MouseAction::None) { if (const auto view = App::hoveredItem()) { - auto dateText = view->dateTime().toString( - QLocale::system().dateTimeFormat(QLocale::LongFormat)); - if (const auto editedDate = view->displayedEditDate()) { - dateText += '\n' + tr::lng_edited_date( - tr::now, - lt_date, - base::unixtime::parse(editedDate).toString( - QLocale::system().dateTimeFormat( - QLocale::LongFormat))); - } - if (const auto forwarded = view->data()->Get()) { - dateText += '\n' + tr::lng_forwarded_date( - tr::now, - lt_date, - base::unixtime::parse(forwarded->originalDate).toString( - QLocale::system().dateTimeFormat( - QLocale::LongFormat))); - if (const auto media = view->media()) { - if (media->hidesForwardedInfo()) { - dateText += "\n" + tr::lng_forwarded( - tr::now, - lt_user, - (forwarded->originalSender - ? forwarded->originalSender->shortName() - : forwarded->hiddenSenderInfo->firstName)); - } - } - } - if (const auto msgsigned = view->data()->Get()) { - if (msgsigned->isElided && !msgsigned->isAnonymousRank) { - dateText += '\n' + tr::lng_signed_author(tr::now, lt_user, msgsigned->author); - } - } - return dateText; + return HistoryView::DateTooltipText(view); } } else if (_mouseCursorState == CursorState::Forwarded && _mouseAction == MouseAction::None) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index eb533b848..52a488d99 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_large_emoji.h" #include "history/history.h" +#include "base/unixtime.h" #include "core/application.h" #include "core/core_settings.h" #include "main/main_session.h" @@ -157,6 +158,40 @@ TextSelection ShiftItemSelection( return ShiftItemSelection(selection, byText.length()); } +QString DateTooltipText(not_null view) { + const auto format = QLocale::system().dateTimeFormat(QLocale::LongFormat); + auto dateText = view->dateTime().toString(format); + if (const auto editedDate = view->displayedEditDate()) { + dateText += '\n' + tr::lng_edited_date( + tr::now, + lt_date, + base::unixtime::parse(editedDate).toString(format)); + } + if (const auto forwarded = view->data()->Get()) { + dateText += '\n' + tr::lng_forwarded_date( + tr::now, + lt_date, + base::unixtime::parse(forwarded->originalDate).toString(format)); + if (const auto media = view->media()) { + if (media->hidesForwardedInfo()) { + dateText += '\n' + tr::lng_forwarded( + tr::now, + lt_user, + (forwarded->originalSender + ? forwarded->originalSender->shortName() + : forwarded->hiddenSenderInfo->firstName)); + } + } + } + if (const auto msgsigned = view->data()->Get()) { + if (msgsigned->isElided && !msgsigned->isAnonymousRank) { + dateText += '\n' + + tr::lng_signed_author(tr::now, lt_user, msgsigned->author); + } + } + return dateText; +} + void UnreadBar::init(const QString &string) { text = string; width = st::semiboldFont->width(text); diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 82c610eee..2515e9e91 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -126,6 +126,8 @@ TextSelection ShiftItemSelection( TextSelection selection, const Ui::Text::String &byText); +QString DateTooltipText(not_null view); + // Any HistoryView::Element can have this Component for // displaying the unread messages bar above the message. struct UnreadBar : public RuntimeComponent { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index e75789e2a..2fa498844 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1216,8 +1216,7 @@ QString ListWidget::tooltipText() const { ? _overElement->data().get() : nullptr; if (_mouseCursorState == CursorState::Date && item) { - return _overElement->dateTime().toString( - QLocale::system().dateTimeFormat(QLocale::LongFormat)); + return HistoryView::DateTooltipText(_overElement); } else if (_mouseCursorState == CursorState::Forwarded && item) { if (const auto forwarded = item->Get()) { return forwarded->text.toString(); From 574d915c23ffb58da1eb0af9e477b9e54daa3dc2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Jan 2021 16:16:18 +0400 Subject: [PATCH 080/396] Fix build and tray icon menu on Windows. --- .../boxes/peer_list_controllers.cpp | 4 +- .../SourceFiles/data/data_replies_list.cpp | 2 +- .../data/data_scheduled_messages.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 93 ++++++++----------- Telegram/SourceFiles/mainwindow.h | 25 ++--- .../platform/linux/main_window_linux.cpp | 19 ++-- .../platform/linux/main_window_linux.h | 5 +- .../platform/mac/main_window_mac.mm | 22 +++-- .../platform/win/main_window_win.cpp | 30 ++++++ .../platform/win/main_window_win.h | 7 ++ Telegram/SourceFiles/window/main_window.cpp | 29 ++++++ Telegram/SourceFiles/window/main_window.h | 11 +++ .../SourceFiles/window/window_main_menu.cpp | 6 +- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 16 files changed, 169 insertions(+), 92 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index e16f9136a..3d8544108 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "history/history.h" #include "dialogs/dialogs_main_list.h" -#include "window/window_session_controller.h" // onShowAddContact() +#include "window/window_session_controller.h" // showAddContact() #include "facades.h" #include "styles/style_boxes.h" #include "styles/style_profile.h" @@ -112,7 +112,7 @@ object_ptr PrepareContactsBox( box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addLeftButton( tr::lng_profile_add_contact(), - [=] { controller->widget()->onShowAddContact(); }); + [=] { controller->widget()->showAddContact(); }); }; return Box( std::make_unique( diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index e27d2d80b..e184460b4 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -642,7 +642,7 @@ HistoryItem *RepliesList::lastEditableMessage() { const auto it = ranges::find_if(_list, std::move(proj)); return (it == end(_list)) ? nullptr - : _history->owner().groups().findItemToEdit(message(*it)); + : _history->owner().groups().findItemToEdit(message(*it)).get(); } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 7e1d2bdd7..0883ac09c 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -562,7 +562,7 @@ HistoryItem *ScheduledMessages::lastEditableMessage( const auto it = ranges::find_if(items, std::move(proj)); return (it == end(items)) ? nullptr - : history->owner().groups().findItemToEdit((*it).get()); + : history->owner().groups().findItemToEdit((*it).get()).get(); } } // namespace Data diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 90da5d83f..d9bd46488 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2281,7 +2281,7 @@ void InnerWidget::refreshEmptyLabel() { resizeEmptyLabel(); _empty->setClickHandlerFilter([=](const auto &...) { if (_emptyState == EmptyState::NoContacts) { - App::wnd()->onShowAddContact(); + App::wnd()->showAddContact(); } else if (_emptyState == EmptyState::EmptyFolder) { editOpenedFilter(); } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index c80e59c11..188213ead 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -146,11 +146,24 @@ void MainWindow::createTrayIconMenu() { : tr::lng_enable_notifications_from_tray(tr::now); if (Platform::IsLinux() && !Platform::IsWayland()) { - trayIconMenu->addAction(tr::lng_open_from_tray(tr::now), this, SLOT(showFromTray())); + trayIconMenu->addAction(tr::lng_open_from_tray(tr::now), [=] { + showFromTray(); + }); } - trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), this, SLOT(minimizeToTray())); - trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray())); - trayIconMenu->addAction(tr::lng_quit_from_tray(tr::now), this, SLOT(quitFromTray())); + const auto showLifetime = std::make_shared(); + trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), [=] { + if (_activeForTrayIconAction) { + minimizeToTray(); + } else { + showFromTrayMenu(); + } + }); + trayIconMenu->addAction(notificationActionText, [=] { + toggleDisplayNotifyFromTray(); + }); + trayIconMenu->addAction(tr::lng_quit_from_tray(tr::now), [=] { + quitFromTray(); + }); initTrayMenuHook(); } @@ -635,18 +648,17 @@ void MainWindow::updateTrayMenu(bool force) { auto actions = trayIconMenu->actions(); if (Platform::IsLinux() && !Platform::IsWayland()) { - auto minimizeAction = actions.at(1); + const auto minimizeAction = actions.at(1); minimizeAction->setEnabled(isVisible()); } else { - updateIsActive(); - auto active = Platform::IsWayland() ? isVisible() : isActive(); - auto toggleAction = actions.at(0); - disconnect(toggleAction, SIGNAL(triggered(bool)), this, SLOT(minimizeToTray())); - disconnect(toggleAction, SIGNAL(triggered(bool)), this, SLOT(showFromTray())); - connect(toggleAction, SIGNAL(triggered(bool)), this, active ? SLOT(minimizeToTray()) : SLOT(showFromTray())); - toggleAction->setText(active - ? tr::lng_minimize_to_tray(tr::now) - : tr::lng_open_from_tray(tr::now)); + const auto active = isActiveForTrayMenu(); + if (_activeForTrayIconAction != active) { + _activeForTrayIconAction = active; + const auto toggleAction = actions.at(0); + toggleAction->setText(_activeForTrayIconAction + ? tr::lng_minimize_to_tray(tr::now) + : tr::lng_open_from_tray(tr::now)); + } } auto notificationAction = actions.at(Platform::IsLinux() && !Platform::IsWayland() ? 2 : 1); auto notificationActionText = Core::App().settings().desktopNotify() @@ -657,8 +669,10 @@ void MainWindow::updateTrayMenu(bool force) { psTrayMenuUpdated(); } -void MainWindow::onShowAddContact() { - if (isHidden()) showFromTray(); +void MainWindow::showAddContact() { + if (isHidden()) { + showFromTray(); + } if (const auto controller = sessionController()) { Ui::show( @@ -667,8 +681,10 @@ void MainWindow::onShowAddContact() { } } -void MainWindow::onShowNewGroup() { - if (isHidden()) showFromTray(); +void MainWindow::showNewGroup() { + if (isHidden()) { + showFromTray(); + } if (const auto controller = sessionController()) { Ui::show( @@ -677,8 +693,10 @@ void MainWindow::onShowNewGroup() { } } -void MainWindow::onShowNewChannel() { - if (isHidden()) showFromTray(); +void MainWindow::showNewChannel() { + if (isHidden()) { + showFromTray(); + } if (const auto controller = sessionController()) { Ui::show( @@ -720,24 +738,6 @@ void MainWindow::showLogoutConfirmation() { callback)); } -void MainWindow::quitFromTray() { - App::quit(); -} - -void MainWindow::activate() { - bool wasHidden = !isVisible(); - setWindowState(windowState() & ~Qt::WindowMinimized); - setVisible(true); - psActivateProcess(); - activateWindow(); - controller().updateIsActiveFocus(); - if (wasHidden) { - if (_main) { - _main->windowShown(); - } - } -} - bool MainWindow::takeThirdSectionFromLayer() { return _layer ? _layer->takeToThirdSection() : false; } @@ -749,23 +749,12 @@ void MainWindow::fixOrder() { if (_testingThemeWarning) _testingThemeWarning->raise(); } -void MainWindow::showFromTray(QSystemTrayIcon::ActivationReason reason) { - if (reason != QSystemTrayIcon::Context) { - base::call_delayed(1, this, [this] { - updateTrayMenu(); - updateGlobalMenu(); - }); - activate(); - updateUnreadCounter(); - } -} - void MainWindow::handleTrayIconActication( QSystemTrayIcon::ActivationReason reason) { updateIsActive(); if (Platform::IsMac() && isActive()) { if (trayIcon && !trayIcon->contextMenu()) { - showFromTray(reason); + showFromTray(); } return; } @@ -775,10 +764,10 @@ void MainWindow::handleTrayIconActication( psShowTrayMenu(); }); } else if (!skipTrayClick()) { - if (Platform::IsWayland() ? isVisible() : isActive()) { + if (isActiveForTrayMenu()) { minimizeToTray(); } else { - showFromTray(reason); + showFromTray(); } _lastTrayClickTime = crl::now(); } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index d7d925cac..38bf87dd8 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -40,8 +40,6 @@ class LayerStackWidget; class MediaPreviewWidget; class MainWindow : public Platform::MainWindow { - Q_OBJECT - public: explicit MainWindow(not_null controller); ~MainWindow(); @@ -55,11 +53,17 @@ public: void setupIntro(Intro::EnterPoint point); void setupMain(); + void showSettings(); + void showAddContact(); + void showNewGroup(); + void showNewChannel(); + + void setInnerFocus(); + MainWidget *sessionContent() const; [[nodiscard]] bool doWeMarkAsRead(); - void activate(); bool takeThirdSectionFromLayer(); @@ -115,18 +119,6 @@ protected: void updateIsActiveHook() override; void clearWidgetsHook() override; -public slots: - void showSettings(); - void setInnerFocus(); - - void quitFromTray(); - void showFromTray(QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Unknown); - void toggleDisplayNotifyFromTray(); - - void onShowAddContact(); - void onShowNewGroup(); - void onShowNewChannel(); - private: [[nodiscard]] bool skipTrayClick() const; @@ -140,12 +132,15 @@ private: void themeUpdated(const Window::Theme::BackgroundUpdate &data); + void toggleDisplayNotifyFromTray(); + QPixmap grabInner(); QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64; crl::time _lastTrayClickTime = 0; QPoint _lastMousePosition; + bool _activeForTrayIconAction = true; object_ptr _passcodeLock = { nullptr }; object_ptr _intro = { nullptr }; diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 34f410584..26ed2304d 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -593,6 +593,11 @@ bool MainWindow::hasTrayIcon() const { #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } +bool MainWindow::isActiveForTrayMenu() { + updateIsActive(); + return Platform::IsWayland() ? isVisible() : isActive(); +} + void MainWindow::psShowTrayMenu() { _trayIconMenuXEmbed->popup(QCursor::pos()); } @@ -661,7 +666,7 @@ void MainWindow::sniSignalEmitted( gchar *signal_name, GVariant *parameters, gpointer user_data) { - if(signal_name == qstr("StatusNotifierHostRegistered")) { + if (signal_name == qstr("StatusNotifierHostRegistered")) { crl::on_main([] { if (const auto window = App::wnd()) { window->handleSNIHostRegistered(); @@ -684,7 +689,7 @@ void MainWindow::handleSNIHostRegistered() { LOG(("Switching to SNI tray icon...")); if (trayIcon) { - trayIcon->setContextMenu(0); + trayIcon->setContextMenu(nullptr); trayIcon->deleteLater(); } trayIcon = nullptr; @@ -886,7 +891,7 @@ void MainWindow::LibsLoaded() { } void MainWindow::initTrayMenuHook() { - _trayIconMenuXEmbed = new Ui::PopupMenu(nullptr, trayIconMenu); + _trayIconMenuXEmbed.emplace(nullptr, trayIconMenu); _trayIconMenuXEmbed->deleteOnHide(false); } @@ -1031,19 +1036,19 @@ void MainWindow::createGlobalMenu() { psAddContact = tools->addAction( tr::lng_mac_menu_add_contact(tr::now), App::wnd(), - [=] { App::wnd()->onShowAddContact(); }); + [=] { App::wnd()->showAddContact(); }); tools->addSeparator(); psNewGroup = tools->addAction( tr::lng_mac_menu_new_group(tr::now), App::wnd(), - [=] { App::wnd()->onShowNewGroup(); }); + [=] { App::wnd()->showNewGroup(); }); psNewChannel = tools->addAction( tr::lng_mac_menu_new_channel(tr::now), App::wnd(), - [=] { App::wnd()->onShowNewChannel(); }); + [=] { App::wnd()->showNewChannel(); }); auto help = psMainMenu->addMenu(tr::lng_linux_menu_help(tr::now)); @@ -1222,8 +1227,6 @@ MainWindow::~MainWindow() { g_object_unref(_sniDBusProxy); #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION - - delete _trayIconMenuXEmbed; } } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 0460829af..62ac1e2fc 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_main_window.h" +#include "base/unique_qptr.h" namespace Ui { class PopupMenu; @@ -43,6 +44,8 @@ public: return _sniAvailable || QSystemTrayIcon::isSystemTrayAvailable(); } + bool isActiveForTrayMenu() override; + static void LibsLoaded(); ~MainWindow(); @@ -75,7 +78,7 @@ protected: private: bool _sniAvailable = false; - Ui::PopupMenu *_trayIconMenuXEmbed = nullptr; + base::unique_qptr _trayIconMenuXEmbed; void updateIconCounters(); void updateWaylandDecorationColors(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index d99bb9630..9df1eeb3c 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -692,7 +692,9 @@ void MainWindow::createGlobalMenu() { about->setMenuRole(QAction::AboutQtRole); main->addSeparator(); - QAction *prefs = main->addAction(tr::lng_mac_menu_preferences(tr::now), App::wnd(), SLOT(showSettings()), QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); + QAction *prefs = main->addAction(tr::lng_mac_menu_preferences(tr::now), App::wnd(), [=] { + App::wnd()->showSettings(); + }, QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); prefs->setMenuRole(QAction::PreferencesRole); QMenu *file = psMainMenu.addMenu(tr::lng_mac_menu_file(tr::now)); @@ -728,19 +730,27 @@ void MainWindow::createGlobalMenu() { psContacts = window->addAction(tr::lng_mac_menu_contacts(tr::now)); connect(psContacts, &QAction::triggered, psContacts, crl::guard(this, [=] { if (isHidden()) { - App::wnd()->showFromTray(); + showFromTray(); } if (!sessionController()) { return; } Ui::show(PrepareContactsBox(sessionController())); })); - psAddContact = window->addAction(tr::lng_mac_menu_add_contact(tr::now), App::wnd(), SLOT(onShowAddContact())); + psAddContact = window->addAction(tr::lng_mac_menu_add_contact(tr::now), App::wnd(), [=] { + App::wnd()->showAddContact(); + }); window->addSeparator(); - psNewGroup = window->addAction(tr::lng_mac_menu_new_group(tr::now), App::wnd(), SLOT(onShowNewGroup())); - psNewChannel = window->addAction(tr::lng_mac_menu_new_channel(tr::now), App::wnd(), SLOT(onShowNewChannel())); + psNewGroup = window->addAction(tr::lng_mac_menu_new_group(tr::now), App::wnd(), [=] { + App::wnd()->showNewGroup(); + }); + psNewChannel = window->addAction(tr::lng_mac_menu_new_channel(tr::now), App::wnd(), [=] { + App::wnd()->showNewChannel(); + }); window->addSeparator(); - psShowTelegram = window->addAction(tr::lng_mac_menu_show(tr::now), App::wnd(), SLOT(showFromTray())); + psShowTelegram = window->addAction(tr::lng_mac_menu_show(tr::now), App::wnd(), [=] { + showFromTray(); + }); updateGlobalMenu(); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 61ce7f500..445f407c7 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -47,6 +47,13 @@ Q_DECLARE_METATYPE(QMargins); namespace Platform { namespace { +// Mouse down on tray icon deactivates the application. +// So there is no way to know for sure if the tray icon was clicked from +// active application or from inactive application. So we assume that +// if the application was deactivated less than 0.5s ago, then the tray +// icon click (both left or right button) was made from the active app. +constexpr auto kKeepActiveForTrayIcon = crl::time(500); + HICON createHIconFromQIcon(const QIcon &icon, int xSize, int ySize) { if (!icon.isNull()) { const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); @@ -113,6 +120,13 @@ MainWindow::MainWindow(not_null controller) } }); setupNativeWindowFrame(); + + using namespace rpl::mappers; + Core::App().appDeactivatedValue( + ) | rpl::distinct_until_changed( + ) | rpl::filter(_1) | rpl::start_with_next([=] { + _lastDeactivateTime = crl::now(); + }, lifetime()); } void MainWindow::setupNativeWindowFrame() { @@ -280,6 +294,11 @@ void MainWindow::updateWindowIcon() { updateIconCounters(); } +bool MainWindow::isActiveForTrayMenu() { + return !_lastDeactivateTime + || (_lastDeactivateTime + kKeepActiveForTrayIcon >= crl::now()); +} + void MainWindow::unreadCounterChangedHook() { setWindowTitle(titleText()); updateIconCounters(); @@ -604,6 +623,17 @@ void MainWindow::fixMaximizedWindow() { } } +void MainWindow::showFromTrayMenu() { + // If we try to activate() window before the trayIconMenu is hidden, + // then the window will be shown in semi-active state (Qt bug). + // It will receive input events, but it will be rendered as inactive. + using namespace rpl::mappers; + _showFromTrayLifetime = trayIconMenu->shownValue( + ) | rpl::filter(_1) | rpl::take(1) | rpl::start_with_next([=] { + showFromTray(); + }); +} + HWND MainWindow::psHwnd() const { return ps_hWnd; } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 2ac8161e6..61042afcd 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -24,6 +24,8 @@ class MainWindow : public Window::MainWindow { public: explicit MainWindow(not_null controller); + void showFromTrayMenu() override; + HWND psHwnd() const; HMENU psMenu() const; @@ -32,6 +34,7 @@ public: void updateCustomMargins(); void updateWindowIcon() override; + bool isActiveForTrayMenu() override; void psRefreshTaskbarIcon(); @@ -103,6 +106,10 @@ private: bool _wasNativeFrame = false; bool _hasActiveFrame = false; + // Workarounds for activation from tray icon. + crl::time _lastDeactivateTime = 0; + rpl::lifetime _showFromTrayLifetime; + HWND ps_hWnd = nullptr; HWND ps_tbHider_hWnd = nullptr; HMENU ps_menu = nullptr; diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index f9733b6a4..7740d2ce8 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "apiwrap.h" #include "mainwindow.h" +#include "mainwidget.h" // session->content()->windowShown(). #include "facades.h" #include "app.h" #include "styles/style_window.h" @@ -296,6 +297,34 @@ void MainWindow::handleVisibleChanged(bool visible) { handleVisibleChangedHook(visible); } +void MainWindow::showFromTray() { + base::call_delayed(1, this, [this] { + updateTrayMenu(); + updateGlobalMenu(); + }); + activate(); + updateUnreadCounter(); +} + +void MainWindow::quitFromTray() { + App::quit(); +} + +void MainWindow::activate() { + bool wasHidden = !isVisible(); + setWindowState(windowState() & ~Qt::WindowMinimized); + setVisible(true); + psActivateProcess(); + raise(); + activateWindow(); + controller().updateIsActiveFocus(); + if (wasHidden) { + if (const auto session = sessionController()) { + session->content()->windowShown(); + } + } +} + void MainWindow::updatePalette() { Ui::ForceFullRepaint(this); diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 264782419..c209138af 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -50,6 +50,13 @@ public: bool hideNoQuit(); + void showFromTray(); + void quitFromTray(); + void activate(); + virtual void showFromTrayMenu() { + showFromTray(); + } + void init(); HitTestResult hitTest(const QPoint &p) const; @@ -58,6 +65,10 @@ public: bool isActive() const { return _isActive; } + virtual bool isActiveForTrayMenu() { + updateIsActive(); + return isActive(); + } bool positionInited() const { return _positionInited; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index a59bd0342..14faab386 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -859,10 +859,10 @@ void MainMenu::refreshMenu() { if (!_controller->session().supportMode()) { const auto controller = _controller; _menu->addAction(tr::lng_create_group_title(tr::now), [] { - App::wnd()->onShowNewGroup(); + App::wnd()->showNewGroup(); }, &st::mainMenuNewGroup, &st::mainMenuNewGroupOver); _menu->addAction(tr::lng_create_channel_title(tr::now), [] { - App::wnd()->onShowNewChannel(); + App::wnd()->showNewChannel(); }, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver); _menu->addAction(tr::lng_menu_contacts(tr::now), [=] { Ui::show(PrepareContactsBox(controller)); @@ -883,7 +883,7 @@ void MainMenu::refreshMenu() { } } else { _menu->addAction(tr::lng_profile_add_contact(tr::now), [] { - App::wnd()->onShowAddContact(); + App::wnd()->showAddContact(); }, &st::mainMenuContacts, &st::mainMenuContactsOver); const auto fix = std::make_shared>(); diff --git a/Telegram/lib_base b/Telegram/lib_base index dc89f34be..d7342985e 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit dc89f34be5387bef731c59aeca3ca201e042ecc3 +Subproject commit d7342985eb85b0ab791dee0514cffb0b05b3010d diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 9ebd51c8f..912f0b48a 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 9ebd51c8f89799d5b6125e34028528d52b0b0b8e +Subproject commit 912f0b48a648e7f12e7bcba82a0f0ec326aab8dd From e55581e0c96e0663ee1ebe830707e24b4d4d1599 Mon Sep 17 00:00:00 2001 From: the-vyld Date: Mon, 23 Nov 2020 13:56:41 +0530 Subject: [PATCH 081/396] fix typos in changelog.txt --- changelog.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 0f490d483..c4bded047 100644 --- a/changelog.txt +++ b/changelog.txt @@ -111,7 +111,7 @@ 2.4.2 (02.10.20) -- Allow block, report and delete all message from user from "user joined" service message context menu. +- Allow block, report and delete all messages from a user from "user joined" service message context menu. - Fix admin badge display in groups. - Fix loading and opening of comments in channels. @@ -133,7 +133,7 @@ 2.3.1 (21.08.20) -- Fix Calls Settings for Video Calls. +- Fix Call Settings for Video Calls. 2.3 (14.08.20) @@ -142,7 +142,7 @@ 2.2 (26.07.20) - Quickly switch between different Telegram accounts if you use multiple phone numbers. -- Share and store unlimited files of any type, now up to 2'000 MB each. +- Share and store unlimited files of any type, now up to 2000 MB each. - Edit your scheduled messages. - Use Auto-Night Mode to make Telegram night mode match the system Dark Mode settings. - Also added an option to switch to system window frame in Windows and Linux. From 49736cd879db4283043e901b89ba132af385b296 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 9 Jan 2021 06:10:06 +0400 Subject: [PATCH 082/396] Recreate notification manager on notification service owner change --- Telegram/CMakeLists.txt | 4 + .../linux_notification_service_watcher.cpp | 37 ++++ .../linux_notification_service_watcher.h | 24 +++ .../linux/notifications_manager_linux.cpp | 180 ++++++++---------- .../platform/linux/specific_linux.cpp | 26 +++ .../platform/linux/specific_linux.h | 1 + 6 files changed, 172 insertions(+), 100 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 9d8dcff78..f5f02c7b9 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -823,6 +823,8 @@ PRIVATE platform/linux/linux_gsd_media_keys.h platform/linux/linux_libs.cpp platform/linux/linux_libs.h + platform/linux/linux_notification_service_watcher.cpp + platform/linux/linux_notification_service_watcher.h platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h platform/linux/linux_xlib_helper.cpp @@ -1116,6 +1118,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.h + platform/linux/linux_notification_service_watcher.cpp + platform/linux/linux_notification_service_watcher.h platform/linux/notifications_manager_linux.cpp ) diff --git a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp new file mode 100644 index 000000000..1aa0fb969 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp @@ -0,0 +1,37 @@ +/* +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 "platform/linux/linux_notification_service_watcher.h" + +#include "core/application.h" +#include "main/main_domain.h" +#include "window/notifications_manager.h" + +#include + +namespace Platform { +namespace internal { + +NotificationServiceWatcher::NotificationServiceWatcher() +: _dbusWatcher( + qsl("org.freedesktop.Notifications"), + QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForOwnerChange) { + const auto signal = &QDBusServiceWatcher::serviceOwnerChanged; + QObject::connect(&_dbusWatcher, signal, [=] { + crl::on_main([=] { + if (!Core::App().domain().started()) { + return; + } + + Core::App().notifications().createManager(); + }); + }); +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h new file mode 100644 index 000000000..50295d089 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h @@ -0,0 +1,24 @@ +/* +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 + +namespace Platform { +namespace internal { + +class NotificationServiceWatcher { +public: + NotificationServiceWatcher(); + +private: + QDBusServiceWatcher _dbusWatcher; +}; + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 771db12f6..931cdafd8 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +#include #include #include #include @@ -41,112 +42,89 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; constexpr auto kImageDataType = "(iiibii@ay)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; -bool NotificationsSupported = false; +bool ServiceRegistered = false; bool InhibitedNotSupported = false; +std::vector CurrentServerInformation; +QStringList CurrentCapabilities; -void ComputeSupported(bool wait = false) { - const auto message = QDBusMessage::createMethodCall( - kService.utf16(), - kObjectPath.utf16(), - kInterface.utf16(), - qsl("GetServerInformation")); +bool GetServiceRegistered() { + const auto interface = QDBusConnection::sessionBus().interface(); + const auto activatable = IsNotificationServiceActivatable(); - auto async = QDBusConnection::sessionBus().asyncCall(message); - auto watcher = new QDBusPendingCallWatcher(async); - - QObject::connect( - watcher, - &QDBusPendingCallWatcher::finished, - [=](QDBusPendingCallWatcher *call) { - QDBusPendingReply< - QString, - QString, - QString, - QString> reply = *call; - - if (reply.isValid()) { - NotificationsSupported = true; - } - - call->deleteLater(); - }); - - if (wait) { - watcher->waitForFinished(); - } -} - -void GetSupported() { - static auto Checked = false; - if (Checked) { - return; - } - Checked = true; - - if (Core::App().settings().nativeNotifications() && !IsWayland()) { - ComputeSupported(true); - } else { - ComputeSupported(); - } -} - -std::vector ComputeServerInformation() { - std::vector serverInformation; - - const auto message = QDBusMessage::createMethodCall( - kService.utf16(), - kObjectPath.utf16(), - kInterface.utf16(), - qsl("GetServerInformation")); - - const auto reply = QDBusConnection::sessionBus().call(message); - - if (reply.type() == QDBusMessage::ReplyMessage) { - ranges::transform( - reply.arguments(), - ranges::back_inserter(serverInformation), - &QVariant::toString - ); - } else if (reply.type() == QDBusMessage::ErrorMessage) { - LOG(("Native notification error: %1").arg(reply.errorMessage())); - } else { - LOG(("Native notification error: " - "invalid reply from GetServerInformation")); - } - - return serverInformation; + return interface + ? interface->isServiceRegistered(kService.utf16()) || activatable + : activatable; } std::vector GetServerInformation() { - static const auto Result = ComputeServerInformation(); - return Result; + std::vector result; + + const auto message = QDBusMessage::createMethodCall( + kService.utf16(), + kObjectPath.utf16(), + kInterface.utf16(), + qsl("GetServerInformation")); + + // We may be launched earlier than notification daemon + while (true) { + const auto reply = QDBusConnection::sessionBus().call(message); + + if (reply.type() == QDBusMessage::ReplyMessage) { + ranges::transform( + reply.arguments(), + ranges::back_inserter(result), + &QVariant::toString); + } else if (reply.type() == QDBusMessage::ErrorMessage) { + LOG(("Native notification error: %1").arg(reply.errorMessage())); + + if (reply.errorName() + == qsl("org.freedesktop.DBus.Error.NoReply")) { + continue; + } + } else { + LOG(("Native notification error: " + "invalid reply from GetServerInformation")); + } + + break; + } + + return result; } -QStringList ComputeCapabilities() { +QStringList GetCapabilities() { const auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), kInterface.utf16(), qsl("GetCapabilities")); - const QDBusReply reply = QDBusConnection::sessionBus().call( - message); + // We may be launched earlier than notification daemon + while (true) { + const QDBusReply reply = QDBusConnection::sessionBus() + .call(message); + + if (reply.isValid()) { + return reply.value(); + } - if (reply.isValid()) { - return reply.value(); - } else { LOG(("Native notification error: %1").arg(reply.error().message())); + + if (reply.error().type() != QDBusError::NoReply) { + break; + } } return {}; } -QStringList GetCapabilities() { - static const auto Result = ComputeCapabilities(); - return Result; -} - bool Inhibited() { + if (!Supported() + || !CurrentCapabilities.contains(qsl("inhibitions")) + || InhibitedNotSupported) { + return false; + } + auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), @@ -277,8 +255,7 @@ NotificationData::NotificationData( bool hideReplyButton) : _manager(manager) , _title(title) -, _imageKey(GetImageKey(ParseSpecificationVersion( - GetServerInformation()))) +, _imageKey(GetImageKey(ParseSpecificationVersion(CurrentServerInformation))) , _id(id) { GError *error = nullptr; @@ -293,7 +270,7 @@ NotificationData::NotificationData( return; } - const auto capabilities = GetCapabilities(); + const auto capabilities = CurrentCapabilities; if (capabilities.contains(qsl("body-markup"))) { _body = subtitle.isEmpty() @@ -613,30 +590,33 @@ void NotificationData::notificationReplied(uint id, const QString &text) { } // namespace bool SkipAudio() { - if (Supported() - && GetCapabilities().contains(qsl("inhibitions")) - && !InhibitedNotSupported) { - return Inhibited(); - } - - return false; + return Inhibited(); } bool SkipToast() { - return SkipAudio(); + return Inhibited(); } bool SkipFlashBounce() { - return SkipAudio(); + return Inhibited(); } bool Supported() { - return NotificationsSupported; + return ServiceRegistered; } std::unique_ptr Create( Window::Notifications::System *system) { - GetSupported(); + ServiceRegistered = GetServiceRegistered(); + InhibitedNotSupported = false; + + if (Supported()) { + CurrentServerInformation = GetServerInformation(); + CurrentCapabilities = GetCapabilities(); + } else { + CurrentServerInformation = {}; + CurrentCapabilities = QStringList{}; + } if ((Core::App().settings().nativeNotifications() && Supported()) || IsWayland()) { @@ -683,8 +663,8 @@ Manager::Private::Private(not_null manager, Type type) return; } - const auto serverInformation = GetServerInformation(); - const auto capabilities = GetCapabilities(); + const auto serverInformation = CurrentServerInformation; + const auto capabilities = CurrentCapabilities; if (!serverInformation.empty()) { LOG(("Notification daemon product name: %1") diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 5eac9bd28..251d5db7b 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_notification_service_watcher.h" #include "platform/linux/linux_gsd_media_keys.h" #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -87,6 +88,8 @@ constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; QStringList PlatformThemes; #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +std::unique_ptr NSWInstance; + QStringList ListDBusActivatableNames() { static const auto Result = [&] { const auto message = QDBusMessage::createMethodCall( @@ -632,6 +635,17 @@ bool CanOpenDirectoryWithPortal() { return false; } +bool IsNotificationServiceActivatable() { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + static const auto Result = ListDBusActivatableNames().contains( + qsl("org.freedesktop.Notifications")); + + return Result; +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION + + return false; +} + QString AppRuntimeDirectory() { static const auto Result = [&] { auto runtimeDir = QStandardPaths::writableLocation( @@ -1231,9 +1245,21 @@ void start() { if (const auto waylandIntegration = WaylandIntegration::Instance()) { waylandIntegration->waitForInterfaceAnnounce(); } + +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (!IsNotificationServiceActivatable()) { + NSWInstance = std::make_unique< + internal::NotificationServiceWatcher>(); + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } void finish() { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (NSWInstance) { + NSWInstance = nullptr; + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } } // namespace ThirdParty diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 80584a23d..2f279c5d4 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -25,6 +25,7 @@ bool UseGtkIntegration(); bool IsGtkIntegrationForced(); bool UseXDGDesktopPortal(); bool CanOpenDirectoryWithPortal(); +bool IsNotificationServiceActivatable(); QString AppRuntimeDirectory(); QString GetLauncherBasename(); From 64b12bde559114a3d66c8e85f480b410b097dd81 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 13:56:49 +0400 Subject: [PATCH 083/396] Allow qualified notification daemons by default on Linux --- .../linux/notifications_manager_linux.cpp | 83 +++++++++++++++---- .../notifications_manager_linux_dummy.cpp | 8 +- .../platform/mac/notifications_manager_mac.mm | 4 + .../platform/platform_notifications_manager.h | 1 + .../win/notifications_manager_win.cpp | 4 + .../settings/settings_notifications.cpp | 10 +-- 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 931cdafd8..4dc5cec13 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -43,7 +43,7 @@ constexpr auto kImageDataType = "(iiibii@ay)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; bool ServiceRegistered = false; -bool InhibitedNotSupported = false; +bool InhibitionSupported = false; std::vector CurrentServerInformation; QStringList CurrentCapabilities; @@ -118,10 +118,42 @@ QStringList GetCapabilities() { return {}; } +bool GetInhibitionSupported() { + auto message = QDBusMessage::createMethodCall( + kService.utf16(), + kObjectPath.utf16(), + kPropertiesInterface.utf16(), + qsl("Get")); + + message.setArguments({ + qsl("org.freedesktop.Notifications"), + qsl("Inhibited") + }); + + // We may be launched earlier than notification daemon + while (true) { + const QDBusError error = QDBusConnection::sessionBus().call(message); + + if (!error.isValid()) { + return true; + } else if (error.type() == QDBusError::InvalidArgs) { + break; + } + + LOG(("Native notification error: %1").arg(error.message())); + + if (error.type() != QDBusError::NoReply) { + break; + } + } + + return false; +} + bool Inhibited() { if (!Supported() || !CurrentCapabilities.contains(qsl("inhibitions")) - || InhibitedNotSupported) { + || !InhibitionSupported) { return false; } @@ -139,26 +171,36 @@ bool Inhibited() { const QDBusReply reply = QDBusConnection::sessionBus().call( message); - static const auto NotSupportedErrors = { - QDBusError::ServiceUnknown, - QDBusError::InvalidArgs, - }; - if (reply.isValid()) { return reply.value().toBool(); - } else if (ranges::contains(NotSupportedErrors, reply.error().type())) { - InhibitedNotSupported = true; - } else { - if (reply.error().type() == QDBusError::AccessDenied) { - InhibitedNotSupported = true; - } - - LOG(("Native notification error: %1").arg(reply.error().message())); } + LOG(("Native notification error: %1").arg(reply.error().message())); return false; } +bool IsQualifiedDaemon() { + // A list of capabilities that offer feature parity + // with custom notifications + static const auto NeededCapabilities = { + // To show message content + qsl("body"), + // To make the sender name bold + qsl("body-markup"), + // To have buttons on notifications + qsl("actions"), + // To have quick reply + qsl("inline-reply"), + // To not to play sound with Don't Disturb activated + // (no, using sound capability is not a way) + qsl("inhibitions"), + }; + + return ranges::all_of(NeededCapabilities, [&](const auto &capability) { + return CurrentCapabilities.contains(capability); + }) && InhibitionSupported; +} + QVersionNumber ParseSpecificationVersion( const std::vector &serverInformation) { if (serverInformation.size() >= 4) { @@ -605,21 +647,28 @@ bool Supported() { return ServiceRegistered; } +bool Enforced() { + // Wayland doesn't support positioning + // and custom notifications don't work here + return IsQualifiedDaemon() || IsWayland(); +} + std::unique_ptr Create( Window::Notifications::System *system) { ServiceRegistered = GetServiceRegistered(); - InhibitedNotSupported = false; if (Supported()) { CurrentServerInformation = GetServerInformation(); CurrentCapabilities = GetCapabilities(); + InhibitionSupported = GetInhibitionSupported(); } else { CurrentServerInformation = {}; CurrentCapabilities = QStringList{}; + InhibitionSupported = false; } if ((Core::App().settings().nativeNotifications() && Supported()) - || IsWayland()) { + || Enforced()) { return std::make_unique(system); } diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp index 44ef96fa1..cc39a3a7b 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -27,9 +27,15 @@ bool Supported() { return false; } +bool Enforced() { + // Wayland doesn't support positioning + // and custom notifications don't work here + return IsWayland(); +} + std::unique_ptr Create( Window::Notifications::System *system) { - if (IsWayland()) { + if (Enforced()) { return std::make_unique(system); } diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 827954675..88d086066 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -162,6 +162,10 @@ bool Supported() { return Platform::IsMac10_8OrGreater(); } +bool Enforced() { + return Supported(); +} + std::unique_ptr Create(Window::Notifications::System *system) { if (Supported()) { return std::make_unique(system); diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 4afeadf97..cf8201ec7 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -17,6 +17,7 @@ namespace Notifications { [[nodiscard]] bool SkipFlashBounce(); [[nodiscard]] bool Supported(); +[[nodiscard]] bool Enforced(); [[nodiscard]] std::unique_ptr Create( Window::Notifications::System *system); diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index 5fd6c591f..ff631e954 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -325,6 +325,10 @@ bool Supported() { return false; } +bool Enforced() { + return false; +} + std::unique_ptr Create(Window::Notifications::System *system) { #ifndef __MINGW32__ if (Core::App().settings().nativeNotifications() && Supported()) { diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 19ce0db2b..95bb96958 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -676,14 +676,13 @@ void SetupNotificationsContent( }, joined->lifetime()); const auto nativeText = [&] { - if (!Platform::Notifications::Supported()) { + if (!Platform::Notifications::Supported() + || Platform::Notifications::Enforced()) { return QString(); } else if (Platform::IsWindows()) { return tr::lng_settings_use_windows(tr::now); - } else if (Platform::IsLinux() && !Platform::IsWayland()) { - return tr::lng_settings_use_native_notifications(tr::now); } - return QString(); + return tr::lng_settings_use_native_notifications(tr::now); }(); const auto native = [&]() -> Ui::Checkbox* { if (nativeText.isEmpty()) { @@ -697,8 +696,7 @@ void SetupNotificationsContent( return addCheckbox(nativeText, settings.nativeNotifications()); }(); - const auto advancedSlide = !Platform::IsMac10_8OrGreater() - && !Platform::IsWayland() + const auto advancedSlide = !Platform::Notifications::Enforced() ? container->add( object_ptr>( container, From d042963a47dd4e9263cc81e6ba478b21332d78e7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 20 Jan 2021 02:31:27 +0400 Subject: [PATCH 084/396] Make notification show method async --- .../linux/notifications_manager_linux.cpp | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 4dc5cec13..4f90c0c0c 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -250,7 +250,7 @@ public: ~NotificationData(); - bool show(); + void show(); void close(); void setImage(const QString &imagePath); @@ -275,6 +275,11 @@ private: void actionInvoked(uint id, const QString &actionName); void notificationReplied(uint id, const QString &text); + static void notificationShown( + GObject *source_object, + GAsyncResult *res, + gpointer user_data); + static void signalEmitted( GDBusConnection *connection, const gchar *sender_name, @@ -443,7 +448,7 @@ NotificationData::~NotificationData() { } } -bool NotificationData::show() { +void NotificationData::show() { GVariantBuilder actionsBuilder, hintsBuilder; GError *error = nullptr; @@ -470,7 +475,7 @@ bool NotificationData::show() { ? GetIconName() : QString(); - auto reply = g_dbus_connection_call_sync( + g_dbus_connection_call( _dbusConnection, kService.utf8(), kObjectPath.utf8(), @@ -490,19 +495,40 @@ bool NotificationData::show() { G_DBUS_CALL_FLAGS_NONE, kDBusTimeout, nullptr, + notificationShown, + this); +} + +void NotificationData::notificationShown( + GObject *source_object, + GAsyncResult *res, + gpointer user_data) { + const auto notificationData = reinterpret_cast( + user_data); + + if (!notificationData) { + return; + } + + GError *error = nullptr; + + auto reply = g_dbus_connection_call_finish( + notificationData->_dbusConnection, + res, &error); - const auto replyValid = !error; - - if (replyValid) { - g_variant_get(reply, "(u)", &_notificationId); + if (!error) { + g_variant_get(reply, "(u)", ¬ificationData->_notificationId); g_variant_unref(reply); } else { + const auto manager = notificationData->_manager; + const auto my = notificationData->_id; + crl::on_main(manager, [=] { + manager->clearNotification(my); + }); LOG(("Native notification error: %1").arg(error->message)); g_error_free(error); } - - return replyValid; } void NotificationData::close() { @@ -782,15 +808,7 @@ void Manager::Private::showNotification( base::flat_map()).first; } i->second.emplace(msgId, notification); - if (!notification->show()) { - i = _notifications.find(key); - if (i != _notifications.cend()) { - i->second.remove(msgId); - if (i->second.empty()) { - _notifications.erase(i); - } - } - } + notification->show(); } void Manager::Private::clearAll() { From a0a71687e70bbcdc21b6ffdf0396c9a64409d679 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 16 Jan 2021 08:47:07 +0400 Subject: [PATCH 085/396] Use QDBusPendingReply in GetServerInformation --- .../linux/notifications_manager_linux.cpp | 100 +++++++++--------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 4f90c0c0c..1637aff50 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include #include #include @@ -42,9 +43,16 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; constexpr auto kImageDataType = "(iiibii@ay)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; +struct ServerInformation { + QString name; + QString vendor; + QVersionNumber version; + QVersionNumber specVersion; +}; + bool ServiceRegistered = false; bool InhibitionSupported = false; -std::vector CurrentServerInformation; +std::optional CurrentServerInformation; QStringList CurrentCapabilities; bool GetServiceRegistered() { @@ -56,8 +64,12 @@ bool GetServiceRegistered() { : activatable; } -std::vector GetServerInformation() { - std::vector result; +std::optional GetServerInformation() { + using ServerInformationReply = QDBusPendingReply< + QString, + QString, + QString, + QString>; const auto message = QDBusMessage::createMethodCall( kService.utf16(), @@ -67,29 +79,28 @@ std::vector GetServerInformation() { // We may be launched earlier than notification daemon while (true) { - const auto reply = QDBusConnection::sessionBus().call(message); + ServerInformationReply reply = QDBusConnection::sessionBus() + .asyncCall(message); - if (reply.type() == QDBusMessage::ReplyMessage) { - ranges::transform( - reply.arguments(), - ranges::back_inserter(result), - &QVariant::toString); - } else if (reply.type() == QDBusMessage::ErrorMessage) { - LOG(("Native notification error: %1").arg(reply.errorMessage())); + reply.waitForFinished(); - if (reply.errorName() - == qsl("org.freedesktop.DBus.Error.NoReply")) { - continue; - } - } else { - LOG(("Native notification error: " - "invalid reply from GetServerInformation")); + if (reply.isValid()) { + return { + reply.argumentAt<0>(), + reply.argumentAt<1>(), + QVersionNumber::fromString(reply.argumentAt<2>()), + QVersionNumber::fromString(reply.argumentAt<3>()), + }; } - break; + LOG(("Native notification error: %1").arg(reply.error().message())); + + if (reply.error().type() != QDBusError::NoReply) { + break; + } } - return result; + return std::nullopt; } QStringList GetCapabilities() { @@ -201,34 +212,25 @@ bool IsQualifiedDaemon() { }) && InhibitionSupported; } -QVersionNumber ParseSpecificationVersion( - const std::vector &serverInformation) { - if (serverInformation.size() >= 4) { - return QVersionNumber::fromString(serverInformation[3]); - } else { - LOG(("Native notification error: " - "server information should have 4 elements")); - } - - return QVersionNumber(); +ServerInformation CurrentServerInformationValue() { + return CurrentServerInformation.value_or(ServerInformation{}); } QString GetImageKey(const QVersionNumber &specificationVersion) { - if (!specificationVersion.isNull()) { - if (specificationVersion >= QVersionNumber(1, 2)) { - return qsl("image-data"); - } else if (specificationVersion == QVersionNumber(1, 1)) { - return qsl("image_data"); - } else if (specificationVersion < QVersionNumber(1, 1)) { - return qsl("icon_data"); - } else { - LOG(("Native notification error: unknown specification version")); - } - } else { + const auto normalizedVersion = specificationVersion.normalized(); + + if (normalizedVersion.isNull()) { LOG(("Native notification error: specification version is null")); + return QString(); } - return QString(); + if (normalizedVersion >= QVersionNumber(1, 2)) { + return qsl("image-data"); + } else if (normalizedVersion == QVersionNumber(1, 1)) { + return qsl("image_data"); + } + + return qsl("icon_data"); } class NotificationData { @@ -302,7 +304,7 @@ NotificationData::NotificationData( bool hideReplyButton) : _manager(manager) , _title(title) -, _imageKey(GetImageKey(ParseSpecificationVersion(CurrentServerInformation))) +, _imageKey(GetImageKey(CurrentServerInformationValue().specVersion)) , _id(id) { GError *error = nullptr; @@ -688,7 +690,7 @@ std::unique_ptr Create( CurrentCapabilities = GetCapabilities(); InhibitionSupported = GetInhibitionSupported(); } else { - CurrentServerInformation = {}; + CurrentServerInformation = std::nullopt; CurrentCapabilities = QStringList{}; InhibitionSupported = false; } @@ -741,18 +743,18 @@ Manager::Private::Private(not_null manager, Type type) const auto serverInformation = CurrentServerInformation; const auto capabilities = CurrentCapabilities; - if (!serverInformation.empty()) { + if (serverInformation.has_value()) { LOG(("Notification daemon product name: %1") - .arg(serverInformation[0])); + .arg(serverInformation->name)); LOG(("Notification daemon vendor name: %1") - .arg(serverInformation[1])); + .arg(serverInformation->vendor)); LOG(("Notification daemon version: %1") - .arg(serverInformation[2])); + .arg(serverInformation->version.toString())); LOG(("Notification daemon specification version: %1") - .arg(serverInformation[3])); + .arg(serverInformation->specVersion.toString())); } if (!capabilities.isEmpty()) { From e8edbb16ae383d03de69378bc8f1a8cbe27357f3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 21 Jan 2021 14:18:40 +0400 Subject: [PATCH 086/396] Make notification manager creation async --- .../linux_notification_service_watcher.cpp | 9 +- .../linux/notifications_manager_linux.cpp | 153 +++++++++++------- .../notifications_manager_linux_dummy.cpp | 10 +- .../platform/linux/specific_linux.cpp | 9 +- .../platform/mac/notifications_manager_mac.mm | 7 +- .../platform/platform_notifications_manager.h | 3 +- .../win/notifications_manager_win.cpp | 7 +- .../window/notifications_manager.cpp | 13 +- .../window/notifications_manager.h | 19 +++ .../window/notifications_manager_default.h | 4 + 10 files changed, 152 insertions(+), 82 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp index 1aa0fb969..3198eed94 100644 --- a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "main/main_domain.h" #include "window/notifications_manager.h" +#include "platform/linux/specific_linux.h" #include @@ -22,10 +23,16 @@ NotificationServiceWatcher::NotificationServiceWatcher() QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange) { const auto signal = &QDBusServiceWatcher::serviceOwnerChanged; - QObject::connect(&_dbusWatcher, signal, [=] { + QObject::connect(&_dbusWatcher, signal, [=]( + const QString &service, + const QString &oldOwner, + const QString &newOwner) { crl::on_main([=] { if (!Core::App().domain().started()) { return; + } else if (IsNotificationServiceActivatable() + && newOwner.isEmpty()) { + return; } Core::App().notifications().createManager(); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 1637aff50..57775dba2 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include +#include #include #include #include @@ -64,7 +66,7 @@ bool GetServiceRegistered() { : activatable; } -std::optional GetServerInformation() { +void GetServerInformation(Fn)> callback) { using ServerInformationReply = QDBusPendingReply< QString, QString, @@ -77,59 +79,63 @@ std::optional GetServerInformation() { kInterface.utf16(), qsl("GetServerInformation")); - // We may be launched earlier than notification daemon - while (true) { - ServerInformationReply reply = QDBusConnection::sessionBus() - .asyncCall(message); + const auto async = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(async); - reply.waitForFinished(); + const auto finished = [=](QDBusPendingCallWatcher *call) { + const ServerInformationReply reply = *call; if (reply.isValid()) { - return { - reply.argumentAt<0>(), - reply.argumentAt<1>(), - QVersionNumber::fromString(reply.argumentAt<2>()), - QVersionNumber::fromString(reply.argumentAt<3>()), - }; + crl::on_main([=] { + callback(ServerInformation{ + reply.argumentAt<0>(), + reply.argumentAt<1>(), + QVersionNumber::fromString(reply.argumentAt<2>()), + QVersionNumber::fromString(reply.argumentAt<3>()), + }); + }); + } else { + LOG(("Native notification error: %1").arg( + reply.error().message())); + + crl::on_main([=] { callback(std::nullopt); }); } - LOG(("Native notification error: %1").arg(reply.error().message())); + call->deleteLater(); + }; - if (reply.error().type() != QDBusError::NoReply) { - break; - } - } - - return std::nullopt; + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished); } -QStringList GetCapabilities() { +void GetCapabilities(Fn callback) { const auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), kInterface.utf16(), qsl("GetCapabilities")); - // We may be launched earlier than notification daemon - while (true) { - const QDBusReply reply = QDBusConnection::sessionBus() - .call(message); + const auto async = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(async); + + const auto finished = [=](QDBusPendingCallWatcher *call) { + const QDBusPendingReply reply = *call; if (reply.isValid()) { - return reply.value(); + crl::on_main([=] { callback(reply.value()); }); + } else { + LOG(("Native notification error: %1").arg( + reply.error().message())); + + crl::on_main([=] { callback({}); }); } - LOG(("Native notification error: %1").arg(reply.error().message())); + call->deleteLater(); + }; - if (reply.error().type() != QDBusError::NoReply) { - break; - } - } - - return {}; + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished); } -bool GetInhibitionSupported() { +void GetInhibitionSupported(Fn callback) { auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), @@ -141,24 +147,21 @@ bool GetInhibitionSupported() { qsl("Inhibited") }); - // We may be launched earlier than notification daemon - while (true) { - const QDBusError error = QDBusConnection::sessionBus().call(message); + const auto async = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(async); - if (!error.isValid()) { - return true; - } else if (error.type() == QDBusError::InvalidArgs) { - break; + const auto finished = [=](QDBusPendingCallWatcher *call) { + const auto error = QDBusPendingReply(*call).error(); + + if (error.isValid() && error.type() != QDBusError::InvalidArgs) { + LOG(("Native notification error: %1").arg(error.message())); } - LOG(("Native notification error: %1").arg(error.message())); + crl::on_main([=] { callback(!error.isValid()); }); + call->deleteLater(); + }; - if (error.type() != QDBusError::NoReply) { - break; - } - } - - return false; + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished); } bool Inhibited() { @@ -681,26 +684,56 @@ bool Enforced() { return IsQualifiedDaemon() || IsWayland(); } -std::unique_ptr Create( - Window::Notifications::System *system) { +void Create(Window::Notifications::System *system) { ServiceRegistered = GetServiceRegistered(); - if (Supported()) { - CurrentServerInformation = GetServerInformation(); - CurrentCapabilities = GetCapabilities(); - InhibitionSupported = GetInhibitionSupported(); + const auto managerSetter = [=] { + using ManagerType = Window::Notifications::ManagerType; + if ((Core::App().settings().nativeNotifications() && Supported()) + || Enforced()) { + if (*system->managerType() != ManagerType::Native) { + system->setManager(std::make_unique(system)); + } + } else { + if (*system->managerType() != ManagerType::Default) { + system->setManager(nullptr); + } + } + }; + + if (!system->managerType().has_value()) { + using DummyManager = Window::Notifications::DummyManager; + system->setManager(std::make_unique(system)); + } + + if (ServiceRegistered) { + const auto counter = std::make_shared(3); + const auto oneReady = [=] { + if (!--*counter) { + managerSetter(); + } + }; + + GetServerInformation([=](std::optional result) { + CurrentServerInformation = result; + oneReady(); + }); + + GetCapabilities([=](QStringList result) { + CurrentCapabilities = result; + oneReady(); + }); + + GetInhibitionSupported([=](bool result) { + InhibitionSupported = result; + oneReady(); + }); } else { CurrentServerInformation = std::nullopt; CurrentCapabilities = QStringList{}; InhibitionSupported = false; + managerSetter(); } - - if ((Core::App().settings().nativeNotifications() && Supported()) - || Enforced()) { - return std::make_unique(system); - } - - return nullptr; } class Manager::Private { diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp index cc39a3a7b..a078b852f 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -33,13 +33,13 @@ bool Enforced() { return IsWayland(); } -std::unique_ptr Create( - Window::Notifications::System *system) { +void Create(Window::Notifications::System *system) { if (Enforced()) { - return std::make_unique(system); + using DummyManager = Window::Notifications::DummyManager; + system->setManager(std::make_unique(system)); + } else { + system->setManager(nullptr); } - - return nullptr; } } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 251d5db7b..c4c68a724 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -1247,18 +1247,13 @@ void start() { } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - if (!IsNotificationServiceActivatable()) { - NSWInstance = std::make_unique< - internal::NotificationServiceWatcher>(); - } + NSWInstance = std::make_unique(); #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } void finish() { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - if (NSWInstance) { - NSWInstance = nullptr; - } + NSWInstance = nullptr; #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 88d086066..dec09219e 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -166,11 +166,12 @@ bool Enforced() { return Supported(); } -std::unique_ptr Create(Window::Notifications::System *system) { +void Create(Window::Notifications::System *system) { if (Supported()) { - return std::make_unique(system); + system->setManager(std::make_unique(system)); + } else { + system->setManager(nullptr); } - return nullptr; } class Manager::Private : public QObject, private base::Subscriber { diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index cf8201ec7..2a4a7849f 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -18,8 +18,7 @@ namespace Notifications { [[nodiscard]] bool Supported(); [[nodiscard]] bool Enforced(); -[[nodiscard]] std::unique_ptr Create( - Window::Notifications::System *system); +void Create(Window::Notifications::System *system); } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index ff631e954..fe0b73b72 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -329,16 +329,17 @@ bool Enforced() { return false; } -std::unique_ptr Create(Window::Notifications::System *system) { +void Create(Window::Notifications::System *system) { #ifndef __MINGW32__ if (Core::App().settings().nativeNotifications() && Supported()) { auto result = std::make_unique(system); if (result->init()) { - return std::move(result); + system->setManager(std::move(result)); + return; } } #endif // !__MINGW32__ - return nullptr; + system->setManager(nullptr); } #ifndef __MINGW32__ diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 02d26f9b8..6b6b68034 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -66,12 +66,23 @@ System::System() } void System::createManager() { - _manager = Platform::Notifications::Create(this); + Platform::Notifications::Create(this); +} + +void System::setManager(std::unique_ptr manager) { + _manager = std::move(manager); if (!_manager) { _manager = std::make_unique(this); } } +std::optional System::managerType() const { + if (_manager) { + return _manager->type(); + } + return std::nullopt; +} + Main::Session *System::findSession(uint64 sessionId) const { for (const auto &[index, account] : Core::App().domain().accounts()) { if (const auto session = account->maybeSession()) { diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index f3953f95e..5e21ae30d 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -34,6 +34,12 @@ class Track; namespace Window { namespace Notifications { +enum class ManagerType { + Dummy, + Default, + Native, +}; + enum class ChangeType { SoundEnabled, FlashBounceEnabled, @@ -70,6 +76,8 @@ public: [[nodiscard]] Main::Session *findSession(uint64 sessionId) const; void createManager(); + void setManager(std::unique_ptr manager); + [[nodiscard]] std::optional managerType() const; void checkDelayed(); void schedule(not_null item); @@ -189,6 +197,8 @@ public: const QString &title, not_null session); + [[nodiscard]] virtual ManagerType type() const = 0; + virtual ~Manager() = default; protected: @@ -221,6 +231,11 @@ private: }; class NativeManager : public Manager { +public: + [[nodiscard]] ManagerType type() const override { + return ManagerType::Native; + } + protected: using Manager::Manager; @@ -252,6 +267,10 @@ class DummyManager : public NativeManager { public: using NativeManager::NativeManager; + [[nodiscard]] ManagerType type() const override { + return ManagerType::Dummy; + } + protected: void doShowNativeNotification( not_null peer, diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 1950480f2..68d7c9614 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -43,6 +43,10 @@ public: Manager(System *system); ~Manager(); + [[nodiscard]] ManagerType type() const override { + return ManagerType::Default; + } + template void enumerateNotifications(Method method) { for (const auto ¬ification : _notifications) { From 894d6028bd8c0b05c7b6998b83332757f486dbd7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 14 Jan 2021 13:05:11 +0400 Subject: [PATCH 087/396] Don't skip native notification toasts --- .../platform/linux/notifications_manager_linux.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 57775dba2..4a572d841 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -667,6 +667,13 @@ bool SkipAudio() { } bool SkipToast() { + // Do not skip native notifications because of Do not disturb. + // They respect this setting anyway. + if ((Core::App().settings().nativeNotifications() && Supported()) + || Enforced()) { + return false; + } + return Inhibited(); } From 4348ddf93873440d73c0d372e11ed5794c774110 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 16 Jan 2021 10:23:50 +0400 Subject: [PATCH 088/396] Adjust some constexprs in linux platform code --- .../platform/linux/linux_gsd_media_keys.cpp | 5 ++--- .../platform/linux/notifications_manager_linux.cpp | 13 +++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp index 95c72047c..e6bda1eb0 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp @@ -23,7 +23,6 @@ namespace Platform { namespace internal { namespace { -constexpr auto kDBusTimeout = 30000; constexpr auto kService = "org.gnome.SettingsDaemon.MediaKeys"_cs; constexpr auto kOldService = "org.gnome.SettingsDaemon"_cs; constexpr auto kMATEService = "org.mate.SettingsDaemon"_cs; @@ -114,7 +113,7 @@ GSDMediaKeys::GSDMediaKeys() { 0), nullptr, G_DBUS_CALL_FLAGS_NONE, - kDBusTimeout, + -1, nullptr, &error); @@ -160,7 +159,7 @@ GSDMediaKeys::~GSDMediaKeys() { QCoreApplication::applicationName().toUtf8().constData()), nullptr, G_DBUS_CALL_FLAGS_NONE, - kDBusTimeout, + -1, nullptr, &error); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 4a572d841..5d47ca8c2 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -37,13 +37,10 @@ namespace Platform { namespace Notifications { namespace { -constexpr auto kDBusTimeout = 30000; constexpr auto kService = "org.freedesktop.Notifications"_cs; constexpr auto kObjectPath = "/org/freedesktop/Notifications"_cs; constexpr auto kInterface = kService; constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; -constexpr auto kImageDataType = "(iiibii@ay)"_cs; -constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; struct ServerInformation { QString name; @@ -143,7 +140,7 @@ void GetInhibitionSupported(Fn callback) { qsl("Get")); message.setArguments({ - qsl("org.freedesktop.Notifications"), + kInterface.utf16(), qsl("Inhibited") }); @@ -178,7 +175,7 @@ bool Inhibited() { qsl("Get")); message.setArguments({ - qsl("org.freedesktop.Notifications"), + kInterface.utf16(), qsl("Inhibited") }); @@ -487,7 +484,7 @@ void NotificationData::show() { kInterface.utf8(), "Notify", g_variant_new( - kNotifyArgsType.utf8(), + "(susssasa{sv}i)", AppName.utf8().constData(), 0, iconName.toUtf8().constData(), @@ -498,7 +495,7 @@ void NotificationData::show() { -1), nullptr, G_DBUS_CALL_FLAGS_NONE, - kDBusTimeout, + -1, nullptr, notificationShown, this); @@ -560,7 +557,7 @@ void NotificationData::setImage(const QString &imagePath) { _image = QImage(imagePath).convertToFormat(QImage::Format_RGBA8888); _hints.emplace(_imageKey, g_variant_new( - kImageDataType.utf8(), + "(iiibii@ay)", _image.width(), _image.height(), _image.bytesPerLine(), From bad888496c74eff1ad0a85aeda9964d5cb40d3e8 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 15 Jan 2021 11:10:10 +0400 Subject: [PATCH 089/396] Decrease some indentation in linux platform code --- .../platform/linux/main_window_linux.cpp | 11 +++++++++-- .../platform/linux/specific_linux.cpp | 19 +++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 26ed2304d..5bbd467eb 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -420,10 +420,17 @@ bool IsSNIAvailable() { if (reply.isValid()) { return reply.value().toBool(); - } else if (reply.error().type() != QDBusError::ServiceUnknown) { - LOG(("SNI Error: %1").arg(reply.error().message())); } + switch (reply.error().type()) { + case QDBusError::Disconnected: + case QDBusError::ServiceUnknown: + return false; + default: + break; + } + + LOG(("SNI Error: %1").arg(reply.error().message())); return false; } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index c4c68a724..83c8cd84f 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -103,7 +103,7 @@ QStringList ListDBusActivatableNames() { if (reply.isValid()) { return reply.value(); - } else { + } else if (reply.error().type() != QDBusError::Disconnected) { LOG(("App Error: %1: %2") .arg(reply.error().name()) .arg(reply.error().message())); @@ -155,13 +155,12 @@ void PortalAutostart(bool autostart, bool silent = false) { if (silent) { QDBusConnection::sessionBus().send(message); - } else { - const QDBusReply reply = QDBusConnection::sessionBus().call( - message); + return; + } - if (!reply.isValid()) { - LOG(("Flatpak autostart error: %1").arg(reply.error().message())); - } + const QDBusError error = QDBusConnection::sessionBus().call(message); + if (error.isValid()) { + LOG(("Flatpak autostart error: %1").arg(error.message())); } } @@ -217,11 +216,11 @@ uint FileChooserPortalVersion() { if (reply.isValid()) { return reply.value().toUInt(); - } else { - LOG(("Error getting FileChooser portal version: %1") - .arg(reply.error().message())); } + LOG(("Error getting FileChooser portal version: %1") + .arg(reply.error().message())); + return 0; }(); From 1e2759840d91f65789e21521d16b0a2acaf692d6 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 15 Jan 2021 14:46:13 +0400 Subject: [PATCH 090/396] Check _sniDBusProxy for nullptr before connecting to signals --- .../platform/linux/main_window_linux.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 5bbd467eb..6192aed09 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -528,11 +528,13 @@ void MainWindow::initHook() { nullptr, nullptr); - g_signal_connect( - _sniDBusProxy, - "g-signal", - G_CALLBACK(sniSignalEmitted), - nullptr); + if (_sniDBusProxy) { + g_signal_connect( + _sniDBusProxy, + "g-signal", + G_CALLBACK(sniSignalEmitted), + nullptr); + } auto sniWatcher = new QDBusServiceWatcher( kSNIWatcherService.utf16(), @@ -1232,7 +1234,9 @@ MainWindow::~MainWindow() { delete _mainMenuExporter; delete psMainMenu; - g_object_unref(_sniDBusProxy); + if (_sniDBusProxy) { + g_object_unref(_sniDBusProxy); + } #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } From 690c5df87cd2bc962ae1f8c50c03f1a56edaf3ca Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 22 Jan 2021 16:07:31 +0400 Subject: [PATCH 091/396] Format dbus errors logging --- .../platform/linux/main_window_linux.cpp | 5 +++- .../linux/notifications_manager_linux.cpp | 25 ++++++++++++------- .../platform/linux/specific_linux.cpp | 9 ++++--- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 6192aed09..e99aef7d0 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -430,7 +430,10 @@ bool IsSNIAvailable() { break; } - LOG(("SNI Error: %1").arg(reply.error().message())); + LOG(("SNI Error: %1: %2") + .arg(reply.error().name()) + .arg(reply.error().message())); + return false; } diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 5d47ca8c2..31e3775dc 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -92,8 +92,9 @@ void GetServerInformation(Fn)> callback) { }); }); } else { - LOG(("Native notification error: %1").arg( - reply.error().message())); + LOG(("Native Notification Error: %1: %2") + .arg(reply.error().name()) + .arg(reply.error().message())); crl::on_main([=] { callback(std::nullopt); }); } @@ -120,8 +121,9 @@ void GetCapabilities(Fn callback) { if (reply.isValid()) { crl::on_main([=] { callback(reply.value()); }); } else { - LOG(("Native notification error: %1").arg( - reply.error().message())); + LOG(("Native Notification Error: %1: %2") + .arg(reply.error().name()) + .arg(reply.error().message())); crl::on_main([=] { callback({}); }); } @@ -151,7 +153,9 @@ void GetInhibitionSupported(Fn callback) { const auto error = QDBusPendingReply(*call).error(); if (error.isValid() && error.type() != QDBusError::InvalidArgs) { - LOG(("Native notification error: %1").arg(error.message())); + LOG(("Native Notification Error: %1: %2") + .arg(error.name()) + .arg(error.message())); } crl::on_main([=] { callback(!error.isValid()); }); @@ -186,7 +190,10 @@ bool Inhibited() { return reply.value().toBool(); } - LOG(("Native notification error: %1").arg(reply.error().message())); + LOG(("Native Notification Error: %1: %2") + .arg(reply.error().name()) + .arg(reply.error().message())); + return false; } @@ -220,7 +227,7 @@ QString GetImageKey(const QVersionNumber &specificationVersion) { const auto normalizedVersion = specificationVersion.normalized(); if (normalizedVersion.isNull()) { - LOG(("Native notification error: specification version is null")); + LOG(("Native Notification Error: specification version is null")); return QString(); } @@ -314,7 +321,7 @@ NotificationData::NotificationData( &error); if (error) { - LOG(("Native notification error: %1").arg(error->message)); + LOG(("Native Notification Error: %1").arg(error->message)); g_error_free(error); return; } @@ -528,7 +535,7 @@ void NotificationData::notificationShown( crl::on_main(manager, [=] { manager->clearNotification(my); }); - LOG(("Native notification error: %1").arg(error->message)); + LOG(("Native Notification Error: %1").arg(error->message)); g_error_free(error); } } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 83c8cd84f..3ba1f7994 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -104,7 +104,7 @@ QStringList ListDBusActivatableNames() { if (reply.isValid()) { return reply.value(); } else if (reply.error().type() != QDBusError::Disconnected) { - LOG(("App Error: %1: %2") + LOG(("ListActivatableNames Error: %1: %2") .arg(reply.error().name()) .arg(reply.error().message())); } @@ -160,7 +160,9 @@ void PortalAutostart(bool autostart, bool silent = false) { const QDBusError error = QDBusConnection::sessionBus().call(message); if (error.isValid()) { - LOG(("Flatpak autostart error: %1").arg(error.message())); + LOG(("Flatpak Autostart Error: %1: %2") + .arg(error.name()) + .arg(error.message())); } } @@ -218,7 +220,8 @@ uint FileChooserPortalVersion() { return reply.value().toUInt(); } - LOG(("Error getting FileChooser portal version: %1") + LOG(("Error getting FileChooser portal version: %1: %2") + .arg(reply.error().name()) .arg(reply.error().message())); return 0; From a986d7a3d6da2dc23a58ea9c32ff6d133a363aed Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 11 Jan 2021 07:02:35 +0400 Subject: [PATCH 092/396] Fix checking cover stream on seeking --- .../SourceFiles/media/streaming/media_streaming_file.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp index 8c5079832..7e1957a98 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp @@ -148,6 +148,10 @@ Stream File::Context::initStream( const auto info = format->streams[index]; if (type == AVMEDIA_TYPE_VIDEO) { + if (info->disposition & AV_DISPOSITION_ATTACHED_PIC) { + // ignore cover streams + return Stream(); + } result.rotation = FFmpeg::ReadRotationFromMetadata(info); result.aspect = FFmpeg::ValidateAspectRatio(info->sample_aspect_ratio); } else if (type == AVMEDIA_TYPE_AUDIO) { @@ -159,10 +163,6 @@ Stream File::Context::initStream( result.codec = FFmpeg::MakeCodecPointer(info); if (!result.codec) { - if (info->codecpar->codec_id == AV_CODEC_ID_MJPEG) { - // mp3 files contain such "video stream", just ignore it. - return Stream(); - } return result; } From 2fe2105a5f6ab44e5175fc771426177f91680133 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 15 Jan 2021 14:02:48 +0400 Subject: [PATCH 093/396] Don't add counter when icon theme has 'panel' icon These icons should have a dot indicating unread messages, counter is redudant for them --- .../platform/linux/main_window_linux.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index e99aef7d0..e25200c24 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -204,6 +204,13 @@ QIcon TrayIconGen(int counter, bool muted) { } const auto iconName = GetTrayIconName(counter, muted); + const auto panelIconName = GetPanelIconName(counter, muted); + + if (iconName == panelIconName) { + const auto result = QIcon::fromTheme(iconName); + UpdateIconRegenerationNeeded(result, counter, muted, iconThemeName); + return result; + } QIcon result; QIcon systemIcon; @@ -624,7 +631,17 @@ void MainWindow::psTrayMenuUpdated() { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION void MainWindow::setSNITrayIcon(int counter, bool muted) { - if (IsIndicatorApplication()) { + const auto iconName = GetTrayIconName(counter, muted); + const auto panelIconName = GetPanelIconName(counter, muted); + + if (iconName == panelIconName) { + if (_sniTrayIcon->iconName() == iconName) { + return; + } + + _sniTrayIcon->setIconByName(iconName); + _sniTrayIcon->setToolTipIconByName(iconName); + } else if (IsIndicatorApplication()) { if (!IsIconRegenerationNeeded(counter, muted) && _trayIconFile && _sniTrayIcon->iconName() == _trayIconFile->fileName()) { From cbdd86d39890dd703cbe87f6326b150cdf43b9f9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 15 Jan 2021 04:00:27 +0400 Subject: [PATCH 094/396] Fix deadlock on OpenAL errors --- Telegram/SourceFiles/media/audio/media_audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp index 931a838ec..8220844e7 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio.cpp @@ -587,7 +587,7 @@ Mixer::Mixer(not_null instance) }, _lifetime); connect(this, SIGNAL(loaderOnStart(const AudioMsgId&, qint64)), _loader, SLOT(onStart(const AudioMsgId&, qint64))); - connect(this, SIGNAL(loaderOnCancel(const AudioMsgId&)), _loader, SLOT(onCancel(const AudioMsgId&))); + connect(this, SIGNAL(loaderOnCancel(const AudioMsgId&)), _loader, SLOT(onCancel(const AudioMsgId&)), Qt::QueuedConnection); connect(_loader, SIGNAL(needToCheck()), _fader, SLOT(onTimer())); connect(_loader, SIGNAL(error(const AudioMsgId&)), this, SLOT(onError(const AudioMsgId&))); connect(_fader, SIGNAL(needToPreload(const AudioMsgId&)), _loader, SLOT(onLoad(const AudioMsgId&))); From 57f9ae4b2a850cb41d081aa7429a0258539bbfa9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 15 Jan 2021 04:00:57 +0400 Subject: [PATCH 095/396] Fix speed control support check --- Telegram/SourceFiles/media/audio/media_audio.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp index 8220844e7..7bb79a333 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio.cpp @@ -276,7 +276,16 @@ void StopDetachIfNotUsedSafe() { } bool SupportsSpeedControl() { - return OpenAL::HasEFXExtension(); + return OpenAL::HasEFXExtension() + && (alGetEnumValue("AL_AUXILIARY_SEND_FILTER") != 0) + && (alGetEnumValue("AL_DIRECT_FILTER") != 0) + && (alGetEnumValue("AL_EFFECT_TYPE") != 0) + && (alGetEnumValue("AL_EFFECT_PITCH_SHIFTER") != 0) + && (alGetEnumValue("AL_FILTER_TYPE") != 0) + && (alGetEnumValue("AL_FILTER_LOWPASS") != 0) + && (alGetEnumValue("AL_LOWPASS_GAIN") != 0) + && (alGetEnumValue("AL_PITCH_SHIFTER_COARSE_TUNE") != 0) + && (alGetEnumValue("AL_EFFECTSLOT_EFFECT") != 0); } } // namespace Audio From 7656a546b00a55301bb569784b1000800d726be9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 00:41:15 +0400 Subject: [PATCH 096/396] Update libxkbcommon to latest version --- Telegram/build/docker/centos_env/Dockerfile | 30 ++++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index ef88c6941..d60b31798 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -1,7 +1,7 @@ FROM centos:7 AS builder ENV GIT https://github.com -ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig:/usr/local/share/pkgconfig +ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig ENV QT 5_15_2 ENV QT_TAG v5.15.2 ENV QT_PREFIX /usr/local/desktop-app/Qt-5.15.2 @@ -14,8 +14,8 @@ RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n RUN yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpm RUN yum -y install centos-release-scl -RUN yum -y install git cmake3 zlib-devel gtk2-devel gtk3-devel \ - libdrm-devel autoconf automake libtool fontconfig-devel \ +RUN yum -y install git cmake3 meson ninja-build autoconf automake libtool \ + zlib-devel gtk2-devel gtk3-devel libdrm-devel fontconfig-devel \ freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \ pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel \ pkgconfig bison yasm file which xorg-x11-util-macros \ @@ -422,17 +422,22 @@ WORKDIR .. RUN rm -rf $opensslDir FROM builder AS xkbcommon -RUN git clone -b xkbcommon-0.8.4 --depth=1 $GIT/xkbcommon/libxkbcommon.git +COPY --from=xcb ${LibrariesPath}/xcb-cache / + +RUN git clone -b xkbcommon-1.0.3 --depth=1 $GIT/xkbcommon/libxkbcommon.git WORKDIR libxkbcommon -RUN ./autogen.sh \ - --disable-docs \ - --disable-wayland \ - --with-xkb-config-root=/usr/share/X11/xkb \ - --with-x-locale-root=/usr/share/X11/locale +RUN meson build \ + --default-library=both \ + -Denable-docs=false \ + -Denable-wayland=false \ + -Denable-xkbregistry=false \ + -Dxkb-config-root=/usr/share/X11/xkb \ + -Dxkb-config-extra-path=/etc/xkb \ + -Dx-locale-root=/usr/share/X11/locale -RUN make -j$(nproc) -RUN make DESTDIR="$LibrariesPath/xkbcommon-cache" install +RUN meson compile -C build -j$(nproc) +RUN DESTDIR="$LibrariesPath/xkbcommon-cache" meson install -C build WORKDIR .. RUN rm -rf libxkbcommon @@ -479,8 +484,7 @@ RUN echo './configure -prefix '$'\"''$QT_PREFIX'$'\"'' \ -I '$'\"''$OPENSSL_PREFIX/include'$'\"'' \ OPENSSL_LIBS='$'\"''$OPENSSL_PREFIX/lib/libssl.a $OPENSSL_PREFIX/lib/libcrypto.a -lz -ldl -lpthread'$'\"'' \ -nomake examples \ - -nomake tests \ - -L /usr/local/lib64' >> ./run_configure.sh + -nomake tests' >> ./run_configure.sh RUN cat ./run_configure.sh RUN chmod a+x ./run_configure.sh RUN ./run_configure.sh From e799fdaa3d34a93a011e7d5a3e5bb72c72907602 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 00:41:36 +0400 Subject: [PATCH 097/396] Update wayland-protocols to latest version --- Telegram/build/docker/centos_env/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index d60b31798..6585853c4 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -220,7 +220,7 @@ RUN rm -rf wayland FROM builder AS wayland-protocols COPY --from=wayland ${LibrariesPath}/wayland-cache / -RUN git clone -b 1.18 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols.git +RUN git clone -b 1.20 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols.git WORKDIR wayland-protocols RUN ./autogen.sh From 73018ff95842067920b5311d1a41eb1d60b0a832 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 05:53:20 +0400 Subject: [PATCH 098/396] Update libva to latest version --- Telegram/build/docker/centos_env/Dockerfile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 6585853c4..a46c54741 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -250,10 +250,13 @@ COPY --from=libXext ${LibrariesPath}/libXext-cache / COPY --from=libXfixes ${LibrariesPath}/libXfixes-cache / COPY --from=wayland ${LibrariesPath}/wayland-cache / -RUN git clone -b 2.9.0 --depth=1 $GIT/intel/libva.git +RUN git clone -b 2.10.0 --depth=1 $GIT/intel/libva.git WORKDIR libva -RUN ./autogen.sh --enable-static +RUN ./autogen.sh \ + --enable-static \ + --with-drivers-path=/usr/lib/dri + RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/libva-cache" install From 1008774aef0bc1a98e4429ea304f40f1aae453e3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 05:56:50 +0400 Subject: [PATCH 099/396] Update vdpau to latest version --- Telegram/build/docker/centos_env/Dockerfile | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index a46c54741..b01cd9198 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -264,12 +264,19 @@ WORKDIR .. RUN rm -rf libva FROM builder AS libvdpau -RUN git clone -b libvdpau-1.2 --depth=1 https://gitlab.freedesktop.org/vdpau/libvdpau.git +RUN git clone -b 1.4 --depth=1 https://gitlab.freedesktop.org/vdpau/libvdpau.git WORKDIR libvdpau -RUN ./autogen.sh --enable-static -RUN make -j$(nproc) -RUN make DESTDIR="$LibrariesPath/libvdpau-cache" install +RUN sed -i 's/shared_library/library/g' src/meson.build + +RUN meson build \ + --default-library=both \ + --sysconfdir=/etc \ + -Ddocumentation=false \ + -Dmoduledir=/usr/lib/vdpau + +RUN meson compile -C build -j$(nproc) +RUN DESTDIR="$LibrariesPath/libvdpau-cache" meson install -C build WORKDIR .. RUN rm -rf libvdpau From b115ea74d0f5ebd1872eff35f6f770073bc48d61 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 13 Jan 2021 06:23:49 +0400 Subject: [PATCH 100/396] Set config dir for OpenSSL and disable OpenSSL DSO System-provided engines may crash bundled OpenSSL --- Telegram/build/docker/centos_env/Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index b01cd9198..91944c82e 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -424,7 +424,12 @@ RUN git clone -b OpenSSL_${OPENSSL_VER}-stable --depth=1 \ $GIT/openssl/openssl.git $opensslDir WORKDIR ${opensslDir} -RUN ./config --prefix="$OPENSSL_PREFIX" no-tests +RUN ./config \ + --prefix="$OPENSSL_PREFIX" \ + --openssldir=/etc/ssl \ + no-tests \ + no-dso + RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/openssl-cache" install_sw From bb016e14896bfe44fffafb8565e6e0f0f309a32e Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 12 Jan 2021 19:35:32 +0400 Subject: [PATCH 101/396] Restore frameless hint on showing to workaround a bug in Qt --- Telegram/SourceFiles/window/window_title_qt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 2dbc133b6..2ee27b67f 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -306,6 +306,12 @@ void TitleWidgetQt::windowStateChanged(Qt::WindowState state) { void TitleWidgetQt::visibleChanged(bool visible) { if (visible) { updateWindowExtents(); + + // workaround a bug in Qt 5.12, works ok in Qt 5.15 + // https://github.com/telegramdesktop/tdesktop/issues/10119 + if (!_windowWasFrameless) { + toggleFramelessWindow(true); + } } } From ada22ee6cc86f3158d600b83783601f117edbb9d Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 07:51:38 +0400 Subject: [PATCH 102/396] Split GTK integration into a singleton --- Telegram/CMakeLists.txt | 29 +- .../platform/linux/file_utilities_linux.cpp | 733 +----------------- .../platform/linux/file_utilities_linux.h | 118 --- .../platform/linux/linux_gdk_helper.cpp | 26 +- .../platform/linux/linux_gdk_helper.h | 4 - .../platform/linux/linux_gtk_file_dialog.cpp | 714 +++++++++++++++++ .../platform/linux/linux_gtk_file_dialog.h | 31 + .../platform/linux/linux_gtk_integration.cpp | 491 ++++++++++++ .../platform/linux/linux_gtk_integration.h | 59 ++ .../linux/linux_gtk_integration_dummy.cpp | 74 ++ .../platform/linux/linux_gtk_integration_p.h | 158 ++++ .../SourceFiles/platform/linux/linux_libs.cpp | 360 --------- .../SourceFiles/platform/linux/linux_libs.h | 303 -------- .../platform/linux/linux_open_with_dialog.cpp | 132 ++++ .../platform/linux/linux_open_with_dialog.h | 18 + .../platform/linux/linux_xlib_helper.cpp | 2 - .../platform/linux/linux_xlib_helper.h | 2 - .../platform/linux/main_window_linux.cpp | 13 +- .../platform/linux/main_window_linux.h | 2 - .../platform/linux/specific_linux.cpp | 159 ++-- .../platform/linux/specific_linux.h | 1 - 21 files changed, 1800 insertions(+), 1629 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_integration.h create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h delete mode 100644 Telegram/SourceFiles/platform/linux/linux_libs.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/linux_libs.h create mode 100644 Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index f5f02c7b9..81df3d5aa 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -821,10 +821,15 @@ PRIVATE platform/linux/linux_gdk_helper.h platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.h - platform/linux/linux_libs.cpp - platform/linux/linux_libs.h + platform/linux/linux_gtk_file_dialog.cpp + platform/linux/linux_gtk_file_dialog.h + platform/linux/linux_gtk_integration_p.h + platform/linux/linux_gtk_integration.cpp + platform/linux/linux_gtk_integration.h platform/linux/linux_notification_service_watcher.cpp platform/linux/linux_notification_service_watcher.h + platform/linux/linux_open_with_dialog.cpp + platform/linux/linux_open_with_dialog.h platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h platform/linux/linux_xlib_helper.cpp @@ -1134,6 +1139,26 @@ if (LINUX AND DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp) endif() +if (LINUX AND TDESKTOP_DISABLE_GTK_INTEGRATION) + remove_target_sources(Telegram ${src_loc} + platform/linux/linux_gdk_helper.cpp + platform/linux/linux_gdk_helper.h + platform/linux/linux_gtk_file_dialog.cpp + platform/linux/linux_gtk_file_dialog.h + platform/linux/linux_gtk_integration_p.h + platform/linux/linux_gtk_integration.cpp + platform/linux/linux_open_with_dialog.cpp + platform/linux/linux_open_with_dialog.h + platform/linux/linux_xlib_helper.cpp + platform/linux/linux_xlib_helper.h + ) + + nice_target_sources(Telegram ${src_loc} + PRIVATE + platform/linux/linux_gtk_integration_dummy.cpp + ) +endif() + if (NOT DESKTOP_APP_USE_PACKAGED) nice_target_sources(Telegram ${src_loc} PRIVATE platform/mac/mac_iconv_helper.c) endif() diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 1c09e2edd..fd1bae21d 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -7,14 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/file_utilities_linux.h" -#include "platform/linux/linux_libs.h" -#include "platform/linux/linux_gdk_helper.h" -#include "platform/linux/linux_desktop_environment.h" +#include "platform/linux/linux_gtk_integration.h" #include "platform/linux/specific_linux.h" -#include "storage/localstorage.h" -#include "base/qt_adapters.h" -#include "window/window_controller.h" -#include "core/application.h" #include @@ -24,121 +18,10 @@ extern "C" { #define signals public } // extern "C" -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -#include - -extern "C" { -#undef signals -#include -#include -#define signals public -} // extern "C" -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION +using Platform::internal::GtkIntegration; namespace Platform { namespace File { -namespace { - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -bool ShowOpenWithSupported() { - return Platform::internal::GdkHelperLoaded() - && (Libs::gtk_app_chooser_dialog_new != nullptr) - && (Libs::gtk_app_chooser_get_app_info != nullptr) - && (Libs::gtk_app_chooser_get_type != nullptr) - && (Libs::gtk_widget_get_window != nullptr) - && (Libs::gtk_widget_realize != nullptr) - && (Libs::gtk_widget_show != nullptr) - && (Libs::gtk_widget_destroy != nullptr); -} - -class OpenWithDialog : public QWindow { -public: - OpenWithDialog(const QString &filepath); - ~OpenWithDialog(); - - bool exec(); - -private: - static void handleResponse(OpenWithDialog *dialog, int responseId); - - GFile *_gfileInstance = nullptr; - GtkWidget *_gtkWidget = nullptr; - QEventLoop _loop; - std::optional _result = std::nullopt; -}; - -OpenWithDialog::OpenWithDialog(const QString &filepath) -: _gfileInstance(g_file_new_for_path(filepath.toUtf8())) -, _gtkWidget(Libs::gtk_app_chooser_dialog_new( - nullptr, - GTK_DIALOG_MODAL, - _gfileInstance)) { - g_signal_connect_swapped( - _gtkWidget, - "response", - G_CALLBACK(handleResponse), - this); -} - -OpenWithDialog::~OpenWithDialog() { - Libs::gtk_widget_destroy(_gtkWidget); - g_object_unref(_gfileInstance); -} - -bool OpenWithDialog::exec() { - Libs::gtk_widget_realize(_gtkWidget); - - if (const auto activeWindow = Core::App().activeWindow()) { - Platform::internal::XSetTransientForHint( - Libs::gtk_widget_get_window(_gtkWidget), - activeWindow->widget().get()->windowHandle()->winId()); - } - - QGuiApplicationPrivate::showModalWindow(this); - Libs::gtk_widget_show(_gtkWidget); - - if (!_result.has_value()) { - _loop.exec(); - } - - QGuiApplicationPrivate::hideModalWindow(this); - return *_result; -} - -void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) { - GAppInfo *chosenAppInfo = nullptr; - dialog->_result = true; - - switch (responseId) { - case GTK_RESPONSE_OK: - chosenAppInfo = Libs::gtk_app_chooser_get_app_info( - Libs::gtk_app_chooser_cast(dialog->_gtkWidget)); - - if (chosenAppInfo) { - GList *uris = nullptr; - uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance)); - g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr); - g_list_free(uris); - g_object_unref(chosenAppInfo); - } - - break; - - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_DELETE_EVENT: - break; - - default: - dialog->_result = false; - break; - } - - dialog->_loop.quit(); -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -} // namespace - namespace internal { QByteArray EscapeShell(const QByteArray &content) { @@ -183,18 +66,16 @@ void UnsafeOpenEmailLink(const QString &email) { } bool UnsafeShowOpenWith(const QString &filepath) { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (InFlatpak() - || InSnap() - || !ShowOpenWithSupported()) { + if (InFlatpak() || InSnap()) { return false; } - const auto absolutePath = QFileInfo(filepath).absoluteFilePath(); - return OpenWithDialog(absolutePath).exec(); -#else // !TDESKTOP_DISABLE_GTK_INTEGRATION + if (const auto integration = GtkIntegration::Instance()) { + const auto absolutePath = QFileInfo(filepath).absoluteFilePath(); + return integration->showOpenWithDialog(absolutePath); + } + return false; -#endif // TDESKTOP_DISABLE_GTK_INTEGRATION } void UnsafeLaunch(const QString &filepath) { @@ -213,136 +94,6 @@ void UnsafeLaunch(const QString &filepath) { } // namespace File namespace FileDialog { -namespace { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - -// GTK file chooser image preview: thanks to Chromium - -// The size of the preview we display for selected image files. We set height -// larger than width because generally there is more free space vertically -// than horiztonally (setting the preview image will alway expand the width of -// the dialog, but usually not the height). The image's aspect ratio will always -// be preserved. -constexpr auto kPreviewWidth = 256; -constexpr auto kPreviewHeight = 512; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -using Type = ::FileDialog::internal::Type; - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -bool UseNative(Type type = Type::ReadFile) { - // use gtk file dialog on gtk-based desktop environments - // or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3) - // or if portals are used and operation is to open folder - // and portal doesn't support folder choosing - const auto sandboxedOrCustomPortal = InFlatpak() - || InSnap() - || UseXDGDesktopPortal(); - - const auto neededForPortal = (type == Type::ReadFolder) - && !CanOpenDirectoryWithPortal(); - - const auto neededNonForced = DesktopEnvironment::IsGtkBased() - || (sandboxedOrCustomPortal && neededForPortal); - - const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal; - - return IsGtkIntegrationForced() - || (neededNonForced && !excludeNonForced); -} - -bool NativeSupported() { - return Platform::internal::GdkHelperLoaded() - && (Libs::gtk_widget_hide_on_delete != nullptr) - && (Libs::gtk_clipboard_store != nullptr) - && (Libs::gtk_clipboard_get != nullptr) - && (Libs::gtk_widget_destroy != nullptr) - && (Libs::gtk_dialog_get_type != nullptr) - && (Libs::gtk_dialog_run != nullptr) - && (Libs::gtk_widget_realize != nullptr) - && (Libs::gdk_window_set_modal_hint != nullptr) - && (Libs::gtk_widget_show != nullptr) - && (Libs::gdk_window_focus != nullptr) - && (Libs::gtk_widget_hide != nullptr) - && (Libs::gtk_widget_hide_on_delete != nullptr) - && (Libs::gtk_file_chooser_dialog_new != nullptr) - && (Libs::gtk_file_chooser_get_type != nullptr) - && (Libs::gtk_file_chooser_set_current_folder != nullptr) - && (Libs::gtk_file_chooser_get_current_folder != nullptr) - && (Libs::gtk_file_chooser_set_current_name != nullptr) - && (Libs::gtk_file_chooser_select_filename != nullptr) - && (Libs::gtk_file_chooser_get_filenames != nullptr) - && (Libs::gtk_file_chooser_set_filter != nullptr) - && (Libs::gtk_file_chooser_get_filter != nullptr) - && (Libs::gtk_window_get_type != nullptr) - && (Libs::gtk_window_set_title != nullptr) - && (Libs::gtk_file_chooser_set_local_only != nullptr) - && (Libs::gtk_file_chooser_set_action != nullptr) - && (Libs::gtk_file_chooser_set_select_multiple != nullptr) - && (Libs::gtk_file_chooser_set_do_overwrite_confirmation != nullptr) - && (Libs::gtk_file_chooser_remove_filter != nullptr) - && (Libs::gtk_file_filter_set_name != nullptr) - && (Libs::gtk_file_filter_add_pattern != nullptr) - && (Libs::gtk_file_chooser_add_filter != nullptr) - && (Libs::gtk_file_filter_new != nullptr); -} - -bool PreviewSupported() { - return NativeSupported() - && (Libs::gdk_pixbuf_new_from_file_at_size != nullptr); -} - -bool GetNative( - QPointer parent, - QStringList &files, - QByteArray &remoteContent, - const QString &caption, - const QString &filter, - Type type, - QString startFile) { - internal::GtkFileDialog dialog(parent, caption, QString(), filter); - - dialog.setModal(true); - if (type == Type::ReadFile || type == Type::ReadFiles) { - dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile); - dialog.setAcceptMode(QFileDialog::AcceptOpen); - } else if (type == Type::ReadFolder) { - dialog.setAcceptMode(QFileDialog::AcceptOpen); - dialog.setFileMode(QFileDialog::Directory); - dialog.setOption(QFileDialog::ShowDirsOnly); - } else { - dialog.setFileMode(QFileDialog::AnyFile); - dialog.setAcceptMode(QFileDialog::AcceptSave); - } - if (startFile.isEmpty() || startFile.at(0) != '/') { - startFile = cDialogLastPath() + '/' + startFile; - } - dialog.selectFile(startFile); - - int res = dialog.exec(); - - QString path = dialog.directory().absolutePath(); - if (path != cDialogLastPath()) { - cSetDialogLastPath(path); - Local::writeSettings(); - } - - if (res == QDialog::Accepted) { - if (type == Type::ReadFiles) { - files = dialog.selectedFiles(); - } else { - files = dialog.selectedFiles().mid(0, 1); - } - return true; - } - - files = QStringList(); - remoteContent = QByteArray(); - return false; -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -} // namespace bool Get( QPointer parent, @@ -350,23 +101,24 @@ bool Get( QByteArray &remoteContent, const QString &caption, const QString &filter, - Type type, + ::FileDialog::internal::Type type, QString startFile) { if (parent) { parent = parent->window(); } -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (UseNative(type) && NativeSupported()) { - return GetNative( - parent, - files, - remoteContent, - caption, - filter, - type, - startFile); + if (const auto integration = GtkIntegration::Instance()) { + if (integration->fileDialogSupported() + && integration->useFileDialog(type)) { + return integration->getFileDialog( + parent, + files, + remoteContent, + caption, + filter, + type, + startFile); + } } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION return ::FileDialog::internal::GetDefault( parent, files, @@ -377,448 +129,5 @@ bool Get( startFile); } -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -namespace internal { - -QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) { - g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); - g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(Libs::gtk_widget_hide_on_delete), nullptr); - if (PreviewSupported()) { - _preview = Libs::gtk_image_new(); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "update-preview", G_CALLBACK(onUpdatePreview), this); - Libs::gtk_file_chooser_set_preview_widget(Libs::gtk_file_chooser_cast(gtkWidget), _preview); - } -} - -QGtkDialog::~QGtkDialog() { - Libs::gtk_clipboard_store(Libs::gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); - Libs::gtk_widget_destroy(gtkWidget); -} - -GtkDialog *QGtkDialog::gtkDialog() const { - return Libs::gtk_dialog_cast(gtkWidget); -} - -void QGtkDialog::exec() { - if (modality() == Qt::ApplicationModal) { - // block input to the whole app, including other GTK dialogs - Libs::gtk_dialog_run(gtkDialog()); - } else { - // block input to the window, allow input to other GTK dialogs - QEventLoop loop; - connect(this, SIGNAL(accept()), &loop, SLOT(quit())); - connect(this, SIGNAL(reject()), &loop, SLOT(quit())); - loop.exec(); - } -} - -void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { - connect(parent, &QWindow::destroyed, this, &QGtkDialog::onParentWindowDestroyed, - Qt::UniqueConnection); - setParent(parent); - setFlags(flags); - setModality(modality); - - Libs::gtk_widget_realize(gtkWidget); // creates X window - - if (parent) { - Platform::internal::XSetTransientForHint(Libs::gtk_widget_get_window(gtkWidget), parent->winId()); - } - - if (modality != Qt::NonModal) { - Libs::gdk_window_set_modal_hint(Libs::gtk_widget_get_window(gtkWidget), true); - QGuiApplicationPrivate::showModalWindow(this); - } - - Libs::gtk_widget_show(gtkWidget); - Libs::gdk_window_focus(Libs::gtk_widget_get_window(gtkWidget), 0); -} - -void QGtkDialog::hide() { - QGuiApplicationPrivate::hideModalWindow(this); - Libs::gtk_widget_hide(gtkWidget); -} - -void QGtkDialog::onResponse(QGtkDialog *dialog, int response) { - if (response == GTK_RESPONSE_OK) - emit dialog->accept(); - else - emit dialog->reject(); -} - -void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) { - auto filename = Libs::gtk_file_chooser_get_preview_filename(Libs::gtk_file_chooser_cast(dialog->gtkWidget)); - if (!filename) { - Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false); - return; - } - - // Don't attempt to open anything which isn't a regular file. If a named pipe, - // this may hang. See https://crbug.com/534754. - struct stat stat_buf; - if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) { - g_free(filename); - Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false); - return; - } - - // This will preserve the image's aspect ratio. - auto pixbuf = Libs::gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr); - g_free(filename); - if (pixbuf) { - Libs::gtk_image_set_from_pixbuf(Libs::gtk_image_cast(dialog->_preview), pixbuf); - g_object_unref(pixbuf); - } - Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false); -} - -void QGtkDialog::onParentWindowDestroyed() { - // The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it. - setParent(nullptr); -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -namespace { - -const char *filterRegExp = -"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; - -QStringList makeFilterList(const QString &filter) { - QString f(filter); - - if (f.isEmpty()) - return QStringList(); - - QString sep(QLatin1String(";;")); - int i = f.indexOf(sep, 0); - if (i == -1) { - if (f.indexOf(QLatin1Char('\n'), 0) != -1) { - sep = QLatin1Char('\n'); - i = f.indexOf(sep, 0); - } - } - - return f.split(sep); -} - -// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" -QStringList cleanFilterList(const QString &filter) { - QRegExp regexp(QString::fromLatin1(filterRegExp)); - Q_ASSERT(regexp.isValid()); - QString f = filter; - int i = regexp.indexIn(f); - if (i >= 0) - f = regexp.cap(2); - return f.split(QLatin1Char(' '), base::QStringSkipEmptyParts); -} - -} // namespace - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : QDialog(parent) -, _windowTitle(caption) -, _initialDirectory(directory) { - auto filters = makeFilterList(filter); - const int numFilters = filters.count(); - _nameFilters.reserve(numFilters); - for (int i = 0; i < numFilters; ++i) { - _nameFilters << filters[i].simplified(); - } - - d.reset(new QGtkDialog(Libs::gtk_file_chooser_dialog_new("", nullptr, - GTK_FILE_CHOOSER_ACTION_OPEN, - // https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new - // first_button_text doesn't need explicit conversion to char*, while all others are vardict - tr::lng_cancel(tr::now).toUtf8(), GTK_RESPONSE_CANCEL, - tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr))); - connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); - connect(d.data(), SIGNAL(reject()), this, SLOT(onRejected())); - - g_signal_connect(Libs::gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); - g_signal_connect_swapped(Libs::gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); -} - -GtkFileDialog::~GtkFileDialog() { -} - -void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { - _dir.clear(); - _selection.clear(); - - applyOptions(); - return d->show(flags, modality, parent); -} - -void GtkFileDialog::setVisible(bool visible) { - if (visible) { - if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) { - return; - } - } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) { - return; - } - - if (visible) { - showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr); - } else { - hideHelper(); - } - - // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below - // updates the state correctly, but skips showing the non-native version: - setAttribute(Qt::WA_DontShowOnScreen); - - QDialog::setVisible(visible); -} - -int GtkFileDialog::exec() { - d->setModality(windowModality()); - - bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_DeleteOnClose, false); - - bool wasShowModal = testAttribute(Qt::WA_ShowModal); - setAttribute(Qt::WA_ShowModal, true); - setResult(0); - - show(); - - QPointer guard = this; - d->exec(); - if (guard.isNull()) - return QDialog::Rejected; - - setAttribute(Qt::WA_ShowModal, wasShowModal); - - return result(); -} - -void GtkFileDialog::hideHelper() { - // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() - // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual - // values before hiding the dialog - _dir = directory().absolutePath(); - _selection = selectedFiles(); - - d->hide(); -} - -bool GtkFileDialog::defaultNameFilterDisables() const { - return false; -} - -void GtkFileDialog::setDirectory(const QString &directory) { - GtkDialog *gtkDialog = d->gtkDialog(); - Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), directory.toUtf8()); -} - -QDir GtkFileDialog::directory() const { - // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() - // returns a bogus value -> return the cached value before hiding - if (!_dir.isEmpty()) - return _dir; - - QString ret; - GtkDialog *gtkDialog = d->gtkDialog(); - gchar *folder = Libs::gtk_file_chooser_get_current_folder(Libs::gtk_file_chooser_cast(gtkDialog)); - if (folder) { - ret = QString::fromUtf8(folder); - g_free(folder); - } - return QDir(ret); -} - -void GtkFileDialog::selectFile(const QString &filename) { - _initialFiles.clear(); - _initialFiles.append(filename); -} - -QStringList GtkFileDialog::selectedFiles() const { - // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() - // returns a bogus value -> return the cached value before hiding - if (!_selection.isEmpty()) - return _selection; - - QStringList selection; - GtkDialog *gtkDialog = d->gtkDialog(); - GSList *filenames = Libs::gtk_file_chooser_get_filenames(Libs::gtk_file_chooser_cast(gtkDialog)); - for (GSList *it = filenames; it; it = it->next) - selection += QString::fromUtf8((const char*)it->data); - g_slist_free(filenames); - return selection; -} - -void GtkFileDialog::setFilter() { - applyOptions(); -} - -void GtkFileDialog::selectNameFilter(const QString &filter) { - GtkFileFilter *gtkFilter = _filters.value(filter); - if (gtkFilter) { - GtkDialog *gtkDialog = d->gtkDialog(); - Libs::gtk_file_chooser_set_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter); - } -} - -QString GtkFileDialog::selectedNameFilter() const { - GtkDialog *gtkDialog = d->gtkDialog(); - GtkFileFilter *gtkFilter = Libs::gtk_file_chooser_get_filter(Libs::gtk_file_chooser_cast(gtkDialog)); - return _filterNames.value(gtkFilter); -} - -void GtkFileDialog::onAccepted() { - emit accept(); - -// QString filter = selectedNameFilter(); -// if (filter.isEmpty()) -// emit filterSelected(filter); - -// QList files = selectedFiles(); -// emit filesSelected(files); -// if (files.count() == 1) -// emit fileSelected(files.first()); -} - -void GtkFileDialog::onRejected() { - emit reject(); - -// -} - -void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) { -// QString selection; -// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); -// if (filename) { -// selection = QString::fromUtf8(filename); -// g_free(filename); -// } -// emit helper->currentChanged(QUrl::fromLocalFile(selection)); -} - -void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) { -// emit dialog->directoryEntered(dialog->directory()); -} - -GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) { - switch (fileMode) { - case QFileDialog::AnyFile: - case QFileDialog::ExistingFile: - case QFileDialog::ExistingFiles: - if (acceptMode == QFileDialog::AcceptOpen) - return GTK_FILE_CHOOSER_ACTION_OPEN; - else - return GTK_FILE_CHOOSER_ACTION_SAVE; - case QFileDialog::Directory: - default: - if (acceptMode == QFileDialog::AcceptOpen) - return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - else - return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; - } -} - -bool CustomButtonsSupported() { - return (Libs::gtk_dialog_get_widget_for_response != nullptr) - && (Libs::gtk_button_set_label != nullptr) - && (Libs::gtk_button_get_type != nullptr); -} - -void GtkFileDialog::applyOptions() { - GtkDialog *gtkDialog = d->gtkDialog(); - - Libs::gtk_window_set_title(Libs::gtk_window_cast(gtkDialog), _windowTitle.toUtf8()); - Libs::gtk_file_chooser_set_local_only(Libs::gtk_file_chooser_cast(gtkDialog), true); - - const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode); - Libs::gtk_file_chooser_set_action(Libs::gtk_file_chooser_cast(gtkDialog), action); - - const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles); - Libs::gtk_file_chooser_set_select_multiple(Libs::gtk_file_chooser_cast(gtkDialog), selectMultiple); - - const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite); - Libs::gtk_file_chooser_set_do_overwrite_confirmation(Libs::gtk_file_chooser_cast(gtkDialog), confirmOverwrite); - - if (!_nameFilters.isEmpty()) - setNameFilters(_nameFilters); - - if (!_initialDirectory.isEmpty()) - setDirectory(_initialDirectory); - - for_const (const auto &filename, _initialFiles) { - if (_acceptMode == QFileDialog::AcceptSave) { - QFileInfo fi(filename); - Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8()); - Libs::gtk_file_chooser_set_current_name(Libs::gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8()); - } else if (filename.endsWith('/')) { - Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); - } else { - Libs::gtk_file_chooser_select_filename(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); - } - } - - const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front(); - if (!initialNameFilter.isEmpty()) - selectNameFilter(initialNameFilter); - - if (CustomButtonsSupported()) { - GtkWidget *acceptButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); - if (acceptButton) { - /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) - Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); - else*/ if (_acceptMode == QFileDialog::AcceptOpen) - Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8()); - else - Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8()); - } - - GtkWidget *rejectButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); - if (rejectButton) { - /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) - Libs::gtk_button_set_label(Libs::gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); - else*/ - Libs::gtk_button_set_label(Libs::gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8()); - } - } -} - -void GtkFileDialog::setNameFilters(const QStringList &filters) { - GtkDialog *gtkDialog = d->gtkDialog(); - foreach (GtkFileFilter *filter, _filters) - Libs::gtk_file_chooser_remove_filter(Libs::gtk_file_chooser_cast(gtkDialog), filter); - - _filters.clear(); - _filterNames.clear(); - - for_const (auto &filter, filters) { - GtkFileFilter *gtkFilter = Libs::gtk_file_filter_new(); - auto name = filter;//.left(filter.indexOf(QLatin1Char('('))); - auto extensions = cleanFilterList(filter); - - Libs::gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); - for_const (auto &ext, extensions) { - auto caseInsensitiveExt = QString(); - caseInsensitiveExt.reserve(4 * ext.size()); - for_const (auto ch, ext) { - auto chLower = ch.toLower(); - auto chUpper = ch.toUpper(); - if (chLower != chUpper) { - caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']'); - } else { - caseInsensitiveExt.append(ch); - } - } - - Libs::gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8()); - } - - Libs::gtk_file_chooser_add_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter); - - _filters.insert(filter, gtkFilter); - _filterNames.insert(gtkFilter, filter); - } -} - -} // namespace internal -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION } // namespace FileDialog } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h index a0dc9c186..50c6d6a6d 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h @@ -9,15 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_file_utilities.h" -#include -#include - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -typedef struct _GtkWidget GtkWidget; -typedef struct _GtkDialog GtkDialog; -typedef struct _GtkFileFilter GtkFileFilter; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - namespace Platform { namespace File { namespace internal { @@ -45,114 +36,5 @@ inline void InitLastPath() { ::FileDialog::internal::InitLastPathDefault(); } -namespace internal { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - -// This is a patched copy of qgtk2 theme plugin. -// We need to use our own gtk file dialog instead of -// styling Qt file dialog, because Qt only works with gtk2. -// We need to be able to work with gtk2 and gtk3, because -// we use gtk3 to work with appindicator3. -class QGtkDialog : public QWindow { - Q_OBJECT - -public: - QGtkDialog(GtkWidget *gtkWidget); - ~QGtkDialog(); - - GtkDialog *gtkDialog() const; - - void exec(); - void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); - void hide(); - -signals: - void accept(); - void reject(); - -protected: - static void onResponse(QGtkDialog *dialog, int response); - static void onUpdatePreview(QGtkDialog *dialog); - -private slots: - void onParentWindowDestroyed(); - -private: - GtkWidget *gtkWidget; - GtkWidget *_preview = nullptr; - -}; - -class GtkFileDialog : public QDialog { - Q_OBJECT - -public: - GtkFileDialog(QWidget *parent = Q_NULLPTR, - const QString &caption = QString(), - const QString &directory = QString(), - const QString &filter = QString()); - ~GtkFileDialog(); - - void setVisible(bool visible) override; - - void setWindowTitle(const QString &windowTitle) { - _windowTitle = windowTitle; - } - void setAcceptMode(QFileDialog::AcceptMode acceptMode) { - _acceptMode = acceptMode; - } - void setFileMode(QFileDialog::FileMode fileMode) { - _fileMode = fileMode; - } - void setOption(QFileDialog::Option option, bool on = true) { - if (on) { - _options |= option; - } else { - _options &= ~option; - } - } - - int exec() override; - - bool defaultNameFilterDisables() const; - void setDirectory(const QString &directory); - QDir directory() const; - void selectFile(const QString &filename); - QStringList selectedFiles() const; - void setFilter(); - void selectNameFilter(const QString &filter); - QString selectedNameFilter() const; - -private slots: - void onAccepted(); - void onRejected(); - -private: - static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper); - static void onCurrentFolderChanged(GtkFileDialog *helper); - void applyOptions(); - void setNameFilters(const QStringList &filters); - - void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); - void hideHelper(); - - // Options - QFileDialog::Options _options; - QString _windowTitle = "Choose file"; - QString _initialDirectory; - QStringList _initialFiles; - QStringList _nameFilters; - QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen; - QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile; - - QString _dir; - QStringList _selection; - QHash _filters; - QHash _filterNames; - QScopedPointer d; -}; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -} // namespace internal } // namespace FileDialog } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp index 35f3c0c1c..08b1fad2f 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp @@ -5,10 +5,9 @@ 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 */ -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION #include "platform/linux/linux_gdk_helper.h" -#include "platform/linux/linux_libs.h" +#include "platform/linux/linux_gtk_integration_p.h" extern "C" { #undef signals @@ -19,6 +18,8 @@ extern "C" { namespace Platform { namespace internal { +using namespace Platform::Gtk; + enum class GtkLoaded { GtkNone, Gtk2, @@ -44,7 +45,7 @@ f_gdk_x11_window_get_type gdk_x11_window_get_type = nullptr; // To be able to compile with gtk-2.0 headers as well template inline bool gdk_is_x11_window_check(Object *obj) { - return Libs::g_type_cit_helper(obj, gdk_x11_window_get_type()); + return g_type_cit_helper(obj, gdk_x11_window_get_type()); } using f_gdk_window_get_display = GdkDisplay*(*)(GdkWindow *window); @@ -57,20 +58,20 @@ using f_gdk_x11_window_get_xid = Window(*)(GdkWindow *window); f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr; bool GdkHelperLoadGtk2(QLibrary &lib) { -#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY +#ifdef LINK_TO_GTK return false; -#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY - if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xdisplay", gdk_x11_drawable_get_xdisplay)) return false; - if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false; +#else // LINK_TO_GTK + if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_drawable_get_xdisplay", gdk_x11_drawable_get_xdisplay)) return false; + if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false; return true; -#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY +#endif // !LINK_TO_GTK } bool GdkHelperLoadGtk3(QLibrary &lib) { - if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false; - if (!LOAD_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false; - if (!LOAD_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false; - if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false; + if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false; + if (!LOAD_GTK_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false; + if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false; + if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false; return true; } @@ -103,4 +104,3 @@ void XSetTransientForHint(GdkWindow *window, quintptr winId) { } // namespace internal } // namespace Platform -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h index a7efb5685..6ddee649b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h @@ -7,11 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include - class QLibrary; -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION extern "C" { #undef signals #include @@ -28,4 +25,3 @@ void XSetTransientForHint(GdkWindow *window, quintptr winId); } // namespace internal } // namespace Platform -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp new file mode 100644 index 000000000..d9adb924d --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -0,0 +1,714 @@ +/* +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 "platform/linux/linux_gtk_file_dialog.h" + +#include "platform/linux/linux_gtk_integration_p.h" +#include "platform/linux/linux_gdk_helper.h" +#include "platform/linux/linux_desktop_environment.h" +#include "platform/linux/specific_linux.h" +#include "lang/lang_keys.h" +#include "storage/localstorage.h" +#include "base/qt_adapters.h" + +#include +#include +#include + +namespace Platform { +namespace FileDialog { +namespace Gtk { + +using namespace Platform::Gtk; + +namespace { + +// GTK file chooser image preview: thanks to Chromium + +// The size of the preview we display for selected image files. We set height +// larger than width because generally there is more free space vertically +// than horiztonally (setting the preview image will alway expand the width of +// the dialog, but usually not the height). The image's aspect ratio will always +// be preserved. +constexpr auto kPreviewWidth = 256; +constexpr auto kPreviewHeight = 512; + +const char *filterRegExp = +"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + +QStringList makeFilterList(const QString &filter) { + QString f(filter); + + if (f.isEmpty()) + return QStringList(); + + QString sep(QLatin1String(";;")); + int i = f.indexOf(sep, 0); + if (i == -1) { + if (f.indexOf(QLatin1Char('\n'), 0) != -1) { + sep = QLatin1Char('\n'); + i = f.indexOf(sep, 0); + } + } + + return f.split(sep); +} + +// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" +QStringList cleanFilterList(const QString &filter) { + QRegExp regexp(QString::fromLatin1(filterRegExp)); + Assert(regexp.isValid()); + QString f = filter; + int i = regexp.indexIn(f); + if (i >= 0) + f = regexp.cap(2); + return f.split(QLatin1Char(' '), base::QStringSkipEmptyParts); +} + +bool PreviewSupported() { + return (gdk_pixbuf_new_from_file_at_size != nullptr); +} + +bool CustomButtonsSupported() { + return (gtk_dialog_get_widget_for_response != nullptr) + && (gtk_button_set_label != nullptr) + && (gtk_button_get_type != nullptr); +} + +// This is a patched copy of qgtk2 theme plugin. +// We need to use our own gtk file dialog instead of +// styling Qt file dialog, because Qt only works with gtk2. +// We need to be able to work with gtk2 and gtk3, because +// we use gtk3 to work with appindicator3. +class QGtkDialog : public QWindow { +public: + QGtkDialog(GtkWidget *gtkWidget); + ~QGtkDialog(); + + GtkDialog *gtkDialog() const; + + void exec(); + void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + void hide(); + + rpl::producer<> accept(); + rpl::producer<> reject(); + +protected: + static void onResponse(QGtkDialog *dialog, int response); + static void onUpdatePreview(QGtkDialog *dialog); + +private: + void onParentWindowDestroyed(); + + GtkWidget *gtkWidget = nullptr; + GtkWidget *_preview = nullptr; + + rpl::event_stream<> _accept; + rpl::event_stream<> _reject; + rpl::lifetime _lifetime; + +}; + +class GtkFileDialog : public QDialog { +public: + GtkFileDialog(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &directory = QString(), + const QString &filter = QString()); + ~GtkFileDialog(); + + void setVisible(bool visible) override; + + void setWindowTitle(const QString &windowTitle) { + _windowTitle = windowTitle; + } + void setAcceptMode(QFileDialog::AcceptMode acceptMode) { + _acceptMode = acceptMode; + } + void setFileMode(QFileDialog::FileMode fileMode) { + _fileMode = fileMode; + } + void setOption(QFileDialog::Option option, bool on = true) { + if (on) { + _options |= option; + } else { + _options &= ~option; + } + } + + int exec() override; + + bool defaultNameFilterDisables() const; + void setDirectory(const QString &directory); + QDir directory() const; + void selectFile(const QString &filename); + QStringList selectedFiles() const; + void setFilter(); + void selectNameFilter(const QString &filter); + QString selectedNameFilter() const; + +private: + static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper); + static void onCurrentFolderChanged(GtkFileDialog *helper); + void applyOptions(); + void setNameFilters(const QStringList &filters); + + void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + void hideHelper(); + + void onAccepted(); + void onRejected(); + + // Options + QFileDialog::Options _options; + QString _windowTitle = "Choose file"; + QString _initialDirectory; + QStringList _initialFiles; + QStringList _nameFilters; + QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen; + QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile; + + QString _dir; + QStringList _selection; + QHash _filters; + QHash _filterNames; + QScopedPointer d; + + rpl::lifetime _lifetime; +}; + +QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) { + g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); + g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr); + if (PreviewSupported()) { + _preview = gtk_image_new(); + g_signal_connect_swapped(G_OBJECT(gtkWidget), "update-preview", G_CALLBACK(onUpdatePreview), this); + gtk_file_chooser_set_preview_widget(gtk_file_chooser_cast(gtkWidget), _preview); + } +} + +QGtkDialog::~QGtkDialog() { + gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); + gtk_widget_destroy(gtkWidget); +} + +GtkDialog *QGtkDialog::gtkDialog() const { + return gtk_dialog_cast(gtkWidget); +} + +void QGtkDialog::exec() { + if (modality() == Qt::ApplicationModal) { + // block input to the whole app, including other GTK dialogs + gtk_dialog_run(gtkDialog()); + } else { + // block input to the window, allow input to other GTK dialogs + QEventLoop loop; + + accept( + ) | rpl::start_with_next([=, &loop] { + loop.quit(); + }, _lifetime); + + reject( + ) | rpl::start_with_next([=, &loop] { + loop.quit(); + }, _lifetime); + + loop.exec(); + } +} + +void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { + connect(parent, &QWindow::destroyed, this, [=] { onParentWindowDestroyed(); }, + Qt::UniqueConnection); + setParent(parent); + setFlags(flags); + setModality(modality); + + gtk_widget_realize(gtkWidget); // creates X window + + if (parent) { + internal::XSetTransientForHint(gtk_widget_get_window(gtkWidget), parent->winId()); + } + + if (modality != Qt::NonModal) { + gdk_window_set_modal_hint(gtk_widget_get_window(gtkWidget), true); + QGuiApplicationPrivate::showModalWindow(this); + } + + gtk_widget_show(gtkWidget); + gdk_window_focus(gtk_widget_get_window(gtkWidget), 0); +} + +void QGtkDialog::hide() { + QGuiApplicationPrivate::hideModalWindow(this); + gtk_widget_hide(gtkWidget); +} + +rpl::producer<> QGtkDialog::accept() { + return _accept.events(); +} + +rpl::producer<> QGtkDialog::reject() { + return _reject.events(); +} + +void QGtkDialog::onResponse(QGtkDialog *dialog, int response) { + if (response == GTK_RESPONSE_OK) + dialog->_accept.fire({}); + else + dialog->_reject.fire({}); +} + +void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) { + auto filename = gtk_file_chooser_get_preview_filename(gtk_file_chooser_cast(dialog->gtkWidget)); + if (!filename) { + gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false); + return; + } + + // Don't attempt to open anything which isn't a regular file. If a named pipe, + // this may hang. See https://crbug.com/534754. + struct stat stat_buf; + if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) { + g_free(filename); + gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false); + return; + } + + // This will preserve the image's aspect ratio. + auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr); + g_free(filename); + if (pixbuf) { + gtk_image_set_from_pixbuf(gtk_image_cast(dialog->_preview), pixbuf); + g_object_unref(pixbuf); + } + gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false); +} + +void QGtkDialog::onParentWindowDestroyed() { + // The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it. + setParent(nullptr); +} + +GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : QDialog(parent) +, _windowTitle(caption) +, _initialDirectory(directory) { + auto filters = makeFilterList(filter); + const int numFilters = filters.count(); + _nameFilters.reserve(numFilters); + for (int i = 0; i < numFilters; ++i) { + _nameFilters << filters[i].simplified(); + } + + d.reset(new QGtkDialog(gtk_file_chooser_dialog_new("", nullptr, + GTK_FILE_CHOOSER_ACTION_OPEN, + // https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new + // first_button_text doesn't need explicit conversion to char*, while all others are vardict + tr::lng_cancel(tr::now).toUtf8(), GTK_RESPONSE_CANCEL, + tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr))); + + d.data()->accept( + ) | rpl::start_with_next([=] { + onAccepted(); + }, _lifetime); + + d.data()->reject( + ) | rpl::start_with_next([=] { + onRejected(); + }, _lifetime); + + g_signal_connect(gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); + g_signal_connect_swapped(gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); +} + +GtkFileDialog::~GtkFileDialog() { +} + +void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { + _dir.clear(); + _selection.clear(); + + applyOptions(); + return d->show(flags, modality, parent); +} + +void GtkFileDialog::setVisible(bool visible) { + if (visible) { + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) { + return; + } + } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) { + return; + } + + if (visible) { + showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr); + } else { + hideHelper(); + } + + // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below + // updates the state correctly, but skips showing the non-native version: + setAttribute(Qt::WA_DontShowOnScreen); + + QDialog::setVisible(visible); +} + +int GtkFileDialog::exec() { + d->setModality(windowModality()); + + bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_DeleteOnClose, false); + + bool wasShowModal = testAttribute(Qt::WA_ShowModal); + setAttribute(Qt::WA_ShowModal, true); + setResult(0); + + show(); + + QPointer guard = this; + d->exec(); + if (guard.isNull()) + return QDialog::Rejected; + + setAttribute(Qt::WA_ShowModal, wasShowModal); + + return result(); +} + +void GtkFileDialog::hideHelper() { + // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() + // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual + // values before hiding the dialog + _dir = directory().absolutePath(); + _selection = selectedFiles(); + + d->hide(); +} + +bool GtkFileDialog::defaultNameFilterDisables() const { + return false; +} + +void GtkFileDialog::setDirectory(const QString &directory) { + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), directory.toUtf8()); +} + +QDir GtkFileDialog::directory() const { + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() + // returns a bogus value -> return the cached value before hiding + if (!_dir.isEmpty()) + return _dir; + + QString ret; + GtkDialog *gtkDialog = d->gtkDialog(); + gchar *folder = gtk_file_chooser_get_current_folder(gtk_file_chooser_cast(gtkDialog)); + if (folder) { + ret = QString::fromUtf8(folder); + g_free(folder); + } + return QDir(ret); +} + +void GtkFileDialog::selectFile(const QString &filename) { + _initialFiles.clear(); + _initialFiles.append(filename); +} + +QStringList GtkFileDialog::selectedFiles() const { + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() + // returns a bogus value -> return the cached value before hiding + if (!_selection.isEmpty()) + return _selection; + + QStringList selection; + GtkDialog *gtkDialog = d->gtkDialog(); + GSList *filenames = gtk_file_chooser_get_filenames(gtk_file_chooser_cast(gtkDialog)); + for (GSList *it = filenames; it; it = it->next) + selection += QString::fromUtf8((const char*)it->data); + g_slist_free(filenames); + return selection; +} + +void GtkFileDialog::setFilter() { + applyOptions(); +} + +void GtkFileDialog::selectNameFilter(const QString &filter) { + GtkFileFilter *gtkFilter = _filters.value(filter); + if (gtkFilter) { + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter); + } +} + +QString GtkFileDialog::selectedNameFilter() const { + GtkDialog *gtkDialog = d->gtkDialog(); + GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(gtk_file_chooser_cast(gtkDialog)); + return _filterNames.value(gtkFilter); +} + +void GtkFileDialog::onAccepted() { + emit accept(); + +// QString filter = selectedNameFilter(); +// if (filter.isEmpty()) +// emit filterSelected(filter); + +// QList files = selectedFiles(); +// emit filesSelected(files); +// if (files.count() == 1) +// emit fileSelected(files.first()); +} + +void GtkFileDialog::onRejected() { + emit reject(); + +// +} + +void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) { +// QString selection; +// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); +// if (filename) { +// selection = QString::fromUtf8(filename); +// g_free(filename); +// } +// emit helper->currentChanged(QUrl::fromLocalFile(selection)); +} + +void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) { +// emit dialog->directoryEntered(dialog->directory()); +} + +GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) { + switch (fileMode) { + case QFileDialog::AnyFile: + case QFileDialog::ExistingFile: + case QFileDialog::ExistingFiles: + if (acceptMode == QFileDialog::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_OPEN; + else + return GTK_FILE_CHOOSER_ACTION_SAVE; + case QFileDialog::Directory: + default: + if (acceptMode == QFileDialog::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + else + return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; + } +} + +void GtkFileDialog::applyOptions() { + GtkDialog *gtkDialog = d->gtkDialog(); + + gtk_window_set_title(gtk_window_cast(gtkDialog), _windowTitle.toUtf8()); + gtk_file_chooser_set_local_only(gtk_file_chooser_cast(gtkDialog), true); + + const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode); + gtk_file_chooser_set_action(gtk_file_chooser_cast(gtkDialog), action); + + const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles); + gtk_file_chooser_set_select_multiple(gtk_file_chooser_cast(gtkDialog), selectMultiple); + + const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite); + gtk_file_chooser_set_do_overwrite_confirmation(gtk_file_chooser_cast(gtkDialog), confirmOverwrite); + + if (!_nameFilters.isEmpty()) + setNameFilters(_nameFilters); + + if (!_initialDirectory.isEmpty()) + setDirectory(_initialDirectory); + + for_const (const auto &filename, _initialFiles) { + if (_acceptMode == QFileDialog::AcceptSave) { + QFileInfo fi(filename); + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8()); + gtk_file_chooser_set_current_name(gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8()); + } else if (filename.endsWith('/')) { + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); + } else { + gtk_file_chooser_select_filename(gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); + } + } + + const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front(); + if (!initialNameFilter.isEmpty()) + selectNameFilter(initialNameFilter); + + if (CustomButtonsSupported()) { + GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); + if (acceptButton) { + /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) + gtk_button_set_label(gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); + else*/ if (_acceptMode == QFileDialog::AcceptOpen) + gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8()); + else + gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8()); + } + + GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); + if (rejectButton) { + /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) + gtk_button_set_label(gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); + else*/ + gtk_button_set_label(gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8()); + } + } +} + +void GtkFileDialog::setNameFilters(const QStringList &filters) { + GtkDialog *gtkDialog = d->gtkDialog(); + foreach (GtkFileFilter *filter, _filters) + gtk_file_chooser_remove_filter(gtk_file_chooser_cast(gtkDialog), filter); + + _filters.clear(); + _filterNames.clear(); + + for_const (auto &filter, filters) { + GtkFileFilter *gtkFilter = gtk_file_filter_new(); + auto name = filter;//.left(filter.indexOf(QLatin1Char('('))); + auto extensions = cleanFilterList(filter); + + gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); + for_const (auto &ext, extensions) { + auto caseInsensitiveExt = QString(); + caseInsensitiveExt.reserve(4 * ext.size()); + for_const (auto ch, ext) { + auto chLower = ch.toLower(); + auto chUpper = ch.toUpper(); + if (chLower != chUpper) { + caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']'); + } else { + caseInsensitiveExt.append(ch); + } + } + + gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8()); + } + + gtk_file_chooser_add_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter); + + _filters.insert(filter, gtkFilter); + _filterNames.insert(gtkFilter, filter); + } +} + +} // namespace + +bool Supported() { + return internal::GdkHelperLoaded() + && (gtk_widget_hide_on_delete != nullptr) + && (gtk_clipboard_store != nullptr) + && (gtk_clipboard_get != nullptr) + && (gtk_widget_destroy != nullptr) + && (gtk_dialog_get_type != nullptr) + && (gtk_dialog_run != nullptr) + && (gtk_widget_realize != nullptr) + && (gdk_window_set_modal_hint != nullptr) + && (gtk_widget_show != nullptr) + && (gdk_window_focus != nullptr) + && (gtk_widget_hide != nullptr) + && (gtk_widget_hide_on_delete != nullptr) + && (gtk_file_chooser_dialog_new != nullptr) + && (gtk_file_chooser_get_type != nullptr) + && (gtk_file_chooser_set_current_folder != nullptr) + && (gtk_file_chooser_get_current_folder != nullptr) + && (gtk_file_chooser_set_current_name != nullptr) + && (gtk_file_chooser_select_filename != nullptr) + && (gtk_file_chooser_get_filenames != nullptr) + && (gtk_file_chooser_set_filter != nullptr) + && (gtk_file_chooser_get_filter != nullptr) + && (gtk_window_get_type != nullptr) + && (gtk_window_set_title != nullptr) + && (gtk_file_chooser_set_local_only != nullptr) + && (gtk_file_chooser_set_action != nullptr) + && (gtk_file_chooser_set_select_multiple != nullptr) + && (gtk_file_chooser_set_do_overwrite_confirmation != nullptr) + && (gtk_file_chooser_remove_filter != nullptr) + && (gtk_file_filter_set_name != nullptr) + && (gtk_file_filter_add_pattern != nullptr) + && (gtk_file_chooser_add_filter != nullptr) + && (gtk_file_filter_new != nullptr); +} + +bool Use(Type type) { + // use gtk file dialog on gtk-based desktop environments + // or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3) + // or if portals are used and operation is to open folder + // and portal doesn't support folder choosing + const auto sandboxedOrCustomPortal = InFlatpak() + || InSnap() + || UseXDGDesktopPortal(); + + const auto neededForPortal = (type == Type::ReadFolder) + && !CanOpenDirectoryWithPortal(); + + const auto neededNonForced = DesktopEnvironment::IsGtkBased() + || (sandboxedOrCustomPortal && neededForPortal); + + const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal; + + return IsGtkIntegrationForced() + || (neededNonForced && !excludeNonForced); +} + +bool Get( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + Type type, + QString startFile) { + GtkFileDialog dialog(parent, caption, QString(), filter); + + dialog.setModal(true); + if (type == Type::ReadFile || type == Type::ReadFiles) { + dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + } else if (type == Type::ReadFolder) { + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::Directory); + dialog.setOption(QFileDialog::ShowDirsOnly); + } else { + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + } + if (startFile.isEmpty() || startFile.at(0) != '/') { + startFile = cDialogLastPath() + '/' + startFile; + } + dialog.selectFile(startFile); + + int res = dialog.exec(); + + QString path = dialog.directory().absolutePath(); + if (path != cDialogLastPath()) { + cSetDialogLastPath(path); + Local::writeSettings(); + } + + if (res == QDialog::Accepted) { + if (type == Type::ReadFiles) { + files = dialog.selectedFiles(); + } else { + files = dialog.selectedFiles().mid(0, 1); + } + return true; + } + + files = QStringList(); + remoteContent = QByteArray(); + return false; +} + +} // namespace Gtk +} // namespace FileDialog +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h new file mode 100644 index 000000000..0891a0e10 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h @@ -0,0 +1,31 @@ +/* +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 "core/file_utilities.h" + +namespace Platform { +namespace FileDialog { +namespace Gtk { + +using Type = ::FileDialog::internal::Type; + +bool Supported(); +bool Use(Type type = Type::ReadFile); +bool Get( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + Type type, + QString startFile); + +} // namespace Gtk +} // namespace FileDialog +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp new file mode 100644 index 000000000..a5ae6dac0 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -0,0 +1,491 @@ +/* +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 "platform/linux/linux_gtk_integration.h" + +#include "platform/linux/linux_gtk_integration_p.h" +#include "base/platform/base_platform_info.h" +#include "platform/linux/linux_xlib_helper.h" +#include "platform/linux/linux_gdk_helper.h" +#include "platform/linux/linux_gtk_file_dialog.h" +#include "platform/linux/linux_open_with_dialog.h" +#include "platform/linux/specific_linux.h" +#include "core/sandbox.h" +#include "core/core_settings.h" +#include "core/application.h" +#include "main/main_domain.h" +#include "mainwindow.h" + +namespace Platform { +namespace internal { + +using namespace Platform::Gtk; + +namespace { + +bool GtkTriedToInit = false; +bool GtkLoaded = false; + +bool LoadLibrary(QLibrary &lib, const char *name, int version) { +#ifdef LINK_TO_GTK + return true; +#else // LINK_TO_GTK + DEBUG_LOG(("Loading '%1' with version %2...").arg( + QLatin1String(name)).arg(version)); + lib.setFileNameAndVersion(QLatin1String(name), version); + if (lib.load()) { + DEBUG_LOG(("Loaded '%1' with version %2!").arg( + QLatin1String(name)).arg(version)); + return true; + } + lib.setFileNameAndVersion(QLatin1String(name), QString()); + if (lib.load()) { + DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name))); + return true; + } + LOG(("Could not load '%1' with version %2 :(").arg( + QLatin1String(name)).arg(version)); + return false; +#endif // !LINK_TO_GTK +} + +void GtkMessageHandler( + const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer unused_data) { + // Silence false-positive Gtk warnings (we are using Xlib to set + // the WM_TRANSIENT_FOR hint). + if (message != qstr("GtkDialog mapped without a transient parent. " + "This is discouraged.")) { + // For other messages, call the default handler. + g_log_default_handler(log_domain, log_level, message, unused_data); + } +} + +bool SetupGtkBase(QLibrary &lib_gtk) { + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false; + + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide", gtk_widget_hide)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_get_window", gtk_widget_get_window)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_get_type", gtk_window_get_type)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_set_title", gtk_window_set_title)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_action", gtk_file_chooser_set_action)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_new", gtk_image_new)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false; + + if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_type", gtk_dialog_get_type)) return false; + if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_run", gtk_dialog_run)) return false; + + if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false; + + if (LOAD_GTK_SYMBOL(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) { + // We work only with Wayland and X11 GDK backends. + // Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call. + // See https://github.com/telegramdesktop/tdesktop/issues/3176 + // See https://github.com/telegramdesktop/tdesktop/issues/3162 + if(IsWayland()) { + DEBUG_LOG(("Limit allowed GDK backends to wayland,x11")); + gdk_set_allowed_backends("wayland,x11"); + } else { + DEBUG_LOG(("Limit allowed GDK backends to x11,wayland")); + gdk_set_allowed_backends("x11,wayland"); + } + } + + // gtk_init will reset the Xlib error handler, + // and that causes Qt applications to quit on X errors. + // Therefore, we need to manually restore it. + XErrorHandlerRestorer handlerRestorer; + + DEBUG_LOG(("Library gtk functions loaded!")); + GtkTriedToInit = true; + if (!gtk_init_check(0, 0)) { + gtk_init_check = nullptr; + DEBUG_LOG(("Failed to gtk_init_check(0, 0)!")); + return false; + } + DEBUG_LOG(("Checked gtk with gtk_init_check!")); + + // Use our custom log handler. + g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, GtkMessageHandler, nullptr); + + return true; +} + +bool GetImageFromClipboardSupported() { + return (gtk_clipboard_get != nullptr) + && (gtk_clipboard_wait_for_contents != nullptr) + && (gtk_clipboard_wait_for_image != nullptr) + && (gtk_selection_data_targets_include_image != nullptr) + && (gtk_selection_data_free != nullptr) + && (gdk_pixbuf_get_pixels != nullptr) + && (gdk_pixbuf_get_width != nullptr) + && (gdk_pixbuf_get_height != nullptr) + && (gdk_pixbuf_get_rowstride != nullptr) + && (gdk_pixbuf_get_has_alpha != nullptr) + && (gdk_atom_intern != nullptr); +} + +template +std::optional GtkSetting(const QString &propertyName) { + const auto integration = GtkIntegration::Instance(); + if (!integration + || !integration->loaded() + || gtk_settings_get_default == nullptr) { + return std::nullopt; + } + auto settings = gtk_settings_get_default(); + T value; + g_object_get(settings, propertyName.toUtf8(), &value, nullptr); + return value; +} + +bool IconThemeShouldBeSet() { + // change the icon theme only if + // it isn't already set by a platformtheme plugin + // if QT_QPA_PLATFORMTHEME=(gtk2|gtk3), then force-apply the icon theme + static const auto Result = + // QGenericUnixTheme + (QIcon::themeName() == qstr("hicolor") + && QIcon::fallbackThemeName() == qstr("hicolor")) + // QGnomeTheme + || (QIcon::themeName() == qstr("Adwaita") + && QIcon::fallbackThemeName() == qstr("gnome")) + // qt5ct + || (QIcon::themeName().isEmpty() + && QIcon::fallbackThemeName().isEmpty()) + || IsGtkIntegrationForced(); + + return Result; +} + +bool CursorSizeShouldBeSet() { + // change the cursor size only on Wayland and if it wasn't already set + static const auto Result = IsWayland() + && qEnvironmentVariableIsEmpty("XCURSOR_SIZE"); + + return Result; +} + +void SetIconTheme() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + const auto integration = GtkIntegration::Instance(); + + if (!integration + || !IconThemeShouldBeSet()) { + return; + } + + const auto themeName = integration->getStringSetting( + qsl("gtk-icon-theme-name")); + + const auto fallbackThemeName = integration->getStringSetting( + qsl("gtk-fallback-icon-theme")); + + if (!themeName.has_value() || !fallbackThemeName.has_value()) { + return; + } + + DEBUG_LOG(("Setting GTK icon theme")); + + QIcon::setThemeName(*themeName); + QIcon::setFallbackThemeName(*fallbackThemeName); + + DEBUG_LOG(("New icon theme: %1").arg(QIcon::themeName())); + DEBUG_LOG(("New fallback icon theme: %1").arg( + QIcon::fallbackThemeName())); + + SetApplicationIcon(Window::CreateIcon()); + if (App::wnd()) { + App::wnd()->setWindowIcon(Window::CreateIcon()); + } + + Core::App().domain().notifyUnreadBadgeChanged(); + }); +} + +void SetCursorSize() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + const auto integration = GtkIntegration::Instance(); + + if (!integration + || !CursorSizeShouldBeSet()) { + return; + } + + const auto newCursorSize = integration->getIntSetting( + qsl("gtk-cursor-theme-size")); + + if (!newCursorSize.has_value()) { + return; + } + + DEBUG_LOG(("Setting GTK cursor size")); + qputenv("XCURSOR_SIZE", QByteArray::number(*newCursorSize)); + DEBUG_LOG(("New cursor size: %1").arg(*newCursorSize)); + }); +} + +void DarkModeChanged() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + Core::App().settings().setSystemDarkMode(IsDarkMode()); + }); +} + +void DecorationLayoutChanged() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + Core::App().settings().setWindowControlsLayout( + WindowControlsLayout()); + }); +} + +} // namespace + +GtkIntegration::GtkIntegration() { +} + +GtkIntegration *GtkIntegration::Instance() { + static const auto useGtkIntegration = !qEnvironmentVariableIsSet( + kDisableGtkIntegration.utf8()); + + if (!useGtkIntegration) { + return nullptr; + } + + static GtkIntegration instance; + return &instance; +} + +void GtkIntegration::load() { + DEBUG_LOG(("Loading GTK")); + + QLibrary lib_gtk; + lib_gtk.setLoadHints(QLibrary::DeepBindHint); + + if (LoadLibrary(lib_gtk, "gtk-3", 0)) { + GtkLoaded = SetupGtkBase(lib_gtk); + } + if (!GtkLoaded + && !GtkTriedToInit + && LoadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { + GtkLoaded = SetupGtkBase(lib_gtk); + } + + if (GtkLoaded) { + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); + + GdkHelperLoad(lib_gtk); + + LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); + LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label); + LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type); + + LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); + LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); + LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type); + + SetIconTheme(); + SetCursorSize(); + + const auto settings = gtk_settings_get_default(); + + g_signal_connect( + settings, + "notify::gtk-icon-theme-name", + G_CALLBACK(SetIconTheme), + nullptr); + + g_signal_connect( + settings, + "notify::gtk-theme-name", + G_CALLBACK(DarkModeChanged), + nullptr); + + g_signal_connect( + settings, + "notify::gtk-cursor-theme-size", + G_CALLBACK(SetCursorSize), + nullptr); + + if (checkVersion(3, 0, 0)) { + g_signal_connect( + settings, + "notify::gtk-application-prefer-dark-theme", + G_CALLBACK(DarkModeChanged), + nullptr); + } + + if (checkVersion(3, 12, 0)) { + g_signal_connect( + settings, + "notify::gtk-decoration-layout", + G_CALLBACK(DecorationLayoutChanged), + nullptr); + } + } else { + LOG(("Could not load gtk-3 or gtk-x11-2.0!")); + } +} + +bool GtkIntegration::loaded() const { + return GtkLoaded; +} + +bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const { + return (loaded() && gtk_check_version != nullptr) + ? !gtk_check_version(major, minor, micro) + : false; +} + +std::optional GtkIntegration::getBoolSetting( + const QString &propertyName) const { + const auto value = GtkSetting(propertyName); + if (!value.has_value()) { + return std::nullopt; + } + DEBUG_LOG(("Getting GTK setting, %1: %2") + .arg(propertyName) + .arg(Logs::b(*value))); + return *value; +} + +std::optional GtkIntegration::getIntSetting( + const QString &propertyName) const { + const auto value = GtkSetting(propertyName); + if (value.has_value()) { + DEBUG_LOG(("Getting GTK setting, %1: %2") + .arg(propertyName) + .arg(*value)); + } + return value; +} + +std::optional GtkIntegration::getStringSetting( + const QString &propertyName) const { + auto value = GtkSetting(propertyName); + if (!value.has_value()) { + return std::nullopt; + } + const auto str = QString::fromUtf8(*value); + g_free(*value); + DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str)); + return str; +} + +bool GtkIntegration::fileDialogSupported() const { + return FileDialog::Gtk::Supported(); +} + +bool GtkIntegration::useFileDialog(FileDialogType type) const { + return FileDialog::Gtk::Use(type); +} + +bool GtkIntegration::getFileDialog( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + FileDialogType type, + QString startFile) const { + return FileDialog::Gtk::Get( + parent, + files, + remoteContent, + caption, + filter, + type, + startFile); +} + +bool GtkIntegration::showOpenWithDialog(const QString &filepath) const { + return File::internal::ShowOpenWithDialog(filepath); +} + +QImage GtkIntegration::getImageFromClipboard() const { + QImage data; + + if (!GetImageFromClipboardSupported()) { + return data; + } + + const auto clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + if (!clipboard) { + return data; + } + + auto gsel = gtk_clipboard_wait_for_contents( + clipboard, + gdk_atom_intern("TARGETS", true)); + + if (gsel) { + if (gtk_selection_data_targets_include_image(gsel, false)) { + auto img = gtk_clipboard_wait_for_image(clipboard); + + if (img) { + data = QImage( + gdk_pixbuf_get_pixels(img), + gdk_pixbuf_get_width(img), + gdk_pixbuf_get_height(img), + gdk_pixbuf_get_rowstride(img), + gdk_pixbuf_get_has_alpha(img) + ? QImage::Format_RGBA8888 + : QImage::Format_RGB888).copy(); + + g_object_unref(img); + } + } + + gtk_selection_data_free(gsel); + } + + return data; +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h new file mode 100644 index 000000000..df393befa --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h @@ -0,0 +1,59 @@ +/* +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 "core/file_utilities.h" + +namespace Platform { +namespace internal { + +inline constexpr auto kDisableGtkIntegration = "TDESKTOP_DISABLE_GTK_INTEGRATION"_cs; + +class GtkIntegration { +public: + static GtkIntegration *Instance(); + + void load(); + [[nodiscard]] bool loaded() const; + [[nodiscard]] bool checkVersion( + uint major, + uint minor, + uint micro) const; + + [[nodiscard]] std::optional getBoolSetting( + const QString &propertyName) const; + + [[nodiscard]] std::optional getIntSetting( + const QString &propertyName) const; + + [[nodiscard]] std::optional getStringSetting( + const QString &propertyName) const; + + using FileDialogType = ::FileDialog::internal::Type; + [[nodiscard]] bool fileDialogSupported() const; + [[nodiscard]] bool useFileDialog( + FileDialogType type = FileDialogType::ReadFile) const; + [[nodiscard]] bool getFileDialog( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + FileDialogType type, + QString startFile) const; + + [[nodiscard]] bool showOpenWithDialog(const QString &filepath) const; + + [[nodiscard]] QImage getImageFromClipboard() const; + +private: + GtkIntegration(); +}; + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp new file mode 100644 index 000000000..00df2f046 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp @@ -0,0 +1,74 @@ +/* +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 "platform/linux/linux_gtk_integration.h" + +namespace Platform { +namespace internal { + +GtkIntegration::GtkIntegration() { +} + +GtkIntegration *GtkIntegration::Instance() { + return nullptr; +} + +void GtkIntegration::load() { +} + +bool GtkIntegration::loaded() const { + return false; +} + +bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const { + return false; +} + +std::optional GtkIntegration::getBoolSetting( + const QString &propertyName) const { + return std::nullopt; +} + +std::optional GtkIntegration::getIntSetting( + const QString &propertyName) const { + return std::nullopt; +} + +std::optional GtkIntegration::getStringSetting( + const QString &propertyName) const { + return std::nullopt; +} + +bool GtkIntegration::fileDialogSupported() const { + return false; +} + +bool GtkIntegration::useFileDialog(FileDialogType type) const { + return false; +} + +bool GtkIntegration::getFileDialog( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + FileDialogType type, + QString startFile) const { + return false; +} + +bool GtkIntegration::showOpenWithDialog(const QString &filepath) const { + return false; +} + +QImage GtkIntegration::getImageFromClipboard() const { + return {}; +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h new file mode 100644 index 000000000..301a828e5 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h @@ -0,0 +1,158 @@ +/* +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 + +extern "C" { +#undef signals +#include +#include +#define signals public +} // extern "C" + +#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY +#define LINK_TO_GTK +#endif // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY + +#ifdef LINK_TO_GTK +#define LOAD_GTK_SYMBOL(lib, name, func) (func = ::func) +#else // LINK_TO_GTK +#define LOAD_GTK_SYMBOL Platform::Gtk::LoadSymbol +#endif // !LINK_TO_GTK + +// To be able to compile with gtk-2.0 headers as well +typedef struct _GtkAppChooser GtkAppChooser; + +namespace Platform { +namespace Gtk { + +template +bool LoadSymbol(QLibrary &lib, const char *name, Function &func) { + func = nullptr; + if (!lib.isLoaded()) { + return false; + } + + func = reinterpret_cast(lib.resolve(name)); + if (func) { + return true; + } + LOG(("Error: failed to load '%1' function!").arg(name)); + return false; +} + +inline gboolean (*gtk_init_check)(int *argc, char ***argv) = nullptr; +inline const gchar* (*gtk_check_version)(guint required_major, guint required_minor, guint required_micro) = nullptr; +inline GtkSettings* (*gtk_settings_get_default)(void) = nullptr; +inline void (*gtk_widget_show)(GtkWidget *widget) = nullptr; +inline void (*gtk_widget_hide)(GtkWidget *widget) = nullptr; +inline GdkWindow* (*gtk_widget_get_window)(GtkWidget *widget) = nullptr; +inline void (*gtk_widget_realize)(GtkWidget *widget) = nullptr; +inline gboolean (*gtk_widget_hide_on_delete)(GtkWidget *widget) = nullptr; +inline void (*gtk_widget_destroy)(GtkWidget *widget) = nullptr; +inline GtkClipboard* (*gtk_clipboard_get)(GdkAtom selection) = nullptr; +inline void (*gtk_clipboard_store)(GtkClipboard *clipboard) = nullptr; +inline GtkSelectionData* (*gtk_clipboard_wait_for_contents)(GtkClipboard *clipboard, GdkAtom target) = nullptr; +inline GdkPixbuf* (*gtk_clipboard_wait_for_image)(GtkClipboard *clipboard) = nullptr; +inline gboolean (*gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable) = nullptr; +inline void (*gtk_selection_data_free)(GtkSelectionData *data) = nullptr; +inline GtkWidget* (*gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED = nullptr; +inline gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename) = nullptr; +inline gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser) = nullptr; +inline void (*gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name) = nullptr; +inline gboolean (*gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename) = nullptr; +inline GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser) = nullptr; +inline void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr; +inline GtkFileFilter* (*gtk_file_chooser_get_filter)(GtkFileChooser *chooser) = nullptr; +inline void (*gtk_window_set_title)(GtkWindow *window, const gchar *title) = nullptr; +inline void (*gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only) = nullptr; +inline void (*gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action) = nullptr; +inline void (*gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple) = nullptr; +inline void (*gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation) = nullptr; +inline GtkWidget* (*gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id) = nullptr; +inline void (*gtk_button_set_label)(GtkButton *button, const gchar *label) = nullptr; +inline void (*gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr; +inline void (*gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name) = nullptr; +inline void (*gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern) = nullptr; +inline void (*gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr; +inline void (*gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget) = nullptr; +inline gchar* (*gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser) = nullptr; +inline void (*gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active) = nullptr; +inline GtkFileFilter* (*gtk_file_filter_new)(void) = nullptr; +inline GtkWidget* (*gtk_image_new)(void) = nullptr; +inline void (*gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf) = nullptr; +inline GtkWidget* (*gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file) = nullptr; +inline GAppInfo* (*gtk_app_chooser_get_app_info)(GtkAppChooser *self) = nullptr; +inline void (*gdk_set_allowed_backends)(const gchar *backends) = nullptr; +inline void (*gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal) = nullptr; +inline void (*gdk_window_focus)(GdkWindow *window, guint32 timestamp) = nullptr; + +template +inline Result *g_type_cic_helper(Object *instance, GType iface_type) { + return reinterpret_cast(g_type_check_instance_cast(reinterpret_cast(instance), iface_type)); +} + +inline GType (*gtk_dialog_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkDialog *gtk_dialog_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_dialog_get_type()); +} + +inline GType (*gtk_file_chooser_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_file_chooser_get_type()); +} + +inline GType (*gtk_image_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkImage *gtk_image_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_image_get_type()); +} + +inline GType (*gtk_button_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkButton *gtk_button_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_button_get_type()); +} + +inline GType (*gtk_window_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkWindow *gtk_window_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_window_get_type()); +} + +inline GType (*gtk_app_chooser_get_type)(void) G_GNUC_CONST = nullptr; +template +inline GtkAppChooser *gtk_app_chooser_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_app_chooser_get_type()); +} + +template +inline bool g_type_cit_helper(Object *instance, GType iface_type) { + if (!instance) return false; + + auto ginstance = reinterpret_cast(instance); + if (ginstance->g_class && ginstance->g_class->g_type == iface_type) { + return true; + } + return g_type_check_instance_is_a(ginstance, iface_type); +} + +inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr; +inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr; +inline GdkPixbuf* (*gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error) = nullptr; +inline gboolean (*gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf) = nullptr; +inline guchar* (*gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf) = nullptr; +inline int (*gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf) = nullptr; +inline int (*gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf) = nullptr; +inline int (*gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf) = nullptr; + +} // namespace Gtk +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp deleted file mode 100644 index 2dbddebb8..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_libs.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* -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 "platform/linux/linux_libs.h" - -#include "base/platform/base_platform_info.h" -#include "platform/linux/linux_xlib_helper.h" -#include "platform/linux/linux_gdk_helper.h" -#include "platform/linux/specific_linux.h" -#include "core/sandbox.h" -#include "core/core_settings.h" -#include "core/application.h" -#include "main/main_domain.h" -#include "mainwindow.h" - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -using Platform::internal::XErrorHandlerRestorer; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -namespace Platform { -namespace Libs { -namespace { - -bool gtkTriedToInit = false; -bool gtkLoaded = false; - -bool loadLibrary(QLibrary &lib, const char *name, int version) { -#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY - return true; -#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY - DEBUG_LOG(("Loading '%1' with version %2...").arg(QLatin1String(name)).arg(version)); - lib.setFileNameAndVersion(QLatin1String(name), version); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version)); - return true; - } - lib.setFileNameAndVersion(QLatin1String(name), QString()); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name))); - return true; - } - LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version)); - return false; -#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY -} - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -void gtkMessageHandler( - const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer unused_data) { - // Silence false-positive Gtk warnings (we are using Xlib to set - // the WM_TRANSIENT_FOR hint). - if (message != qstr("GtkDialog mapped without a transient parent. " - "This is discouraged.")) { - // For other messages, call the default handler. - g_log_default_handler(log_domain, log_level, message, unused_data); - } -} - -bool setupGtkBase(QLibrary &lib_gtk) { - if (!LOAD_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false; - - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_hide", gtk_widget_hide)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_get_window", gtk_widget_get_window)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_window_get_type", gtk_window_get_type)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_window_set_title", gtk_window_set_title)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_action", gtk_file_chooser_set_action)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_image_new", gtk_image_new)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false; - - if (!LOAD_SYMBOL(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_dialog_get_type", gtk_dialog_get_type)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_dialog_run", gtk_dialog_run)) return false; - - if (!LOAD_SYMBOL(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false; - - if (LOAD_SYMBOL(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) { - // We work only with Wayland and X11 GDK backends. - // Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call. - // See https://github.com/telegramdesktop/tdesktop/issues/3176 - // See https://github.com/telegramdesktop/tdesktop/issues/3162 - if(IsWayland()) { - DEBUG_LOG(("Limit allowed GDK backends to wayland,x11")); - gdk_set_allowed_backends("wayland,x11"); - } else { - DEBUG_LOG(("Limit allowed GDK backends to x11,wayland")); - gdk_set_allowed_backends("x11,wayland"); - } - } - - // gtk_init will reset the Xlib error handler, and that causes - // Qt applications to quit on X errors. Therefore, we need to manually restore it. - XErrorHandlerRestorer handlerRestorer; - - DEBUG_LOG(("Library gtk functions loaded!")); - gtkTriedToInit = true; - if (!gtk_init_check(0, 0)) { - gtk_init_check = nullptr; - DEBUG_LOG(("Failed to gtk_init_check(0, 0)!")); - return false; - } - DEBUG_LOG(("Checked gtk with gtk_init_check!")); - - // Use our custom log handler. - g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, gtkMessageHandler, nullptr); - - return true; -} - -bool IconThemeShouldBeSet() { - // change the icon theme only if it isn't already set by a platformtheme plugin - // if QT_QPA_PLATFORMTHEME=(gtk2|gtk3), then force-apply the icon theme - static const auto Result = - // QGenericUnixTheme - (QIcon::themeName() == qstr("hicolor") - && QIcon::fallbackThemeName() == qstr("hicolor")) - // QGnomeTheme - || (QIcon::themeName() == qstr("Adwaita") - && QIcon::fallbackThemeName() == qstr("gnome")) - // qt5ct - || (QIcon::themeName().isEmpty() - && QIcon::fallbackThemeName().isEmpty()) - || IsGtkIntegrationForced(); - - return Result; -} - -bool CursorSizeShouldBeSet() { - // change the cursor size only on Wayland and if it wasn't already set - static const auto Result = IsWayland() - && qEnvironmentVariableIsEmpty("XCURSOR_SIZE"); - - return Result; -} - -void SetIconTheme() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - if (GtkSettingSupported() - && GtkLoaded() - && IconThemeShouldBeSet()) { - DEBUG_LOG(("Setting GTK icon theme")); - QIcon::setThemeName(GtkSetting("gtk-icon-theme-name")); - QIcon::setFallbackThemeName(GtkSetting("gtk-fallback-icon-theme")); - - DEBUG_LOG(("New icon theme: %1").arg(QIcon::themeName())); - DEBUG_LOG(("New fallback icon theme: %1").arg(QIcon::fallbackThemeName())); - - SetApplicationIcon(Window::CreateIcon()); - if (App::wnd()) { - App::wnd()->setWindowIcon(Window::CreateIcon()); - } - - Core::App().domain().notifyUnreadBadgeChanged(); - } - }); -} - -void SetCursorSize() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - if (GtkSettingSupported() - && GtkLoaded() - && CursorSizeShouldBeSet()) { - DEBUG_LOG(("Setting GTK cursor size")); - - const auto newCursorSize = GtkSetting("gtk-cursor-theme-size"); - qputenv("XCURSOR_SIZE", QByteArray::number(newCursorSize)); - - DEBUG_LOG(("New cursor size: %1").arg(newCursorSize)); - } - }); -} - -void DarkModeChanged() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - Core::App().settings().setSystemDarkMode(IsDarkMode()); - }); -} - -void DecorationLayoutChanged() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - Core::App().settings().setWindowControlsLayout(WindowControlsLayout()); - }); -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -} // namespace - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -f_gtk_init_check gtk_init_check = nullptr; -f_gtk_check_version gtk_check_version = nullptr; -f_gtk_settings_get_default gtk_settings_get_default = nullptr; -f_gtk_widget_show gtk_widget_show = nullptr; -f_gtk_widget_hide gtk_widget_hide = nullptr; -f_gtk_widget_get_window gtk_widget_get_window = nullptr; -f_gtk_widget_realize gtk_widget_realize = nullptr; -f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete = nullptr; -f_gtk_widget_destroy gtk_widget_destroy = nullptr; -f_gtk_clipboard_get gtk_clipboard_get = nullptr; -f_gtk_clipboard_store gtk_clipboard_store = nullptr; -f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents = nullptr; -f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image = nullptr; -f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image = nullptr; -f_gtk_selection_data_free gtk_selection_data_free = nullptr; -f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new = nullptr; -f_gtk_file_chooser_get_type gtk_file_chooser_get_type = nullptr; -f_gtk_image_get_type gtk_image_get_type = nullptr; -f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder = nullptr; -f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder = nullptr; -f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name = nullptr; -f_gtk_file_chooser_select_filename gtk_file_chooser_select_filename = nullptr; -f_gtk_file_chooser_get_filenames gtk_file_chooser_get_filenames = nullptr; -f_gtk_file_chooser_set_filter gtk_file_chooser_set_filter = nullptr; -f_gtk_file_chooser_get_filter gtk_file_chooser_get_filter = nullptr; -f_gtk_window_get_type gtk_window_get_type = nullptr; -f_gtk_window_set_title gtk_window_set_title = nullptr; -f_gtk_file_chooser_set_local_only gtk_file_chooser_set_local_only = nullptr; -f_gtk_file_chooser_set_action gtk_file_chooser_set_action = nullptr; -f_gtk_file_chooser_set_select_multiple gtk_file_chooser_set_select_multiple = nullptr; -f_gtk_file_chooser_set_do_overwrite_confirmation gtk_file_chooser_set_do_overwrite_confirmation = nullptr; -f_gtk_file_chooser_remove_filter gtk_file_chooser_remove_filter = nullptr; -f_gtk_file_filter_set_name gtk_file_filter_set_name = nullptr; -f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern = nullptr; -f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter = nullptr; -f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget = nullptr; -f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename = nullptr; -f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active = nullptr; -f_gtk_file_filter_new gtk_file_filter_new = nullptr; -f_gtk_image_new gtk_image_new = nullptr; -f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf = nullptr; -f_gtk_dialog_get_widget_for_response gtk_dialog_get_widget_for_response = nullptr; -f_gtk_button_set_label gtk_button_set_label = nullptr; -f_gtk_button_get_type gtk_button_get_type = nullptr; -f_gtk_app_chooser_dialog_new gtk_app_chooser_dialog_new = nullptr; -f_gtk_app_chooser_get_app_info gtk_app_chooser_get_app_info = nullptr; -f_gtk_app_chooser_get_type gtk_app_chooser_get_type = nullptr; -f_gdk_set_allowed_backends gdk_set_allowed_backends = nullptr; -f_gdk_window_set_modal_hint gdk_window_set_modal_hint = nullptr; -f_gdk_window_focus gdk_window_focus = nullptr; -f_gtk_dialog_get_type gtk_dialog_get_type = nullptr; -f_gtk_dialog_run gtk_dialog_run = nullptr; -f_gdk_atom_intern gdk_atom_intern = nullptr; -f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size = nullptr; -f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha = nullptr; -f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels = nullptr; -f_gdk_pixbuf_get_width gdk_pixbuf_get_width = nullptr; -f_gdk_pixbuf_get_height gdk_pixbuf_get_height = nullptr; -f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride = nullptr; - -bool GtkLoaded() { - return gtkLoaded; -} - -::GtkClipboard *GtkClipboard() { - if (gtk_clipboard_get != nullptr) { - return gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - } - - return nullptr; -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -void start() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (!UseGtkIntegration()) { - return; - } - - DEBUG_LOG(("Loading libraries")); - - QLibrary lib_gtk; - lib_gtk.setLoadHints(QLibrary::DeepBindHint); - - if (loadLibrary(lib_gtk, "gtk-3", 0)) { - gtkLoaded = setupGtkBase(lib_gtk); - } - if (!gtkLoaded && !gtkTriedToInit && loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { - gtkLoaded = setupGtkBase(lib_gtk); - } - - if (gtkLoaded) { - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width); - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height); - LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); - - internal::GdkHelperLoad(lib_gtk); - - LOAD_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); - LOAD_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label); - LOAD_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type); - - LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); - LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); - LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type); - - SetIconTheme(); - SetCursorSize(); - - const auto settings = gtk_settings_get_default(); - g_signal_connect(settings, "notify::gtk-icon-theme-name", G_CALLBACK(SetIconTheme), nullptr); - g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(DarkModeChanged), nullptr); - g_signal_connect(settings, "notify::gtk-cursor-theme-size", G_CALLBACK(SetCursorSize), nullptr); - - if (!gtk_check_version(3, 0, 0)) { - g_signal_connect(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(DarkModeChanged), nullptr); - } - - if (!gtk_check_version(3, 12, 0)) { - g_signal_connect(settings, "notify::gtk-decoration-layout", G_CALLBACK(DecorationLayoutChanged), nullptr); - } - } else { - LOG(("Could not load gtk-3 or gtk-x11-2.0!")); - } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION -} - -} // namespace Libs -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.h b/Telegram/SourceFiles/platform/linux/linux_libs.h deleted file mode 100644 index b9661512b..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_libs.h +++ /dev/null @@ -1,303 +0,0 @@ -/* -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 - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - -extern "C" { -#undef signals -#include -#include -#define signals public -} // extern "C" - -// present starting with gtk 3.0, we can build with gtk2 headers -typedef struct _GtkAppChooser GtkAppChooser; - -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY -#define LOAD_SYMBOL(lib, name, func) (func = ::func) -#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY -#define LOAD_SYMBOL Platform::Libs::load -#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY - -namespace Platform { -namespace Libs { - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -bool GtkLoaded(); -::GtkClipboard *GtkClipboard(); -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -void start(); - -template -bool load(QLibrary &lib, const char *name, Function &func) { - func = nullptr; - if (!lib.isLoaded()) { - return false; - } - - func = reinterpret_cast(lib.resolve(name)); - if (func) { - return true; - } - LOG(("Error: failed to load '%1' function!").arg(name)); - return false; -} - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -typedef gboolean (*f_gtk_init_check)(int *argc, char ***argv); -extern f_gtk_init_check gtk_init_check; - -typedef const gchar* (*f_gtk_check_version)(guint required_major, guint required_minor, guint required_micro); -extern f_gtk_check_version gtk_check_version; - -typedef GtkSettings* (*f_gtk_settings_get_default)(void); -extern f_gtk_settings_get_default gtk_settings_get_default; - -typedef void (*f_gtk_widget_show)(GtkWidget *widget); -extern f_gtk_widget_show gtk_widget_show; - -typedef void (*f_gtk_widget_hide)(GtkWidget *widget); -extern f_gtk_widget_hide gtk_widget_hide; - -typedef GdkWindow* (*f_gtk_widget_get_window)(GtkWidget *widget); -extern f_gtk_widget_get_window gtk_widget_get_window; - -typedef void (*f_gtk_widget_realize)(GtkWidget *widget); -extern f_gtk_widget_realize gtk_widget_realize; - -typedef gboolean (*f_gtk_widget_hide_on_delete)(GtkWidget *widget); -extern f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete; - -typedef void (*f_gtk_widget_destroy)(GtkWidget *widget); -extern f_gtk_widget_destroy gtk_widget_destroy; - -typedef ::GtkClipboard* (*f_gtk_clipboard_get)(GdkAtom selection); -extern f_gtk_clipboard_get gtk_clipboard_get; - -typedef void (*f_gtk_clipboard_store)(::GtkClipboard *clipboard); -extern f_gtk_clipboard_store gtk_clipboard_store; - -typedef GtkSelectionData* (*f_gtk_clipboard_wait_for_contents)(::GtkClipboard *clipboard, GdkAtom target); -extern f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents; - -typedef GdkPixbuf* (*f_gtk_clipboard_wait_for_image)(::GtkClipboard *clipboard); -extern f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image; - -typedef gboolean (*f_gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable); -extern f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image; - -typedef void (*f_gtk_selection_data_free)(GtkSelectionData *data); -extern f_gtk_selection_data_free gtk_selection_data_free; - -typedef GtkWidget* (*f_gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED; -extern f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new; - -typedef gboolean (*f_gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename); -extern f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder; - -typedef gchar* (*f_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser); -extern f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder; - -typedef void (*f_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name); -extern f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name; - -typedef gboolean (*f_gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename); -extern f_gtk_file_chooser_select_filename gtk_file_chooser_select_filename; - -typedef GSList* (*f_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser); -extern f_gtk_file_chooser_get_filenames gtk_file_chooser_get_filenames; - -typedef void (*f_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter); -extern f_gtk_file_chooser_set_filter gtk_file_chooser_set_filter; - -typedef GtkFileFilter* (*f_gtk_file_chooser_get_filter)(GtkFileChooser *chooser); -extern f_gtk_file_chooser_get_filter gtk_file_chooser_get_filter; - -typedef void (*f_gtk_window_set_title)(GtkWindow *window, const gchar *title); -extern f_gtk_window_set_title gtk_window_set_title; - -typedef void (*f_gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only); -extern f_gtk_file_chooser_set_local_only gtk_file_chooser_set_local_only; - -typedef void (*f_gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action); -extern f_gtk_file_chooser_set_action gtk_file_chooser_set_action; - -typedef void (*f_gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple); -extern f_gtk_file_chooser_set_select_multiple gtk_file_chooser_set_select_multiple; - -typedef void (*f_gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation); -extern f_gtk_file_chooser_set_do_overwrite_confirmation gtk_file_chooser_set_do_overwrite_confirmation; - -typedef GtkWidget* (*f_gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id); -extern f_gtk_dialog_get_widget_for_response gtk_dialog_get_widget_for_response; - -typedef void (*f_gtk_button_set_label)(GtkButton *button, const gchar *label); -extern f_gtk_button_set_label gtk_button_set_label; - -typedef void (*f_gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter); -extern f_gtk_file_chooser_remove_filter gtk_file_chooser_remove_filter; - -typedef void (*f_gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name); -extern f_gtk_file_filter_set_name gtk_file_filter_set_name; - -typedef void (*f_gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern); -extern f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern; - -typedef void (*f_gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter); -extern f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter; - -typedef void (*f_gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget); -extern f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget; - -typedef gchar* (*f_gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser); -extern f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename; - -typedef void (*f_gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active); -extern f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active; - -typedef GtkFileFilter* (*f_gtk_file_filter_new)(void); -extern f_gtk_file_filter_new gtk_file_filter_new; - -typedef GtkWidget* (*f_gtk_image_new)(void); -extern f_gtk_image_new gtk_image_new; - -typedef void (*f_gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf); -extern f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf; - -typedef GtkWidget* (*f_gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file); -extern f_gtk_app_chooser_dialog_new gtk_app_chooser_dialog_new; - -typedef GAppInfo* (*f_gtk_app_chooser_get_app_info)(GtkAppChooser *self); -extern f_gtk_app_chooser_get_app_info gtk_app_chooser_get_app_info; - -typedef void (*f_gdk_set_allowed_backends)(const gchar *backends); -extern f_gdk_set_allowed_backends gdk_set_allowed_backends; - -typedef void (*f_gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal); -extern f_gdk_window_set_modal_hint gdk_window_set_modal_hint; - -typedef void (*f_gdk_window_focus)(GdkWindow *window, guint32 timestamp); -extern f_gdk_window_focus gdk_window_focus; - -template -inline Result *g_type_cic_helper(Object *instance, GType iface_type) { - return reinterpret_cast(g_type_check_instance_cast(reinterpret_cast(instance), iface_type)); -} - -typedef GType (*f_gtk_dialog_get_type)(void) G_GNUC_CONST; -extern f_gtk_dialog_get_type gtk_dialog_get_type; - -template -inline GtkDialog *gtk_dialog_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_dialog_get_type()); -} - -typedef GType (*f_gtk_file_chooser_get_type)(void) G_GNUC_CONST; -extern f_gtk_file_chooser_get_type gtk_file_chooser_get_type; - -template -inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_file_chooser_get_type()); -} - -typedef GType (*f_gtk_image_get_type)(void) G_GNUC_CONST; -extern f_gtk_image_get_type gtk_image_get_type; - -template -inline GtkImage *gtk_image_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_image_get_type()); -} - -typedef GType (*f_gtk_button_get_type)(void) G_GNUC_CONST; -extern f_gtk_button_get_type gtk_button_get_type; - -template -inline GtkButton *gtk_button_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_button_get_type()); -} - -typedef GType (*f_gtk_window_get_type)(void) G_GNUC_CONST; -extern f_gtk_window_get_type gtk_window_get_type; - -template -inline GtkWindow *gtk_window_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_window_get_type()); -} - -typedef GType (*f_gtk_app_chooser_get_type)(void) G_GNUC_CONST; -extern f_gtk_app_chooser_get_type gtk_app_chooser_get_type; - -template -inline GtkAppChooser *gtk_app_chooser_cast(Object *obj) { - return g_type_cic_helper(obj, gtk_app_chooser_get_type()); -} - -template -inline bool g_type_cit_helper(Object *instance, GType iface_type) { - if (!instance) return false; - - auto ginstance = reinterpret_cast(instance); - if (ginstance->g_class && ginstance->g_class->g_type == iface_type) { - return true; - } - return g_type_check_instance_is_a(ginstance, iface_type); -} - -typedef gint (*f_gtk_dialog_run)(GtkDialog *dialog); -extern f_gtk_dialog_run gtk_dialog_run; - -typedef GdkAtom (*f_gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists); -extern f_gdk_atom_intern gdk_atom_intern; - -typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error); -extern f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size; - -typedef gboolean (*f_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf); -extern f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha; - -typedef guchar* (*f_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf); -extern f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels; - -typedef int (*f_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf); -extern f_gdk_pixbuf_get_width gdk_pixbuf_get_width; - -typedef int (*f_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf); -extern f_gdk_pixbuf_get_height gdk_pixbuf_get_height; - -typedef int (*f_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf); -extern f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride; - -inline bool GtkSettingSupported() { - return gtk_settings_get_default != nullptr; -} - -template -inline T GtkSetting(const gchar *propertyName) { - GtkSettings *settings = gtk_settings_get_default(); - T value; - g_object_get(settings, propertyName, &value, nullptr); - return value; -} - -inline QString GtkSetting(const gchar *propertyName) { - gchararray value = GtkSetting(propertyName); - QString str = QString::fromUtf8(value); - g_free(value); - DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str)); - return str; -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - -} // namespace Libs -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp new file mode 100644 index 000000000..63ffba923 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp @@ -0,0 +1,132 @@ +/* +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 "platform/linux/linux_open_with_dialog.h" + +#include "platform/linux/linux_gtk_integration_p.h" +#include "platform/linux/linux_gdk_helper.h" +#include "window/window_controller.h" +#include "core/application.h" + +#include + +namespace Platform { +namespace File { +namespace internal { +namespace { + +using namespace Platform::Gtk; + +bool OpenWithDialogSupported() { + return Platform::internal::GdkHelperLoaded() + && (gtk_app_chooser_dialog_new != nullptr) + && (gtk_app_chooser_get_app_info != nullptr) + && (gtk_app_chooser_get_type != nullptr) + && (gtk_widget_get_window != nullptr) + && (gtk_widget_realize != nullptr) + && (gtk_widget_show != nullptr) + && (gtk_widget_destroy != nullptr); +} + +class OpenWithDialog : public QWindow { +public: + OpenWithDialog(const QString &filepath); + ~OpenWithDialog(); + + bool exec(); + +private: + static void handleResponse(OpenWithDialog *dialog, int responseId); + + GFile *_gfileInstance = nullptr; + GtkWidget *_gtkWidget = nullptr; + QEventLoop _loop; + std::optional _result; +}; + +OpenWithDialog::OpenWithDialog(const QString &filepath) +: _gfileInstance(g_file_new_for_path(filepath.toUtf8())) +, _gtkWidget(gtk_app_chooser_dialog_new( + nullptr, + GTK_DIALOG_MODAL, + _gfileInstance)) { + g_signal_connect_swapped( + _gtkWidget, + "response", + G_CALLBACK(handleResponse), + this); +} + +OpenWithDialog::~OpenWithDialog() { + gtk_widget_destroy(_gtkWidget); + g_object_unref(_gfileInstance); +} + +bool OpenWithDialog::exec() { + gtk_widget_realize(_gtkWidget); + + if (const auto activeWindow = Core::App().activeWindow()) { + Platform::internal::XSetTransientForHint( + gtk_widget_get_window(_gtkWidget), + activeWindow->widget().get()->windowHandle()->winId()); + } + + QGuiApplicationPrivate::showModalWindow(this); + gtk_widget_show(_gtkWidget); + + if (!_result.has_value()) { + _loop.exec(); + } + + QGuiApplicationPrivate::hideModalWindow(this); + return *_result; +} + +void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) { + GAppInfo *chosenAppInfo = nullptr; + dialog->_result = true; + + switch (responseId) { + case GTK_RESPONSE_OK: + chosenAppInfo = gtk_app_chooser_get_app_info( + gtk_app_chooser_cast(dialog->_gtkWidget)); + + if (chosenAppInfo) { + GList *uris = nullptr; + uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance)); + g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr); + g_list_free(uris); + g_object_unref(chosenAppInfo); + } + + break; + + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + break; + + default: + dialog->_result = false; + break; + } + + dialog->_loop.quit(); +} + +} // namespace + +bool ShowOpenWithDialog(const QString &filepath) { + if (!OpenWithDialogSupported()) { + return false; + } + + return OpenWithDialog(filepath).exec(); +} + +} // namespace internal +} // namespace File +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h new file mode 100644 index 000000000..3a29f6b09 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h @@ -0,0 +1,18 @@ +/* +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 + +namespace Platform { +namespace File { +namespace internal { + +bool ShowOpenWithDialog(const QString &filepath); + +} // namespace internal +} // namespace File +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp index 3396f1763..270f0ae8f 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_xlib_helper.h" -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION extern "C" { #include } @@ -37,4 +36,3 @@ XErrorHandlerRestorer::~XErrorHandlerRestorer() = default; } // namespace internal } // namespace Platform -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h index def4c9bba..abd172066 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h +++ b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION namespace Platform { namespace internal { @@ -23,4 +22,3 @@ private: } // namespace internal } // namespace Platform -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index e25200c24..80820c163 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -522,6 +522,11 @@ void ForceDisabled(QAction *action, bool disabled) { MainWindow::MainWindow(not_null controller) : Window::MainWindow(controller) { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } void MainWindow::initHook() { @@ -911,14 +916,6 @@ void MainWindow::updateWaylandDecorationColors() { windowHandle()->resize(windowHandle()->size()); } -void MainWindow::LibsLoaded() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION -} - void MainWindow::initTrayMenuHook() { _trayIconMenuXEmbed.emplace(nullptr, trayIconMenu); _trayIconMenuXEmbed->deleteOnHide(false); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 62ac1e2fc..ee7ecbee5 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -46,8 +46,6 @@ public: bool isActiveForTrayMenu() override; - static void LibsLoaded(); - ~MainWindow(); protected: diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 3ba1f7994..578dbba84 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/specific_linux.h" -#include "platform/linux/linux_libs.h" +#include "platform/linux/linux_gtk_integration.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" #include "base/qt_adapters.h" @@ -68,11 +68,11 @@ extern "C" { using namespace Platform; using Platform::File::internal::EscapeShell; using Platform::internal::WaylandIntegration; +using Platform::internal::GtkIntegration; namespace Platform { namespace { -constexpr auto kDisableGtkIntegration = "TDESKTOP_DISABLE_GTK_INTEGRATION"_cs; constexpr auto kIgnoreGtkIncompatibility = "TDESKTOP_I_KNOW_ABOUT_GTK_INCOMPATIBILITY"_cs; constexpr auto kDesktopFile = ":/misc/telegramdesktop.desktop"_cs; @@ -332,21 +332,6 @@ bool GenerateDesktopFile( } } -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -bool GetImageFromClipboardSupported() { - return (Libs::gtk_clipboard_wait_for_contents != nullptr) - && (Libs::gtk_clipboard_wait_for_image != nullptr) - && (Libs::gtk_selection_data_targets_include_image != nullptr) - && (Libs::gtk_selection_data_free != nullptr) - && (Libs::gdk_pixbuf_get_pixels != nullptr) - && (Libs::gdk_pixbuf_get_width != nullptr) - && (Libs::gdk_pixbuf_get_height != nullptr) - && (Libs::gdk_pixbuf_get_rowstride != nullptr) - && (Libs::gdk_pixbuf_get_has_alpha != nullptr) - && (Libs::gdk_atom_intern != nullptr); -} -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - uint XCBMoveResizeFromEdges(Qt::Edges edges) { if (edges == (Qt::TopEdge | Qt::LeftEdge)) return 0; @@ -564,28 +549,17 @@ bool IsStaticBinary() { #endif // !DESKTOP_APP_USE_PACKAGED } -bool UseGtkIntegration() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - static const auto Result = !qEnvironmentVariableIsSet( - kDisableGtkIntegration.utf8()); - - return Result; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - - return false; -} - bool IsGtkIntegrationForced() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION static const auto Result = [&] { + if (!GtkIntegration::Instance()) { + return false; + } + return PlatformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive) || PlatformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive); }(); return Result; -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - - return false; } bool AreQtPluginsBundled() { @@ -723,64 +697,38 @@ QString GetIconName() { } QImage GetImageFromClipboard() { - QImage data; - -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (!GetImageFromClipboardSupported() || !Libs::GtkClipboard()) { - return data; + if (const auto integration = GtkIntegration::Instance()) { + return integration->getImageFromClipboard(); } - auto gsel = Libs::gtk_clipboard_wait_for_contents( - Libs::GtkClipboard(), - Libs::gdk_atom_intern("TARGETS", true)); - - if (gsel) { - if (Libs::gtk_selection_data_targets_include_image(gsel, false)) { - auto img = Libs::gtk_clipboard_wait_for_image( - Libs::GtkClipboard()); - - if (img) { - data = QImage( - Libs::gdk_pixbuf_get_pixels(img), - Libs::gdk_pixbuf_get_width(img), - Libs::gdk_pixbuf_get_height(img), - Libs::gdk_pixbuf_get_rowstride(img), - Libs::gdk_pixbuf_get_has_alpha(img) - ? QImage::Format_RGBA8888 - : QImage::Format_RGB888).copy(); - - g_object_unref(img); - } - } - - Libs::gtk_selection_data_free(gsel); - } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - - return data; + return {}; } std::optional IsDarkMode() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (Libs::GtkSettingSupported() && Libs::GtkLoaded()) { - if (Libs::gtk_check_version != nullptr - && !Libs::gtk_check_version(3, 0, 0) - && Libs::GtkSetting( - "gtk-application-prefer-dark-theme")) { - return true; - } - - const auto themeName = Libs::GtkSetting("gtk-theme-name").toLower(); - - if (themeName.contains(qsl("-dark"))) { - return true; - } - - return false; + const auto integration = GtkIntegration::Instance(); + if (!integration) { + return std::nullopt; } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION - return std::nullopt; + if (integration->checkVersion(3, 0, 0)) { + const auto preferDarkTheme = integration->getBoolSetting( + qsl("gtk-application-prefer-dark-theme")); + + if (!preferDarkTheme.has_value()) { + return std::nullopt; + } else if (*preferDarkTheme) { + return true; + } + } + + const auto themeName = integration->getStringSetting(qsl("gtk-theme-name")); + if (!themeName.has_value()) { + return std::nullopt; + } else if (themeName->toLower().contains(qsl("-dark"))) { + return true; + } + + return false; } bool AutostartSupported() { @@ -848,38 +796,44 @@ bool WindowsNeedShadow() { } Window::ControlsLayout WindowControlsLayout() { -#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (Libs::GtkSettingSupported() - && Libs::GtkLoaded() - && Libs::gtk_check_version != nullptr - && !Libs::gtk_check_version(3, 12, 0)) { - const auto decorationLayout = Libs::GtkSetting( - "gtk-decoration-layout").split(':'); + const auto gtkResult = []() -> std::optional { + const auto integration = GtkIntegration::Instance(); + if (!integration || !integration->checkVersion(3, 12, 0)) { + return std::nullopt; + } + + const auto decorationLayoutSetting = integration->getStringSetting( + qsl("gtk-decoration-layout")); + + if (!decorationLayoutSetting.has_value()) { + return std::nullopt; + } + + const auto decorationLayout = decorationLayoutSetting->split(':'); std::vector controlsLeft; ranges::transform( decorationLayout[0].split(','), ranges::back_inserter(controlsLeft), - GtkKeywordToWindowControl - ); + GtkKeywordToWindowControl); std::vector controlsRight; if (decorationLayout.size() > 1) { ranges::transform( decorationLayout[1].split(','), ranges::back_inserter(controlsRight), - GtkKeywordToWindowControl - ); + GtkKeywordToWindowControl); } return Window::ControlsLayout{ .left = controlsLeft, .right = controlsRight }; - } -#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION + }(); - if (DesktopEnvironment::IsUnity()) { + if (gtkResult.has_value()) { + return *gtkResult; + } else if (DesktopEnvironment::IsUnity()) { return Window::ControlsLayout{ .left = { Window::Control::Close, @@ -983,7 +937,7 @@ void start() { // if gtk integration and qgtk3/qgtk2 platformtheme (or qgtk2 style) // is used at the same time, the app will crash - if (UseGtkIntegration() + if (GtkIntegration::Instance() && !IsStaticBinary() && !qEnvironmentVariableIsSet( kIgnoreGtkIncompatibility.utf8())) { @@ -1004,7 +958,7 @@ void start() { "Keep in mind that this will lead to clipboard issues " "and tdesktop will be unable to get settings from GTK " "(such as decoration layout, dark mode & more).", - kDisableGtkIntegration.utf8().constData()); + internal::kDisableGtkIntegration.utf8().constData()); qunsetenv("QT_QPA_PLATFORMTHEME"); qunsetenv("QT_STYLE_OVERRIDE"); @@ -1015,7 +969,7 @@ void start() { } } - if (!UseGtkIntegration()) { + if (!GtkIntegration::Instance()) { g_warning( "GTK integration was disabled on build or in runtime. " "This will lead to clipboard issues and a lack of some features " @@ -1240,8 +1194,9 @@ void start() { DEBUG_LOG(("Icon theme: %1").arg(QIcon::themeName())); DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName())); - Libs::start(); - MainWindow::LibsLoaded(); + if (const auto integration = GtkIntegration::Instance()) { + return integration->load(); + } // wait for interface announce to know if native window frame is supported if (const auto waylandIntegration = WaylandIntegration::Instance()) { diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 2f279c5d4..39b91baef 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -21,7 +21,6 @@ bool InFlatpak(); bool InSnap(); bool IsStaticBinary(); bool AreQtPluginsBundled(); -bool UseGtkIntegration(); bool IsGtkIntegrationForced(); bool UseXDGDesktopPortal(); bool CanOpenDirectoryWithPortal(); From 5e60b87cf9dfb65d08038f32bed0ace4a5f51dc3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 07:52:18 +0400 Subject: [PATCH 103/396] Remove platform_specific.h include from mainwindow.h In order to avoid mass rebuilds on specific_*.h changing --- Telegram/SourceFiles/core/sandbox.cpp | 1 + Telegram/SourceFiles/core/shortcuts.cpp | 1 + Telegram/SourceFiles/mainwindow.h | 1 - Telegram/SourceFiles/platform/mac/main_window_mac.mm | 1 + Telegram/SourceFiles/platform/win/main_window_win.cpp | 1 + Telegram/SourceFiles/settings/settings_chat.cpp | 1 + Telegram/SourceFiles/settings/settings_notifications.cpp | 1 + Telegram/SourceFiles/window/main_window.cpp | 1 + Telegram/SourceFiles/window/notifications_manager_default.cpp | 1 + Telegram/SourceFiles/window/themes/window_theme.cpp | 1 + 10 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index ab441b564..bbf608b4f 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/sandbox.h" #include "base/platform/base_platform_info.h" +#include "platform/platform_specific.h" #include "mainwidget.h" #include "mainwindow.h" #include "storage/localstorage.h" diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp index 4e988bd13..b59887976 100644 --- a/Telegram/SourceFiles/core/shortcuts.cpp +++ b/Telegram/SourceFiles/core/shortcuts.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "media/player/media_player_instance.h" #include "base/platform/base_platform_info.h" +#include "platform/platform_specific.h" #include "base/parse_helper.h" #include "facades.h" diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 38bf87dd8..6e2a05fa6 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "platform/platform_specific.h" #include "platform/platform_main_window.h" #include "base/unique_qptr.h" #include "ui/layers/layer_widget.h" diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 9df1eeb3c..08630c158 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/themes/window_theme.h" #include "platform/mac/touchbar/mac_touchbar_manager.h" +#include "platform/platform_specific.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" #include "boxes/peer_list_controllers.h" diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 445f407c7..c93530ba4 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/win/main_window_win.h" #include "styles/style_window.h" +#include "platform/platform_specific.h" #include "platform/platform_notifications_manager.h" #include "platform/win/windows_dlls.h" #include "platform/win/windows_event_filter.h" diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index ac92f78cb..f1d653504 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "chat_helpers/emoji_sets_manager.h" #include "base/platform/base_platform_info.h" +#include "platform/platform_specific.h" #include "base/call_delayed.h" #include "support/support_common.h" #include "support/support_templates.h" diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 95bb96958..9c2981c1d 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "window/notifications_manager.h" #include "window/window_session_controller.h" +#include "platform/platform_specific.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 7740d2ce8..4760636a9 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/main_window.h" #include "storage/localstorage.h" +#include "platform/platform_specific.h" #include "platform/platform_window_title.h" #include "base/platform/base_platform_info.h" #include "ui/platform/ui_platform_utility.h" diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 27d534b4f..b923e5d3a 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_manager_default.h" #include "platform/platform_notifications_manager.h" +#include "platform/platform_specific.h" #include "core/application.h" #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index c90ba42f9..6e7543734 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_themes_embedded.h" #include "window/themes/window_theme_editor.h" #include "window/window_controller.h" +#include "platform/platform_specific.h" #include "mainwidget.h" #include "main/main_session.h" #include "apiwrap.h" From 36b6f7061341a0be4c8a3a901e3ba9de340fe451 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 08:53:11 +0400 Subject: [PATCH 104/396] Get rid of unneeded includes in specific_linux --- .../platform/linux/linux_desktop_environment.cpp | 1 - .../SourceFiles/platform/linux/specific_linux.cpp | 11 ++++------- Telegram/SourceFiles/platform/linux/specific_linux.h | 2 -- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp index 9a74925e0..bc9ce7293 100644 --- a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_desktop_environment.h" -#include "platform/linux/specific_linux.h" #include "base/qt_adapters.h" namespace Platform { diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 578dbba84..681c56f98 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -7,19 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/specific_linux.h" -#include "platform/linux/linux_gtk_integration.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" -#include "base/qt_adapters.h" -#include "lang/lang_keys.h" -#include "mainwidget.h" -#include "mainwindow.h" #include "platform/linux/linux_desktop_environment.h" #include "platform/linux/file_utilities_linux.h" +#include "platform/linux/linux_gtk_integration.h" #include "platform/linux/linux_wayland_integration.h" -#include "platform/platform_notifications_manager.h" +#include "base/qt_adapters.h" +#include "lang/lang_keys.h" +#include "mainwindow.h" #include "storage/localstorage.h" -#include "core/crash_reports.h" #include "core/update_checker.h" #include "window/window_controller.h" #include "core/application.h" diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 39b91baef..8b87b8997 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" -#include - namespace Data { class LocationPoint; } // namespace Data From ea9813825dbfe88947677d3e3188287adbfa6f6b Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 09:02:21 +0400 Subject: [PATCH 105/396] Move EscapeShell to specific_linux --- .../platform/linux/file_utilities_linux.cpp | 29 ------------------- .../platform/linux/file_utilities_linux.h | 5 ---- .../platform/linux/specific_linux.cpp | 28 ++++++++++++++++-- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index fd1bae21d..d13b80488 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -22,35 +22,6 @@ using Platform::internal::GtkIntegration; namespace Platform { namespace File { -namespace internal { - -QByteArray EscapeShell(const QByteArray &content) { - auto result = QByteArray(); - - auto b = content.constData(), e = content.constEnd(); - for (auto ch = b; ch != e; ++ch) { - if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') { - if (result.isEmpty()) { - result.reserve(content.size() * 2); - } - if (ch > b) { - result.append(b, ch - b); - } - result.append('\\'); - b = ch; - } - } - if (result.isEmpty()) { - return content; - } - - if (e > b) { - result.append(b, e - b); - } - return result; -} - -} // namespace internal void UnsafeOpenUrl(const QString &url) { if (!g_app_info_launch_default_for_uri( diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h index 50c6d6a6d..00008a8ca 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h @@ -11,11 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace File { -namespace internal { - -QByteArray EscapeShell(const QByteArray &content); - -} // namespace internal inline QString UrlToLocal(const QUrl &url) { return ::File::internal::UrlToLocalDefault(url); diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 681c56f98..4f3cc3542 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" #include "platform/linux/linux_desktop_environment.h" -#include "platform/linux/file_utilities_linux.h" #include "platform/linux/linux_gtk_integration.h" #include "platform/linux/linux_wayland_integration.h" #include "base/qt_adapters.h" @@ -63,7 +62,6 @@ extern "C" { #include using namespace Platform; -using Platform::File::internal::EscapeShell; using Platform::internal::WaylandIntegration; using Platform::internal::GtkIntegration; @@ -228,6 +226,32 @@ uint FileChooserPortalVersion() { } #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION +QByteArray EscapeShell(const QByteArray &content) { + auto result = QByteArray(); + + auto b = content.constData(), e = content.constEnd(); + for (auto ch = b; ch != e; ++ch) { + if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') { + if (result.isEmpty()) { + result.reserve(content.size() * 2); + } + if (ch > b) { + result.append(b, ch - b); + } + result.append('\\'); + b = ch; + } + } + if (result.isEmpty()) { + return content; + } + + if (e > b) { + result.append(b, e - b); + } + return result; +} + QString EscapeShellInLauncher(const QString &content) { return EscapeShell(content.toUtf8()).replace('\\', "\\\\"); } From f1e0b36f6164a976f75872e61775f1f691e80199 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 16:27:12 +0400 Subject: [PATCH 106/396] Use operator-> for tray icon biggest size --- Telegram/SourceFiles/platform/linux/main_window_linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 80820c163..9bdec9036 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -257,7 +257,7 @@ QIcon TrayIconGen(int counter, bool muted) { std::less<>(), &QSize::width); - if ((*biggestSize).width() > firstAttemptSize.width()) { + if (biggestSize->width() > firstAttemptSize.width()) { currentImageBack = systemIcon .pixmap(*biggestSize) .toImage(); @@ -388,7 +388,7 @@ std::unique_ptr TrayIconFile( std::less<>(), &QSize::width); - if ((*biggestSize).width() > firstAttemptSize.width()) { + if (biggestSize->width() > firstAttemptSize.width()) { scalePixmap(icon.pixmap(*biggestSize)).save(ret.get()); } else { scalePixmap(firstAttempt).save(ret.get()); From 5cb081ca9a07d5d2664a2997f1cb1683ec3ffe60 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 Jan 2021 18:32:03 +0400 Subject: [PATCH 107/396] Fix build without dbus --- .../platform/linux/notifications_manager_linux_dummy.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp index a078b852f..3e11353d9 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/notifications_manager_linux.h" +#include "base/platform/base_platform_info.h" + namespace Platform { namespace Notifications { From d5cdb5582b65f934b510dd8a22847a9fc4fb0e11 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 18 Jan 2021 11:50:19 +0400 Subject: [PATCH 108/396] Add config for no-response bot --- .github/no-response.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/no-response.yml diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 000000000..2c2b9cdbd --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,11 @@ +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 30 +# Label requiring a response +responseRequiredLabel: waiting for answer +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. From 655731741c7732bb320072fa2c48180de0fa7198 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 18 Jan 2021 12:03:02 +0400 Subject: [PATCH 109/396] Add config for lock bot --- .github/lock.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/lock.yml diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..645802fc1 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,21 @@ +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 45 + +# Skip issues and pull requests created before a given timestamp. Timestamp must +# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable +skipCreatedBefore: false + +# Issues and pull requests with these labels will be ignored. Set to `[]` to disable +exemptLabels: [] + +# Label to add before locking, such as `outdated`. Set to `false` to disable +lockLabel: false + +# Comment to post before locking. Set to `false` to disable +lockComment: > + This thread has been automatically locked since there has not been + any recent activity after it was closed. Please open a new issue for + related bugs. + +# Assign `resolved` as the reason for locking. Set to `false` to disable +setLockReason: true From c698327b2493db127241b03e7c4da37e18d9ecc2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Jan 2021 17:42:06 +0400 Subject: [PATCH 110/396] Update tg_owt commit in Snap. --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 94343903e..faea78c5e 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -253,7 +253,7 @@ parts: webrtc: source: https://github.com/desktop-app/tg_owt.git source-depth: 1 - source-commit: d91d618889cc743aafd809151449012de62e5327 + source-commit: ace538a7dfe0fe5fc735f258ba49e7420a5d10b0 plugin: cmake build-packages: - yasm From 606f5377d535cf6faa1c2576dc77733eecd4c19f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Jan 2021 18:08:49 +0400 Subject: [PATCH 111/396] Cherry-pick fix for Pulseaudio OpenAL backend. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- snap/snapcraft.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 91944c82e..8b6723ba0 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -401,7 +401,7 @@ RUN make -j$(nproc) RUN make DESTDIR="$LibrariesPath/ffmpeg-cache" install FROM builder AS openal -RUN git clone -b openal-soft-1.21.0 --depth=1 $GIT/kcat/openal-soft.git +RUN git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git WORKDIR openal-soft RUN cmake3 -B build . \ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index faea78c5e..b6c6afc66 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -214,9 +214,9 @@ parts: - -./usr/share openal: - source: https://github.com/kcat/openal-soft.git + source: https://github.com/telegramdesktop/openal-soft.git source-depth: 1 - source-tag: openal-soft-1.21.0 + source-branch: fix_pulse_default plugin: cmake build-packages: - libasound2-dev From f90e13f8b1f52d9a0ae66a8d652eb345eee7cd70 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Jan 2021 18:19:27 +0400 Subject: [PATCH 112/396] Fix crash after account reset after QR login. --- Telegram/SourceFiles/intro/intro_widget.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/intro/intro_widget.cpp b/Telegram/SourceFiles/intro/intro_widget.cpp index 05777d2d1..0d2afd9b7 100644 --- a/Telegram/SourceFiles/intro/intro_widget.cpp +++ b/Telegram/SourceFiles/intro/intro_widget.cpp @@ -478,10 +478,17 @@ void Widget::resetAccount() { _resetRequest = 0; Ui::hideLayer(); - moveToStep( - new SignupWidget(this, _account, getData()), - StackAction::Replace, - Animate::Forward); + if (getData()->phone.isEmpty()) { + moveToStep( + new QrWidget(this, _account, getData()), + StackAction::Replace, + Animate::Back); + } else { + moveToStep( + new SignupWidget(this, _account, getData()), + StackAction::Replace, + Animate::Forward); + } }).fail([=](const RPCError &error) { _resetRequest = 0; From 24f2ca744348ea11401671252e9a8393a5199473 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Jan 2021 19:00:11 +0400 Subject: [PATCH 113/396] Beta version 2.5.6. - Press Up arrow to edit your last sent comment. - Add more information to date tooltips in Recent Actions and channel comments. - Bug and crash fixes. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 9 +++++++++ Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 6 ++++++ 7 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 9dc2b015b..eabfd46fd 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.5.6.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index ae2932ce5..1ec6edc21 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,5,0 - PRODUCTVERSION 2,5,5,0 + FILEVERSION 2,5,6,0 + PRODUCTVERSION 2,5,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.5.0" + VALUE "FileVersion", "2.5.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.5.0" + VALUE "ProductVersion", "2.5.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index b60403993..f92532279 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,5,0 - PRODUCTVERSION 2,5,5,0 + FILEVERSION 2,5,6,0 + PRODUCTVERSION 2,5,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.5.0" + VALUE "FileVersion", "2.5.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.5.0" + VALUE "ProductVersion", "2.5.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 7900cf6ed..52d1c8d4c 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -105,6 +105,15 @@ std::map BetaLogs() { "- Fix media viewer zoom and crashing.\n" }, + { + 2005006, + "- Press Up arrow to edit your last sent comment.\n" + + "- Add more information to date tooltips " + "in Recent Actions and channel comments.\n" + + "- Bug and crash fixes.\n" + } }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 809e8a840..a94af807e 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005005; -constexpr auto AppVersionStr = "2.5.5"; +constexpr auto AppVersion = 2005006; +constexpr auto AppVersionStr = "2.5.6"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 606bbc64c..667a3ae8e 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005005 +AppVersion 2005006 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.5 -AppVersionStr 2.5.5 +AppVersionStrSmall 2.5.6 +AppVersionStr 2.5.6 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 2.5.5.beta +AppVersionOriginal 2.5.6.beta diff --git a/changelog.txt b/changelog.txt index c4bded047..002eaba88 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +2.5.6 beta (22.01.21) + +- Press Up arrow to edit your last sent comment. +- Add more information to date tooltips in Recent Actions and channel comments. +- Bug and crash fixes. + 2.5.5 beta (10.01.21) - Fix recording of audio in voice chats. From 89ccc95023aa194ff324c881c7d1511585537cbc Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 23 Jan 2021 04:20:07 +0400 Subject: [PATCH 114/396] Fix early return from Platform::ThirdParty::start on Linux --- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 4f3cc3542..f70ef0b94 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -1216,7 +1216,7 @@ void start() { DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName())); if (const auto integration = GtkIntegration::Instance()) { - return integration->load(); + integration->load(); } // wait for interface announce to know if native window frame is supported From 3967052375e616f8bceb4fe999306dcb94dfa736 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 23 Jan 2021 04:22:40 +0400 Subject: [PATCH 115/396] Get scale factor from GTK on Linux --- .../platform/linux/linux_gtk_integration.cpp | 40 +++++++++++++++++++ .../platform/linux/linux_gtk_integration.h | 2 + .../linux/linux_gtk_integration_dummy.cpp | 4 ++ .../platform/linux/linux_gtk_integration_p.h | 4 ++ 4 files changed, 50 insertions(+) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index a5ae6dac0..9bf04761a 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gtk_integration_p.h" #include "base/platform/base_platform_info.h" +#include "platform/linux/linux_desktop_environment.h" #include "platform/linux/linux_xlib_helper.h" #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_gtk_file_dialog.h" @@ -207,6 +208,28 @@ bool CursorSizeShouldBeSet() { return Result; } +void SetScaleFactor() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + const auto integration = GtkIntegration::Instance(); + if (!integration || !DesktopEnvironment::IsGtkBased()) { + return; + } + + const auto scaleFactor = integration->scaleFactor(); + if (!scaleFactor.has_value()) { + return; + } + + LOG(("GTK scale factor: %1").arg(*scaleFactor)); + + const int scale = *scaleFactor + * 100 + / Core::Sandbox::Instance().devicePixelRatio(); + + cSetScreenScale(std::clamp(scale, 100, 300)); + }); +} + void SetIconTheme() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); @@ -312,6 +335,10 @@ void GtkIntegration::load() { } if (GtkLoaded) { + LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_default", gdk_display_get_default); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); @@ -329,6 +356,7 @@ void GtkIntegration::load() { LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type); + SetScaleFactor(); SetIconTheme(); SetCursorSize(); @@ -417,6 +445,18 @@ std::optional GtkIntegration::getStringSetting( return str; } +std::optional GtkIntegration::scaleFactor() const { + if (!loaded() + || (gdk_display_get_default == nullptr) + || (gdk_display_get_primary_monitor == nullptr) + || (gdk_monitor_get_scale_factor == nullptr)) { + return std::nullopt; + } + + return gdk_monitor_get_scale_factor( + gdk_display_get_primary_monitor(gdk_display_get_default())); +} + bool GtkIntegration::fileDialogSupported() const { return FileDialog::Gtk::Supported(); } diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h index df393befa..25774161b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h @@ -34,6 +34,8 @@ public: [[nodiscard]] std::optional getStringSetting( const QString &propertyName) const; + [[nodiscard]] std::optional scaleFactor() const; + using FileDialogType = ::FileDialog::internal::Type; [[nodiscard]] bool fileDialogSupported() const; [[nodiscard]] bool useFileDialog( diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp index 00df2f046..59225e947 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp @@ -43,6 +43,10 @@ std::optional GtkIntegration::getStringSetting( return std::nullopt; } +std::optional GtkIntegration::scaleFactor() const { + return std::nullopt; +} + bool GtkIntegration::fileDialogSupported() const { return false; } diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h index 301a828e5..99d3579dd 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h @@ -27,6 +27,7 @@ extern "C" { #endif // !LINK_TO_GTK // To be able to compile with gtk-2.0 headers as well +#define GdkMonitor GdkScreen typedef struct _GtkAppChooser GtkAppChooser; namespace Platform { @@ -147,6 +148,9 @@ inline bool g_type_cit_helper(Object *instance, GType iface_type) { inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr; inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr; +inline GdkDisplay* (*gdk_display_get_default)(void) = nullptr; +inline GdkMonitor* (*gdk_display_get_primary_monitor)(GdkDisplay *display) = nullptr; +inline int (*gdk_monitor_get_scale_factor)(GdkMonitor *monitor) = nullptr; inline GdkPixbuf* (*gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error) = nullptr; inline gboolean (*gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf) = nullptr; inline guchar* (*gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf) = nullptr; From 6374d4eedab93b84e3f7f146f5f1bea954fd958c Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 23 Jan 2021 16:03:24 +0400 Subject: [PATCH 116/396] Some cosmetic changes in settigs setters --- .../SourceFiles/platform/linux/linux_gtk_integration.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 9bf04761a..2d677e441 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -233,9 +233,7 @@ void SetScaleFactor() { void SetIconTheme() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); - - if (!integration - || !IconThemeShouldBeSet()) { + if (!integration || !IconThemeShouldBeSet()) { return; } @@ -270,9 +268,7 @@ void SetIconTheme() { void SetCursorSize() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); - - if (!integration - || !CursorSizeShouldBeSet()) { + if (!integration || !CursorSizeShouldBeSet()) { return; } From b919a0627a29ce82208367de427f5f2a1f200286 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 23 Jan 2021 04:39:58 +0400 Subject: [PATCH 117/396] Ensure GtkIntegration::load() is called only once --- Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 2d677e441..8367a07f5 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -316,6 +316,7 @@ GtkIntegration *GtkIntegration::Instance() { } void GtkIntegration::load() { + Expects(!GtkLoaded); DEBUG_LOG(("Loading GTK")); QLibrary lib_gtk; From 0d1b77861222b8265bd26237cf43886ff34b5fc7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Jan 2021 16:14:37 +0400 Subject: [PATCH 118/396] Beta version 2.5.6: Fix build on macOS. --- Telegram/SourceFiles/platform/mac/specific_mac_p.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 5fdf9397a..c0a00c48f 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "base/timer.h" #include "styles/style_window.h" +#include "platform/platform_specific.h" #include #include From 3793f7c3c911df36d17186d9f1da7fef5a6b3fcb Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 23 Jan 2021 20:30:48 +0400 Subject: [PATCH 119/396] Update tg_owt commit in snap build. --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index b6c6afc66..c4c1f1f12 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -253,7 +253,7 @@ parts: webrtc: source: https://github.com/desktop-app/tg_owt.git source-depth: 1 - source-commit: ace538a7dfe0fe5fc735f258ba49e7420a5d10b0 + source-commit: 429ec57ecfecb9f7ddde71665adff89158fc2f22 plugin: cmake build-packages: - yasm From 8ed56bb4e4d9783154d05b8110082ee9272ddc18 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 23 Jan 2021 21:07:32 +0400 Subject: [PATCH 120/396] Don't mess GTK scale factor with other scaling settings Have this order for scaling settings: 1. devicePixelRatio 2. GTK 3. DPI --- .../platform/linux/linux_gtk_integration.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 8367a07f5..964dff0f1 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gtk_integration_p.h" #include "base/platform/base_platform_info.h" -#include "platform/linux/linux_desktop_environment.h" #include "platform/linux/linux_xlib_helper.h" #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_gtk_file_dialog.h" @@ -211,22 +210,18 @@ bool CursorSizeShouldBeSet() { void SetScaleFactor() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); - if (!integration || !DesktopEnvironment::IsGtkBased()) { + const auto ratio = Core::Sandbox::Instance().devicePixelRatio(); + if (!integration || ratio > 1.) { return; } - const auto scaleFactor = integration->scaleFactor(); - if (!scaleFactor.has_value()) { + const auto scaleFactor = integration->scaleFactor().value_or(1); + if (scaleFactor == 1) { return; } - LOG(("GTK scale factor: %1").arg(*scaleFactor)); - - const int scale = *scaleFactor - * 100 - / Core::Sandbox::Instance().devicePixelRatio(); - - cSetScreenScale(std::clamp(scale, 100, 300)); + LOG(("GTK scale factor: %1").arg(scaleFactor)); + cSetScreenScale(std::clamp(scaleFactor * 100, 100, 300)); }); } From ae74f7b3da1fa9eb047301a2a5723566b1ce07b7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 24 Jan 2021 06:29:47 +0400 Subject: [PATCH 121/396] Build WebRTC without NEON for armhf ARM support is community maintained in tg_owt and no one seem to fix build with NEON, thus disabling it to get new armhf builds on snapcraft. --- snap/snapcraft.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c4c1f1f12..291fc61e4 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -268,6 +268,8 @@ parts: - -DBUILD_SHARED_LIBS=OFF - -DJPEG_LIBRARY_RELEASE=$SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so - -DJPEG_INCLUDE_DIR=$SNAPCRAFT_STAGE/usr/include + # NEON support for arm 32-bit is broken and no one seem to fix it + - -DTG_OWT_ARCH_ARMV7_USE_NEON=OFF prime: [-./*] after: - ffmpeg From c21125f9f23338d02effe076dbfc172cb903d362 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 24 Jan 2021 00:32:37 +0400 Subject: [PATCH 122/396] Don't log UnknownProperty error When checking notification inhibition support --- .../platform/linux/notifications_manager_linux.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 31e3775dc..a13918362 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -149,10 +149,16 @@ void GetInhibitionSupported(Fn callback) { const auto async = QDBusConnection::sessionBus().asyncCall(message); auto watcher = new QDBusPendingCallWatcher(async); + static const auto DontLogErrors = { + QDBusError::NoError, + QDBusError::InvalidArgs, + QDBusError::UnknownProperty, + }; + const auto finished = [=](QDBusPendingCallWatcher *call) { const auto error = QDBusPendingReply(*call).error(); - if (error.isValid() && error.type() != QDBusError::InvalidArgs) { + if (!ranges::contains(DontLogErrors, error.type())) { LOG(("Native Notification Error: %1: %2") .arg(error.name()) .arg(error.message())); From 4895e5e110524b68dd03cb0f315a65f44acd9a4e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 22 Jan 2021 19:31:34 +0300 Subject: [PATCH 123/396] Fixed possible crash on deleting messages. --- Telegram/SourceFiles/boxes/confirm_box.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 61208028b..051a582fc 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -860,9 +860,12 @@ void DeleteMessagesBox::deleteAndClear() { _deleteConfirmedCallback(); } + // deleteMessages can initiate closing of the current section, + // which will cause this box to be destroyed. + const auto session = _session; + _session->data().histories().deleteMessages(_ids, revoke); - const auto session = _session; Ui::hideLayer(); session->data().sendHistoryChangeNotifications(); } From dd01ece14a2cfb12f172f033653e04ca0e80a10f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 23 Jan 2021 06:29:50 +0300 Subject: [PATCH 124/396] Replaced snap util function with std::clamp. --- Telegram/SourceFiles/apiwrap.cpp | 2 +- Telegram/SourceFiles/boxes/edit_color_box.cpp | 22 +++++------ Telegram/SourceFiles/boxes/edit_color_box.h | 4 +- Telegram/SourceFiles/boxes/peer_list_box.cpp | 5 ++- .../chat_helpers/emoji_suggestions_widget.cpp | 13 ++++--- .../chat_helpers/stickers_list_widget.cpp | 37 +++++++++++++++---- .../SourceFiles/chat_helpers/tabbed_panel.cpp | 2 +- .../chat_helpers/tabbed_selector.cpp | 5 ++- Telegram/SourceFiles/core/core_settings.cpp | 7 +++- Telegram/SourceFiles/core/utils.h | 19 +++++----- Telegram/SourceFiles/data/data_document.cpp | 2 +- .../SourceFiles/data/data_peer_values.cpp | 2 +- Telegram/SourceFiles/data/data_photo.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 16 +++++++- .../admin_log/history_admin_log_inner.cpp | 4 +- .../history/feed/history_feed_section.cpp | 12 ++++-- .../history/history_inner_widget.cpp | 20 ++++++++-- .../SourceFiles/history/history_widget.cpp | 5 ++- .../history/view/history_view_list_widget.cpp | 4 +- .../history/view/history_view_message.cpp | 2 +- .../view/history_view_pinned_section.cpp | 7 +++- .../view/history_view_replies_section.cpp | 7 +++- .../view/history_view_schedule_box.cpp | 2 +- .../view/history_view_scheduled_section.cpp | 7 +++- .../view/media/history_view_document.cpp | 13 ++++++- .../SourceFiles/info/info_layer_widget.cpp | 2 +- .../inline_bots/inline_results_widget.cpp | 5 ++- Telegram/SourceFiles/intro/intro_step.cpp | 6 ++- .../main/main_session_settings.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- .../media/player/media_player_float.cpp | 2 +- .../media/player/media_player_widget.cpp | 4 +- .../view/media_view_playback_controls.cpp | 4 +- .../view/media_view_playback_progress.cpp | 4 +- .../SourceFiles/mtproto/session_private.cpp | 5 ++- .../passport/passport_panel_details_row.cpp | 2 +- .../SourceFiles/settings/settings_intro.cpp | 2 +- .../settings/settings_notifications.cpp | 2 +- .../details/storage_settings_scheme.cpp | 4 +- .../storage/storage_cloud_blob.cpp | 2 +- .../support/support_autocomplete.cpp | 2 +- .../SourceFiles/ui/effects/round_checkbox.cpp | 4 +- .../ui/widgets/continuous_sliders.cpp | 4 +- .../ui/widgets/discrete_sliders.cpp | 6 ++- 44 files changed, 193 insertions(+), 92 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index f116c33cf..af47774f1 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -354,7 +354,7 @@ void ApiWrap::requestTermsUpdate() { const auto requestNext = [&](auto &&data) { const auto timeout = (data.vexpires().v - base::unixtime::now()); - _termsUpdateSendAt = crl::now() + snap( + _termsUpdateSendAt = crl::now() + std::clamp( timeout * crl::time(1000), kTermsUpdateTimeoutMin, kTermsUpdateTimeoutMax); diff --git a/Telegram/SourceFiles/boxes/edit_color_box.cpp b/Telegram/SourceFiles/boxes/edit_color_box.cpp index 86582b30d..8e39dae79 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_color_box.cpp @@ -228,8 +228,8 @@ void EditColorBox::Picker::preparePaletteHSL() { } void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) { - auto x = snap(localPosition.x(), 0, width()) / float64(width()); - auto y = snap(localPosition.y(), 0, height()) / float64(height()); + auto x = std::clamp(localPosition.x(), 0, width()) / float64(width()); + auto y = std::clamp(localPosition.y(), 0, height()) / float64(height()); if (_x != x || _y != y) { _x = x; _y = y; @@ -245,14 +245,14 @@ void EditColorBox::Picker::setHSB(HSB hsb) { _topright = _topright.toRgb(); _bottomleft = _bottomright = QColor(0, 0, 0); - _x = snap(hsb.saturation / 255., 0., 1.); - _y = 1. - snap(hsb.brightness / 255., 0., 1.); + _x = std::clamp(hsb.saturation / 255., 0., 1.); + _y = 1. - std::clamp(hsb.brightness / 255., 0., 1.); } else { _topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness); _bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness); - _x = snap(hsb.hue / 360., 0., 1.); - _y = 1. - snap(hsb.saturation / 255., 0., 1.); + _x = std::clamp(hsb.hue / 360., 0., 1.); + _y = 1. - std::clamp(hsb.saturation / 255., 0., 1.); } _paletteInvalidated = true; @@ -291,7 +291,7 @@ public: return _value; } void setValue(float64 value) { - _value = snap(value, 0., 1.); + _value = std::clamp(value, 0., 1.); update(); } void setHSB(HSB hsb); @@ -508,12 +508,12 @@ float64 EditColorBox::Slider::valueFromColor(QColor color) const { } float64 EditColorBox::Slider::valueFromHue(int hue) const { - return (1. - snap(hue, 0, 360) / 360.); + return (1. - std::clamp(hue, 0, 360) / 360.); } void EditColorBox::Slider::setAlpha(int alpha) { if (_type == Type::Opacity) { - _value = snap(alpha, 0, 255) / 255.; + _value = std::clamp(alpha, 0, 255) / 255.; update(); } } @@ -534,7 +534,7 @@ void EditColorBox::Slider::updatePixmapFromMask() { void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) { auto coord = (isHorizontal() ? localPosition.x() : localPosition.y()) - st::colorSliderSkip; auto maximum = (isHorizontal() ? width() : height()) - 2 * st::colorSliderSkip; - auto value = snap(coord, 0, maximum) / float64(maximum); + auto value = std::clamp(coord, 0, maximum) / float64(maximum); if (_value != value) { _value = value; update(); @@ -663,7 +663,7 @@ void EditColorBox::Field::wheelEvent(QWheelEvent *e) { void EditColorBox::Field::changeValue(int delta) { auto currentValue = value(); - auto newValue = snap(currentValue + delta, 0, _limit); + auto newValue = std::clamp(currentValue + delta, 0, _limit); if (newValue != currentValue) { setText(QString::number(newValue)); setFocus(); diff --git a/Telegram/SourceFiles/boxes/edit_color_box.h b/Telegram/SourceFiles/boxes/edit_color_box.h index 28ad8cc5a..5c2e7170a 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.h +++ b/Telegram/SourceFiles/boxes/edit_color_box.h @@ -66,10 +66,10 @@ private: [[nodiscard]] QColor applyLimits(QColor color) const; int percentFromByte(int byte) { - return snap(qRound(byte * 100 / 255.), 0, 100); + return std::clamp(qRound(byte * 100 / 255.), 0, 100); } int percentToByte(int percent) { - return snap(qRound(percent * 255 / 100.), 0, 255); + return std::clamp(qRound(percent * 255 / 100.), 0, 255); } class Picker; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 56e0f1568..281de0b95 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -1440,7 +1440,10 @@ PeerListContent::SkipResult PeerListContent::selectSkip(int direction) { } // Snap the index. - newSelectedIndex = snap(newSelectedIndex, firstEnabled - 1, lastEnabled); + newSelectedIndex = std::clamp( + newSelectedIndex, + firstEnabled - 1, + lastEnabled); // Skip the disabled rows. if (newSelectedIndex < firstEnabled) { diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index 772d2e0ce..d96472682 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -179,12 +179,15 @@ void SuggestionsWidget::scrollByWheelEvent(not_null e) { const auto delta = e->pixelDelta().x() ? e->pixelDelta().x() : e->angleDelta().x(); - return snap(current - ((rtl() ? -1 : 1) * delta), 0, _scrollMax); + return std::clamp( + current - ((rtl() ? -1 : 1) * delta), + 0, + _scrollMax); } else if (vertical) { const auto delta = e->pixelDelta().y() ? e->pixelDelta().y() : e->angleDelta().y(); - return snap(current - delta, 0, _scrollMax); + return std::clamp(current - delta, 0, _scrollMax); } return current; }(); @@ -241,7 +244,7 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) { void SuggestionsWidget::paintFadings(Painter &p) const { const auto scroll = scrollCurrent(); - const auto o_left = snap( + const auto o_left = std::clamp( scroll / float64(st::emojiSuggestionsFadeAfter), 0., 1.); @@ -256,7 +259,7 @@ void SuggestionsWidget::paintFadings(Painter &p) const { st::emojiSuggestionsFadeLeft.fill(p, rect); p.setOpacity(1.); } - const auto o_right = snap( + const auto o_right = std::clamp( (_scrollMax - scroll) / float64(st::emojiSuggestionsFadeAfter), 0., 1.); @@ -422,7 +425,7 @@ void SuggestionsWidget::mouseMoveEvent(QMouseEvent *e) { const auto globalPosition = e->globalPos(); if (_dragScrollStart >= 0) { const auto delta = (_mousePressPosition.x() - globalPosition.x()); - const auto scroll = snap( + const auto scroll = std::clamp( _dragScrollStart + (rtl() ? -1 : 1) * delta, 0, _scrollMax); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 017c5e27e..8a92680ad 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -404,7 +404,7 @@ void StickersListWidget::Footer::setSelectedIcon( auto iconsCountForCentering = (2 * _iconSel + 1); auto iconsWidthForCentering = iconsCountForCentering * st::stickerIconWidth; - auto iconsXFinal = snap( + auto iconsXFinal = std::clamp( (_iconsLeft + iconsWidthForCentering + _iconsRight - width()) / 2, 0, _iconsMax); @@ -486,13 +486,19 @@ void StickersListWidget::Footer::paintSelectionBar(Painter &p) const { } void StickersListWidget::Footer::paintLeftRightFading(Painter &p) const { - auto o_left = snap(_iconsX.current() / st::stickerIconLeft.width(), 0., 1.); + auto o_left = std::clamp( + _iconsX.current() / st::stickerIconLeft.width(), + 0., + 1.); if (o_left > 0) { p.setOpacity(o_left); st::stickerIconLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiFooterHeight, width())); p.setOpacity(1.); } - auto o_right = snap((_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); + auto o_right = std::clamp( + (_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), + 0., + 1.); if (o_right > 0) { p.setOpacity(o_right); st::stickerIconRight.fill(p, style::rtlrect(width() - _iconsRight - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiFooterHeight, width())); @@ -554,7 +560,11 @@ void StickersListWidget::Footer::mouseMoveEvent(QMouseEvent *e) { } } if (_iconsDragging) { - auto newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); + auto newX = std::clamp( + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()) + + _iconsStartX, + 0, + _iconsMax); if (newX != qRound(_iconsX.current())) { _iconsX = anim::value(newX, newX); _iconsStartAnim = 0; @@ -589,7 +599,10 @@ void StickersListWidget::Footer::mouseReleaseEvent(QMouseEvent *e) { } void StickersListWidget::Footer::finishDragging() { - auto newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); + auto newX = std::clamp( + _iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), + 0, + _iconsMax); if (newX != qRound(_iconsX.current())) { _iconsX = anim::value(newX, newX); _iconsStartAnim = 0; @@ -621,9 +634,19 @@ void StickersListWidget::Footer::scrollByWheelEvent( } auto newX = qRound(_iconsX.current()); if (/*_horizontal && */horizontal) { - newX = snap(newX - (rtl() ? -1 : 1) * (e->pixelDelta().x() ? e->pixelDelta().x() : e->angleDelta().x()), 0, _iconsMax); + newX = std::clamp( + newX - (rtl() ? -1 : 1) * (e->pixelDelta().x() + ? e->pixelDelta().x() + : e->angleDelta().x()), + 0, + _iconsMax); } else if (/*!_horizontal && */vertical) { - newX = snap(newX - (e->pixelDelta().y() ? e->pixelDelta().y() : e->angleDelta().y()), 0, _iconsMax); + newX = std::clamp( + newX - (e->pixelDelta().y() + ? e->pixelDelta().y() + : e->angleDelta().y()), + 0, + _iconsMax); } if (newX != qRound(_iconsX.current())) { _iconsX = anim::value(newX, newX); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp index 04afc8f33..bb946ec44 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp @@ -151,7 +151,7 @@ void TabbedPanel::updateContentHeight() { auto marginsHeight = _selector->marginTop() + _selector->marginBottom(); auto availableHeight = _bottom - marginsHeight; auto wantedContentHeight = qRound(_heightRatio * availableHeight) - addedHeight; - auto contentHeight = marginsHeight + snap( + auto contentHeight = marginsHeight + std::clamp( wantedContentHeight, _minContentHeight, _maxContentHeight); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 9b9a92910..739b9ccc4 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -153,8 +153,9 @@ void TabbedSelector::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 auto rightAlpha = (leftToRight ? departingAlpha : arrivingAlpha); // _innerLeft ..(left).. leftTo ..(both).. bothTo ..(none).. noneTo ..(right).. _innerRight - auto leftTo = _innerLeft + snap(_innerWidth + leftCoord, 0, _innerWidth); - auto rightFrom = _innerLeft + snap(rightCoord, 0, _innerWidth); + auto leftTo = _innerLeft + + std::clamp(_innerWidth + leftCoord, 0, _innerWidth); + auto rightFrom = _innerLeft + std::clamp(rightCoord, 0, _innerWidth); auto painterRightFrom = rightFrom / cIntRetinaFactor(); if (opacity < 1.) { _frame.fill(Qt::transparent); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 5eb5bbcd8..ca70b71c6 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -101,7 +101,7 @@ QByteArray Settings::serialize() const { << qint32(_floatPlayerColumn) << qint32(_floatPlayerCorner) << qint32(_thirdSectionInfoEnabled ? 1 : 0) - << qint32(snap( + << qint32(std::clamp( qRound(_dialogsWidthRatio.current() * 1000000), 0, 1000000)) @@ -259,7 +259,10 @@ void Settings::addFromSerialized(const QByteArray &serialized) { >> thirdColumnWidth >> thirdSectionExtendedBy >> notifyFromAll; - dialogsWidthRatio = snap(dialogsWidthRatioInt / 1000000., 0., 1.); + dialogsWidthRatio = std::clamp( + dialogsWidthRatioInt / 1000000., + 0., + 1.); } if (!stream.atEnd()) { stream >> nativeWindowFrame; diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 90ae40d0e..f71ba5216 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -124,11 +124,6 @@ T rand_value() { return result; } -template -inline T snap(const T &v, const T &_min, const T &_max) { - return (v < _min) ? _min : ((v > _max) ? _max : v); -} - QString translitRusEng(const QString &rus); QString rusKeyboardLayoutSwitch(const QString &from); @@ -150,16 +145,22 @@ inline int rowscount(int fullCount, int countPerRow) { return (fullCount + countPerRow - 1) / countPerRow; } inline int floorclamp(int value, int step, int lowest, int highest) { - return qMin(qMax(value / step, lowest), highest); + return std::clamp(value / step, lowest, highest); } inline int floorclamp(float64 value, int step, int lowest, int highest) { - return qMin(qMax(static_cast(std::floor(value / step)), lowest), highest); + return std::clamp( + static_cast(std::floor(value / step)), + lowest, + highest); } inline int ceilclamp(int value, int step, int lowest, int highest) { - return qMax(qMin((value + step - 1) / step, highest), lowest); + return std::clamp((value + step - 1) / step, lowest, highest); } inline int ceilclamp(float64 value, int32 step, int32 lowest, int32 highest) { - return qMax(qMin(static_cast(std::ceil(value / step)), highest), lowest); + return std::clamp( + static_cast(std::ceil(value / step)), + lowest, + highest); } static int32 FullArcLength = 360 * 16; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index a615e17f0..c241c6b24 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -893,7 +893,7 @@ float64 DocumentData::progress() const { if (uploadingData->size > 0) { const auto result = float64(uploadingData->offset) / uploadingData->size; - return snap(result, 0., 1.); + return std::clamp(result, 0., 1.); } return 0.; } diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 85f28a301..606a5d757 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -350,7 +350,7 @@ TimeId SortByOnlineValue(not_null user, TimeId now) { crl::time OnlineChangeTimeout(TimeId online, TimeId now) { const auto result = OnlinePhraseChangeInSeconds(online, now); Assert(result >= 0); - return snap( + return std::clamp( result * crl::time(1000), kMinOnlineChangeTimeout, kMaxOnlineChangeTimeout); diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index b1d67d1cd..c382511f8 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -166,7 +166,7 @@ float64 PhotoData::progress() const { if (uploadingData->size > 0) { const auto result = float64(uploadingData->offset) / uploadingData->size; - return snap(result, 0., 1.); + return std::clamp(result, 0., 1.); } return 0.; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index d9bd46488..a4c87531c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2437,7 +2437,13 @@ void InnerWidget::selectSkip(int32 direction) { ? _collapsedSelected : int(_collapsedRows.size() + (list->cfind(_selected) - list->cbegin() - _skipTopDialogs)); - cur = snap(cur + direction, 0, static_cast(_collapsedRows.size() + list->size() - _skipTopDialogs - 1)); + cur = std::clamp( + cur + direction, + 0, + static_cast(_collapsedRows.size() + + list->size() + - _skipTopDialogs + - 1)); if (cur < _collapsedRows.size()) { _collapsedSelected = cur; _selected = nullptr; @@ -2477,7 +2483,13 @@ void InnerWidget::selectSkip(int32 direction) { : (base::in_range(_peerSearchSelected, 0, _peerSearchResults.size()) ? (_peerSearchSelected + _filterResults.size() + _hashtagResults.size()) : (_searchedSelected + _peerSearchResults.size() + _filterResults.size() + _hashtagResults.size()))); - cur = snap(cur + direction, 0, static_cast(_hashtagResults.size() + _filterResults.size() + _peerSearchResults.size() + _searchResults.size()) - 1); + cur = std::clamp( + cur + direction, + 0, + static_cast(_hashtagResults.size() + + _filterResults.size() + + _peerSearchResults.size() + + _searchResults.size()) - 1); if (cur < _hashtagResults.size()) { _hashtagSelected = cur; _filteredSelected = _peerSearchSelected = _searchedSelected = -1; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index c3781e0d2..bc26d4a18 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1535,7 +1535,9 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but void InnerWidget::updateSelected() { auto mousePosition = mapFromGlobal(_mousePosition); - auto point = QPoint(snap(mousePosition.x(), 0, width()), snap(mousePosition.y(), _visibleTop, _visibleBottom)); + auto point = QPoint( + std::clamp(mousePosition.x(), 0, width()), + std::clamp(mousePosition.y(), _visibleTop, _visibleBottom)); auto itemPoint = QPoint(); auto begin = std::rbegin(_items), end = std::rend(_items); diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index 571492247..3ecf7ee0d 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -181,10 +181,13 @@ void Widget::showAtPosition(Data::MessagePosition position) { bool Widget::showAtPositionNow(Data::MessagePosition position) { if (const auto scrollTop = _inner->scrollTopForPosition(position)) { const auto currentScrollTop = _scroll->scrollTop(); - const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); + const auto wanted = std::clamp( + *scrollTop, + 0, + _scroll->scrollTopMax()); const auto fullDelta = (wanted - currentScrollTop); const auto limit = _scroll->height(); - const auto scrollDelta = snap(fullDelta, -limit, limit); + const auto scrollDelta = std::clamp(fullDelta, -limit, limit); _inner->animatedScrollTo( wanted, position, @@ -432,7 +435,10 @@ void Widget::listContentRefreshed() { } const auto position = *base::take(_nextAnimatedScrollPosition); if (const auto scrollTop = _inner->scrollTopForPosition(position)) { - const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); + const auto wanted = std::clamp( + *scrollTop, + 0, + _scroll->scrollTopMax()); _inner->animatedScrollTo( wanted, position, diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 3ccafebf4..38ab5c227 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -838,16 +838,28 @@ void HistoryInner::touchUpdateSpeed() { const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) && (oldSpeedX <= 0 && newSpeedX <= 0)) || (oldSpeedX >= 0 && newSpeedX >= 0)) { - _touchSpeed.setY(snap((oldSpeedY + (newSpeedY / 4)), -Ui::kMaxScrollAccelerated, +Ui::kMaxScrollAccelerated)); - _touchSpeed.setX(snap((oldSpeedX + (newSpeedX / 4)), -Ui::kMaxScrollAccelerated, +Ui::kMaxScrollAccelerated)); + _touchSpeed.setY(std::clamp( + (oldSpeedY + (newSpeedY / 4)), + -Ui::kMaxScrollAccelerated, + +Ui::kMaxScrollAccelerated)); + _touchSpeed.setX(std::clamp( + (oldSpeedX + (newSpeedX / 4)), + -Ui::kMaxScrollAccelerated, + +Ui::kMaxScrollAccelerated)); } else { _touchSpeed = QPoint(); } } else { // we average the speed to avoid strange effects with the last delta if (!_touchSpeed.isNull()) { - _touchSpeed.setX(snap((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -Ui::kMaxScrollFlick, +Ui::kMaxScrollFlick)); - _touchSpeed.setY(snap((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -Ui::kMaxScrollFlick, +Ui::kMaxScrollFlick)); + _touchSpeed.setX(std::clamp( + (_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), + -Ui::kMaxScrollFlick, + +Ui::kMaxScrollFlick)); + _touchSpeed.setY(std::clamp( + (_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), + -Ui::kMaxScrollFlick, + +Ui::kMaxScrollFlick)); } else { _touchSpeed = QPoint(newSpeedX, newSpeedY); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8772e78ac..bdd9a0e6e 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -976,7 +976,7 @@ void HistoryWidget::animatedScrollToItem(MsgId msgId) { return; } - auto scrollTo = snap( + auto scrollTo = std::clamp( itemTopForHighlight(to->mainView()), 0, _scroll->scrollTopMax()); @@ -6625,7 +6625,8 @@ void HistoryWidget::noSelectingScroll() { } bool HistoryWidget::touchScroll(const QPoint &delta) { - int32 scTop = _scroll->scrollTop(), scMax = _scroll->scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); + int32 scTop = _scroll->scrollTop(), scMax = _scroll->scrollTopMax(); + const auto scNew = std::clamp(scTop - delta.y(), 0, scMax); if (scNew == scTop) return false; _scroll->scrollToY(scNew); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 2fa498844..49089a05a 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2269,7 +2269,9 @@ void ListWidget::mouseActionFinish( void ListWidget::mouseActionUpdate() { auto mousePosition = mapFromGlobal(_mousePosition); - auto point = QPoint(snap(mousePosition.x(), 0, width()), snap(mousePosition.y(), _visibleTop, _visibleBottom)); + auto point = QPoint( + std::clamp(mousePosition.x(), 0, width()), + std::clamp(mousePosition.y(), _visibleTop, _visibleBottom)); const auto view = strictFindItemByY(point.y()); const auto item = view ? view->data().get() : nullptr; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index d5dac0735..3c2465a08 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1319,7 +1319,7 @@ TextState Message::textState( } checkForPointInTime(); if (const auto size = rightActionSize()) { - const auto fastShareSkip = snap( + const auto fastShareSkip = std::clamp( (g.height() - size->height()) / 2, 0, st::historyFastShareBottom); diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 00199717d..b347da9f7 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -221,10 +221,13 @@ bool PinnedWidget::showAtPositionNow( const auto use = item ? item->position() : position; if (const auto scrollTop = _inner->scrollTopForPosition(use)) { const auto currentScrollTop = _scroll->scrollTop(); - const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); + const auto wanted = std::clamp( + *scrollTop, + 0, + _scroll->scrollTopMax()); const auto fullDelta = (wanted - currentScrollTop); const auto limit = _scroll->height(); - const auto scrollDelta = snap(fullDelta, -limit, limit); + const auto scrollDelta = std::clamp(fullDelta, -limit, limit); const auto type = (animated == anim::type::instant) ? AnimatedScroll::None : (std::abs(fullDelta) > limit) diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index caf5059fe..1ec922262 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1219,10 +1219,13 @@ bool RepliesWidget::showAtPositionNow( calculateNextReplyReturn(); } const auto currentScrollTop = _scroll->scrollTop(); - const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); + const auto wanted = std::clamp( + *scrollTop, + 0, + _scroll->scrollTopMax()); const auto fullDelta = (wanted - currentScrollTop); const auto limit = _scroll->height(); - const auto scrollDelta = snap(fullDelta, -limit, limit); + const auto scrollDelta = std::clamp(fullDelta, -limit, limit); const auto type = (animated == anim::type::instant) ? AnimatedScroll::None : (std::abs(fullDelta) > limit) diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index f09de4f01..52b05ae4a 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -433,7 +433,7 @@ void TimeInput::paintEvent(QPaintEvent *e) { auto borderShownDegree = _a_borderShown.value(1.); auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = snap(_borderAnimationStart, 0, width()); + auto borderStart = std::clamp(_borderAnimationStart, 0, width()); auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); if (borderTo > borderFrom) { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 9d72215a5..1755b6ed6 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -810,10 +810,13 @@ void ScheduledWidget::showAtPosition(Data::MessagePosition position) { bool ScheduledWidget::showAtPositionNow(Data::MessagePosition position) { if (const auto scrollTop = _inner->scrollTopForPosition(position)) { const auto currentScrollTop = _scroll->scrollTop(); - const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax()); + const auto wanted = std::clamp( + *scrollTop, + 0, + _scroll->scrollTopMax()); const auto fullDelta = (wanted - currentScrollTop); const auto limit = _scroll->height(); - const auto scrollDelta = snap(fullDelta, -limit, limit); + const auto scrollDelta = std::clamp(fullDelta, -limit, limit); _inner->scrollTo( wanted, position, diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index a28aabf31..4536e7f39 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -778,7 +778,11 @@ void Document::updatePressed(QPoint point) { const auto &st = thumbed ? st::msgFileThumbLayout : st::msgFileLayout; const auto nameleft = st.padding.left() + st.thumbSize + st.padding.right(); const auto nameright = st.padding.left(); - voice->setSeekingCurrent(snap((point.x() - nameleft) / float64(width() - nameleft - nameright), 0., 1.)); + voice->setSeekingCurrent(std::clamp( + (point.x() - nameleft) + / float64(width() - nameleft - nameright), + 0., + 1.)); history()->owner().requestViewRepaint(_parent); } } @@ -863,7 +867,12 @@ bool Document::updateStatusText() const { bool was = (voice->_playback != nullptr); voice->ensurePlayback(this); if (!was || state.position != voice->_playback->position) { - auto prg = state.length ? snap(float64(state.position) / state.length, 0., 1.) : 0.; + auto prg = state.length + ? std::clamp( + float64(state.position) / state.length, + 0., + 1.) + : 0.; if (voice->_playback->position < state.position) { voice->_playback->progress.start(prg); } else { diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp index 0c4b30780..750e30856 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.cpp +++ b/Telegram/SourceFiles/info/info_layer_widget.cpp @@ -207,7 +207,7 @@ int LayerWidget::resizeGetHeight(int newWidth) { auto windowWidth = parentSize.width(); auto windowHeight = parentSize.height(); auto newLeft = (windowWidth - newWidth) / 2; - auto newTop = snap( + auto newTop = std::clamp( windowHeight / 24, st::infoLayerTopMinimal, st::infoLayerTopMaximal); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index a384d1f2e..b599c3e7c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -87,7 +87,10 @@ void Widget::moveBottom(int bottom) { void Widget::updateContentHeight() { auto addedHeight = innerPadding().top() + innerPadding().bottom(); auto wantedContentHeight = qRound(st::emojiPanHeightRatio * _bottom) - addedHeight; - auto contentHeight = snap(wantedContentHeight, st::inlineResultsMinHeight, st::inlineResultsMaxHeight); + auto contentHeight = std::clamp( + wantedContentHeight, + st::inlineResultsMinHeight, + st::inlineResultsMaxHeight); accumulate_min(contentHeight, _bottom - addedHeight); accumulate_min(contentHeight, _contentMaxHeight); auto resultTop = _bottom - addedHeight - contentHeight; diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index 92a627d13..ea4f05bd5 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -411,7 +411,11 @@ int Step::contentTop() const { accumulate_max(result, st::introStepTopMin); if (_hasCover) { const auto currentHeightFull = result + st::introNextTop + st::introContentTopAdd; - auto added = 1. - snap(float64(currentHeightFull - st::windowMinHeight) / (st::introStepHeightFull - st::windowMinHeight), 0., 1.); + auto added = 1. - std::clamp( + float64(currentHeightFull - st::windowMinHeight) + / (st::introStepHeightFull - st::windowMinHeight), + 0., + 1.); result += qRound(added * st::introContentTopAdd); } return result; diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index f38f2e3e4..ba7e97919 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -189,7 +189,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { qint32 value = 0; stream >> value; - appDialogsWidthRatio = snap(value / 1000000., 0., 1.); + appDialogsWidthRatio = std::clamp(value / 1000000., 0., 1.); stream >> value; appThirdColumnWidth = value; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 1949de3e3..e7c258519 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2406,7 +2406,7 @@ void MainWidget::ensureThirdColumnResizeAreaCreated() { if (!Adaptive::ThreeColumn() || !_thirdSection) { return; } - Core::App().settings().setThirdColumnWidth(snap( + Core::App().settings().setThirdColumnWidth(std::clamp( Core::App().settings().thirdColumnWidth(), st::columnMinimalWidthThird, st::columnMaximalWidthThird)); diff --git a/Telegram/SourceFiles/media/player/media_player_float.cpp b/Telegram/SourceFiles/media/player/media_player_float.cpp index 637f3a70b..bb7615205 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.cpp +++ b/Telegram/SourceFiles/media/player/media_player_float.cpp @@ -111,7 +111,7 @@ float64 Float::outRatio() const { if (y() + height() > parent.y() + parent.height()) { accumulate_min(min, 1. - (y() + height() - parent.y() - parent.height()) / float64(height())); } - return snap(min, 0., 1.); + return std::clamp(min, 0., 1.); } void Float::mouseReleaseEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index c4f731963..16c65351f 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -250,7 +250,7 @@ Widget::~Widget() = default; void Widget::handleSeekProgress(float64 progress) { if (!_lastDurationMs) return; - const auto positionMs = snap( + const auto positionMs = std::clamp( static_cast(progress * _lastDurationMs), crl::time(0), _lastDurationMs); @@ -265,7 +265,7 @@ void Widget::handleSeekProgress(float64 progress) { void Widget::handleSeekFinished(float64 progress) { if (!_lastDurationMs) return; - const auto positionMs = snap( + const auto positionMs = std::clamp( static_cast(progress * _lastDurationMs), crl::time(0), _lastDurationMs); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 3b8bb05be..5c6cd0994 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -104,7 +104,7 @@ PlaybackControls::PlaybackControls( void PlaybackControls::handleSeekProgress(float64 progress) { if (!_lastDurationMs) return; - const auto positionMs = snap( + const auto positionMs = std::clamp( static_cast(progress * _lastDurationMs), crl::time(0), _lastDurationMs); @@ -120,7 +120,7 @@ void PlaybackControls::handleSeekProgress(float64 progress) { void PlaybackControls::handleSeekFinished(float64 progress) { if (!_lastDurationMs) return; - const auto positionMs = snap( + const auto positionMs = std::clamp( static_cast(progress * _lastDurationMs), crl::time(0), _lastDurationMs); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp b/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp index 9a8ecbc78..745c4374f 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp @@ -58,10 +58,10 @@ void PlaybackProgress::updateState( const auto progress = (position > length) ? 1. : length - ? snap(float64(position) / length, 0., 1.) + ? std::clamp(float64(position) / length, 0., 1.) : 0.; const auto availableTillProgress = (availableTill > position) - ? snap(float64(availableTill) / length, 0., 1.) + ? std::clamp(float64(availableTill) / length, 0., 1.) : -1.; const auto animatedPosition = position + (state.frequency * kPlaybackAnimationDurationMs / 1000); const auto animatedProgress = length ? qMax(float64(animatedPosition) / length, 0.) : 0.; diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 8bbcf7caa..9ddab55b8 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -1073,7 +1073,10 @@ void SessionPrivate::onSentSome(uint64 size) { if (!_oldConnection) { // 8kb / sec, so 512 kb give 64 sec auto remainBySize = size * _waitForReceived / 8192; - remain = snap(remainBySize, remain, uint64(kMaxReceiveTimeout)); + remain = std::clamp( + remainBySize, + remain, + uint64(kMaxReceiveTimeout)); if (remain != _waitForReceived) { DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain)); } diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp index 150baa35f..20b7c9732 100644 --- a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp @@ -680,7 +680,7 @@ void DateRow::paintEvent(QPaintEvent *e) { auto borderShownDegree = _a_borderShown.value(1.); auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = snap(_borderAnimationStart, 0, width); + auto borderStart = std::clamp(_borderAnimationStart, 0, width); auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); auto borderTo = borderStart + qRound((width - borderStart) * borderShownDegree); if (borderTo > borderFrom) { diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 9e2695bcc..44f0ef147 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -473,7 +473,7 @@ int LayerWidget::resizeGetHeight(int newWidth) { _tillTop = _tillBottom = true; return windowHeight; } - auto newTop = snap( + auto newTop = std::clamp( windowHeight / 24, st::infoLayerTopMinimal, st::infoLayerTopMaximal); diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 9c2981c1d..f6ce983b0 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -41,7 +41,7 @@ namespace { constexpr auto kMaxNotificationsCount = 5; [[nodiscard]] int CurrentCount() { - return snap( + return std::clamp( Core::App().settings().notificationsCount(), 1, kMaxNotificationsCount); diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index 535deee66..4efc521cf 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -1053,7 +1053,7 @@ bool ReadSetting( stream >> v; if (!CheckStreamStatus(stream)) return false; - Core::App().settings().setSongVolume(snap(v / 1e6, 0., 1.)); + Core::App().settings().setSongVolume(std::clamp(v / 1e6, 0., 1.)); context.legacyRead = true; } break; @@ -1062,7 +1062,7 @@ bool ReadSetting( stream >> v; if (!CheckStreamStatus(stream)) return false; - Core::App().settings().setVideoVolume(snap(v / 1e6, 0., 1.)); + Core::App().settings().setVideoVolume(std::clamp(v / 1e6, 0., 1.)); context.legacyRead = true; } break; diff --git a/Telegram/SourceFiles/storage/storage_cloud_blob.cpp b/Telegram/SourceFiles/storage/storage_cloud_blob.cpp index 6928dfebc..c4c8f0578 100644 --- a/Telegram/SourceFiles/storage/storage_cloud_blob.cpp +++ b/Telegram/SourceFiles/storage/storage_cloud_blob.cpp @@ -80,7 +80,7 @@ QString StateDescription(const BlobState &state, tr::phrase<> activeText) { return activeText(tr::now); }, [](const Loading &data) { const auto percent = (data.size > 0) - ? snap((data.already * 100) / float64(data.size), 0., 100.) + ? std::clamp((data.already * 100) / float64(data.size), 0., 100.) : 0.; return tr::lng_emoji_set_loading( tr::now, diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp index e6b96c52c..d7ceabdd9 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.cpp +++ b/Telegram/SourceFiles/support/support_autocomplete.cpp @@ -528,7 +528,7 @@ void ConfirmContactBox::prepare() { _contact->initDimensions(); accumulate_max(maxWidth, _contact->maxWidth()); maxWidth += st::boxPadding.left() + st::boxPadding.right(); - const auto width = snap(maxWidth, st::boxWidth, st::boxWideWidth); + const auto width = std::clamp(maxWidth, st::boxWidth, st::boxWideWidth); const auto available = width - st::boxPadding.left() - st::boxPadding.right(); diff --git a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp index f018132b1..881081b27 100644 --- a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp +++ b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp @@ -423,7 +423,7 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) { if (selectionLevel > 0) { PainterHighQualityEnabler hq(p); - p.setOpacity(snap(selectionLevel, 0., 1.)); + p.setOpacity(std::clamp(selectionLevel, 0., 1.)); p.setBrush(Qt::NoBrush); auto pen = _st.selectFg->p; pen.setWidth(_st.selectWidth); @@ -438,7 +438,7 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) { } float64 RoundImageCheckbox::checkedAnimationRatio() const { - return snap(_selection.value(checked() ? 1. : 0.), 0., 1.); + return std::clamp(_selection.value(checked() ? 1. : 0.), 0., 1.); } void RoundImageCheckbox::setChecked(bool newChecked, anim::type animated) { diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index e6aa9cec5..09f88c890 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -78,7 +78,7 @@ float64 ContinuousSlider::computeValue(const QPoint &pos) const { const auto result = isHorizontal() ? (pos.x() - seekRect.x()) / float64(seekRect.width()) : (1. - (pos.y() - seekRect.y()) / float64(seekRect.height())); - const auto snapped = snap(result, 0., 1.); + const auto snapped = std::clamp(result, 0., 1.); return _adjustCallback ? _adjustCallback(snapped) : snapped; } @@ -120,7 +120,7 @@ void ContinuousSlider::wheelEvent(QWheelEvent *e) { deltaX *= -1; } auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY; - auto finalValue = snap(_value + delta * coef, 0., 1.); + auto finalValue = std::clamp(_value + delta * coef, 0., 1.); setValue(finalValue); if (_changeProgressCallback) { _changeProgressCallback(finalValue); diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index e4cb7cdb9..9de847c78 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -280,7 +280,11 @@ void SettingsSlider::paintEvent(QPaintEvent *e) { auto activeLeft = getCurrentActiveLeft(); enumerateSections([&](Section §ion) { - auto active = 1. - snap(qAbs(activeLeft - section.left) / float64(section.width), 0., 1.); + auto active = 1. + - std::clamp( + qAbs(activeLeft - section.left) / float64(section.width), + 0., + 1.); if (section.ripple) { auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active); section.ripple->paint(p, section.left, 0, width(), &color); From c90258664d8f7f0071489f179cf68b16d3d87f28 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 24 Jan 2021 01:14:13 +0300 Subject: [PATCH 125/396] Removed unused StaticNeverFreedPointer class from utils. --- Telegram/SourceFiles/core/utils.h | 43 ------------------------------- 1 file changed, 43 deletions(-) diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index f71ba5216..1d7f833ec 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -216,46 +216,3 @@ private: T *_p; }; - -// This pointer is used for static non-POD variables that are allocated -// on first use by constructor and are never automatically freed. -template -class StaticNeverFreedPointer { -public: - explicit StaticNeverFreedPointer(T *p) : _p(p) { - } - StaticNeverFreedPointer(const StaticNeverFreedPointer &other) = delete; - StaticNeverFreedPointer &operator=(const StaticNeverFreedPointer &other) = delete; - - T *data() const { - return _p; - } - T *release() { - return base::take(_p); - } - void reset(T *p = nullptr) { - delete _p; - _p = p; - } - bool isNull() const { - return data() == nullptr; - } - - void clear() { - reset(); - } - T *operator->() const { - return data(); - } - T &operator*() const { - Assert(!isNull()); - return *data(); - } - explicit operator bool() const { - return !isNull(); - } - -private: - T *_p = nullptr; - -}; From d4bbbdb65cd8c9a0d0e96f0d2f53799f7ba340cb Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 24 Jan 2021 01:34:40 +0300 Subject: [PATCH 126/396] Replaced rand_value util function with openssl::RandomValue. --- Telegram/SourceFiles/api/api_sending.cpp | 5 ++- Telegram/SourceFiles/apiwrap.cpp | 14 +++--- .../SourceFiles/boxes/add_contact_box.cpp | 3 +- .../SourceFiles/boxes/create_poll_box.cpp | 3 +- .../boxes/peer_list_controllers.cpp | 3 +- Telegram/SourceFiles/calls/calls_call.cpp | 2 +- .../SourceFiles/calls/calls_group_call.cpp | 3 +- .../chat_helpers/field_autocomplete.cpp | 3 +- Telegram/SourceFiles/core/utils.h | 6 --- Telegram/SourceFiles/data/data_session.cpp | 7 +-- .../data/stickers/data_stickers.cpp | 3 +- .../SourceFiles/history/history_message.cpp | 3 +- .../history_view_voice_record_bar.cpp | 3 +- .../inline_bots/inline_bot_result.cpp | 3 +- .../media/view/media_view_overlay_widget.cpp | 3 +- .../SourceFiles/mtproto/config_loader.cpp | 4 +- .../SourceFiles/mtproto/connection_http.cpp | 3 +- .../SourceFiles/mtproto/connection_tcp.cpp | 6 +-- .../SourceFiles/mtproto/session_private.cpp | 2 +- .../passport/passport_encryption.cpp | 2 +- .../passport/passport_form_controller.cpp | 2 +- .../platform/mac/notifications_manager_mac.mm | 3 +- .../details/storage_file_utilities.cpp | 2 +- .../SourceFiles/storage/localimageloader.cpp | 10 ++--- .../SourceFiles/storage/localimageloader.h | 43 +++++++++++++++++-- .../window/notifications_utilities.cpp | 5 ++- .../window/themes/window_theme.cpp | 3 +- .../window/themes/window_theme_editor_box.cpp | 5 ++- .../themes/window_themes_generate_name.cpp | 6 ++- 29 files changed, 106 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 967048a39..fea236ed2 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_sending.h" #include "api/api_text_entities.h" +#include "base/openssl_help.h" #include "base/unixtime.h" #include "data/data_document.h" #include "data/data_photo.h" @@ -76,7 +77,7 @@ void SendExistingMedia( const auto newId = FullMsgId( peerToChannel(peer->id), session->data().nextLocalMessageId()); - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; auto clientFlags = NewMessageClientFlags(); @@ -249,7 +250,7 @@ bool SendDice(Api::MessageToSend &message) { const auto newId = FullMsgId( peerToChannel(peer->id), session->data().nextLocalMessageId()); - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); auto &histories = history->owner().histories(); auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index af47774f1..820df0929 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4025,7 +4025,7 @@ void ApiWrap::forwardMessages( ids.reserve(count); randomIds.reserve(count); for (const auto item : items) { - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); if (genClientSideMessage) { if (const auto message = item->toHistoryMessage()) { const auto newId = FullMsgId( @@ -4348,7 +4348,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { auto newId = FullMsgId( peerToChannel(peer->id), _session->data().nextLocalMessageId()); - auto randomId = rand_value(); + auto randomId = openssl::RandomValue(); TextUtilities::Trim(sending); @@ -4481,7 +4481,7 @@ void ApiWrap::sendBotStart(not_null bot, PeerData *chat) { sendMessage(std::move(message)); return; } - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); request(MTPmessages_StartBot( bot->inputUser, chat ? chat->input : MTP_inputPeerEmpty(), @@ -4507,7 +4507,7 @@ void ApiWrap::sendInlineResult( const auto newId = FullMsgId( peerToChannel(peer->id), _session->data().nextLocalMessageId()); - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; auto clientFlags = NewMessageClientFlags(); @@ -4659,7 +4659,7 @@ void ApiWrap::sendMedia( not_null item, const MTPInputMedia &media, Api::SendOptions options) { - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); _session->data().registerMessageRandomId(randomId, item->fullId()); sendMediaWithRandomId(item, media, options, randomId); @@ -4727,7 +4727,7 @@ void ApiWrap::sendAlbumWithUploaded( const MessageGroupId &groupId, const MTPInputMedia &media) { const auto localId = item->fullId(); - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); _session->data().registerMessageRandomId(randomId, localId); const auto albumIt = _sendingAlbums.find(groupId.raw()); @@ -5275,7 +5275,7 @@ void ApiWrap::createPoll( MTP_int(replyTo), PollDataToInputMedia(&data), MTP_string(), - MTP_long(rand_value()), + MTP_long(openssl::RandomValue()), MTPReplyMarkup(), MTPVector(), MTP_int(action.options.scheduled) diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 599edfc10..296fa10e6 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mtproto/sender.h" #include "base/flat_set.h" +#include "base/openssl_help.h" #include "boxes/confirm_box.h" #include "boxes/confirm_phone_box.h" // ExtractPhonePrefix. #include "boxes/photo_crop_box.h" @@ -382,7 +383,7 @@ void AddContactBox::save() { lastName = QString(); } _sentName = firstName; - _contactId = rand_value(); + _contactId = openssl::RandomValue(); _addRequest = _session->api().request(MTPcontacts_ImportContacts( MTP_vector( 1, diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 07d0dd0c5..7bd2ba965 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "base/event_filter.h" #include "base/call_delayed.h" +#include "base/openssl_help.h" #include "window/window_session_controller.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -885,7 +886,7 @@ not_null CreatePollBox::setupSolution( object_ptr CreatePollBox::setupContent() { using namespace Settings; - const auto id = rand_value(); + const auto id = openssl::RandomValue(); const auto error = lifetime().make_state(Error::Question); auto result = object_ptr(this); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 3d8544108..b745c48eb 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peer_list_controllers.h" +#include "base/openssl_help.h" #include "boxes/confirm_box.h" #include "ui/widgets/checkbox.h" #include "ui/ui_utility.h" @@ -35,7 +36,7 @@ void ShareBotGame(not_null bot, not_null chat) { auto &histories = history->owner().histories(); const auto requestType = Data::Histories::RequestType::Send; histories.sendRequest(history, requestType, [=](Fn finish) { - const auto randomId = rand_value(); + const auto randomId = openssl::RandomValue(); const auto api = &chat->session().api(); history->sendRequestId = api->request(MTPmessages_SendMedia( MTP_flags(0), diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 2adc74912..74bd55bdb 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -232,7 +232,7 @@ void Call::startOutgoing() { _api.request(MTPphone_RequestCall( MTP_flags(flags), _user->inputUser, - MTP_int(rand_value()), + MTP_int(openssl::RandomValue()), MTP_bytes(_gaHash), MTP_phoneCallProtocol( MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index d4b55dac3..6d2198661 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_session.h" #include "base/global_shortcuts.h" +#include "base/openssl_help.h" #include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_create_adm.h" @@ -201,7 +202,7 @@ void GroupCall::playConnectingSoundOnce() { void GroupCall::start() { _createRequestId = _api.request(MTPphone_CreateGroupCall( _peer->input, - MTP_int(rand_value()) + MTP_int(openssl::RandomValue()) )).done([=](const MTPUpdates &result) { _acceptFields = true; _peer->session().api().applyUpdates(result); diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 36eddd08e..87ee66884 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" #include "base/unixtime.h" +#include "base/openssl_help.h" #include "window/window_session_controller.h" #include "facades.h" #include "styles/style_chat.h" @@ -636,7 +637,7 @@ void FieldAutocomplete::showAnimated() { return; } if (_cache.isNull()) { - _stickersSeed = rand_value(); + _stickersSeed = openssl::RandomValue(); _scroll->show(); _cache = Ui::GrabWidget(this); } diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 1d7f833ec..f2d47c438 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -117,12 +117,6 @@ inline std::array hashMd5Hex(const void *data, int size) { // good random (using openssl implementation) void memset_rand(void *data, uint32 len); -template -T rand_value() { - T result; - memset_rand(&result, sizeof(result)); - return result; -} QString translitRusEng(const QString &rus); QString rusKeyboardLayoutSwitch(const QString &from); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 39b2353fd..beee6604c 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -59,6 +59,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "base/call_delayed.h" +#include "base/openssl_help.h" #include "facades.h" // Notify::switchInlineBotButtonReceived #include "app.h" #include "styles/style_boxes.h" // st::backgroundSize @@ -2427,7 +2428,7 @@ PhotoData *Session::photoFromWeb( return nullptr; } return photo( - rand_value(), + openssl::RandomValue(), uint64(0), QByteArray(), base::unixtime::now(), @@ -2692,7 +2693,7 @@ DocumentData *Session::documentFromWeb( const ImageLocation &thumbnailLocation, const ImageLocation &videoThumbnailLocation) { const auto result = document( - rand_value(), + openssl::RandomValue(), uint64(0), QByteArray(), base::unixtime::now(), @@ -2714,7 +2715,7 @@ DocumentData *Session::documentFromWeb( const ImageLocation &thumbnailLocation, const ImageLocation &videoThumbnailLocation) { const auto result = document( - rand_value(), + openssl::RandomValue(), uint64(0), QByteArray(), base::unixtime::now(), diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index 96f5566bb..13cb46bc0 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_config.h" #include "ui/toast/toast.h" #include "ui/image/image_location_factory.h" +#include "base/openssl_help.h" #include "base/unixtime.h" #include "styles/style_chat_helpers.h" @@ -313,7 +314,7 @@ bool Stickers::applyArchivedResultFake() { const auto raw = set.get(); if ((raw->flags & MTPDstickerSet::Flag::f_installed_date) && !(raw->flags & MTPDstickerSet_ClientFlag::f_special)) { - if (rand_value() % 128 < 64) { + if (openssl::RandomValue() % 128 < 64) { const auto data = MTP_stickerSet( MTP_flags(raw->flags | MTPDstickerSet::Flag::f_archived), MTP_int(raw->installDate), diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 0185d871d..8c1a11eaf 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/history_message.h" +#include "base/openssl_help.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" @@ -291,7 +292,7 @@ void FastShareMessage(not_null item) { auto generateRandom = [&] { auto result = QVector(data->msgIds.size()); for (auto &value : result) { - value = rand_value(); + value = openssl::RandomValue(); } return result; }; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp index 13c8eeb1e..4bc2f6a01 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_send_progress.h" #include "base/event_filter.h" +#include "base/openssl_help.h" #include "base/unixtime.h" #include "boxes/confirm_box.h" #include "core/application.h" @@ -99,7 +100,7 @@ enum class FilterType { [[nodiscard]] not_null DummyDocument( not_null owner) { return owner->document( - rand_value(), + openssl::RandomValue(), uint64(0), QByteArray(), base::unixtime::now(), diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 73e833611..e8e2e75e7 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_result.h" #include "api/api_text_entities.h" +#include "base/openssl_help.h" #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_session.h" @@ -417,7 +418,7 @@ void Result::createGame(not_null session) { return; } - const auto gameId = rand_value(); + const auto gameId = openssl::RandomValue(); _game = session->data().game( gameId, 0, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 26dba3153..9e403dd5b 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/window_controller.h" #include "base/platform/base_platform_info.h" +#include "base/openssl_help.h" #include "base/unixtime.h" #include "main/main_account.h" #include "main/main_domain.h" // Domain::activeSessionValue. @@ -2688,7 +2689,7 @@ void OverlayWidget::initThemePreview() { const auto weakSession = base::make_weak(&_document->session()); const auto path = _document->location().name(); - const auto id = _themePreviewId = rand_value(); + const auto id = _themePreviewId = openssl::RandomValue(); const auto weak = Ui::MakeWeak(this); crl::async([=, data = std::move(current)]() mutable { auto preview = GeneratePreview( diff --git a/Telegram/SourceFiles/mtproto/config_loader.cpp b/Telegram/SourceFiles/mtproto/config_loader.cpp index ef2151f50..949c44947 100644 --- a/Telegram/SourceFiles/mtproto/config_loader.cpp +++ b/Telegram/SourceFiles/mtproto/config_loader.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/config_loader.h" +#include "base/openssl_help.h" #include "mtproto/special_config_request.h" #include "mtproto/facade.h" #include "mtproto/mtproto_dc_options.h" @@ -174,7 +175,8 @@ void ConfigLoader::sendSpecialRequest() { } const auto weak = base::make_weak(this); - const auto index = rand_value() % _specialEndpoints.size(); + const auto index = openssl::RandomValue() + % _specialEndpoints.size(); const auto endpoint = _specialEndpoints.begin() + index; _specialEnumCurrent = specialToRealDcId(endpoint->dcId); diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp index 3f9d005b7..c4a6bdc1c 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.cpp +++ b/Telegram/SourceFiles/mtproto/connection_http.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/connection_http.h" +#include "base/openssl_help.h" #include "base/qthelp_url.h" namespace MTP { @@ -20,7 +21,7 @@ constexpr auto kFullConnectionTimeout = crl::time(8000); HttpConnection::HttpConnection(QThread *thread, const ProxyData &proxy) : AbstractConnection(thread, proxy) -, _checkNonce(rand_value()) { +, _checkNonce(openssl::RandomValue()) { _manager.moveToThread(thread); _manager.setProxy(ToNetworkProxy(proxy)); } diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index f195c7c44..90958753d 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -192,11 +192,11 @@ bytes::span TcpConnection::Protocol::VersionD::finalizePacket( Expects(buffer.size() > 2 && buffer.size() < 0x1000003U); const auto intsSize = uint32(buffer.size() - 2); - const auto padding = rand_value() & 0x0F; + const auto padding = openssl::RandomValue() & 0x0F; const auto bytesSize = intsSize * sizeof(mtpPrime) + padding; buffer[1] = bytesSize; for (auto added = 0; added < padding; added += 4) { - buffer.push_back(rand_value()); + buffer.push_back(openssl::RandomValue()); } return bytes::make_span(buffer).subspan(4, 4 + bytesSize); @@ -244,7 +244,7 @@ TcpConnection::TcpConnection( const ProxyData &proxy) : AbstractConnection(thread, proxy) , _instance(instance) -, _checkNonce(rand_value()) { +, _checkNonce(openssl::RandomValue()) { } ConnectionPointer TcpConnection::clone(const ProxyData &proxy) { diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 9ddab55b8..ea5b3b30f 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -2518,7 +2518,7 @@ void SessionPrivate::authKeyChecked() { resendAll(); } // else receive salt in bad_server_salt first, then try to send all the requests - _pingIdToSend = rand_value(); // get server_salt + _pingIdToSend = openssl::RandomValue(); // get server_salt _sessionData->queueNeedToResumeAndSend(); } diff --git a/Telegram/SourceFiles/passport/passport_encryption.cpp b/Telegram/SourceFiles/passport/passport_encryption.cpp index 4d9b3520e..3e3d4684f 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.cpp +++ b/Telegram/SourceFiles/passport/passport_encryption.cpp @@ -334,7 +334,7 @@ EncryptedData EncryptData( constexpr auto kFromPadding = kMinPadding + kAlignTo - 1; constexpr auto kPaddingDelta = kMaxPadding - kFromPadding; const auto randomPadding = kFromPadding - + (rand_value() % kPaddingDelta); + + (openssl::RandomValue() % kPaddingDelta); const auto padding = randomPadding - ((bytes.size() + randomPadding) % kAlignTo); Assert(padding >= kMinPadding && padding <= kMaxPadding); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index c0a928f40..a1288a8b8 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1421,7 +1421,7 @@ void FormController::restoreScan( void FormController::prepareFile( EditFile &file, const QByteArray &content) { - const auto fileId = rand_value(); + const auto fileId = openssl::RandomValue(); file.fields.size = content.size(); file.fields.id = fileId; file.fields.dcId = _controller->session().mainDcId(); diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index dec09219e..522f585bb 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" #include "base/platform/mac/base_utilities_mac.h" +#include "base/openssl_help.h" #include "history/history.h" #include "ui/empty_userpic.h" #include "main/main_session.h" @@ -229,7 +230,7 @@ private: }; Manager::Private::Private(Manager *manager) -: _managerId(rand_value()) +: _managerId(openssl::RandomValue()) , _managerIdString(QString::number(_managerId)) , _delegate([[NotificationDelegate alloc] initWithManager:manager managerId:_managerId]) { updateDelegate(); diff --git a/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp index 55dacabdd..9591255cb 100644 --- a/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp +++ b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp @@ -58,7 +58,7 @@ FileKey GenerateKey(const QString &basePath) { path.reserve(basePath.size() + 0x11); path += basePath; do { - result = rand_value(); + result = openssl::RandomValue(); path.resize(basePath.size()); path += ToFilePart(result); } while (!result || KeyAlreadyUsed(path)); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index d9c53522f..bad70f0a6 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -58,7 +58,7 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) { return {}; } auto result = PreparedFileThumbnail(); - result.id = rand_value(); + result.id = openssl::RandomValue(); const auto scaled = (width > kThumbnailSize || height > kThumbnailSize); const auto scaledWidth = [&] { return (width > height) @@ -222,7 +222,7 @@ SendMediaReady PreparePeerPhoto(MTP::DcId dcId, PeerId peerId, QImage &&image) { push("b", scaled(320)); push("c", std::move(image), jpeg); - const auto id = rand_value(); + const auto id = openssl::RandomValue(); const auto photo = MTP_photo( MTP_flags(0), MTP_long(id), @@ -402,7 +402,7 @@ void TaskQueueWorker::onTaskAdded() { _inTaskAdded = false; } -SendingAlbum::SendingAlbum() : groupId(rand_value()) { +SendingAlbum::SendingAlbum() : groupId(openssl::RandomValue()) { } void SendingAlbum::fillMedia( @@ -496,7 +496,7 @@ FileLoadTask::FileLoadTask( const TextWithTags &caption, std::shared_ptr album, MsgId msgIdToEdit) -: _id(rand_value()) +: _id(openssl::RandomValue()) , _session(session) , _dcId(session->mainDcId()) , _to(to) @@ -518,7 +518,7 @@ FileLoadTask::FileLoadTask( const VoiceWaveform &waveform, const FileLoadTo &to, const TextWithTags &caption) -: _id(rand_value()) +: _id(openssl::RandomValue()) , _session(session) , _dcId(session->mainDcId()) , _to(to) diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 02a403d9b..25f772998 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/openssl_help.h" #include "base/variant.h" #include "api/api_common.h" #include "ui/chat/attach/attach_prepare.h" @@ -22,13 +23,47 @@ enum class SendMediaType { }; struct SendMediaPrepare { - SendMediaPrepare(const QString &file, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), file(file), peer(peer), type(type), replyTo(replyTo) { + SendMediaPrepare( + const QString &file, + const PeerId &peer, + SendMediaType type, + MsgId replyTo) : id(openssl::RandomValue()), + file(file), + peer(peer), + type(type), + replyTo(replyTo) { } - SendMediaPrepare(const QImage &img, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), img(img), peer(peer), type(type), replyTo(replyTo) { + SendMediaPrepare( + const QImage &img, + const PeerId &peer, + SendMediaType type, + MsgId replyTo) : id(openssl::RandomValue()), + img(img), + peer(peer), + type(type), + replyTo(replyTo) { } - SendMediaPrepare(const QByteArray &data, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(type), replyTo(replyTo) { + SendMediaPrepare( + const QByteArray &data, + const PeerId &peer, + SendMediaType type, + MsgId replyTo) : id(openssl::RandomValue()), + data(data), + peer(peer), + type(type), + replyTo(replyTo) { } - SendMediaPrepare(const QByteArray &data, int duration, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(type), duration(duration), replyTo(replyTo) { + SendMediaPrepare( + const QByteArray &data, + int duration, + const PeerId &peer, + SendMediaType type, + MsgId replyTo) : id(openssl::RandomValue()), + data(data), + peer(peer), + type(type), + duration(duration), + replyTo(replyTo) { } PhotoId id; QString file; diff --git a/Telegram/SourceFiles/window/notifications_utilities.cpp b/Telegram/SourceFiles/window/notifications_utilities.cpp index 34b0c9cb0..724584cee 100644 --- a/Telegram/SourceFiles/window/notifications_utilities.cpp +++ b/Telegram/SourceFiles/window/notifications_utilities.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_utilities.h" #include "base/platform/base_platform_file_utilities.h" +#include "base/openssl_help.h" #include "core/application.h" #include "data/data_peer.h" #include "ui/empty_userpic.h" @@ -59,7 +60,9 @@ QString CachedUserpics::get( } else { v.until = 0; } - v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value(), 16) + qsl(".png"); + v.path = u"%1tdata/temp/%2.png"_q + .arg(cWorkingDir()) + .arg(QString::number(openssl::RandomValue(), 16)); if (key.first || key.second) { if (peer->isSelf()) { const auto method = (_type == Type::Rounded) diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 6e7543734..40d8526dd 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "storage/localimageloader.h" #include "storage/file_upload.h" +#include "base/openssl_help.h" #include "base/parse_helper.h" #include "base/zlib_help.h" #include "base/unixtime.h" @@ -476,7 +477,7 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { attributes.push_back(MTP_documentAttributeImageSize( MTP_int(image.width()), MTP_int(image.height()))); - const auto id = rand_value(); + const auto id = openssl::RandomValue(); const auto document = MTP_document( MTP_flags(0), MTP_long(id), diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 38c57002d..f343907d8 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/base_file_utilities.h" #include "base/zlib_help.h" #include "base/unixtime.h" +#include "base/openssl_help.h" #include "data/data_session.h" #include "data/data_document.h" #include "data/data_cloud_themes.h" @@ -341,7 +342,7 @@ bool CopyColorsToPalette( auto result = QString(); result.reserve(kRandomSlugSize); for (auto i = 0; i != kRandomSlugSize; ++i) { - const auto value = rand_value() % values; + const auto value = openssl::RandomValue() % values; if (value < letters) { result.append(char('A' + value)); } else if (value < 2 * letters) { @@ -447,7 +448,7 @@ SendMediaReady PrepareThemeMedia( auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = rand_value(); + const auto id = openssl::RandomValue(); const auto document = MTP_document( MTP_flags(0), MTP_long(id), diff --git a/Telegram/SourceFiles/window/themes/window_themes_generate_name.cpp b/Telegram/SourceFiles/window/themes/window_themes_generate_name.cpp index 65d407aee..9e56ae592 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_generate_name.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_generate_name.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/themes/window_themes_generate_name.h" +#include "base/openssl_help.h" + namespace Window { namespace Theme { namespace { @@ -337,13 +339,13 @@ QString GenerateName(const QColor &accent) { return result; }; const auto random = [&](const std::vector &values) { - const auto index = rand_value() % values.size(); + const auto index = openssl::RandomValue() % values.size(); return capitalized(values[index]); }; const auto min = ranges::min_element(kColors, pred); Assert(min != end(kColors)); const auto color = capitalized(min->second); - return (rand_value() % 2 == 0) + return (openssl::RandomValue() % 2 == 0) ? random(kAdjectives) + ' ' + color : color + ' ' + random(kSubjectives); } From 9b9531d2798eb0c4624c65ddeee8c87eda23a820 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 24 Jan 2021 09:40:26 +0300 Subject: [PATCH 127/396] Replaced icon color for songs with bright one. --- .../SourceFiles/boxes/edit_caption_box.cpp | 2 +- .../view/media/history_view_document.cpp | 25 +++++++++++ .../inline_bot_layout_internal.cpp | 2 + Telegram/SourceFiles/overview/overview.style | 43 +++++++++++++++---- .../SourceFiles/overview/overview_layout.cpp | 10 +++-- .../attach/attach_single_file_preview.cpp | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 11 +++++ 7 files changed, 81 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index ca9408b30..ace98f86e 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -963,7 +963,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { } const auto icon = &(_isAudio - ? st::historyFileInPlay + ? st::historyFileSongPlay : _isImage ? st::historyFileInImage : st::historyFileInDocument); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 4536e7f39..edbee6cd6 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -456,19 +456,44 @@ void Document::draw( const auto icon = [&] { if (_data->waitingForAlbum()) { + if (_data->isSong()) { + return &(selected + ? st::historyFileSongWaitingSelected + : st::historyFileSongWaiting); + } return &(outbg ? (selected ? st::historyFileOutWaitingSelected : st::historyFileOutWaiting) : (selected ? st::historyFileInWaitingSelected : st::historyFileInWaiting)); } else if (!cornerDownload && (_data->loading() || _data->uploading())) { + if (_data->isSong()) { + return &(selected + ? st::historyFileSongCancelSelected + : st::historyFileSongCancel); + } return &(outbg ? (selected ? st::historyFileOutCancelSelected : st::historyFileOutCancel) : (selected ? st::historyFileInCancelSelected : st::historyFileInCancel)); } else if (showPause) { + if (_data->isSong()) { + return &(selected + ? st::historyFileSongPauseSelected + : st::historyFileSongPause); + } return &(outbg ? (selected ? st::historyFileOutPauseSelected : st::historyFileOutPause) : (selected ? st::historyFileInPauseSelected : st::historyFileInPause)); } else if (loaded || _dataMedia->canBePlayed()) { if (_dataMedia->canBePlayed()) { + if (_data->isSong()) { + return &(selected + ? st::historyFileSongPlaySelected + : st::historyFileSongPlay); + } return &(outbg ? (selected ? st::historyFileOutPlaySelected : st::historyFileOutPlay) : (selected ? st::historyFileInPlaySelected : st::historyFileInPlay)); } else if (_data->isImage()) { return &(outbg ? (selected ? st::historyFileOutImageSelected : st::historyFileOutImage) : (selected ? st::historyFileInImageSelected : st::historyFileInImage)); } return &(outbg ? (selected ? st::historyFileOutDocumentSelected : st::historyFileOutDocument) : (selected ? st::historyFileInDocumentSelected : st::historyFileInDocument)); } + if (_data->isSong()) { + return &(selected + ? st::historyFileSongDownloadSelected + : st::historyFileSongDownload); + } return &(outbg ? (selected ? st::historyFileOutDownloadSelected : st::historyFileOutDownload) : (selected ? st::historyFileInDownloadSelected : st::historyFileInDownload)); }(); const auto previous = [&]() -> const style::icon* { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index a15bea0d8..c6beea47d 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -896,6 +896,8 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con return &st::historyFileInPause; } else if (_document->isImage()) { return &st::historyFileInImage; + } else if (_document->isSong()) { + return &st::historyFileSongPlay; } else if (_document->isVoiceMessage() || _document->isAudioFile()) { return &st::historyFileInPlay; diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 5cdcd40f1..ac4423d7f 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -28,6 +28,15 @@ OverviewFileLayout { songDownload: icon; songDownloadSelected: icon; + voicePause: icon; + voicePauseSelected: icon; + voicePlay: icon; + voicePlaySelected: icon; + voiceCancel: icon; + voiceCancelSelected: icon; + voiceDownload: icon; + voiceDownloadSelected: icon; + filePadding: margins; fileThumbSize: pixels; fileNameTop: pixels; @@ -64,14 +73,23 @@ overviewFileExtTop: 24px; overviewFileExtFg: windowFgActive; overviewFileExtFont: font(18px semibold); -overviewSongPause: icon {{ "playlist_pause", historyFileInIconFg }}; -overviewSongPauseSelected: icon {{ "playlist_pause", historyFileInIconFgSelected }}; -overviewSongPlay: icon {{ "playlist_play", historyFileInIconFg }}; -overviewSongPlaySelected: icon {{ "playlist_play", historyFileInIconFgSelected }}; -overviewSongCancel: icon {{ "playlist_cancel", historyFileInIconFg }}; -overviewSongCancelSelected: icon {{ "playlist_cancel", historyFileInIconFgSelected }}; -overviewSongDownload: icon {{ "playlist_download", historyFileInIconFg }}; -overviewSongDownloadSelected: icon {{ "playlist_download", historyFileInIconFgSelected }}; +overviewVoicePause: icon {{ "playlist_pause", historyFileInIconFg }}; +overviewVoicePauseSelected: icon {{ "playlist_pause", historyFileInIconFgSelected }}; +overviewVoicePlay: icon {{ "playlist_play", historyFileInIconFg }}; +overviewVoicePlaySelected: icon {{ "playlist_play", historyFileInIconFgSelected }}; +overviewVoiceCancel: icon {{ "playlist_cancel", historyFileInIconFg }}; +overviewVoiceCancelSelected: icon {{ "playlist_cancel", historyFileInIconFgSelected }}; +overviewVoiceDownload: icon {{ "playlist_download", historyFileInIconFg }}; +overviewVoiceDownloadSelected: icon {{ "playlist_download", historyFileInIconFgSelected }}; + +overviewSongPause: icon {{ "playlist_pause", historyFileThumbIconFg }}; +overviewSongPauseSelected: icon {{ "playlist_pause", historyFileThumbIconFgSelected }}; +overviewSongPlay: icon {{ "playlist_play", historyFileThumbIconFg }}; +overviewSongPlaySelected: icon {{ "playlist_play", historyFileThumbIconFgSelected }}; +overviewSongCancel: icon {{ "playlist_cancel", historyFileThumbIconFg }}; +overviewSongCancelSelected: icon {{ "playlist_cancel", historyFileThumbIconFgSelected }}; +overviewSongDownload: icon {{ "playlist_download", historyFileThumbIconFg }}; +overviewSongDownloadSelected: icon {{ "playlist_download", historyFileThumbIconFgSelected }}; overviewSmallCancel: icon {{ "history_audio_cancel", historyFileInIconFg }}; overviewSmallCancelSelected: icon {{ "history_audio_cancel", historyFileInIconFgSelected }}; overviewSmallDownload: icon {{ "history_audio_download", historyFileInIconFg }}; @@ -93,6 +111,15 @@ overviewFileLayout: OverviewFileLayout { songDownload: overviewSongDownload; songDownloadSelected: overviewSongDownloadSelected; + voicePause: overviewVoicePause; + voicePauseSelected: overviewVoicePauseSelected; + voicePlay: overviewVoicePlay; + voicePlaySelected: overviewVoicePlaySelected; + voiceCancel: overviewVoiceCancel; + voiceCancelSelected: overviewVoiceCancelSelected; + voiceDownload: overviewVoiceDownload; + voiceDownloadSelected: overviewVoiceDownloadSelected; + filePadding: margins(0px, 3px, 16px, 3px); fileThumbSize: 70px; fileNameTop: 7px; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index d9ee1a862..ad2c727ac 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -734,13 +734,15 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const const auto icon = [&] { if (_data->loading() || _data->uploading()) { - return &(selected ? _st.songCancelSelected : _st.songCancel); + return &(selected ? _st.voiceCancelSelected : _st.voiceCancel); } else if (showPause) { - return &(selected ? _st.songPauseSelected : _st.songPause); + return &(selected ? _st.voicePauseSelected : _st.voicePause); } else if (_dataMedia->canBePlayed()) { - return &(selected ? _st.songPlaySelected : _st.songPlay); + return &(selected ? _st.voicePlaySelected : _st.voicePlay); } - return &(selected ? _st.songDownloadSelected : _st.songDownload); + return &(selected + ? _st.voiceDownloadSelected + : _st.voiceDownload); }(); icon->paintInCenter(p, inner); } diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp index c5e0d343e..c479ba93a 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -168,7 +168,7 @@ void SingleFilePreview::paintEvent(QPaintEvent *e) { } auto &icon = _fileIsAudio - ? st::historyFileInPlay + ? st::historyFileSongPlay : _fileIsImage ? st::historyFileInImage : st::historyFileInDocument; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index fdc66f585..dab0e9ba8 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -160,6 +160,17 @@ historyFileOutWaitingSelected: icon {{ "mediaview_save_check", historyFileOutIco historyFileInWaiting: icon {{ "mediaview_save_check", historyFileInIconFg }}; historyFileInWaitingSelected: icon {{ "mediaview_save_check", historyFileInIconFgSelected }}; +historyFileSongPause: icon {{ "history_file_pause", historyFileThumbIconFg }}; +historyFileSongPauseSelected: icon {{ "history_file_pause", historyFileThumbIconFgSelected }}; +historyFileSongPlay: icon {{ "history_file_play", historyFileThumbIconFg }}; +historyFileSongPlaySelected: icon {{ "history_file_play", historyFileThumbIconFgSelected }}; +historyFileSongCancel: icon {{ "history_file_cancel", historyFileThumbIconFg }}; +historyFileSongCancelSelected: icon {{ "history_file_cancel", historyFileThumbIconFgSelected }}; +historyFileSongDownload: icon {{ "history_file_download", historyFileThumbIconFg }}; +historyFileSongDownloadSelected: icon {{ "history_file_download", historyFileThumbIconFgSelected }}; +historyFileSongWaiting: icon {{ "mediaview_save_check", historyFileThumbIconFg }}; +historyFileSongWaitingSelected: icon {{ "mediaview_save_check", historyFileThumbIconFgSelected }}; + historyFileThumbDownload: icon {{ "history_file_download", historyFileThumbIconFg }}; historyFileThumbDownloadSelected: icon {{ "history_file_download", historyFileThumbIconFgSelected }}; historyFileThumbCancel: icon {{ "history_file_cancel", historyFileThumbIconFg }}; From efde011f1c000fa584b52cfa37ec9b0beeafc442 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 25 Jan 2021 00:51:38 +0400 Subject: [PATCH 128/396] Update tg_owt commit in snap build. --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 291fc61e4..dcc04b57f 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -253,7 +253,7 @@ parts: webrtc: source: https://github.com/desktop-app/tg_owt.git source-depth: 1 - source-commit: 429ec57ecfecb9f7ddde71665adff89158fc2f22 + source-commit: be23804afce3bb2e80a1d57a7c1318c71b82b7de plugin: cmake build-packages: - yasm From df0bca077eaca9399233d23603b363fc4a91b668 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 25 Jan 2021 00:31:58 +0400 Subject: [PATCH 129/396] Fix build with linked gtk --- Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h index 99d3579dd..c12faa846 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h @@ -27,7 +27,7 @@ extern "C" { #endif // !LINK_TO_GTK // To be able to compile with gtk-2.0 headers as well -#define GdkMonitor GdkScreen +typedef struct _GdkMonitor GdkMonitor; typedef struct _GtkAppChooser GtkAppChooser; namespace Platform { From 82d78e7c452a4978c3eea535a80fe876ef09ddaa Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 25 Jan 2021 01:00:01 +0400 Subject: [PATCH 130/396] Decrease indentation in notification manager creation --- .../linux/notifications_manager_linux.cpp | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index a13918362..9effa1964 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -718,39 +718,42 @@ void Create(Window::Notifications::System *system) { } }; + if (!ServiceRegistered) { + CurrentServerInformation = std::nullopt; + CurrentCapabilities = QStringList{}; + InhibitionSupported = false; + managerSetter(); + return; + } + + // There are some asserts that manager is not nullptr, + // avoid crashes until some real manager is created if (!system->managerType().has_value()) { using DummyManager = Window::Notifications::DummyManager; system->setManager(std::make_unique(system)); } - if (ServiceRegistered) { - const auto counter = std::make_shared(3); - const auto oneReady = [=] { - if (!--*counter) { - managerSetter(); - } - }; + const auto counter = std::make_shared(3); + const auto oneReady = [=] { + if (!--*counter) { + managerSetter(); + } + }; - GetServerInformation([=](std::optional result) { - CurrentServerInformation = result; - oneReady(); - }); + GetServerInformation([=](std::optional result) { + CurrentServerInformation = result; + oneReady(); + }); - GetCapabilities([=](QStringList result) { - CurrentCapabilities = result; - oneReady(); - }); + GetCapabilities([=](QStringList result) { + CurrentCapabilities = result; + oneReady(); + }); - GetInhibitionSupported([=](bool result) { - InhibitionSupported = result; - oneReady(); - }); - } else { - CurrentServerInformation = std::nullopt; - CurrentCapabilities = QStringList{}; - InhibitionSupported = false; - managerSetter(); - } + GetInhibitionSupported([=](bool result) { + InhibitionSupported = result; + oneReady(); + }); } class Manager::Private { From b562a4a4793a447b1fd563c327853333dd265c1c Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 25 Jan 2021 09:11:41 +0400 Subject: [PATCH 131/396] Fix path to libva.conf --- Telegram/build/docker/centos_env/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 8b6723ba0..346badae7 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -255,6 +255,7 @@ RUN git clone -b 2.10.0 --depth=1 $GIT/intel/libva.git WORKDIR libva RUN ./autogen.sh \ --enable-static \ + --sysconfdir=/etc \ --with-drivers-path=/usr/lib/dri RUN make -j$(nproc) From b396244606546839e6044d1762edebe3ae5c9dc4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Dec 2020 16:39:10 +0400 Subject: [PATCH 132/396] Update API scheme to layer 123. --- Telegram/Resources/tl/api.tl | 31 +++++++++++++------ Telegram/SourceFiles/apiwrap.cpp | 5 ++- .../SourceFiles/boxes/add_contact_box.cpp | 5 ++- .../SourceFiles/calls/calls_group_call.cpp | 14 ++++++--- .../calls/calls_group_settings.cpp | 5 ++- Telegram/SourceFiles/data/data_channel.cpp | 13 ++++---- Telegram/SourceFiles/data/data_chat.cpp | 13 ++++---- .../data/data_scheduled_messages.cpp | 7 +++-- .../admin_log/history_admin_log_item.cpp | 7 +++-- 9 files changed, 67 insertions(+), 33 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index ce5735679..11d67bbe6 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -127,8 +127,8 @@ chatForbidden#7328bdb id:int title:string = Chat; channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#dc8c181 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; -channelFull#ef3a6acd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; +chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; +channelFull#7a7de4f7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -140,7 +140,7 @@ chatParticipants#3f460fed chat_id:int participants:Vector versi chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; -messageEmpty#83e5de54 id:int = Message; +messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector = Message; messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message; @@ -535,8 +535,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteEmpty#69df3769 = ExportedChatInvite; -chatInviteExported#fc2e05bc link:string = ExportedChatInvite; +chatInviteExported#a9a847ea flags:# revoked:flags.0?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -820,7 +819,7 @@ payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_inf inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials; inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials; inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials; -inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials; +inputPaymentCredentialsGooglePay#8ac32801 payment_token:DataJSON = InputPaymentCredentials; account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword; @@ -1195,7 +1194,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#56b087c9 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true user_id:int date:int active_date:flags.3?int source:int = GroupCallParticipant; +groupCallParticipant#b881f32b flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int muted_cnt:flags.8?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector participants_next_offset:string users:Vector = phone.GroupCall; @@ -1207,6 +1206,14 @@ inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType; inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType; inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType; +chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; + +messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; + +messages.exportedChatInvite#97c5e3a9 invite:ExportedChatInvite recent_importers:Vector users:Vector = messages.ExportedChatInvite; + +messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1366,7 +1373,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.exportChatInvite#14b9bcd7 flags:# peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1456,6 +1463,10 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; +messages.getExportedChatInvites#6d9cae03 flags:# expired:flags.1?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite; +messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1564,7 +1575,7 @@ phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates; phone.joinGroupCall#5f9c8e62 flags:# muted:flags.0?true call:InputGroupCall params:DataJSON = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; -phone.editGroupCallMember#63146ae4 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser = Updates; +phone.editGroupCallMember#a5e76cd8 flags:# muted:flags.0?true call:InputGroupCall user_id:InputUser volume:flags.1?int = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector = Updates; phone.discardGroupCall#7a777135 call:InputGroupCall = Updates; phone.toggleGroupCallSettings#74bbb43d flags:# call:InputGroupCall join_muted:flags.0?Bool = Updates; @@ -1587,4 +1598,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; -// LAYER 122 +// LAYER 123 diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 820df0929..b2c3596be 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2127,7 +2127,10 @@ void ApiWrap::exportInviteLink(not_null peer) { const auto requestId = [&] { return request(MTPmessages_ExportChatInvite( - peer->input + MTP_flags(0), + peer->input, + MTPint(), // expire_date + MTPint() // usage_limit )).done([=](const MTPExportedChatInvite &result) { _exportInviteRequests.erase(peer); const auto link = (result.type() == mtpc_chatInviteExported) diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 296fa10e6..da9c664cd 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -693,7 +693,10 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio } _createdChannel = channel; _creationRequestId = _api.request(MTPmessages_ExportChatInvite( - _createdChannel->input + MTP_flags(0), + _createdChannel->input, + MTPint(), // expire_date + MTPint() // usage_limit )).done([=](const MTPExportedChatInvite &result) { _creationRequestId = 0; if (result.type() == mtpc_chatInviteExported) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 6d2198661..bcc124441 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -347,10 +347,12 @@ void GroupCall::applySelfInCallLocally() { ? i->lastActive : TimeId(0); const auto canSelfUnmute = (muted() != MuteState::ForceMuted); + const auto mutedCount = (i != end(participants)) ? /*i->mutedCount*/0 : 0; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) | (_mySsrc ? Flag(0) : Flag::f_left) - | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)); + | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)) + | (mutedCount ? Flag::f_muted_cnt : Flag(0)); call->applyUpdateChecked( MTP_updateGroupCallParticipants( inputCall(), @@ -361,7 +363,9 @@ void GroupCall::applySelfInCallLocally() { MTP_int(self->bareId()), MTP_int(date), MTP_int(lastActive), - MTP_int(_mySsrc))), + MTP_int(_mySsrc), + MTP_int(10000), // volume + MTP_int(mutedCount))), MTP_int(0)).c_updateGroupCallParticipants()); } @@ -756,7 +760,8 @@ void GroupCall::sendMutedUpdate() { ? MTPphone_EditGroupCallMember::Flag::f_muted : MTPphone_EditGroupCallMember::Flag(0)), inputCall(), - MTP_inputUserSelf() + MTP_inputUserSelf(), + MTP_int(100000) // volume )).done([=](const MTPUpdates &result) { _updateMuteRequestId = 0; _peer->session().api().applyUpdates(result); @@ -796,7 +801,8 @@ void GroupCall::toggleMute(not_null user, bool mute) { ? MTPphone_EditGroupCallMember::Flag::f_muted : MTPphone_EditGroupCallMember::Flag(0)), inputCall(), - user->inputUser + user->inputUser, + MTP_int(100000) // volume )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); }).fail([=](const RPCError &error) { diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index f16dc6de4..97d52424d 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -442,7 +442,10 @@ void GroupCallSettingsBox( if (!copyLink() && !state->generatingLink) { state->generatingLink = true; peer->session().api().request(MTPmessages_ExportChatInvite( - peer->input + MTP_flags(0), + peer->input, + MTPint(), // expire_date + MTPint() // usage_limit )).done([=](const MTPExportedChatInvite &result) { if (result.type() == mtpc_chatInviteExported) { const auto link = qs( diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 569b2736c..d21af2b41 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -777,12 +777,13 @@ void ApplyChannelUpdate( channel->growSlowmodeLastMessage( next->v - channel->slowmodeSeconds()); } - channel->setInviteLink(update.vexported_invite().match([&]( - const MTPDchatInviteExported &data) { - return qs(data.vlink()); - }, [&](const MTPDchatInviteEmpty &) { - return QString(); - })); + if (const auto invite = update.vexported_invite()) { + invite->match([&](const MTPDchatInviteExported &data) { + channel->setInviteLink(qs(data.vlink())); + }); + } else { + channel->setInviteLink(QString()); + } if (const auto location = update.vlocation()) { channel->setLocation(*location); } else { diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 51cace340..c35953c72 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -388,12 +388,13 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { } else { chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0))); } - chat->setInviteLink(update.vexported_invite().match([&]( - const MTPDchatInviteExported &data) { - return qs(data.vlink()); - }, [&](const MTPDchatInviteEmpty &) { - return QString(); - })); + if (const auto invite = update.vexported_invite()) { + invite->match([&](const MTPDchatInviteExported &data) { + chat->setInviteLink(qs(data.vlink())); + }); + } else { + chat->setInviteLink(QString()); + } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(chat, pinned->v); } diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 0883ac09c..754d78ec8 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -33,8 +33,11 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000); } MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) { - return message.match([&](const MTPDmessageEmpty &) { - return MTP_messageEmpty(MTP_int(id)); + return message.match([&](const MTPDmessageEmpty &data) { + return MTP_messageEmpty( + data.vflags(), + MTP_int(id), + data.vpeer_id() ? *data.vpeer_id() : MTPPeer()); }, [&](const MTPDmessageService &data) { return MTP_messageService( MTP_flags(data.vflags().v diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index ed06eda57..cfac099bf 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -59,8 +59,11 @@ MTPMessage PrepareLogMessage( const MTPMessage &message, MsgId newId, TimeId newDate) { - return message.match([&](const MTPDmessageEmpty &) { - return MTP_messageEmpty(MTP_int(newId)); + return message.match([&](const MTPDmessageEmpty &data) { + return MTP_messageEmpty( + data.vflags(), + MTP_int(newId), + data.vpeer_id() ? *data.vpeer_id() : MTPPeer()); }, [&](const MTPDmessageService &data) { const auto removeFlags = MTPDmessageService::Flag::f_out | MTPDmessageService::Flag::f_post From f63f0a7668f62edca4fb2f40ad063ae8f52f7c2d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Dec 2020 18:54:17 +0400 Subject: [PATCH 133/396] Mute by me / change participant volume. --- .../SourceFiles/calls/calls_group_call.cpp | 83 ++++++++++++-- Telegram/SourceFiles/calls/calls_group_call.h | 7 ++ .../SourceFiles/calls/calls_group_members.cpp | 104 +++++++++++++++--- .../SourceFiles/calls/calls_group_members.h | 8 ++ .../SourceFiles/calls/calls_group_panel.cpp | 7 ++ Telegram/SourceFiles/data/data_group_call.cpp | 2 + Telegram/SourceFiles/data/data_group_call.h | 3 + 7 files changed, 188 insertions(+), 26 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index bcc124441..ad8dacfbb 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -238,11 +238,25 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) { using Update = Data::GroupCall::ParticipantUpdate; _peer->groupCall()->participantUpdated( ) | rpl::filter([=](const Update &update) { - return (_instance != nullptr) && !update.now; + return (_instance != nullptr); }) | rpl::start_with_next([=](const Update &update) { - Expects(update.was.has_value()); - - _instance->removeSsrcs({ update.was->ssrc }); + if (!update.now) { + _instance->removeSsrcs({ update.was->ssrc }); + } else { + const auto &now = *update.now; + const auto &was = update.was; + const auto volumeChanged = was + ? (was->volume != now.volume || was->mutedByMe != now.mutedByMe) + : (now.volume != Data::GroupCall::kDefaultVolume || now.mutedByMe); + if (volumeChanged) { + _instance->setVolume( + now.ssrc, + (now.mutedByMe + ? 0. + : (now.volume + / float64(Data::GroupCall::kDefaultVolume)))); + } + } }, _lifetime); SubscribeToMigration(_peer, _lifetime, [=](not_null group) { @@ -610,6 +624,7 @@ void GroupCall::createAndStartController() { std::move(descriptor)); updateInstanceMuteState(); + updateInstanceVolumes(); //raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled()); } @@ -622,6 +637,27 @@ void GroupCall::updateInstanceMuteState() { && state != MuteState::PushToTalk); } +void GroupCall::updateInstanceVolumes() { + const auto real = _peer->groupCall(); + if (!real || real->id() != _id) { + return; + } + + const auto &participants = real->participants(); + for (const auto &participant : participants) { + const auto setVolume = participant.mutedByMe + || (participant.volume != Data::GroupCall::kDefaultVolume); + if (setVolume && participant.ssrc) { + _instance->setVolume( + participant.ssrc, + (participant.mutedByMe + ? 0. + : (participant.volume + / float64(Data::GroupCall::kDefaultVolume)))); + } + } +} + void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) { Expects(!data.updates.empty()); @@ -792,17 +828,46 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) { } } +[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant( + not_null chat, + uint64 id, + not_null user) { + const auto call = chat->groupCall(); + if (!id || !call || call->id() != id) { + return nullptr; + } + const auto &participants = call->participants(); + const auto i = ranges::find( + participants, + user, + &Data::GroupCall::Participant::user); + return (i != end(participants)) ? &*i : nullptr; +} + void GroupCall::toggleMute(not_null user, bool mute) { - if (!_id) { + editParticipant(user, mute, std::nullopt); +} + +void GroupCall::changeVolume(not_null user, int volume) { + editParticipant(user, false, volume); +} + +void GroupCall::editParticipant( + not_null user, + bool mute, + std::optional volume) { + const auto participant = LookupParticipant(_peer, _id, user); + if (!participant) { return; } + using Flag = MTPphone_EditGroupCallMember::Flag; + const auto flags = (mute ? Flag::f_muted : Flag(0)) + | (volume.has_value() ? Flag::f_volume : Flag(0)); _api.request(MTPphone_EditGroupCallMember( - MTP_flags(mute - ? MTPphone_EditGroupCallMember::Flag::f_muted - : MTPphone_EditGroupCallMember::Flag(0)), + MTP_flags(flags), inputCall(), user->inputUser, - MTP_int(100000) // volume + MTP_int(std::clamp(volume.value_or(0), 1, 20000)) )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); }).fail([=](const RPCError &error) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 8b6907d2c..6ec849bfc 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -132,6 +132,7 @@ public: void setAudioDuckingEnabled(bool enabled); void toggleMute(not_null user, bool mute); + void changeVolume(not_null user, int volume); std::variant> inviteUsers( const std::vector> &users); @@ -163,6 +164,7 @@ private: void maybeSendMutedUpdate(MuteState previous); void sendMutedUpdate(); void updateInstanceMuteState(); + void updateInstanceVolumes(); void applySelfInCallLocally(); void rejoin(); @@ -178,6 +180,11 @@ private: void stopConnectingSound(); void playConnectingSoundOnce(); + void editParticipant( + not_null user, + bool mute, + std::optional volume); + [[nodiscard]] MTPInputGroupCall inputCall() const; const not_null _delegate; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index e311318e0..84e10f746 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -76,7 +76,8 @@ public: QRect rect, float64 speaking, float64 active, - float64 muted) = 0; + float64 muted, + bool mutedByMe) = 0; }; class Row final : public PeerListRow { @@ -87,6 +88,7 @@ public: Active, Inactive, Muted, + MutedByMe, Invited, }; @@ -106,6 +108,9 @@ public: [[nodiscard]] bool speaking() const { return _speaking; } + [[nodiscard]] int volume() const { + return _volume; + } void addActionRipple(QPoint point, Fn updateCallback) override; void stopLastActionRipple() override; @@ -177,6 +182,7 @@ private: void setSpeaking(bool speaking); void setState(State state); void setSsrc(uint32 ssrc); + void setVolume(int volume); void ensureUserpicCache( std::shared_ptr &view, @@ -190,6 +196,7 @@ private: Ui::Animations::Simple _mutedAnimation; // For gray/red icon. Ui::Animations::Simple _activeAnimation; // For icon cross animation. uint32 _ssrc = 0; + int _volume = Data::GroupCall::kDefaultVolume; bool _sounding = false; bool _speaking = false; bool _skipLevelUpdate = false; @@ -207,6 +214,7 @@ public: ~MembersController(); using MuteRequest = GroupMembers::MuteRequest; + using VolumeRequest = GroupMembers::VolumeRequest; Main::Session &session() const override; void prepare() override; @@ -221,6 +229,7 @@ public: return _fullCount.value(); } [[nodiscard]] rpl::producer toggleMuteRequests() const; + [[nodiscard]] rpl::producer changeVolumeRequests() const; [[nodiscard]] auto kickMemberRequests() const -> rpl::producer>; @@ -231,7 +240,8 @@ public: QRect rect, float64 speaking, float64 active, - float64 muted) override; + float64 muted, + bool mutedByMe) override; private: [[nodiscard]] std::unique_ptr createSelfRow(); @@ -271,6 +281,7 @@ private: bool _prepared = false; rpl::event_stream _toggleMuteRequests; + rpl::event_stream _changeVolumeRequests; rpl::event_stream> _kickMemberRequests; rpl::variable _fullCount = 1; rpl::variable _fullCountMin = 0; @@ -305,17 +316,20 @@ void Row::setSkipLevelUpdate(bool value) { void Row::updateState(const Data::GroupCall::Participant *participant) { setSsrc(participant ? participant->ssrc : 0); + setVolume(participant + ? participant->volume + : Data::GroupCall::kDefaultVolume); if (!participant) { setState(State::Invited); setSounding(false); setSpeaking(false); } else if (!participant->muted || (participant->sounding && participant->ssrc != 0)) { - setState(State::Active); + setState(participant->mutedByMe ? State::MutedByMe : State::Active); setSounding(participant->sounding && participant->ssrc != 0); setSpeaking(participant->speaking && participant->ssrc != 0); } else if (participant->canSelfUnmute) { - setState(State::Inactive); + setState(participant->mutedByMe ? State::MutedByMe : State::Inactive); setSounding(false); setSpeaking(false); } else { @@ -384,6 +398,10 @@ void Row::setSsrc(uint32 ssrc) { _ssrc = ssrc; } +void Row::setVolume(int volume) { + _volume = volume; +} + void Row::updateLevel(float level) { Expects(_blobsAnimation != nullptr); @@ -451,13 +469,16 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback { auto userpic = ensureUserpicView(); return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { if (_blobsAnimation) { + const auto mutedByMe = (_state == State::MutedByMe); const auto shift = QPointF(x + size / 2., y + size / 2.); auto hq = PainterHighQualityEnabler(p); p.translate(shift); - const auto brush = anim::brush( - st::groupCallMemberInactiveStatus, - st::groupCallMemberActiveStatus, - _speakingAnimation.value(_speaking ? 1. : 0.)); + const auto brush = mutedByMe + ? st::groupCallMemberMutedIcon->b + : anim::brush( + st::groupCallMemberInactiveStatus, + st::groupCallMemberActiveStatus, + _speakingAnimation.value(_speaking ? 1. : 0.)); _blobsAnimation->blobs.paint(p, brush); p.translate(-shift); p.setOpacity(1.); @@ -502,7 +523,7 @@ void Row::paintStatusText( int availableWidth, int outerWidth, bool selected) { - if (_state != State::Invited) { + if (_state != State::Invited && _state != State::MutedByMe) { PeerListRow::paintStatusText( p, st, @@ -514,12 +535,18 @@ void Row::paintStatusText( return; } p.setFont(st::normalFont); - p.setPen(st::groupCallMemberNotJoinedStatus); + if (_state == State::MutedByMe) { + p.setPen(st::groupCallMemberMutedIcon); + } else { + p.setPen(st::groupCallMemberNotJoinedStatus); + } p.drawTextLeft( x, y, outerWidth, - (peer()->isSelf() + (_state == State::MutedByMe + ? "muted by me" + : peer()->isSelf() ? tr::lng_status_connecting(tr::now) : tr::lng_group_call_invited_status(tr::now))); } @@ -561,7 +588,8 @@ void Row::paintAction( (_state == State::Active) ? 1. : 0.); const auto muted = _mutedAnimation.value( (_state == State::Muted) ? 1. : 0.); - _delegate->rowPaintIcon(p, iconRect, speaking, active, muted); + const auto mutedByMe = (_state == State::MutedByMe); + _delegate->rowPaintIcon(p, iconRect, speaking, active, muted, mutedByMe); } void Row::refreshStatus() { @@ -984,6 +1012,11 @@ auto MembersController::toggleMuteRequests() const return _toggleMuteRequests.events(); } +auto MembersController::changeVolumeRequests() const +-> rpl::producer { + return _changeVolumeRequests.events(); +} + bool MembersController::rowCanMuteMembers() { return _peer->canManageGroupCall(); } @@ -997,11 +1030,12 @@ void MembersController::rowPaintIcon( QRect rect, float64 speaking, float64 active, - float64 muted) { + float64 muted, + bool mutedByMe) { const auto &greenIcon = st::groupCallMemberColoredCrossLine.icon; const auto left = rect.x() + (rect.width() - greenIcon.width()) / 2; const auto top = rect.y() + (rect.height() - greenIcon.height()) / 2; - if (speaking == 1.) { + if (speaking == 1. && !mutedByMe) { // Just green icon, no cross, no coloring. greenIcon.paintInCenter(p, rect); return; @@ -1029,7 +1063,9 @@ void MembersController::rowPaintIcon( } const auto activeInactiveColor = anim::color( st::groupCallMemberInactiveIcon, - st::groupCallMemberActiveIcon, + (mutedByMe + ? st::groupCallMemberMutedIcon + : st::groupCallMemberActiveIcon), speaking); const auto iconColor = anim::color( activeInactiveColor, @@ -1119,7 +1155,8 @@ base::unique_qptr MembersController::createRowContextMenu( } return false; }(); - const auto mute = admin + const auto amCallAdmin = _peer->canManageGroupCall(); + const auto mute = (admin || !amCallAdmin) ? (muteState == Row::State::Active) : (muteState != Row::State::Muted); const auto toggleMute = crl::guard(this, [=] { @@ -1128,6 +1165,12 @@ base::unique_qptr MembersController::createRowContextMenu( .mute = mute, }); }); + const auto changeVolume = crl::guard(this, [=](int volume) { + _changeVolumeRequests.fire(VolumeRequest{ + .user = user, + .volume = std::clamp(volume, 1, 20000), + }); + }); const auto session = &user->session(); const auto getCurrentWindow = [=]() -> Window::SessionController* { @@ -1179,7 +1222,7 @@ base::unique_qptr MembersController::createRowContextMenu( }); if ((muteState != Row::State::Invited) - && _peer->canManageGroupCall() + && amCallAdmin && (!admin || mute)) { result->addAction( (mute @@ -1187,6 +1230,24 @@ base::unique_qptr MembersController::createRowContextMenu( : tr::lng_group_call_context_unmute(tr::now)), toggleMute); } + if (real->ssrc() != 0) { + if (!amCallAdmin + && ((muteState == Row::State::Active) + || (real->state() == Row::State::MutedByMe))) { + result->addAction( + ((muteState == Row::State::Active) + ? "Mute for me" + : "Unmute for me"), + toggleMute); + } + const auto volume = real->volume(); + result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] { + changeVolume(volume + 2000); + }); + result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] { + changeVolume(volume - 2000); + }); + } result->addAction( tr::lng_context_view_profile(tr::now), showProfile); @@ -1238,6 +1299,9 @@ std::unique_ptr MembersController::createInvitedRow( } // namespace + +const int GroupMembers::kDefaultVolume = Data::GroupCall::kDefaultVolume; + GroupMembers::GroupMembers( not_null parent, not_null call) @@ -1258,6 +1322,12 @@ auto GroupMembers::toggleMuteRequests() const _listController.get())->toggleMuteRequests(); } +auto GroupMembers::changeVolumeRequests() const +-> rpl::producer { + return static_cast( + _listController.get())->changeVolumeRequests(); +} + auto GroupMembers::kickMemberRequests() const -> rpl::producer> { return static_cast( diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index e255d2ee4..76ed90e93 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -30,15 +30,23 @@ public: not_null parent, not_null call); + static const int kDefaultVolume;/* = Data::GroupCall::kDefaultVolume*/ + struct MuteRequest { not_null user; bool mute = false; }; + struct VolumeRequest { + not_null user; + int volume = kDefaultVolume; + bool finalized = true; + }; [[nodiscard]] int desiredHeight() const; [[nodiscard]] rpl::producer desiredHeightValue() const override; [[nodiscard]] rpl::producer fullCountValue() const; [[nodiscard]] rpl::producer toggleMuteRequests() const; + [[nodiscard]] rpl::producer changeVolumeRequests() const; [[nodiscard]] auto kickMemberRequests() const -> rpl::producer>; [[nodiscard]] rpl::producer<> addMembersRequests() const { diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index a6e260bb2..32826be62 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -511,6 +511,13 @@ void GroupPanel::initWithCall(GroupCall *call) { } }, _callLifetime); + _members->changeVolumeRequests( + ) | rpl::start_with_next([=](GroupMembers::VolumeRequest request) { + if (_call) { + _call->changeVolume(request.user, request.volume); + } + }, _callLifetime); + _members->kickMemberRequests( ) | rpl::start_with_next([=](not_null user) { kickMember(user); diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 22a1a5961..9ca685979 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -280,8 +280,10 @@ void GroupCall::applyParticipantsSlice( .date = data.vdate().v, .lastActive = lastActive, .ssrc = uint32(data.vsource().v), + .volume = data.vvolume().value_or(kDefaultVolume), .speaking = canSelfUnmute && (was ? was->speaking : false), .muted = data.is_muted(), + .mutedByMe = data.is_muted_by_you(), .canSelfUnmute = canSelfUnmute, }; if (i == end(_participants)) { diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 2aad686a3..5bf7e7c40 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -32,14 +32,17 @@ public: void setPeer(not_null peer); + static constexpr auto kDefaultVolume = 10000; struct Participant { not_null user; TimeId date = 0; TimeId lastActive = 0; uint32 ssrc = 0; + int volume = 0; bool sounding = false; bool speaking = false; bool muted = false; + bool mutedByMe = false; bool canSelfUnmute = false; }; struct ParticipantUpdate { From 40fda9503f83fd6051306383bbc58cbae8f5417d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Dec 2020 19:17:50 +0400 Subject: [PATCH 134/396] Apply mute by me / volume change locally. --- .../SourceFiles/calls/calls_group_call.cpp | 66 ++++++++++++++----- Telegram/SourceFiles/calls/calls_group_call.h | 4 ++ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index ad8dacfbb..45dc658b9 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -55,6 +55,22 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000); settings.callVideoInputDeviceId()); } +[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant( + not_null chat, + uint64 id, + not_null user) { + const auto call = chat->groupCall(); + if (!id || !call || call->id() != id) { + return nullptr; + } + const auto &participants = call->participants(); + const auto i = ranges::find( + participants, + user, + &Data::GroupCall::Participant::user); + return (i != end(participants)) ? &*i : nullptr; +} + } // namespace GroupCall::GroupCall( @@ -383,6 +399,38 @@ void GroupCall::applySelfInCallLocally() { MTP_int(0)).c_updateGroupCallParticipants()); } +void GroupCall::applyParticipantLocally( + not_null user, + bool mute, + std::optional volume) { + const auto participant = LookupParticipant(_peer, _id, user); + if (!participant || !participant->ssrc) { + return; + } + const auto canSelfUnmute = participant->canSelfUnmute; + const auto mutedCount = 0/*participant->mutedCount*/; + using Flag = MTPDgroupCallParticipant::Flag; + const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) + | (participant->lastActive ? Flag::f_active_date : Flag(0)) + | (participant->muted ? Flag::f_muted : Flag(0)) + | (participant->mutedByMe ? Flag::f_muted_by_you : Flag(0)) + | (mutedCount ? Flag::f_muted_cnt : Flag(0)); + _peer->groupCall()->applyUpdateChecked( + MTP_updateGroupCallParticipants( + inputCall(), + MTP_vector( + 1, + MTP_groupCallParticipant( + MTP_flags(flags), + MTP_int(user->bareId()), + MTP_int(participant->date), + MTP_int(participant->lastActive), + MTP_int(participant->ssrc), + MTP_int(volume.value_or(participant->volume)), // volume + MTP_int(mutedCount))), + MTP_int(0)).c_updateGroupCallParticipants()); +} + void GroupCall::hangup() { finish(FinishType::Ended); } @@ -828,22 +876,6 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) { } } -[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant( - not_null chat, - uint64 id, - not_null user) { - const auto call = chat->groupCall(); - if (!id || !call || call->id() != id) { - return nullptr; - } - const auto &participants = call->participants(); - const auto i = ranges::find( - participants, - user, - &Data::GroupCall::Participant::user); - return (i != end(participants)) ? &*i : nullptr; -} - void GroupCall::toggleMute(not_null user, bool mute) { editParticipant(user, mute, std::nullopt); } @@ -860,6 +892,8 @@ void GroupCall::editParticipant( if (!participant) { return; } + applyParticipantLocally(user, mute, volume); + using Flag = MTPphone_EditGroupCallMember::Flag; const auto flags = (mute ? Flag::f_muted : Flag(0)) | (volume.has_value() ? Flag::f_volume : Flag(0)); diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 6ec849bfc..f4f07dae4 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -184,6 +184,10 @@ private: not_null user, bool mute, std::optional volume); + void applyParticipantLocally( + not_null user, + bool mute, + std::optional volume); [[nodiscard]] MTPInputGroupCall inputCall() const; From b58a9770298c62e0819bd13e92d2bf80b9c22fdd Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Dec 2020 20:58:46 +0400 Subject: [PATCH 135/396] Remove volume change on muted rows. --- .../SourceFiles/calls/calls_group_members.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 84e10f746..98b31e790 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1240,13 +1240,16 @@ base::unique_qptr MembersController::createRowContextMenu( : "Unmute for me"), toggleMute); } - const auto volume = real->volume(); - result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume + 2000); - }); - result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume - 2000); - }); + if (muteState != Row::State::Muted + && muteState != Row::State::MutedByMe) { + const auto volume = real->volume(); + result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] { + changeVolume(volume + 2000); + }); + result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] { + changeVolume(volume - 2000); + }); + } } result->addAction( tr::lng_context_view_profile(tr::now), From 02ad5f2772109b9535fd5e0b20d6214aea15849b Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 14 Jan 2021 13:05:14 +0400 Subject: [PATCH 136/396] Update API scheme and start invite links. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 2 + Telegram/Resources/tl/api.tl | 9 +- Telegram/SourceFiles/api/api_invite_links.cpp | 206 ++++++++++++++++++ Telegram/SourceFiles/api/api_invite_links.h | 85 ++++++++ Telegram/SourceFiles/apiwrap.cpp | 38 +--- Telegram/SourceFiles/apiwrap.h | 5 +- .../SourceFiles/boxes/add_contact_box.cpp | 3 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 3 +- .../boxes/peers/edit_peer_info_box.cpp | 71 ++++++ .../boxes/peers/edit_peer_type_box.cpp | 34 +-- 11 files changed, 403 insertions(+), 55 deletions(-) create mode 100644 Telegram/SourceFiles/api/api_invite_links.cpp create mode 100644 Telegram/SourceFiles/api/api_invite_links.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 81df3d5aa..9de5f9162 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -145,6 +145,8 @@ PRIVATE api/api_global_privacy.h api/api_hash.cpp api/api_hash.h + api/api_invite_links.cpp + api/api_invite_links.h api/api_media.cpp api/api_media.h api/api_self_destruct.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index dcfb9aead..0afb64248 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -932,6 +932,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_exceptions" = "Exceptions"; "lng_manage_peer_removed_users" = "Removed users"; "lng_manage_peer_permissions" = "Permissions"; +"lng_manage_peer_invite_links" = "Invite links"; + "lng_manage_peer_group_type" = "Group type"; "lng_manage_peer_channel_type" = "Channel type"; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 11d67bbe6..1d2d02630 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -535,7 +535,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#a9a847ea flags:# revoked:flags.0?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#a9a847ea flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -1210,7 +1210,7 @@ chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; -messages.exportedChatInvite#97c5e3a9 invite:ExportedChatInvite recent_importers:Vector users:Vector = messages.ExportedChatInvite; +messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; @@ -1373,7 +1373,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#14b9bcd7 flags:# peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; +messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1463,9 +1463,8 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6d9cae03 flags:# expired:flags.1?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvites#6d9cae03 flags:# peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; -messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; updates.getState#edd4882a = updates.State; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp new file mode 100644 index 000000000..b8ece44a1 --- /dev/null +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -0,0 +1,206 @@ +/* +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 "api/api_invite_links.h" + +#include "data/data_peer.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_session.h" +#include "data/data_changes.h" +#include "main/main_session.h" +#include "apiwrap.h" + +namespace Api { +namespace { + +constexpr auto kFirstPage = 10; +constexpr auto kPerPage = 50; + +} // namespace + +InviteLinks::InviteLinks(not_null api) : _api(api) { +} + +void InviteLinks::create( + not_null peer, + TimeId expireDate, + int usageLimit) { + if (_createRequests.contains(peer)) { + return; + } + + using Flag = MTPmessages_ExportChatInvite::Flag; + const auto requestId = _api->request(MTPmessages_ExportChatInvite( + MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) + | (usageLimit ? Flag::f_usage_limit : Flag(0))), + peer->input, + MTP_int(expireDate), + MTP_int(usageLimit) + )).done([=](const MTPExportedChatInvite &result) { + _createRequests.erase(peer); + const auto link = (result.type() == mtpc_chatInviteExported) + ? qs(result.c_chatInviteExported().vlink()) + : QString(); + if (!expireDate && !usageLimit) { + editPermanentLink(peer, QString(), link); + } + }).fail([=](const RPCError &error) { + _createRequests.erase(peer); + }).send(); + _createRequests.emplace(peer, requestId); +} + +void InviteLinks::edit( + not_null peer, + const QString &link, + TimeId expireDate, + int usageLimit) { + const auto key = EditKey{ peer, link }; + if (_editRequests.contains(key)) { + return; + } + + using Flag = MTPmessages_EditExportedChatInvite::Flag; + const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( + MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) + | (usageLimit ? Flag::f_usage_limit : Flag(0))), + peer->input, + MTP_string(link), + MTP_int(expireDate), + MTP_int(usageLimit) + )).done([=](const MTPmessages_ExportedChatInvite &result) { + _editRequests.erase(key); + result.match([&](const MTPDmessages_exportedChatInvite &data) { + _api->session().data().processUsers(data.vusers()); + const auto &invite = data.vinvite(); + const auto link = (invite.type() == mtpc_chatInviteExported) + ? qs(invite.c_chatInviteExported().vlink()) + : QString(); + // #TODO links + }); + }).fail([=](const RPCError &error) { + _editRequests.erase(key); + }).send(); + _editRequests.emplace(key, requestId); +} + +void InviteLinks::revoke(not_null peer, const QString &link) { + const auto key = EditKey{ peer, link }; + if (_editRequests.contains(key)) { + return; + } + + const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( + MTP_flags(MTPmessages_EditExportedChatInvite::Flag::f_revoked), + peer->input, + MTP_string(link), + MTPint(), // expire_date + MTPint() // usage_limit + )).done([=](const MTPmessages_ExportedChatInvite &result) { + _editRequests.erase(key); + result.match([&](const MTPDmessages_exportedChatInvite &data) { + _api->session().data().processUsers(data.vusers()); + const auto &invite = data.vinvite(); + const auto link = (invite.type() == mtpc_chatInviteExported) + ? qs(invite.c_chatInviteExported().vlink()) + : QString(); + editPermanentLink(peer, key.link, link); + }); + }).fail([=](const RPCError &error) { + _editRequests.erase(key); + }).send(); + _editRequests.emplace(key, requestId); +} + +void InviteLinks::requestLinks(not_null peer) { + if (_firstSliceRequests.contains(peer)) { + return; + } + const auto requestId = _api->request(MTPmessages_GetExportedChatInvites( + MTP_flags(0), + peer->input, + MTPInputUser(), // admin_id + MTPstring(), // offset_link + MTP_int(kFirstPage) + )).done([=](const MTPmessages_ExportedChatInvites &result) { + _firstSliceRequests.remove(peer); + _firstSlices.emplace_or_assign(peer, parseSlice(peer, result)); + peer->session().changes().peerUpdated( + peer, + Data::PeerUpdate::Flag::InviteLink); + }).fail([=](const RPCError &error) { + _firstSliceRequests.remove(peer); + }).send(); + _firstSliceRequests.emplace(peer, requestId); +} + +auto InviteLinks::links(not_null peer) const -> Links { + const auto i = _firstSlices.find(peer); + return (i != end(_firstSlices)) ? i->second : Links(); +} + +auto InviteLinks::parseSlice( + not_null peer, + const MTPmessages_ExportedChatInvites &slice) const -> Links { + auto result = Links(); + slice.match([&](const MTPDmessages_exportedChatInvites &data) { + auto &owner = peer->session().data(); + owner.processUsers(data.vusers()); + result.count = data.vcount().v; + for (const auto &invite : data.vinvites().v) { + invite.match([&](const MTPDchatInviteExported &data) { + result.links.push_back({ + .link = qs(data.vlink()), + .admin = owner.user(data.vadmin_id().v), + .date = data.vdate().v, + .expireDate = data.vexpire_date().value_or_empty(), + .usageLimit = data.vusage_limit().value_or_empty(), + .usage = data.vusage().value_or_empty(), + .revoked = data.is_revoked(), + }); + }); + } + }); + return result; +} + +void InviteLinks::requestMoreLinks( + not_null peer, + const QString &last, + Fn done) { + _api->request(MTPmessages_GetExportedChatInvites( + MTP_flags(MTPmessages_GetExportedChatInvites::Flag::f_offset_link), + peer->input, + MTPInputUser(), // admin_id, + MTP_string(last), + MTP_int(kPerPage) + )).done([=](const MTPmessages_ExportedChatInvites &result) { + done(parseSlice(peer, result)); + }).fail([=](const RPCError &error) { + done(Links()); + }).send(); +} + +void InviteLinks::editPermanentLink( + not_null peer, + const QString &from, + const QString &to) { + if (const auto chat = peer->asChat()) { + if (chat->inviteLink() == from) { + chat->setInviteLink(to); + } + } else if (const auto channel = peer->asChannel()) { + if (channel->inviteLink() == from) { + channel->setInviteLink(to); + } + } else { + Unexpected("Peer in InviteLinks::editMainLink."); + } +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h new file mode 100644 index 000000000..7de6341be --- /dev/null +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -0,0 +1,85 @@ +/* +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 + +class ApiWrap; + +namespace Api { + +struct InviteLink { + QString link; + not_null admin; + TimeId date; + TimeId expireDate = 0; + int usageLimit = 0; + int usage = 0; + bool revoked = false; +}; + +struct PeerInviteLinks { + std::vector links; + int count = 0; +}; + +class InviteLinks final { +public: + explicit InviteLinks(not_null api); + + using Link = InviteLink; + using Links = PeerInviteLinks; + + void create( + not_null peer, + TimeId expireDate = 0, + int usageLimit = 0); + void edit( + not_null peer, + const QString &link, + TimeId expireDate, + int usageLimit); + void revoke(not_null peer, const QString &link); + + void requestLinks(not_null peer); + [[nodiscard]] Links links(not_null peer) const; + + void requestMoreLinks( + not_null peer, + const QString &last, + Fn done); + +private: + struct EditKey { + not_null peer; + QString link; + + friend inline bool operator<(const EditKey &a, const EditKey &b) { + return (a.peer == b.peer) + ? (a.link < b.link) + : (a.peer < b.peer); + } + }; + + void editPermanentLink( + not_null peer, + const QString &from, + const QString &to); + [[nodiscard]] Links parseSlice( + not_null peer, + const MTPmessages_ExportedChatInvites &slice) const; + + const not_null _api; + + base::flat_map, Links> _firstSlices; + base::flat_map, mtpRequestId> _firstSliceRequests; + + base::flat_map, mtpRequestId> _createRequests; + base::flat_map _editRequests; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b2c3596be..3a311b7a6 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_authorizations.h" #include "api/api_attached_stickers.h" #include "api/api_hash.h" +#include "api/api_invite_links.h" #include "api/api_media.h" #include "api/api_sending.h" #include "api/api_text_entities.h" @@ -194,7 +195,8 @@ ApiWrap::ApiWrap(not_null session) , _attachedStickers(std::make_unique(this)) , _selfDestruct(std::make_unique(this)) , _sensitiveContent(std::make_unique(this)) -, _globalPrivacy(std::make_unique(this)) { +, _globalPrivacy(std::make_unique(this)) +, _inviteLinks(std::make_unique(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -2120,36 +2122,6 @@ void ApiWrap::unblockPeer(not_null peer, Fn onDone) { _blockRequests.emplace(peer, requestId); } -void ApiWrap::exportInviteLink(not_null peer) { - if (_exportInviteRequests.find(peer) != end(_exportInviteRequests)) { - return; - } - - const auto requestId = [&] { - return request(MTPmessages_ExportChatInvite( - MTP_flags(0), - peer->input, - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPExportedChatInvite &result) { - _exportInviteRequests.erase(peer); - const auto link = (result.type() == mtpc_chatInviteExported) - ? qs(result.c_chatInviteExported().vlink()) - : QString(); - if (const auto chat = peer->asChat()) { - chat->setInviteLink(link); - } else if (const auto channel = peer->asChannel()) { - channel->setInviteLink(link); - } else { - Unexpected("Peer in ApiWrap::exportInviteLink."); - } - }).fail([=](const RPCError &error) { - _exportInviteRequests.erase(peer); - }).send(); - }(); - _exportInviteRequests.emplace(peer, requestId); -} - void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) { const auto key = [&] { switch (peer.type()) { @@ -5243,6 +5215,10 @@ Api::GlobalPrivacy &ApiWrap::globalPrivacy() { return *_globalPrivacy; } +Api::InviteLinks &ApiWrap::inviteLinks() { + return *_inviteLinks; +} + void ApiWrap::createPoll( const PollData &data, const SendAction &action, diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index dbe80b6f0..47697939f 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -60,6 +60,7 @@ class AttachedStickers; class SelfDestruct; class SensitiveContent; class GlobalPrivacy; +class InviteLinks; namespace details { @@ -289,7 +290,6 @@ public: void blockPeer(not_null peer); void unblockPeer(not_null peer, Fn onDone = nullptr); - void exportInviteLink(not_null peer); void requestNotifySettings(const MTPInputNotifyPeer &peer); void updateNotifySettingsDelayed(not_null peer); void saveDraftToCloudDelayed(not_null history); @@ -464,6 +464,7 @@ public: [[nodiscard]] Api::SelfDestruct &selfDestruct(); [[nodiscard]] Api::SensitiveContent &sensitiveContent(); [[nodiscard]] Api::GlobalPrivacy &globalPrivacy(); + [[nodiscard]] Api::InviteLinks &inviteLinks(); void createPoll( const PollData &data, @@ -701,7 +702,6 @@ private: QMap _channelAmInRequests; base::flat_map, mtpRequestId> _blockRequests; - base::flat_map, mtpRequestId> _exportInviteRequests; base::flat_map _notifySettingRequests; base::flat_map, mtpRequestId> _draftsSaveRequestIds; base::Timer _draftsSaveTimer; @@ -828,6 +828,7 @@ private: const std::unique_ptr _selfDestruct; const std::unique_ptr _sensitiveContent; const std::unique_ptr _globalPrivacy; + const std::unique_ptr _inviteLinks; base::flat_map _pollVotesRequestIds; base::flat_map _pollCloseRequestIds; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index da9c664cd..54e633fdc 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" +#include "api/api_invite_links.h" #include "main/main_session.h" #include "facades.h" #include "styles/style_layers.h" @@ -942,7 +943,7 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) { void SetupChannelBox::mousePressEvent(QMouseEvent *e) { if (_linkOver) { if (_channel->inviteLink().isEmpty()) { - _channel->session().api().exportInviteLink(_channel); + _channel->session().api().inviteLinks().create(_channel); } else { QGuiApplication::clipboard()->setText(_channel->inviteLink()); Ui::Toast::Show(tr::lng_create_channel_link_copied(tr::now)); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 051a582fc..a9485ea86 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" +#include "api/api_invite_links.h" #include "history/history.h" #include "history/history_item.h" #include "ui/widgets/checkbox.h" @@ -407,7 +408,7 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) { mouseMoveEvent(e); if (_linkOver) { if (_channel->inviteLink().isEmpty()) { - _channel->session().api().exportInviteLink(_channel); + _channel->session().api().inviteLinks().create(_channel); } else { QGuiApplication::clipboard()->setText(_channel->inviteLink()); Ui::Toast::Show(tr::lng_create_channel_link_copied(tr::now)); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index d78421756..edf4465bb 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_peer.h" #include "data/data_session.h" +#include "data/data_changes.h" #include "history/admin_log/history_admin_log_section.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" @@ -45,6 +46,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "info/profile/info_profile_icon.h" #include "app.h" +#include "apiwrap.h" +#include "api/api_invite_links.h" #include "facades.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -236,6 +239,49 @@ void ShowEditPermissions( }, box->lifetime()); } +void ShowEditInviteLinks( + not_null navigation, + not_null peer) { + const auto box = Ui::show( + Box(navigation, peer), + Ui::LayerOption::KeepOther); + const auto saving = box->lifetime().make_state(0); + const auto save = [=]( + not_null peer, + EditPeerPermissionsBox::Result result) { + Expects(result.slowmodeSeconds == 0 || peer->isChannel()); + + const auto close = crl::guard(box, [=] { box->closeBox(); }); + SaveDefaultRestrictions( + peer, + MTP_chatBannedRights(MTP_flags(result.rights), MTP_int(0)), + close); + if (const auto channel = peer->asChannel()) { + SaveSlowmodeSeconds(channel, result.slowmodeSeconds, close); + } + }; + box->saveEvents( + ) | rpl::start_with_next([=](EditPeerPermissionsBox::Result result) { + if (*saving) { + return; + } + *saving = true; + + const auto saveFor = peer->migrateToOrMe(); + const auto chat = saveFor->asChat(); + if (!result.slowmodeSeconds || !chat) { + save(saveFor, result); + return; + } + const auto api = &peer->session().api(); + api->migrateChat(chat, [=](not_null channel) { + save(channel, result); + }, [=](const RPCError &error) { + *saving = false; + }); + }, box->lifetime()); +} + } // namespace namespace { @@ -868,6 +914,11 @@ void Controller::fillManageSection() { ? channel->canEditPermissions() : chat->canEditPermissions(); }(); + const auto canEditInviteLinks = [&] { + return isChannel + ? channel->canHaveInviteLink() + : chat->canHaveInviteLink(); + }(); const auto canViewAdmins = [&] { return isChannel ? channel->canViewAdmins() @@ -949,6 +1000,26 @@ void Controller::fillManageSection() { [=] { ShowEditPermissions(_navigation, _peer); }, st::infoIconPermissions); } + if (canEditInviteLinks) { + AddButtonWithCount( + _controls.buttonsLayout, + tr::lng_manage_peer_invite_links(), + Info::Profile::MigratedOrMeValue( + _peer + ) | rpl::map([=](not_null peer) { + peer->session().api().inviteLinks().requestLinks(peer); + return peer->session().changes().peerUpdates( + peer, + Data::PeerUpdate::Flag::InviteLink + ) | rpl::map([=] { + return peer->session().api().inviteLinks().links( + peer).count; + }); + }) | rpl::flatten_latest( + ) | ToPositiveNumberString(), + [=] { ShowEditInviteLinks(_navigation, _peer); }, + st::infoIconPermissions); + } if (canViewAdmins) { AddButtonWithCount( _controls.buttonsLayout, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 386d1c36e..5b20229e2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_type_box.h" #include "apiwrap.h" +#include "api/api_invite_links.h" #include "main/main_session.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" @@ -126,8 +127,7 @@ private: void refreshEditInviteLink(); void refreshCreateInviteLink(); void createInviteLink(); - void revokeInviteLink(); - void exportInviteLink(const QString &confirmation); + void revokeInviteLink(const QString &link); void fillPrivaciesButtons( not_null parent, @@ -536,22 +536,26 @@ void Controller::showUsernameResult( } void Controller::createInviteLink() { - exportInviteLink((_isGroup - ? tr::lng_group_invite_about - : tr::lng_group_invite_about_channel)(tr::now)); -} - -void Controller::revokeInviteLink() { - exportInviteLink(tr::lng_group_invite_about_new(tr::now)); -} - -void Controller::exportInviteLink(const QString &confirmation) { const auto callback = crl::guard(this, [=](Fn &&close) { close(); - _peer->session().api().exportInviteLink(_peer->migrateToOrMe()); + _peer->session().api().inviteLinks().create(_peer->migrateToOrMe()); }); auto box = Box( - confirmation, + (_isGroup + ? tr::lng_group_invite_about + : tr::lng_group_invite_about_channel)(tr::now), + std::move(callback)); + Ui::show(std::move(box), Ui::LayerOption::KeepOther); +} + +void Controller::revokeInviteLink(const QString &link) { + const auto callback = crl::guard(this, [=](Fn &&close) { + close(); + const auto peer = _peer->migrateToOrMe(); + peer->session().api().inviteLinks().revoke(peer, link); + }); + auto box = Box( + tr::lng_group_invite_about_new(tr::now), std::move(callback)); Ui::show(std::move(box), Ui::LayerOption::KeepOther); } @@ -623,7 +627,7 @@ object_ptr Controller::createInviteLinkEdit() { container, tr::lng_group_invite_create_new(tr::now), st::editPeerInviteLinkButton) - )->addClickHandler([=] { revokeInviteLink(); }); + )->addClickHandler([=] { revokeInviteLink(inviteLinkText()); }); observeInviteLink(); From e5320b4b4e25be85a1c993e6242659bc965f0fee Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 14 Jan 2021 17:13:43 +0400 Subject: [PATCH 137/396] Implement new permanent invite link management. --- .../icons/info/edit/group_manage_actions.png | Bin 0 -> 430 bytes .../info/edit/group_manage_actions@2x.png | Bin 0 -> 780 bytes .../info/edit/group_manage_actions@3x.png | Bin 0 -> 1479 bytes .../icons/info/edit/group_manage_admins.png | Bin 0 -> 1149 bytes .../info/edit/group_manage_admins@2x.png | Bin 0 -> 2350 bytes .../info/edit/group_manage_admins@3x.png | Bin 0 -> 3434 bytes .../icons/info/edit/group_manage_links.png | Bin 0 -> 933 bytes .../icons/info/edit/group_manage_links@2x.png | Bin 0 -> 1696 bytes .../icons/info/edit/group_manage_links@3x.png | Bin 0 -> 2442 bytes .../icons/info/edit/group_manage_members.png | Bin 0 -> 620 bytes .../info/edit/group_manage_members@2x.png | Bin 0 -> 1255 bytes .../info/edit/group_manage_members@3x.png | Bin 0 -> 2238 bytes .../info/edit/group_manage_permissions.png | Bin 0 -> 980 bytes .../info/edit/group_manage_permissions@2x.png | Bin 0 -> 1862 bytes .../info/edit/group_manage_permissions@3x.png | Bin 0 -> 2750 bytes Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/api/api_invite_links.cpp | 312 ++++++++++++++---- Telegram/SourceFiles/api/api_invite_links.h | 54 ++- .../SourceFiles/boxes/add_contact_box.cpp | 58 ++-- Telegram/SourceFiles/boxes/add_contact_box.h | 3 + Telegram/SourceFiles/boxes/confirm_box.cpp | 2 +- .../boxes/peers/edit_peer_info_box.cpp | 26 +- .../boxes/peers/edit_peer_type_box.cpp | 143 ++------ .../boxes/peers/edit_peer_type_box.h | 7 +- .../calls/calls_group_settings.cpp | 22 +- Telegram/SourceFiles/data/data_changes.h | 2 +- Telegram/SourceFiles/data/data_channel.cpp | 14 +- Telegram/SourceFiles/data/data_chat.cpp | 12 +- Telegram/SourceFiles/info/info.style | 9 +- 29 files changed, 385 insertions(+), 280 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_actions@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_admins@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_links@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_members@3x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions.png b/Telegram/Resources/icons/info/edit/group_manage_actions.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1d15b2d90dc2a6750caaac6ea989a4213e4f54 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{CgJJg7!twx zHY}R2MS-W~?}xlsNB2xnH@cX-K{iQv&7N0TXVgp-oieR22f1eV6q>|MKA(NUx4Z9*y}ShXPw!#t}cP^Zs@`66;doOmN+c$-F$obK~3%U15;KW`TROj;*Le%McW?- z#0|NQTw+O@*_I(=aph#CYw9Hyi{g}12^D&$l?wOhZFzV~!EHtVnaeB^u2x@b>lGNC zE_m*qQlF9joz?c76XTvsQ8QjHJ@)@n_KM|`zWuOg`fMXT#msEO@~P3<2h!ImUYr$B z%d(c;g!h>5%-<7M_B%K)IA-RZaNA72S#R2`2zKdj4<*`tH(JChZFyh1^z{s{m?`tu zJ-)>6$+!1gyUK4xp{Z3VGv7A43%FizTM+tyd)5pS-Xtff5Kvfoy85}Sb4q9e01=j* AzyJUM literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions@2x.png b/Telegram/Resources/icons/info/edit/group_manage_actions@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b04ad81f404549bc4d43d370b94361e1263305c0 GIT binary patch literal 780 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz*Oex;uuoF z_%_PB?~nnH?{hJan2zQG)4l_Jw%;9+j&psJwKth|X+r#5wN$C-RkQl%-Z)<-B*m6| z#>0R6m&FPA-SyR#cJtq} zr*2u8&wHqUq1ySp?b7ay-c8aFS-wICAsY6X`ww z*%&Igxq{{Tr(E`YJmX=FUH|XD|NbtR<2{49IPqMolB*o!$~k?KtdDP=*6_|)XzI-7 zkmuLVll=In>?7~3`Ww#@HS4AEq%r(5oo(`#r^Cu3zBdw|*af9eZF_ezrmHj(Wvuh7F=_mZpYN z>TKlJ-Efz+(KIle?!EG-^50yB1||cZdta9_-?}h8`OhSV2OoIfpH=&Gz@@FM)pKuI z>dK#ApBLo%*Z%$?#d!bS!dt&GS+2PKD6p7u`)ylq^)+V66WbO%QEZ#?b+Vn0f+hb&Sswe%Fy~zqm$``gV_@AcYhQs^R7=;P`qebx}Qyh zVe8}g^Ndw|D`bhyKlvdPT|BIdTT(k?5lR$P;b+V4Bj@tGw@hL~*TR0kgC(L6udw7gV z*@TJf!0nb5ZU@xvL?pDhIWSht7P!iBqeYs#=Y<_p9peha_4PlGh#mo@L{C>gmvv4F FO#nQhM|A)I literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_actions@3x.png b/Telegram/Resources/icons/info/edit/group_manage_actions@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..004040f69b26e878bfb2250413950568e4f3b187 GIT binary patch literal 1479 zcmb7^do!7OPjA(p5!PuXv1+csCsquS$tF(|ESks7F<7!j!64#)`@z zQKVQK$r)CUW}zC=cx|IZ!YMMU9>;5HQnffcXZve^?2qsL-pA*h``5j>cwa9Km>vuO z01fOpcYmdm_N8_}8E49G*((i{;P2%Ma0U$)l!bc)0UPP#10a;S8UV_P0aW%8rSy~n z00;{NK$H&J@3FxDOi3)rzxh7cV13ya08}lp?k<5Tpk<*4U#+@|y29uV<2kW)*OmCxQSRU{0_I_FR6cl;Q| zOc>l)TH;7L0xX5@$$m$}Ul-wt8j1lL44#Il2M{z1NcVILv>xa_q?R*woCQ|1h8tX> zD#!gaO3E_mzX41XtEFWWiA0vy)EIB9u9BS5=vmdp=;zMcD8}@C@~T)aCvR?UYK>ZN z3x3j#-5In@TQ4gsTU@J>6kVxs&*G{IB*a%0wI*3+v}aW%XB}^Yr*Ep{)2Xg`K4%FG z2E)3tvhu-u%EqKbVi_D9yvG0V;Tvm5$EzVBjp8nzfDjrgCS{Eb)2+#giKq#gET7ZV zRHNL`S<)J(=T$QV)2&=DJjQ3 z`qbX=NP)h-e(sZ|AS$-6c^4&LW-yt1@uFSn=xBtnw|77wzzCr&EOtprNx$<6jj5*i zlwykH?c3PEhQPix;<1PcB(}D^Pvi6ULpFZ#u$^Cu&A|71+nU3OL@lEeYfJD@J@%@t z#XY&^Vfxhxe86FYMsu+ftD{3IY_cULU-KI+NNQcGAsH6!bmWD@z2p_P!9FoeD=8BE zG&{?ZOdKgKEzSR03z<5OPB&1`e>nGYG$_E2Scc96IGBium6a9m%a@sw$w`}{q9U@| z_RKLQMKAc>v@fa)PKk^Znvn4Xmv=N)dH8uxRgzID><2LZLHK$AsQhkScOwwUE`J{g zoDOf`B7pm*r_tK9Tms@T3=?rrWNr^~aB%3KnyTWNi)OnV4;AhvgLEu13@cRaw0L}e z9GlJVT+hmAKGDCh5YsgNv#L4=>h10QzLhPTT@#w2)O~3ThQ4iK+>IL&t{4n85zNdy zyiKK=h2mD?1uDtWIx?r%+uP%tTU$c&Kco{AovU{ks}hOsLJn{VtOFeM1>}Wx{wdY# zXac2-wU@C;ENoThI=JV^Xci6!04Y-v4+;lA-z$xRb7bZ)Wi| zwa1GDI6y9;$*t@$bsNarVV|NUSnhHoC+(C(rAI{#iiLW*N@X)741cbT_87t$JU6~Ti6IpKaa@m7Pmfu zAorkW+7b^00uI5*Dg>bZBoVJo8`REw1QWQL83wAd5?;(SfVsoxp+TTGL|#fGfNcKN cCFuX*dtk`AXL>{(gs}fau^zte99L52UxJaD;Q#;t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_admins.png b/Telegram/Resources/icons/info/edit/group_manage_admins.png new file mode 100644 index 0000000000000000000000000000000000000000..79c0cb7ca5b5c629df79dc08c88773ac0d1f5f6d GIT binary patch literal 1149 zcmV-@1cLjCP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$?=Sf6CR9Fe^R!Jy*Q5Zk(nGy?< zF(eC_N`z8|g)Hn0Wq}NZjg1A7g=|bo85dZ{P@*W2ovbV*g%Bx2SSZOn^Zn2F{ogs= z_ujeBW%0N9pStJV^ZTaX{mysKIVQ#!@aNZ>`PU*_0{}y^Ydd>RaGn{C50Uw z9kJ2TQMR_W_9J&#godAhfB@*~>Vk)d2e54m3k#5xlw{bKC9bjHK|w*#)6)Y_PfrvV z$G*NkNKa3P>gsAZJw26tkVs-;qERA_2?PfRLw|oiJU>5EfXBy2= zeeE0s1_nZ1T^)plh1tn$iL|!1+UYnXfUL3Q=jSU8ot&ItXJ?0Wwzs#zY&I+PA|fJ4 zXMKHLDd!~A8CY6cBE#tDXr*CgWhKc)S5{W0)bsT8B$fUBeWhHN!0zrY8G3trOT(C$ z7$_|*g|@afxVX3=c|$`3jrbST)YL$3Zm!jmxVX5G{P_4-`my?}E5TwK(`@b>lwnVFd+b9HqknH~Y%vw%Cn>@a8Nzni6(mlw^k>+I}gX=!P! zq@;wfii!$WR8+(!Cnsr+Hvazp^e*hKudivY>XfP3+}tGQ+!4vK=q4s62s=MN2P`h` zOg_oU$#8XbMe?DcAz74BQBi~~FE2}7tsN3l3NXFcQd3i<94mc#dYbTofdPr5kJZ&x z!h3srt#GUfY{SFDR=9|(y99fCdq|GO#>Uc!-RI{g+uGWq`N6>f%gf7SGcz+RD=Uj_ zY-|v|va&*>=;Gqy=vCMWTstb6WoKs-^ZEICEBV;i7~z+fmo$owuQ9#iZpod?ho6vy zCnO|DOe;qsN&!~y?d>fcK0ZD^Qip%Bhzb0wp|-Xbc(x=qI5;SE#U{j$Yl$i1sR^JW zt}`|~jD%r$csQMkxK*1>Cc?2c3JVMAkO~P2k-Bqpb7bGx*eEgKM>FaZ!2SQ`=7#?3 zjEoFNPV8jm<>eIoPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuEm`OxIRCodHT1TiCJrK`(K6~$7 zv7uNFcCmq?9#ll^4SUyviVek^2SM?m*u{3Rp{R)cV8;$(SM0r@qGB)e&2Ra#FWF7@ zzx!_r?+q-QWG9oE-zM3a$z}*4h_4(=nK{<-{|(OtAa@4ja0GIfKn|Y(BFE2wtiLq_ zTD58=+O};gN|r1s9zT98_U+pzo<4p0SJZ(cl$I+qGn4xF?@!mRT_Z33{{5S_Z{JQ0 z8#YXfE{e^mH$G3EJT!RlV7htprq^y?{m-924iF6*G)NZ^sRICp4;?y`Zr{G`Gc;BM zfY`Qe8>In=^)jG()B?^^BTbx$*A&ZM3_d(w&(E9l#|Z${oU0FgWZ^5)G; zW5$f3`}gk~jX~ps2M=iM*s+u^Up{S$SE*8kmMvRm1p<4-)~#Eqe*OAdKV>b)+nzW8 zFlPMt@$~4?Bdg)}?%ktNqec-x3Q}4E@%{UEqpTDF5kCM03KXD8lP1xVCr^yZqVdk1 zJ2ZUwaKf?(GTi$NKrCOroYDY9bO7)?bzP6G!HB!Cpf&{(_x0-L36M!bLjo|-mo8qb)~ z09deKfzkLYSFTXszI`JZjwMv6P@zal2LXYl2$`|K^gIs_K>qys>GS8$8UU9sU#4EY zdQnD3M(BaTyJX1{`t|FV^G4^-pC7ufRK8lZYR-)K@#BZ4&#+;`qRI*nK{8i9+ z(h5M>8Ni=JQL|=Ev2fu+!Mnbz@S#J8#PQ?D-8wH1F?a4Cz=I1Hhh| zI+m0EG6-}O00`c%20FKC)5fac#*G^_`KaJ)5tLI{v}lpmr$>(-R{17fBYA?@$W*FZ zx2{^%${aO{Zr!>$8=(3C8={RkoYfxjej@k{3;qOZ?P{e_qef~~a9FO@V^vZH00#c@ z<%{M3efsoCNbixSw3s+?qUheeyQ|~*^XJ_%3UA-O75sX&Hg81*Ksr!fo>=wJQwCX{ zuV25qQ||Ta*MZX$%Rr@z7cV;eNSU*D=RUe{;R1E-+LbC)sNlAtz<>nmhP;(q$ zGZ3cd#zqsP(}o;?O16ym<3ter%juty|rVkD6?IOAKYaM00kC1ihS2>jAjJm&gyFM;OpQuA zckYaN0&Gzv(2!cSYWWUf@sB~NQl$d>g%~S-03h7-sl&z>X1#|{&M}T-4xjPtW)tTW z6Ez|Z-aQ44>^JXswj+4XL;kXn< z-eCIl>BMO`O)i!kj>8f(TJ4`W08j>j(zreMmXT^RWrYxk_3PI! zux}i|BnJRFFq|Y)h8Io{{vQfTZs4th2WS0WLgb0VSvgJ;@5u!KiecUIU!PtZoch(P zSE+aJ-afw_$<0%zP8s{49mnBGG+LFFd;p**&J5PCUvD%BjreI2d4cB5n>%SQHNcXc zFq81_K+vp70w}2gKq`i;E&d*fO8D^tvqH*A;XSngKyhr4qeqVp{HaO;1WymXMby8_ z5&$Um07&KF9N|w8Aq^!D&hmzbi8$Uj7X=$Y)Ddw*R+!iMIM1akAnXt6#T23 zU|^CWKZ=mQ0zec6Q_UMWBa~`|(k(w1fOI4NuZ@>G1OBx)l5Tx+XF$3U|JTO*4`&1n U+n+>Zo&W#<07*qoM6N<$f?eQF!~g&Q literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_admins@3x.png b/Telegram/Resources/icons/info/edit/group_manage_admins@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c57d003cb51d8301ad2284d83830fabbba484f26 GIT binary patch literal 3434 zcma)9X&{u**MG)X!icetbwakWW-Vc8vc`;LXNK&5A}z?)kj5IKERmgR>`P@|M$_1h zBwNo1oMEJ` z3z928omZBXvE#vW#7fGkZv1At<@0s4O~GzicD0N{KO0H#UMKU+TC z|9*AO2mhb`&+&~4&Kv+BpN&y^H-bSMjtnc@Lwusm&05vX&0q&*49Wrm;pX8%eL&vu zt*)UNoBu&;`Bdlk=|;eNt!AxIji69m1Jms0%-~?lgIyi1y_xJMWd3iz_|9jja!i$k z6pfy|ij)Qv+F2}sLZG1>lu?$vkEa=YI+oUowBaoHK7refM_%l)baSbQw#6|4hrowPn&hpeAr*G7~^8(qRhp z8QN}5sTV}KbE(1OdF2J(DB3TAFpYQI+kD0p?1W91z#Kkb%IBeHP>TSj!d|T{XcH^* zSZrjt$ncO_a7FteB}q6*X*T7y!`3e5k+!WNYC+K0Eik4%B0S>wAi$gKKi%{^ z17S00y?vzd-r$KqQb@EZGMXJvQ@7Kk&?bn8ky5jkd#j(4c&eC4JsdFw%t&qpN6nRx zy*rs-V|andk{yqM7u$87LnbmJEFA_!R2*k74*)+ad`Uu{LbNI)flyume!-#Mt8*w% zS8>|8<-xqIqyaz28c5oT$gLanGw0dD>B-+dT9lovJhH{dPc&9{Wd472 zm)q3^e=0U^UD`M>)v;kkmRVOEY|ggd65LxJ5;Z5NVDpinQMNn%L(CjwRk&sEPsIU_ zF%0}`Ku7f9?lPKoyE%_p`R>$u5q+aMv9-wjG3VH~7-E{|8c=ONa(c9vKnpBydGO^_ z-0ji_u9Tz!K~=Ag(7iQsK|@2pw-?{<6JJlh^_lvKR%*YvdL(vydBwX>J| zvn-!?Tn<0_bxeB98ZkPrW1244yN0g?qO;<_%>i7GFPYhZk;x+3$J|kZaxygtD~Z<` z$ypOOX-A&9OkDebsq>ww_AF466`$KkjwFlfMo)+_;xz*nsz1N66pX&X+<^yyZ3z$<Hppzra)cB0TOjtvuy)j45bf0 zd@3=eNTPH5iPjapwVp$RR8w#XW*=`W!9A0jA6v*b9>*mGxqQKM(3e&JVSyWJ@A_!jvJ@D^(H)L_d@M^!cS8L#I9l4};ov1}_|L?~Pn332 z@-+y<#F#5tkyKh8U-UI?+c`WTl#A&fB>J}@BM@2O{*N}kIsLEAUtWT^hQn38sBi<8 zZG?%s)T%oXr`L22M~}d@wdhQQqP4J_{L_Y%A{@2}0E>QxC3>sV(VurLC%L zfWFLTy3wAb({H9_wJ}vY_i5afzJ(yCrh767hj-hPy3#WWVv2!G;fm} zSVRYGu`~&04S=jmJ?V!?wo-+BirC;Vl66;9;#dAsuhivVfp^!`O7}VuOsr)vw2A{} z;X|BRU*RC;TxmXSH=xTaax}4(`3lDnTstElt#It>I!h#~MEl~)l zw&Z~lB*R^Q{RUi?dY>0AnFEeY!5eFUILkKmH58=gh@==O)wTLTz_}8ZfoZE<}B_7-`a<; zVyOl@_obTNS8k$6o@&Enk@*EfCHZd7YYf7d?l~v$Df03bm}M(DJv(Em+RqeHXvHT! z_J(3m{Lmv1y$gPntcT7}aQY<05LQLs(|C7z4aXU=JSa?QQO*fJJ(=!E=I3Ek(s~koZ`1cU2CdR1}^Wzq4-dl5>Ne5}OHbYSl z4|mAT)|AdxT4}&`QjD`)&CtsP?Y;YJpLfXnz&5CAMsuP4=IL&}hX3PzDM1o7)p?hLm(%6;;Rdy}l*#PJUbgDH}Lm?vC zT}YU^Wm?w+9RR8N2J09)TbZBs_j9hhiQr$Eqw8y3X{r8Mis^n8#4I=oWzL&(c%HfB z#7%!{n>UJz@H*W-73?{+sdRjrD*j++F{gi?u`*K5y<)Pmo+)LAA2tT@2fs9Wy0cpd z4@ra!#~B-FVHukru79b3J$UODZ@f$E6&k4-$UNN?BE?wpjYbr|$4JGq|UU4CQNpEGgCmASOG3>oMsBD z`LGJtzC+`4M)Fi)YXJzzRj5-Tsdx@TtX%27n-Z>^9PRncn|}NJ(7C|(X&B8x-?<_3 z?^P6yU!lz3Oaul`Qp0f)l2Sz?F?HPq*IzEO3j-Vi2qq{f6Desk8jtj1CI`i%7WqR+ zTRhLHbodm#1N2q;P`1IaH`YWsUBuEAtem0y1BR!H^#D?dUOS6?t57NhaY2Y}uMBJ+ zQN|ubwNq(7){`DjLa%`ETS`)0m5O5H_f~A5y5q}D80N02kWBi|;EX$U^xa?bwvcH3 zZbz2Ous5$6R4eeuZJ~&p!%78o+a=s(w&%iRnGl+Hu)J5|ihe{hS`rPOoKS z-dA0(PK7Doj9w)|c6`4!3_sB}2?pV>{}B74Bw?1VZZ`4CWv;aTkK8~M1M63ePKf;h z2>CKL=VRq~{Lk6;p@4Wv3=FzE@z%#CX^15g+97hm&8Xu(y=*r6Vi14_Y#A3^%>wBu z4VD^kI>JDkOu=Z(N~4mzS_Kfu4s6J;j8)|+@taz5S5^-@XTq5htz0kX-z?{QP#1P+ z;h4NJp=8#^ftzLm*d%b(pI^zjSUA;tK^1MQ^F=xM{VoOm=+4s#Un}#R_y2HTEJWc$ z{PQJI7=VP?hKh54m94fOk^cB|Z++};m@q?EY`+amCbn<)T;~~!7Dmh>kT1j71tED|B{&|r_?n;**r<4cXA+o*bH+u=$_XX*1U-6wjgl`JJN z+ng6&&nGk)*pG|Z**m43%Zzuu40NjMaQ4cwnI%Rul2u`}nIO@Y`~NpL>;oB~15wiI WGv?IyTc7^TImTDbQI-16QU3*gfkG7k literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_links.png b/Telegram/Resources/icons/info/edit/group_manage_links.png new file mode 100644 index 0000000000000000000000000000000000000000..f865287ded592413137f8cd049bab60732c669a7 GIT binary patch literal 933 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{&hP2s7!twx zHq_gDQ=ovHuZFVXq9&y^p5r#-<#|gDnxx{j`<~=Bk8y$LwsjPS*O#KrW?IM zAGUGba_QDE(w%$mO!&upSN|;NWnMq8c-~H%^mB8J)fgN;{+&7Fzs{%d2WE|BY&vVj z7M^~}wEsT;A7d;&cwOR&CH_f6t2iH_8&jo=kMotA#L+ZtNAl#IGo};`R2`Z zn-2H&@%j3^>hEh-k>Gjt%IphYNqKqm?YC@`Pr9@;YVD0$`)NjT z3{@Pg?d|N?cD>&6`t#@FmAU~RGEBIPjg13VhCKNCHMH&g`SYLWf8R z&A)HJt(ct)yd6a3UUz*CzjfgEZ|nLX zhbyc$HZ~0zE(IkeDy^^jOfS1k^>#Y1x%cg%?q}C;-aHvQ(Q3i<*H^Dx-26A{dYO-c zsgO{X&X;Z4vNzwB^&CxFqkO4%@#4i=vP*l9#mr~eddWxaFyo4&LAPULV~z1caQx`nn zx_mkN5!2?whn*L!%6FAgyWl0D9DO(~CFRHd4WLwN$Jp5Pt@sPK`%O?5@pScbS?83{ F1OQverEvfN literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_links@2x.png b/Telegram/Resources/icons/info/edit/group_manage_links@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..671b6419b5bac4d50ce44a4c7c711a8cb1a08773 GIT binary patch literal 1696 zcmV;R24DG!P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuC3Q0skRCodHT1zZkUl>3BC}JTZ zr4lL?644gXA}m^Ap(sihEG3e9C!&$qvan&{k*EkZ>QM_}WhW{k9wo7p(3F-m5$}3c z%jx-UI(P25_s+~YckbNIKQkwp%sFSy_kF+bcfR|b#~lxZ5GEg&E&sW!=YJh{6X1>j z7iYlz2)K9vAa?|~Bf!NOa6bYr9stP8nFxrAib4?)5$NvSyU5GS3+?XiqOV`SqR*c{ zI}|1yhW^>-@#Dw1r>6&RZEayoWqW%YcXoE-goFe~;QTkvM@B~Cv9U3HdwXliZ$&?h z9UdOW4<0#3&uZS$8}EQ&UqE0f&c&I5|04Gw7O{8cH9m4smgDTJ<@e zfVXeoih%d__HcZByjHNH-173Ws1qt(U0vdDP8R_H==JN@7*>GL&`_;FDY^Xoe2RcC zU%pVkbCBTp2;cz2>;$Y1uxv^TY_521{clf$KY#v2pFVve{+i4E`}fTa#+t4Kki>@i z`}x<9N&ta%oH0}ts6Y%iiLt#fJA|MrTzP!BLDtOZ@SaN=CZ4Czm1X$HkSXd}E zi#Innt&TU@RQc9MKww}XCO0({2yp(*n>S2AtT`VP6l7HgoC{W0S1H?YVWQ$3OP@9Z z+S=ME0MMwctYk4LBrEwd5Arbcwwwni>+9=bxLDv+7#kbQWLt$KegeY6!tmwgr3e5j z9UUD^0PLB001r7wPTt_)Ad_ts=RuC20QtEdwxMcgdAV+TozKX~z!w)66!H7}`o3Io7a|-}nefN=l;qSXo)2oK@53>FKGMJyz#I-@w2CWfK~8 zb#*rFs$r4$ai0aqahB*Xmz|v*bHhT7WNG1iKyV8q$RmWv^Nh%r*MbDy4KGNfg&eC< zdwaXI%dR>XlkyG7`1m-pJZzlcN{c075|2c^{{H^>-Me>qZf*{jmX?bB(lK__&4yT% zq@|@%;W$4(uQwp0_3;r99v)5+0Lzk}pP$j7^|Z%F07S{g#s)>gt5>gd0p#uNExZGG z^5lsw`dA{GjNvDMBp`|aax-H$U|3#ZMKKkf-ukfM;wRwIqemF_-_inC)mq2|8Q5B{ zudhqhVFPmuKLG%GXlRH63>zZI@Vq3DYcSPU;}B)Mx-^d0MgUws$lve+2~|~9%)sHD zpIis93NKO&Osv`n0Jh;|CVv#bfyKo|4A*3k)a0`Q+v3#JR6IF32}2bH{xhH^C)cA_ z+vBey@};ei{Wv0TWf0l!DioZYoFMX67k&Ho4G~XKaBwh6Nl6jD6@y7fUtb?0$q&KK z(a55hC1RYb4{n6vPYYTL+y?VJI=uL7jOTOKk}}8%BO@ag|NLNyoY~n~{PgKlF2XHg z(AcjJ0Vd;#iHW$oyBm{3k|m!p-1~p{@Byc%r~gaG1>1J#0OGi6A;}Jrmm9+0-pD$S z$b~5)cLG8_AV*=Lttx16r^5&^*bmL-xbFirTXIrCHvvvMF^0i%M}T2eJES;LKM@;8by|92xAFl?AuIbMrB-O3l$T}zRPlrJ)+AP%T3nC zExHIp!jLs7gG^ca(H-;q_RsJ4=l$cn=bZQbJnwU!=X}oRIf<9-vBE+}ga7~#wze{N z1U=!GAq2p4G5?M}=pez4STle&B)0-4=0rDZlARr(3dRTk5`P1L{epmY1T+BPD}?}j zpojdrD~0~=l~Bs}|M(ZM<}}_O{9?=6+|(%?vhH=`i!dH(^0MS5Dl_ALa-2v`QHHMV z^Lv$6X5;F8@JtcM<9KxiE5{s-zOF#`MrCowCu@xZUB$egAKtFd76dO3Q4RctI;psp z*5Kv0?dOJnr9X#qw0rfrUJY@WR#d0`x&llNlB>m!r6@6Upt8+B-`Rkv*Vq(f z_dtI?c@N()Cug zQ%|hY0>j(q?j|4Ba?;iepZ&wNKD@-_zN1pv+AtR%`j6FiN5tlG192d?K}bq9j*ZG; z6&c;p;nSK~Wr1bt#0GurO?4m0)sPWbrN``?CcP!BPSlI|SFO9Zx8-W#eK>66 zkT0SNMq%ppuJy%E4l`|`yK#aN`cu-X@2c#w`>hv~H@reG-HS;5yuIq2sU>Lcj zvN2xgN#@S9-}&q}M9u84D$eL#uMQV1_2i#>ar>GI2JBa&a0l9xmAoTc3(JbbWf!9WyR*&;%`shU95_i;H6h1Uo`k$EPqq@R`A%hosn* zpAO&*ld~!Jf9~*8UPSXu0%to~PbxMBf$fuLrY2o^^@uqApF~?aSVjnYEb%1>Iq-V0 z@T|e$M>{|?gH?e|ubusH-p$AcXkPoUs0^e*IVW*doRJD_u*XP_0pZC$XW>=rq~HEE z%eaC9l6X=cFqqa0JtX2^9^(fu3VV1yJG(0SPq}m98DbE~kGnf-GS#+@3y6$TEY3S@ zW44nq@<>=#fSu)}4>e<6kQ~GxIdgr!-ljParObNftfqH&hwM`4q5C{ylVrKdNu+X0 z4(?{3u)Erv_F9iTnc*(8-ICbITVpk0n)bF=;p{Az>+(*2=Ul?W6k=N&=_oGdj##M2~|jw zHwaSB^cuFLh%mVP4#TRq@gp`Xxf-6$lWZ1s3W>>7e;&&d~FA!x}%$sXwT@5aU= z3MMHte29A+TOAtXN##>g4c?;k1hl2j6aNWX**rMvr?cb=>>e`SXw(GGbv1-C6^r2= zEwL!^0*$2YWkjjL>8Iu@!#j-JMOq>?5o$734a6cTFPXI}VGc>9(+43TTFZ?nqQ2h! zy$}@PT>QnD^rK3u2}2Qky1xyI(r><3%@iqUyDFg2R0)#oK!EGI9sl2>4nS66S2RyE zTshICv?b1b z9^h&3{sw|RdM=!?B7-Ra-pwl^HWyySQ)a3$WW^;R`^bx05ZgRWDMRh3f5M3u>|)cY za*SRVKA;pEIC5ssOu!G?Ff zs@q|A_qNq0#)S# z_YjN#OQ)%pz^ZC$m>{0iSlzGeeL-?Nk{E3^qL<+@P(K|N6vke;l0Zg?9KSk>VwugUPG~bE* zevJ%*@Ba9D&W|2@hJ)-yOh3Qz?oqv0YgWBmLkzCjcJc%*P5W9ejO^X$n<&lF4j92w zR0;#Y(a@tNJ&|x9$u9$iDA|FkuRUs@-JN%Q#Kg!YkqY%`Zx7j22m7 zeD|F#)F9>R(8!9+h0jSyNF8i`J@|52Mn(11d#)*Fnk%^(Ww0a16Pmp$4ju>-5-r(aVv7NF86RQg|uUK<|;< z-~Dw?HR5xB`nzIy5BT7X0T-uB74ce-Q}c^G$q>;?R}?Fi4ya!hw&I9C#-<-7PM-vP z_q;&5>|&faP-^}wTW}_WQTiwEzA{I;S9n8AKnXdbVBekf_ zSAN8s6SSf}a@AzrsZEwv>i90}#qE#Co?vc#QW##gaH-5mI%f6CF$UAt@GsL3>@Rh) zHT$Qg)fKX1tENx+zU?Dn#ZKkwna&@LFm7A7v-XLEg&$fNP`-l0o*eDsOE|35DgQAR z-41J6kpMF2x6a-iQ%O71+Y&gpSNeEk(Iu9cT7!F9wZGFL7=)9F>*FwEx(iHl-l$yA zkT5E}apIN8+;0`PV=t5VFVsGu_-!2%-ud?N&83LS5V{IN1;q%Nex|EfG5sRiv>4nO z2GRg5g$Pss6gA}f5OZp5{cFohpF(iWX zZRp0XLk72;5zPbwpDz8`uVQBE|uy(zU6M-%ewvEM`V8P zXQ*Re)9iYsYHygD0{^|O_nJ3%CmYTW*}~u6EXBjN>SBh+5gCT}ijP(mN1Tz+}sWyzxFpFOX?o*K4Vv~+ju^Ouvv zib6llH4mA7+I8Ke^Xyq0mtB9o^7h+r$tNFw4ES4Tf75y`bGloTzv^tA>E1VQgs}%+ zVTfqnrNEJ5GTYa5_Su%(@4vgru4c?ztJBip8}mGnIidI5OAh1bpCf-5@D$rzpK1M} zYOhFJ@A+r0jrN-=&y??;d*N-F_3`?`M#0N3uguu4x>#dVpvnC6nHDlZGRJq$I=`>Y rayP5Woc+wKFP?5&!xJK_v6uONL9UFz#bP0l+XkK2fG0n literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_members@2x.png b/Telegram/Resources/icons/info/edit/group_manage_members@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f9c210affcddd19a74097be280caa2a2fb54f61a GIT binary patch literal 1255 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuAQAtEWRCodHSidhlK@i{fgv75z zbTldvQ4vu{G}IariT^`FtB~jvMB#tXXe1IMDiT2>lsrKpK|({qeD`y)Ywq6NyW6+x z>v^}6Y;JCMW_ISYv%7Ow5JHe_xus%mBmWBA1K=|t=Lq;Mkn;o(J_CFPVLJ@i8rSJq}suSUT@Q-yQ(0rb+M$1?bd3CfEZIm-Yt-2Q)c3NnKrCK?9GzC_(p^ zv%n4jd8kEWqNSxJDi(`nfuRp$QG$(M1i=mfd9sJ%!79fEuL8taG!tM$jq_)Fdpj*I zF4F1gDaqR;XyDO@F)nMt+W{c2w-5kG^)zQ7!wEK`I^*Nxbai#b#X>yBAQRQA;@u7a ztPCgY`7|dW!wELH4g&l9{Ny6BJY*bzP+I^XwJWWB6bZ=MYzOO7`1bZzF5Wi{4-bF8 zBydp~Zewn4PW1HjXwCQccd@XrAiBG|g9aXbiXaOe8w#bLG-UMd`Vc%jI}0uH{QP{G zKj`CxgVEG)CT|unQw%0`eGO(gQ_{|msE;Y{b^yS*%R=Z+0^qU~Mmlq<`7NN*%hksa ziX2XFZ*K$;R7njUeVm}9{HWq-zXjB!$b`7mcj{V`PdnovISN!F$CUx&>a zfPsMlv9huv&d<+-zrXU37bZ=ncAyIzuoXxB{r%$T=t#W2z6Rd`@+&ZSmP#eDxw$E3 zW@d!^&k(1YsjsR#w5h3y*4EbO>}*i3R+uQ(DU`U-g$+*F4w#GEYi@3)#>Pe}6bjVR z(n6!7qqMcP#nl3Sd3iac$8E&$W&p^;Dl{P+@9gX}nJ`usHliS4t{8{Q`qtOiLq0t` zJWxYJLz!HHEi(Y*X&;<CwKdxF5DSVXiE;TU%S{>FJ59lKktGNmtr80{|5nC#0~c zjU;)SA00b8JKQarnwrYwm+|G<*475lG~M6dYu=WL+KSFq$^PBlU1(UA(V_RG7h?cW zgec;?>iex{)R*Xcl3&IE93CEOTb$ROmdwS)g|@(X-I>4~A0O)x1b=dJqTkb)OnXHd z3FPGOXyw8AoTT#<^dP2=$yV2W71$>%-;{sFDJDgOd> RK*|6B002ovPDHLkV1j2=I*kAT literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_members@3x.png b/Telegram/Resources/icons/info/edit/group_manage_members@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..220e567f3b749fce0fbca9f6b1111f2e4e4c41b1 GIT binary patch literal 2238 zcma)8XHe729{q>3P!QUU?4BMTc)HUNN=3<5aW z3_4mPbNr)vkvacY9~oa>@U&w`oVP+DogzUi?l$FiS5Dmc)n+Y)6Br=aA1}#wkjj>m z_484HT!3(zDGfXDFZT~$=T<$z4(u$%pq&L{U_hX_`|;lBS$428AoJZTVJ z^karKyVeosfx5Y^;Xnq5^45zF9XvAOlDX%c3ZMWE3z^!TDGoUxUS4Dhj{%7isG?vD zfw6vde`B6fe+Dw? z#f_M0ZP(V2vqJ?2PZaG6ET3CEE^Wqs7|jsVU>^M53;i~(WEghv8PpHFcI#~ICF5Zia&Rcg8Syy`XT<22}x7PK5|BXN>ny&7De zs_WT5_ze@AveT9kHag@34}l2e))$AMr0TAwRO#5Xb|J&4t@FVuB0@1co5||#EhZ6b zj1oO8gPyPD8}M^^x-oRg0jhm7^?Z-lXj>SIxxKOQ=~pLlsYA(kUiI7eu9&^t&~tF{ z-si`{U=(`MmV`gc>M5YbnGty>P*v} z5^GYZN*MoxRZF?4z%uN&@s|(!1gzkByomMYv8DNTPvDpoJYPkkCID7ZH*NZ?^b$jH zYG8AEZ7@$mZ|y@@^Be22`ILgH!QdGUfPLPylbgM1kP1^I+or@Xn6S;7)Np&@01ZJP z*E_}2J0wY(6XRJ58q@JYtLDu0k3AQ?s03Fo?&BL}a#I0#~*eLCa-?^x;2kv~n$#)=}Q!KZ4=siiiF0yE-%r+Ve z(l?*$iZkAuwoc>`vd)C!qKkiALh|FwKWDLnF z)$WZzLj2y_y*tLJE16qTFTX3kDb-v`;sxTnsdM8Ufcv z2K53*65rw2m07DvBa_ErBkhqJKKEB=`*~`G;J=B+coo_*qoaL2NC|uWG>?s=VuXkN z2qtCC<*)8U-a7@A3IgJ_XGQT(INWux2m3n@YEae{LTwEdQCtW;U>)umdaS8FZN7{< zvFceaAnn*`IQ)Y(nc)WySxhyr5eIa!3{}IF>S8-@9qn2rQT#YFd3kB9?0$uo&S$sr zrcl^yI+cG?QLrUX!;{j`QRWSy+$G{0aWf^tl7>0H-6*g9?Tyxbmb1AG@1BJBNO%Fr0PxP{N=-LKqzF#E!iGg;sv$dBD5M>jN0p#H*`K zKMZ-vlykqG=+md^=!G)%a4r)`)4~KWK|S{N5}06^WXLTFc4L|M-dSH4O&eSWS?SRCc{C_C~`IAARjBX jLvopj88?OgKMf9nBApKMEWOj?=Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$?K1oDDR9Fe^R?jPZQ4~M(12M!T zzmhUCWhVYm6m9i4qOwEG7Kx#&6Hda#;JCYUA%u1<|lpo24h0K(Nk|H$Bkn_Fg zH1GAE_rn|Wt=Y`2_v`%loXgCDnl=Zk>R~10r zmb=|hC0YkVCDDW;UN>Cl2gi!-o0Kg z+b5GrLwie0;B-1+ZEX$u`ugDY^%YX7l!*EJ5Y38>c6N45jSbleFBI#C(P)%sa({o1 zU0q$K_T)p?=;$c>P*ZAYX)*ILUV{4idZcbf1h<_fh?oIwAO(@ zfO`e`&CbrM^b$eEQ=dS4dpq9T+zni*Hrq}%=65WeiXD}Fq&(BY2 zXlQ`O#zxrR--pf3O@aA;hlcIa(vm9O^g&ta?d{dr3MzTYY*_%!l=}PoK~w{XO$H+M zaBy${x3{+%yL<(P1o8#8>Tl^RunP7UyCQ*tXW$<>40Bx&!)eI?0000di literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png b/Telegram/Resources/icons/info/edit/group_manage_permissions@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..594f89414024b1001923a5bf5f62aaeed7d78b82 GIT binary patch literal 1862 zcmV-M2f6r(P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuCut`KgRCodHT3bjJK^Puy+0C>v z#ptSNB$ZDRXrx6%MYji|^hLb96%|^9(t|<|MW|8GYe5#I(T$)7#Y%~wumrKIVVD|S zAWbp5*x&yzp5xB$%yD)*>l)__9M1f+|NNJ4W_JGhXG0icfWLq(p#kgpnBzeJf-@k% z5eP1U0G|LXI0J$+Aixm_E`b1_04#i}01y=w#b(Z&$!5)(#lphE*zoYM_8S}=WWRs^ zcGOHd47u{l&d!E&=gvWQcQ?q&_wV1~_U+qHR8#~Ak`s1LzVmC>u7w8=9>{!_wtxEc z2@V`M01*)p{sdz300<8c*G63Gq2hz*&!0nbao30t;ofoapG3Hz^Hxe_WWDqvt>z|{Ze&!4b<{d!?PU-lTD$qhF!+E#Ss z$`zCGZ{ECt%*;$>ne*q*hYJ@jnB;u^{27)lTc#{W;FGfe6c-npjBaXb0$dWdj6Qhq zAdHTVaxx!1dSpx17#Pk0fcrY&n&ymcYik4C>;J`IICkt9CzFUhd-m9s=^Oy%<>j2A zLqkKbbm>yNhI0L`U%$@De*OA2U;xGO2&TK}xpM%JQTkF@St&BW)JEGS?iP@flLPVb z@uuhGSYl!#&`VHXva+&-yb}Q73;?OAse0Q0LtCXtZqA%J@c8j#Z{Kg8i(>#RT)2?40uMYKEh9QRJ57&9 zMn+gqPmigcBE~OYzIfa5jKE@JW4*LW#W4VA)bs@d7CkAgpEz-X8F|af${2wlD!4qj z=D4^xj-H5cEFWNW!U%}Wuy^m?u~n;9vHbje?fh`}?p@a0+$>{fbLYN^uMT zaztM+T-B34>olsTPoMV5Lk}r&g4?8*@6Vqp)4&ejWYnVKOF53V)yRdoEX)ehUH_3uhD&WT?$BRq7B$efomipC>m2GrxZQf`<>FP2Y{wyU0t2YZdwNW_U)4?;M!|zYfb%C8)(!oUc3m47cb^y z+HhLmUmbwUmoJ-ar75~$!v?)TX`O;3;~SjP;}aD;3d8yH=VA5g)l%7ZK~O#nZQs6~ z<>lp}p7DZPAKSWht9GL^-pUvqNpX}IMt<6jFWvf5ox4FLQ|L2o-y>RM#z@#DuiJ{du`IkcmQ zWK3q8@;d+sdA|C1`}Qrnbmo zP5tPyQ(rKT2XsP2C#URBc?RHK&kfe5O`EjjC4Ij`vxENk@88ey;9UT-nxo?cOcJ3q zI(9y3!7WlrHu~u%pPF*ImbOw}ym%qJ&2iaT5zuM?&>Q>4jT=fB(0v}o{8P|blTDVt z5u)#MHgDd{@S2;^O$_Y@o`1T$z+)@JbQPmF{r^xb1MojMWyMBE(&APOaGN&>fZHSB zw*KG@aC-#Y)*qY!ZjXT5`hzpT?GbQWf56Xxzw{I`Z6-h%0{{R307*qoM6N<$f@yzr AO#lD@ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png b/Telegram/Resources/icons/info/edit/group_manage_permissions@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6732b326c6dc507205f511ddd28604f7c271b2 GIT binary patch literal 2750 zcmb7GX*kqfAO6qS#uCP^$c#yiy^u0xZDuSzV~=bT*@mbrAtojsvSiI9OAkUkwlRe4 z%9do!5@M`n9eX0nJHEW{=l8?!{C?-0`<(0C=UnH$?kHnJ-BTQ=IRF4SrLTuJWn}x4 zfUq*!nOFW-842WLs;dK(_J}PqI%wzH`Yt#eAj7C30O*+;z#A!wV08-tHciNB|a(y)uC!evxfcEY0Vpe)o7 z=uX(1ySi;)A#_tDi$GiGUCWiwQ`ZV2?aHzkrQKG#>+z(QfBJXlacA0YX0vy7#c1l6 z>z((Nlt;PRMe2bf4MoCKi}~!0IbP+t<$fpcPZM$PcwvFEL(Q1tBm-!Nd#VbFqx5wdSoEOBrGIvJ(t$>#ZR|3|jU?{BsU}3zP()YXi0qsXPr!cq9V|cH|lYPke#zH^But$AbJ=C?% zz2PB1nm%r7Nv_u#XH}G$?;$pj}Tb_~WmhVb}0jCP?>iA5H5^Jeb1NMd# zf|!D}09at?kJ%6>#`x@XOY7$Hh$1#V4_!X+IR31<$PV7k?lj0^!PFZ51FX*K*mJa- z^2STfwI@Th_KXe^(Bis6ZLa*wQ8TgF7hhssnY?4ZL}Irci(+Y4G<*A()e9;msAY6_+`Suq?xvu3o0?x-8I@cp^(`cO?^OX)GtEFf>Zfq zFvHxr^}kJYK+TNr}HlIRuvI;HQwQ|5}K17u8ohkB21n?H6ev3u4$IuFXtc4!Tur` zwBPiV7XTuYvm;-TZ(|w*OzOx4fD|fi@W`2r24LASI@#qmV*NKZAhmw0X=VM7mIk2% zU-do4t4eSgDWcYr6~;^?^Bb?F{OR9|?~RmJowt4e@B`N8W*;|TiWZW)4fD{f$qwK4 zg~BsucvZcEeyvdZ>v@Sb7G?;s4H@$SUd_Pmg6wdYSQKPPI+)?`!__^}<_4`z5)E zV6%L~Sd|NO;r=o}2{FK?#So7d=e4p&$ViiqiBy5WSBT(u{Vg0*tyV`geJ$$ZHb6r1 z)v%%it=>)4`EL>THr13~(%L%~TI+Pp!OwAQJk1AtTNo6^P1d4#YHnu8otP^j$z*6= zb|Kp|&p^9U@hYICth~3svwQ;jns3Boa7n81n^%SJ;s7Ct?-&qx&ef!A!_{ODknTpl z{>*`?|6S(LVk?cM-_!7#LCbcF-HQ@&PCm}@c`mAaem?F@wWn|2`D0KP0ji4)E4B}@ zOi{2)jukB8Qkp|f@j2syLmC}BxZ}87y)&fdqXq@Otfk0VeN3ExSqwXLVu-%}URzby zY6#8$(_rDP7?WPt@&t*J`&QNWC*&+IT^{pFRr7gU8HDje+yx~&bl{tUyQe}GjI&6U z&VqQHb;!ju=g#}F1JKN@>&Ew74U&~aK(|BASFpZII5U}0Gtkn4TXy)iww ziR$R!NLHmiR`r!+a_TOzt9$v?MN80;#iZj5@J%gLIJ+1|?Tl9o{_|@!TrweWVVlaF zld0wtVU(s4g^=y-D%cLBjMR9hbHLT|> z&0bu&|NT|1sZhbRo6z=b2hXyj2sN-USdxn#*&3S4{)E(NAl{*iO2`OLwaD$;PJ~w` zXec)O(Z7qrT^H}j12EB)VII|%euV&4hmU_F#g!#?lODSlYL*wI*$;#8#An^#(*s2 zh1Eg9s~`@SB0RCwrr+_x{=4*vS^5X|#=A)Hpv9##e6~gnFBR{UDmj`L&Tb7Gji8{7 zi;W`Kh_o@4Ji zhg_M<11RyU=OzIXe9An`k&Cvk?WCUvyUw03PVdIH`xeJs@K+J-u|Q6=Y_=iRB-nbq z&_|BWF+;^m3Yc_~2X api) : _api(api) { @@ -28,93 +45,167 @@ InviteLinks::InviteLinks(not_null api) : _api(api) { void InviteLinks::create( not_null peer, + Fn done, TimeId expireDate, int usageLimit) { - if (_createRequests.contains(peer)) { + performCreate(peer, std::move(done), false, expireDate, usageLimit); +} + +void InviteLinks::performCreate( + not_null peer, + Fn done, + bool revokeLegacyPermanent, + TimeId expireDate, + int usageLimit) { + if (const auto i = _createCallbacks.find(peer) + ; i != end(_createCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } return; } + auto &callbacks = _createCallbacks[peer]; + if (done) { + callbacks.push_back(std::move(done)); + } using Flag = MTPmessages_ExportChatInvite::Flag; - const auto requestId = _api->request(MTPmessages_ExportChatInvite( + _api->request(MTPmessages_ExportChatInvite( MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) | (usageLimit ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_int(expireDate), MTP_int(usageLimit) )).done([=](const MTPExportedChatInvite &result) { - _createRequests.erase(peer); - const auto link = (result.type() == mtpc_chatInviteExported) - ? qs(result.c_chatInviteExported().vlink()) - : QString(); - if (!expireDate && !usageLimit) { - editPermanentLink(peer, QString(), link); + const auto callbacks = _createCallbacks.take(peer); + const auto link = prepend(peer, result); + if (callbacks) { + for (const auto &callback : *callbacks) { + callback(link); + } } }).fail([=](const RPCError &error) { - _createRequests.erase(peer); + _createCallbacks.erase(peer); }).send(); - _createRequests.emplace(peer, requestId); +} + +auto InviteLinks::lookupPermanent(not_null peer) -> Link* { + auto i = _firstSlices.find(peer); + return (i != end(_firstSlices)) ? lookupPermanent(i->second) : nullptr; +} + +auto InviteLinks::lookupPermanent(Links &links) -> Link* { + const auto first = links.links.begin(); + return (first != end(links.links) && first->permanent && !first->revoked) + ? &*first + : nullptr; +} + +auto InviteLinks::lookupPermanent(const Links &links) const -> const Link* { + const auto first = links.links.begin(); + return (first != end(links.links) && first->permanent && !first->revoked) + ? &*first + : nullptr; +} + +auto InviteLinks::prepend( + not_null peer, + const MTPExportedChatInvite &invite) -> Link { + const auto link = parse(peer, invite); + auto i = _firstSlices.find(peer); + if (i == end(_firstSlices)) { + i = _firstSlices.emplace(peer).first; + } + auto &links = i->second; + if (link.permanent) { + if (const auto permanent = lookupPermanent(links)) { + permanent->revoked = true; + } + editPermanentLink(peer, link.link); + } + ++links.count; + links.links.insert(begin(links.links), link); + notify(peer); + return link; } void InviteLinks::edit( not_null peer, const QString &link, TimeId expireDate, + int usageLimit, + Fn done) { + performEdit(peer, link, std::move(done), false, expireDate, usageLimit); +} + +void InviteLinks::performEdit( + not_null peer, + const QString &link, + Fn done, + bool revoke, + TimeId expireDate, int usageLimit) { const auto key = EditKey{ peer, link }; - if (_editRequests.contains(key)) { + if (const auto i = _editCallbacks.find(key); i != end(_editCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } return; } + auto &callbacks = _editCallbacks[key]; + if (done) { + callbacks.push_back(std::move(done)); + } + + if (const auto permanent = revoke ? lookupPermanent(peer) : nullptr) { + if (permanent->link == link) { + // In case of revoking a permanent link + // we should just create a new one instead. + performCreate(peer, std::move(done), true); + return; + } + } using Flag = MTPmessages_EditExportedChatInvite::Flag; const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( - MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) - | (usageLimit ? Flag::f_usage_limit : Flag(0))), + MTP_flags((revoke ? Flag::f_revoked : Flag(0)) + | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) + | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_string(link), MTP_int(expireDate), MTP_int(usageLimit) )).done([=](const MTPmessages_ExportedChatInvite &result) { - _editRequests.erase(key); + const auto callbacks = _editCallbacks.take(key); + const auto peer = key.peer; result.match([&](const MTPDmessages_exportedChatInvite &data) { _api->session().data().processUsers(data.vusers()); - const auto &invite = data.vinvite(); - const auto link = (invite.type() == mtpc_chatInviteExported) - ? qs(invite.c_chatInviteExported().vlink()) - : QString(); - // #TODO links + const auto link = parse(peer, data.vinvite()); + auto i = _firstSlices.find(peer); + if (i != end(_firstSlices)) { + const auto j = ranges::find( + i->second.links, + key.link, + &Link::link); + if (j != end(i->second.links)) { + *j = link; + notify(peer); + } + } + for (const auto &callback : *callbacks) { + callback(link); + } }); }).fail([=](const RPCError &error) { - _editRequests.erase(key); + _editCallbacks.erase(key); }).send(); - _editRequests.emplace(key, requestId); } -void InviteLinks::revoke(not_null peer, const QString &link) { - const auto key = EditKey{ peer, link }; - if (_editRequests.contains(key)) { - return; - } - - const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( - MTP_flags(MTPmessages_EditExportedChatInvite::Flag::f_revoked), - peer->input, - MTP_string(link), - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPmessages_ExportedChatInvite &result) { - _editRequests.erase(key); - result.match([&](const MTPDmessages_exportedChatInvite &data) { - _api->session().data().processUsers(data.vusers()); - const auto &invite = data.vinvite(); - const auto link = (invite.type() == mtpc_chatInviteExported) - ? qs(invite.c_chatInviteExported().vlink()) - : QString(); - editPermanentLink(peer, key.link, link); - }); - }).fail([=](const RPCError &error) { - _editRequests.erase(key); - }).send(); - _editRequests.emplace(key, requestId); +void InviteLinks::revoke( + not_null peer, + const QString &link, + Fn done) { + performEdit(peer, link, std::move(done), true); } void InviteLinks::requestLinks(not_null peer) { @@ -129,16 +220,80 @@ void InviteLinks::requestLinks(not_null peer) { MTP_int(kFirstPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { _firstSliceRequests.remove(peer); - _firstSlices.emplace_or_assign(peer, parseSlice(peer, result)); - peer->session().changes().peerUpdated( - peer, - Data::PeerUpdate::Flag::InviteLink); + auto slice = parseSlice(peer, result); + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; + if (!permanent) { + BringPermanentToFront(slice); + const auto j = _firstSlices.emplace_or_assign( + peer, + std::move(slice)).first; + if (const auto permanent = lookupPermanent(j->second)) { + editPermanentLink(peer, permanent->link); + } + } else { + RemovePermanent(slice); + auto &existing = i->second.links; + existing.erase(begin(existing) + 1, end(existing)); + existing.insert( + end(existing), + begin(slice.links), + end(slice.links)); + i->second.count = std::max(slice.count, int(existing.size())); + } + notify(peer); }).fail([=](const RPCError &error) { _firstSliceRequests.remove(peer); }).send(); _firstSliceRequests.emplace(peer, requestId); } +void InviteLinks::setPermanent( + not_null peer, + const MTPExportedChatInvite &invite) { + auto link = parse(peer, invite); + link.permanent = true; // #TODO links remove hack + //if (!link.permanent) { + // LOG(("API Error: " + // "InviteLinks::setPermanent called with non-permanent link.")); + // return; + //} + auto i = _firstSlices.find(peer); + if (i == end(_firstSlices)) { + i = _firstSlices.emplace(peer).first; + } + auto &links = i->second; + if (const auto permanent = lookupPermanent(links)) { + if (permanent->link == link.link) { + if (permanent->usage != link.usage) { + permanent->usage = link.usage; + notify(peer); + } + return; + } + permanent->revoked = true; + } + links.links.insert(begin(links.links), link); + editPermanentLink(peer, link.link); + notify(peer); +} + +void InviteLinks::clearPermanent(not_null peer) { + if (const auto permanent = lookupPermanent(peer)) { + permanent->revoked = true; + editPermanentLink(peer, QString()); + notify(peer); + } +} + +void InviteLinks::notify(not_null peer) { + peer->session().changes().peerUpdated( + peer, + Data::PeerUpdate::Flag::InviteLinks); +} + auto InviteLinks::links(not_null peer) const -> Links { const auto i = _firstSlices.find(peer); return (i != end(_firstSlices)) ? i->second : Links(); @@ -147,28 +302,42 @@ auto InviteLinks::links(not_null peer) const -> Links { auto InviteLinks::parseSlice( not_null peer, const MTPmessages_ExportedChatInvites &slice) const -> Links { + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; auto result = Links(); slice.match([&](const MTPDmessages_exportedChatInvites &data) { - auto &owner = peer->session().data(); - owner.processUsers(data.vusers()); + peer->session().data().processUsers(data.vusers()); result.count = data.vcount().v; for (const auto &invite : data.vinvites().v) { - invite.match([&](const MTPDchatInviteExported &data) { - result.links.push_back({ - .link = qs(data.vlink()), - .admin = owner.user(data.vadmin_id().v), - .date = data.vdate().v, - .expireDate = data.vexpire_date().value_or_empty(), - .usageLimit = data.vusage_limit().value_or_empty(), - .usage = data.vusage().value_or_empty(), - .revoked = data.is_revoked(), - }); - }); + const auto link = parse(peer, invite); + if (!permanent || link.link != permanent->link) { + result.links.push_back(link); + } } }); return result; } +auto InviteLinks::parse( + not_null peer, + const MTPExportedChatInvite &invite) const -> Link { + return invite.match([&](const MTPDchatInviteExported &data) { + return Link{ + .link = qs(data.vlink()), + .admin = peer->session().data().user(data.vadmin_id().v), + .date = data.vdate().v, + .expireDate = data.vexpire_date().value_or_empty(), + .usageLimit = data.vusage_limit().value_or_empty(), + .usage = data.vusage().value_or_empty(), + .permanent = data.is_permanent(), + .expired = data.is_expired(), + .revoked = data.is_revoked(), + }; + }); +} + void InviteLinks::requestMoreLinks( not_null peer, const QString &last, @@ -180,7 +349,9 @@ void InviteLinks::requestMoreLinks( MTP_string(last), MTP_int(kPerPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { - done(parseSlice(peer, result)); + auto slice = parseSlice(peer, result); + RemovePermanent(slice); + done(std::move(slice)); }).fail([=](const RPCError &error) { done(Links()); }).send(); @@ -188,16 +359,11 @@ void InviteLinks::requestMoreLinks( void InviteLinks::editPermanentLink( not_null peer, - const QString &from, - const QString &to) { + const QString &link) { if (const auto chat = peer->asChat()) { - if (chat->inviteLink() == from) { - chat->setInviteLink(to); - } + chat->setInviteLink(link); } else if (const auto channel = peer->asChannel()) { - if (channel->inviteLink() == from) { - channel->setInviteLink(to); - } + channel->setInviteLink(link); } else { Unexpected("Peer in InviteLinks::editMainLink."); } diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 7de6341be..f2534f3d3 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -18,6 +18,8 @@ struct InviteLink { TimeId expireDate = 0; int usageLimit = 0; int usage = 0; + bool permanent = false; + bool expired = false; bool revoked = false; }; @@ -35,14 +37,24 @@ public: void create( not_null peer, + Fn done = nullptr, TimeId expireDate = 0, int usageLimit = 0); void edit( not_null peer, const QString &link, TimeId expireDate, - int usageLimit); - void revoke(not_null peer, const QString &link); + int usageLimit, + Fn done = nullptr); + void revoke( + not_null peer, + const QString &link, + Fn done = nullptr); + + void setPermanent( + not_null peer, + const MTPExportedChatInvite &invite); + void clearPermanent(not_null peer); void requestLinks(not_null peer); [[nodiscard]] Links links(not_null peer) const; @@ -64,21 +76,47 @@ private: } }; - void editPermanentLink( - not_null peer, - const QString &from, - const QString &to); [[nodiscard]] Links parseSlice( not_null peer, const MTPmessages_ExportedChatInvites &slice) const; + [[nodiscard]] Link parse( + not_null peer, + const MTPExportedChatInvite &invite) const; + [[nodiscard]] Link *lookupPermanent(not_null peer); + [[nodiscard]] Link *lookupPermanent(Links &links); + [[nodiscard]] const Link *lookupPermanent(const Links &links) const; + Link prepend( + not_null peer, + const MTPExportedChatInvite &invite); + void notify(not_null peer); + + void editPermanentLink( + not_null peer, + const QString &link); + + void performEdit( + not_null peer, + const QString &link, + Fn done, + bool revoke, + TimeId expireDate = 0, + int usageLimit = 0); + void performCreate( + not_null peer, + Fn done, + bool revokeLegacyPermanent, + TimeId expireDate = 0, + int usageLimit = 0); const not_null _api; base::flat_map, Links> _firstSlices; base::flat_map, mtpRequestId> _firstSliceRequests; - base::flat_map, mtpRequestId> _createRequests; - base::flat_map _editRequests; + base::flat_map< + not_null, + std::vector>> _createCallbacks; + base::flat_map>> _editCallbacks; }; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 54e633fdc..ee74e1c52 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -614,7 +614,9 @@ void GroupInfoBox::createGroup( } void GroupInfoBox::submit() { - if (_creationRequestId) return; + if (_creationRequestId || _creatingInviteLink) { + return; + } auto title = TextUtilities::PrepareForSending(_title->getLastText()); auto description = _description @@ -653,6 +655,8 @@ void GroupInfoBox::submit() { } void GroupInfoBox::createChannel(const QString &title, const QString &description) { + Expects(!_creationRequestId); + const auto flags = (_type == Type::Megagroup) ? MTPchannels_CreateChannel::Flag::f_megagroup : MTPchannels_CreateChannel::Flag::f_broadcast; @@ -692,29 +696,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio channel, std::move(image)); } + using Flag = MTPmessages_ExportChatInvite::Flag; _createdChannel = channel; - _creationRequestId = _api.request(MTPmessages_ExportChatInvite( - MTP_flags(0), - _createdChannel->input, - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPExportedChatInvite &result) { - _creationRequestId = 0; - if (result.type() == mtpc_chatInviteExported) { - auto link = qs(result.c_chatInviteExported().vlink()); - _createdChannel->setInviteLink(link); - } - if (_channelDone) { - const auto callback = _channelDone; - const auto argument = _createdChannel; - closeBox(); - callback(argument); - } else { - Ui::show(Box( - _navigation, - _createdChannel)); - } - }).send(); + checkInviteLink(); }; if (!success) { LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)")); @@ -733,6 +717,32 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio }).send(); } +void GroupInfoBox::checkInviteLink() { + Expects(_createdChannel != nullptr); + + if (!_createdChannel->inviteLink().isEmpty()) { + channelReady(); + return; + } + _creatingInviteLink = true; + _createdChannel->session().api().inviteLinks().create( + _createdChannel, + crl::guard(this, [=](auto&&) { channelReady(); })); +} + +void GroupInfoBox::channelReady() { + if (_channelDone) { + const auto callback = _channelDone; + const auto argument = _createdChannel; + closeBox(); + callback(argument); + } else { + Ui::show(Box( + _navigation, + _createdChannel)); + } +} + void GroupInfoBox::descriptionResized() { updateMaxHeight(); update(); @@ -834,7 +844,7 @@ void SetupChannelBox::prepare() { _channel->session().changes().peerUpdates( _channel, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { rtlupdate(_invitationLink); }, lifetime()); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index d28e57450..39bb98717 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -121,6 +121,8 @@ private: void createGroup(not_null selectUsersBox, const QString &title, const std::vector> &users); void submitName(); void submit(); + void checkInviteLink(); + void channelReady(); void descriptionResized(); void updateMaxHeight(); @@ -138,6 +140,7 @@ private: // group / channel creation mtpRequestId _creationRequestId = 0; + bool _creatingInviteLink = false; ChannelData *_createdChannel = nullptr; }; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index a9485ea86..e5b4f766d 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -394,7 +394,7 @@ void MaxInviteBox::prepare() { _channel->session().changes().peerUpdates( _channel, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { rtlupdate(_invitationLink); }, lifetime()); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index edf4465bb..a3ae08f6e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -331,7 +331,6 @@ private: void showEditLinkedChatBox(); void fillPrivacyTypeButton(); void fillLinkedChatButton(); - void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); void fillManageSection(); @@ -640,6 +639,8 @@ void Controller::refreshHistoryVisibility(anim::type animated) { void Controller::showEditPeerTypeBox( std::optional> error) { + Expects(_privacySavedValue.has_value()); + const auto boxCallback = crl::guard(this, [=]( Privacy checked, QString publicLink) { _privacyTypeUpdates.fire(std::move(checked)); @@ -652,7 +653,7 @@ void Controller::showEditPeerTypeBox( _peer, _channelHasLocationOriginalValue, boxCallback, - _privacySavedValue, + *_privacySavedValue, _usernameSavedValue, error), Ui::LayerOption::KeepOther); @@ -798,22 +799,9 @@ void Controller::fillLinkedChatButton() { _linkedChatUpdates.fire_copy(*_linkedChatSavedValue); } -void Controller::fillInviteLinkButton() { - Expects(_controls.buttonsLayout != nullptr); - - const auto buttonCallback = [=] { - Ui::show(Box(_peer), Ui::LayerOption::KeepOther); - }; - - AddButtonWithText( - _controls.buttonsLayout, - tr::lng_profile_invite_link_section(), - rpl::single(QString()), //Empty text. - buttonCallback); -} - void Controller::fillSignaturesButton() { Expects(_controls.buttonsLayout != nullptr); + const auto channel = _peer->asChannel(); if (!channel) return; @@ -964,8 +952,6 @@ void Controller::fillManageSection() { if (canEditUsername) { fillPrivacyTypeButton(); - } else if (canEditInviteLink) { - fillInviteLinkButton(); } if (canViewOrEditLinkedChat) { fillLinkedChatButton(); @@ -1010,7 +996,7 @@ void Controller::fillManageSection() { peer->session().api().inviteLinks().requestLinks(peer); return peer->session().changes().peerUpdates( peer, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::map([=] { return peer->session().api().inviteLinks().links( peer).count; @@ -1018,7 +1004,7 @@ void Controller::fillManageSection() { }) | rpl::flatten_latest( ) | ToPositiveNumberString(), [=] { ShowEditInviteLinks(_navigation, _peer); }, - st::infoIconPermissions); + st::infoIconInviteLinks); } if (canViewAdmins) { AddButtonWithCount( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 5b20229e2..170bb5dc9 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -58,7 +58,7 @@ public: not_null container, not_null peer, bool useLocationPhrases, - std::optional privacySavedValue, + Privacy privacySavedValue, std::optional usernameSavedValue); void createContent(); @@ -66,17 +66,11 @@ public: void setFocusUsername(); rpl::producer getTitle() { - return _isInviteLink - ? tr::lng_profile_invite_link_section() - : _isGroup + return _isGroup ? tr::lng_manage_peer_group_type() : tr::lng_manage_peer_channel_type(); } - bool isInviteLink() { - return _isInviteLink; - } - bool isAllowSave() { return _isAllowSave; } @@ -98,17 +92,14 @@ private: base::unique_qptr usernameResult; const style::FlatLabel *usernameResultStyle = nullptr; - Ui::SlideWrap *createInviteLinkWrap = nullptr; - Ui::SlideWrap *editInviteLinkWrap = nullptr; + Ui::SlideWrap *inviteLinkWrap = nullptr; Ui::FlatLabel *inviteLink = nullptr; }; Controls _controls; - object_ptr createPrivaciesEdit(); object_ptr createUsernameEdit(); - object_ptr createInviteLinkCreate(); - object_ptr createInviteLinkEdit(); + object_ptr createInviteLinkBlock(); void observeInviteLink(); @@ -124,8 +115,7 @@ private: not_null st); bool canEditInviteLink() const; - void refreshEditInviteLink(); - void refreshCreateInviteLink(); + void refreshInviteLinkBlock(); void createInviteLink(); void revokeInviteLink(const QString &link); @@ -143,12 +133,11 @@ private: not_null _peer; MTP::Sender _api; - std::optional _privacySavedValue; + Privacy _privacySavedValue = Privacy(); std::optional _usernameSavedValue; bool _useLocationPhrases = false; bool _isGroup = false; - bool _isInviteLink = false; bool _isAllowSave = false; base::unique_qptr _wrap; @@ -165,7 +154,7 @@ Controller::Controller( not_null container, not_null peer, bool useLocationPhrases, - std::optional privacySavedValue, + Privacy privacySavedValue, std::optional usernameSavedValue) : _peer(peer) , _api(&_peer->session().mtp()) @@ -173,8 +162,6 @@ Controller::Controller( , _usernameSavedValue(usernameSavedValue) , _useLocationPhrases(useLocationPhrases) , _isGroup(_peer->isChat() || _peer->isMegagroup()) -, _isInviteLink(!_privacySavedValue.has_value() - && !_usernameSavedValue.has_value()) , _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty()) , _wrap(container) , _checkUsernameTimer([=] { checkUsernameAvailability(); }) { @@ -184,18 +171,11 @@ Controller::Controller( void Controller::createContent() { _controls = Controls(); - if (_isInviteLink) { - _wrap->add(createInviteLinkCreate()); - _wrap->add(createInviteLinkEdit()); - return; - } - fillPrivaciesButtons(_wrap, _privacySavedValue); // Skip. _wrap->add(object_ptr(_wrap)); // - _wrap->add(createInviteLinkCreate()); - _wrap->add(createInviteLinkEdit()); + _wrap->add(createInviteLinkBlock()); _wrap->add(createUsernameEdit()); if (_controls.privacy->value() == Privacy::NoUsername) { @@ -378,16 +358,14 @@ void Controller::privacyChanged(Privacy value) { // Otherwise box will change own Y position. if (value == Privacy::HasUsername) { - refreshCreateInviteLink(); - refreshEditInviteLink(); + refreshInviteLinkBlock(); toggleEditUsername(); _controls.usernameResult = nullptr; checkUsernameAvailability(); } else { toggleEditUsername(); - refreshCreateInviteLink(); - refreshEditInviteLink(); + refreshInviteLinkBlock(); } }; if (value == Privacy::HasUsername) { @@ -562,29 +540,26 @@ void Controller::revokeInviteLink(const QString &link) { bool Controller::canEditInviteLink() const { if (const auto channel = _peer->asChannel()) { - return channel->amCreator() - || (channel->adminRights() & ChatAdminRight::f_invite_users); + return channel->canHaveInviteLink(); } else if (const auto chat = _peer->asChat()) { - return chat->amCreator() - || (chat->adminRights() & ChatAdminRight::f_invite_users); + return chat->canHaveInviteLink(); } return false; } void Controller::observeInviteLink() { - if (!_controls.editInviteLinkWrap) { + if (!_controls.inviteLinkWrap) { return; } _peer->session().changes().peerFlagsValue( _peer, - Data::PeerUpdate::Flag::InviteLink + Data::PeerUpdate::Flag::InviteLinks ) | rpl::start_with_next([=] { - refreshCreateInviteLink(); - refreshEditInviteLink(); - }, _controls.editInviteLinkWrap->lifetime()); + refreshInviteLinkBlock(); + }, _controls.inviteLinkWrap->lifetime()); } -object_ptr Controller::createInviteLinkEdit() { +object_ptr Controller::createInviteLinkBlock() { Expects(_wrap != nullptr); if (!canEditInviteLink()) { @@ -595,18 +570,16 @@ object_ptr Controller::createInviteLinkEdit() { _wrap, object_ptr(_wrap), st::editPeerInvitesMargins); - _controls.editInviteLinkWrap = result.data(); + _controls.inviteLinkWrap = result.data(); const auto container = result->entity(); - if (!_isInviteLink) { - container->add(object_ptr( - container, - tr::lng_profile_invite_link_section(), - st::editPeerSectionLabel)); - container->add(object_ptr( - container, - st::editPeerInviteLinkBoxBottomSkip)); - } + container->add(object_ptr( + container, + tr::lng_profile_invite_link_section(), + st::editPeerSectionLabel)); + container->add(object_ptr( + container, + st::editPeerInviteLinkBoxBottomSkip)); _controls.inviteLink = container->add(object_ptr( container, @@ -634,7 +607,7 @@ object_ptr Controller::createInviteLinkEdit() { return result; } -void Controller::refreshEditInviteLink() { +void Controller::refreshInviteLinkBlock() { const auto link = inviteLinkText(); auto text = TextWithEntities(); if (!link.isEmpty()) { @@ -652,74 +625,26 @@ void Controller::refreshEditInviteLink() { _controls.inviteLink->setMarkedText(text); // Hack to expand FlatLabel width to naturalWidth again. - _controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth); + _controls.inviteLinkWrap->resizeToWidth(st::boxWideWidth); - _controls.editInviteLinkWrap->toggle( + _controls.inviteLinkWrap->toggle( inviteLinkShown() && !link.isEmpty(), anim::type::instant); } -object_ptr Controller::createInviteLinkCreate() { - Expects(_wrap != nullptr); - - if (!canEditInviteLink()) { - return nullptr; - } - - auto result = object_ptr>( - _wrap, - object_ptr(_wrap), - st::editPeerInvitesMargins); - const auto container = result->entity(); - - if (!_isInviteLink) { - container->add(object_ptr( - container, - tr::lng_profile_invite_link_section(), - st::editPeerSectionLabel)); - container->add(object_ptr( - container, - st::editPeerInviteLinkSkip)); - } - - container->add(object_ptr( - _wrap, - tr::lng_group_invite_create(tr::now), - st::editPeerInviteLinkButton) - )->addClickHandler([this] { - createInviteLink(); - }); - _controls.createInviteLinkWrap = result.data(); - - observeInviteLink(); - - return result; -} - -void Controller::refreshCreateInviteLink() { - _controls.createInviteLinkWrap->toggle( - inviteLinkShown() && inviteLinkText().isEmpty(), - anim::type::instant); -} - bool Controller::inviteLinkShown() { return !_controls.privacy - || (_controls.privacy->value() == Privacy::NoUsername) - || _isInviteLink; + || (_controls.privacy->value() == Privacy::NoUsername); } } // namespace -EditPeerTypeBox::EditPeerTypeBox(QWidget*, not_null peer) -: EditPeerTypeBox(nullptr, peer, false, {}, {}, {}, {}) { -} - EditPeerTypeBox::EditPeerTypeBox( QWidget*, not_null peer, bool useLocationPhrases, std::optional> savedCallback, - std::optional privacySaved, + Privacy privacySaved, std::optional usernameSaved, std::optional> usernameError) : _peer(peer) @@ -760,7 +685,7 @@ void EditPeerTypeBox::prepare() { setTitle(controller->getTitle()); - if (!controller->isInviteLink() && _savedCallback.has_value()) { + if (_savedCallback.has_value()) { addButton(tr::lng_settings_save(), [=] { const auto v = controller->getPrivacy(); if (!controller->isAllowSave() && (v == Privacy::HasUsername)) { @@ -772,13 +697,11 @@ void EditPeerTypeBox::prepare() { local(v, (v == Privacy::HasUsername) ? controller->getUsernameInput() - : QString()); // We dont need username with private type. + : QString()); // We don't need username with private type. closeBox(); }); } - addButton( - controller->isInviteLink() ? tr::lng_close() : tr::lng_cancel(), - [=] { closeBox(); }); + addButton(tr::lng_cancel(), [=] { closeBox(); }); setDimensionsToContent(st::boxWideWidth, content); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 7e0e6c620..8aae656e2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -32,15 +32,12 @@ enum class UsernameState { class EditPeerTypeBox : public Ui::BoxContent { public: - // Edit just the invite link. - EditPeerTypeBox(QWidget*, not_null peer); - EditPeerTypeBox( QWidget*, not_null peer, bool useLocationPhrases, std::optional> savedCallback, - std::optional privacySaved, + Privacy privacySaved, std::optional usernameSaved, std::optional> usernameError = {}); @@ -53,7 +50,7 @@ private: bool _useLocationPhrases = false; std::optional> _savedCallback; - std::optional _privacySavedValue; + Privacy _privacySavedValue = Privacy(); std::optional _usernameSavedValue; std::optional> _usernameError; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 97d52424d..1ff199880 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_group_call.h" +#include "data/data_changes.h" #include "core/application.h" #include "boxes/single_choice_box.h" #include "webrtc/webrtc_audio_input_tester.h" @@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_calls.h" #include "main/main_session.h" #include "apiwrap.h" +#include "api/api_invite_links.h" #include "styles/style_layers.h" #include "styles/style_calls.h" #include "styles/style_settings.h" @@ -441,23 +443,9 @@ void GroupCallSettingsBox( )->addClickHandler([=] { if (!copyLink() && !state->generatingLink) { state->generatingLink = true; - peer->session().api().request(MTPmessages_ExportChatInvite( - MTP_flags(0), - peer->input, - MTPint(), // expire_date - MTPint() // usage_limit - )).done([=](const MTPExportedChatInvite &result) { - if (result.type() == mtpc_chatInviteExported) { - const auto link = qs( - result.c_chatInviteExported().vlink()); - if (const auto chat = peer->asChat()) { - chat->setInviteLink(link); - } else if (const auto channel = peer->asChannel()) { - channel->setInviteLink(link); - } - copyLink(); - } - }).send(); + peer->session().api().inviteLinks().create( + peer, + crl::guard(layout, [=](auto&&) { copyLink(); })); } }); } diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 568840d82..cf00ebec3 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -74,7 +74,7 @@ struct PeerUpdate { IsBot = (1 << 19), // For chats and channels - InviteLink = (1 << 20), + InviteLinks = (1 << 20), Members = (1 << 21), Admins = (1 << 22), BannedUsers = (1 << 23), diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index d21af2b41..6bc11d7d5 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "main/main_session.h" #include "api/api_chat_invite.h" +#include "api/api_invite_links.h" #include "apiwrap.h" namespace { @@ -98,10 +99,7 @@ void ChannelData::setAccessHash(uint64 accessHash) { } void ChannelData::setInviteLink(const QString &newInviteLink) { - if (newInviteLink != _inviteLink) { - _inviteLink = newInviteLink; - session().changes().peerUpdated(this, UpdateFlag::InviteLink); - } + _inviteLink = newInviteLink; } bool ChannelData::canHaveInviteLink() const { @@ -778,11 +776,11 @@ void ApplyChannelUpdate( next->v - channel->slowmodeSeconds()); } if (const auto invite = update.vexported_invite()) { - invite->match([&](const MTPDchatInviteExported &data) { - channel->setInviteLink(qs(data.vlink())); - }); + channel->session().api().inviteLinks().setPermanent( + channel, + *invite); } else { - channel->setInviteLink(QString()); + channel->session().api().inviteLinks().clearPermanent(channel); } if (const auto location = update.vlocation()) { channel->setLocation(*location); diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index c35953c72..8f4f29659 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "main/main_session.h" #include "apiwrap.h" +#include "api/api_invite_links.h" namespace { @@ -127,10 +128,7 @@ void ChatData::invalidateParticipants() { } void ChatData::setInviteLink(const QString &newInviteLink) { - if (newInviteLink != _inviteLink) { - _inviteLink = newInviteLink; - session().changes().peerUpdated(this, UpdateFlag::InviteLink); - } + _inviteLink = newInviteLink; } bool ChatData::canHaveInviteLink() const { @@ -389,11 +387,9 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0))); } if (const auto invite = update.vexported_invite()) { - invite->match([&](const MTPDchatInviteExported &data) { - chat->setInviteLink(qs(data.vlink())); - }); + chat->session().api().inviteLinks().setPermanent(chat, *invite); } else { - chat->setInviteLink(QString()); + chat->session().api().inviteLinks().clearPermanent(chat); } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(chat, pinned->v); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index bcf621494..72601f186 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -348,7 +348,7 @@ infoProfileSeparatorPadding: margins( infoIconFg: menuIconFg; infoIconInformation: icon {{ "info_information", infoIconFg }}; -infoIconMembers: icon {{ "info_members", infoIconFg }}; +infoIconMembers: icon {{ "info/edit/group_manage_members", infoIconFg, point(-2px, 0px) }}; infoIconNotifications: icon {{ "info_notifications", infoIconFg }}; infoIconActions: icon {{ "info_actions", infoIconFg }}; //infoIconFeed: icon {{ "info_feed", infoIconFg }}; @@ -360,10 +360,11 @@ infoIconMediaLink: icon {{ "info_media_link", infoIconFg }}; infoIconMediaGroup: icon {{ "info_common_groups", infoIconFg }}; infoIconMediaVoice: icon {{ "info_media_voice", infoIconFg }}; infoIconMediaRound: icon {{ "info_media_round", infoIconFg }}; -infoIconRecentActions: icon {{ "info_recent_actions", infoIconFg, point(-2px, 0px) }}; -infoIconAdministrators: icon {{ "info_administrators", infoIconFg, point(-2px, -1px) }}; +infoIconRecentActions: icon {{ "info/edit/group_manage_actions", infoIconFg, point(-2px, -1px) }}; +infoIconAdministrators: icon {{ "info/edit/group_manage_admins", infoIconFg, point(-3px, 0px) }}; infoIconBlacklist: icon {{ "info_blacklist", infoIconFg, point(-2px, -2px) }}; -infoIconPermissions: icon {{ "info_permissions", infoIconFg }}; +infoIconPermissions: icon {{ "info/edit/group_manage_permissions", infoIconFg, point(0px, -2px) }}; +infoIconInviteLinks: icon {{ "info/edit/group_manage_links", infoIconFg, point(-2px, 0px) }}; infoInformationIconPosition: point(25px, 12px); infoNotificationsIconPosition: point(20px, 5px); infoSharedMediaIconPosition: point(20px, 24px); From 754dedc40e795e79cb70cbe0a886e85204d9b3f6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 15:42:26 +0400 Subject: [PATCH 138/396] Improve permanent link edit design. --- .../Resources/icons/info/edit/dotsmini.png | Bin 0 -> 260 bytes .../Resources/icons/info/edit/dotsmini@2x.png | Bin 0 -> 438 bytes .../Resources/icons/info/edit/dotsmini@3x.png | Bin 0 -> 643 bytes Telegram/SourceFiles/api/api_invite_links.cpp | 16 +- Telegram/SourceFiles/api/api_invite_links.h | 2 +- .../boxes/peers/edit_peer_invite_links.cpp | 91 +++++++++++ .../boxes/peers/edit_peer_invite_links.h | 18 +++ .../boxes/peers/edit_peer_type_box.cpp | 152 ++++++------------ Telegram/SourceFiles/info/info.style | 23 ++- .../ui/controls/invite_link_label.cpp | 102 ++++++++++++ .../ui/controls/invite_link_label.h | 37 +++++ Telegram/cmake/td_ui.cmake | 2 + 12 files changed, 332 insertions(+), 111 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/dotsmini.png create mode 100644 Telegram/Resources/icons/info/edit/dotsmini@2x.png create mode 100644 Telegram/Resources/icons/info/edit/dotsmini@3x.png create mode 100644 Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp create mode 100644 Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h create mode 100644 Telegram/SourceFiles/ui/controls/invite_link_label.cpp create mode 100644 Telegram/SourceFiles/ui/controls/invite_link_label.h diff --git a/Telegram/Resources/icons/info/edit/dotsmini.png b/Telegram/Resources/icons/info/edit/dotsmini.png new file mode 100644 index 0000000000000000000000000000000000000000..94bce645cd0c2f9f7b1796640a00686da4c784f1 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfU<$1a|hIsJ4 z4RPc;V8F5Y_uT*WP5gyvCC8J>PGoX1E|glkEH-T#>&#fAXTE6gB0F2&*1?oQ}A?g49Q@9 z8roglHz0BPE#U-=;m9p*M+i^nhQAXM!VV3J{XTN{TF=-W;bSCk5{*5#JM=Bfc z@l0Yg`XInK?SgE>XPb73$O>=^yL z?$(Fee+)rp>4&Nq=YMC^NOClhKD{aB+f|L9>k6-J)n0qJJL1Cad}$6b)BeuV#nKwS z6$__s(mxt}WbaJ{=5n5ngqz%)23xOJmnK|od(--A;(f-Y9a}rR|N5O={(a+((5Nxfoslj^nDBTMU+KQVqbw>4c= zVV=8LwW4yvo7PQ9ZxVKN|9ETsfgxXc-Fb%7 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/dotsmini@3x.png b/Telegram/Resources/icons/info/edit/dotsmini@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1e41886225fc6e27e2533cf2150fe5bdab9005b7 GIT binary patch literal 643 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^v49!D1}U6i==L6{?5(GZV@L(# z+ZlnInhbbcPycRX-QY0&XwaW><~1z;Dti|{?*C$@EFi;gEIaewja$hcPP3+5n#6vc zt8xF-%4PB_3@R~<4h%vpEj{dv9s&YJax4mt4ks!E8dw+=A3LVAUor9B6{GjEZ1>X4 zS?>)vk4?%CeO&?NN|o%aC#ZcsXv< z@o!((=Ds_`zmt6x6Bs;Zt=F*A6wy|gsUA5ud8JMIw%;47XLIWby!Q`_ zm{s1=5O8^ef^1dR@n;M|8yFzYzi^ZBX^5Y@;eXHk)umiVMGe?`!h4xym|ffh85S+p z*u*UB%J?Gs$p)(vmsP*q$m6x+nXugZ#m2i*6V`s;=617so=H68Jg1FJlPZN|kJ>UF zcFb;Lec62O`$sbdAr3~k9Tx+2t~iNwv0CiO5_$0C_`JSFjvBmHT}sv$0!143x$>=U zwuyD>e|zo0VTQ_GEpoTlGTy8@ny_oS->mfv{tF`*JNq8VIX*mKdbsIJWBQ*AZ3fO7 z2BrxL3Mn=m4owXcJ}NM9GC3VDeakmMI?_{S*96nFc)->+k!1mNlF=d#Wzp$P!- CB<~ae literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 8e05c1b59..3000fb7ef 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -254,12 +254,11 @@ void InviteLinks::setPermanent( not_null peer, const MTPExportedChatInvite &invite) { auto link = parse(peer, invite); - link.permanent = true; // #TODO links remove hack - //if (!link.permanent) { - // LOG(("API Error: " - // "InviteLinks::setPermanent called with non-permanent link.")); - // return; - //} + if (!link.permanent) { + LOG(("API Error: " + "InviteLinks::setPermanent called with non-permanent link.")); + return; + } auto i = _firstSlices.find(peer); if (i == end(_firstSlices)) { i = _firstSlices.emplace(peer).first; @@ -294,9 +293,10 @@ void InviteLinks::notify(not_null peer) { Data::PeerUpdate::Flag::InviteLinks); } -auto InviteLinks::links(not_null peer) const -> Links { +auto InviteLinks::links(not_null peer) const -> const Links & { + static const auto kEmpty = Links(); const auto i = _firstSlices.find(peer); - return (i != end(_firstSlices)) ? i->second : Links(); + return (i != end(_firstSlices)) ? i->second : kEmpty; } auto InviteLinks::parseSlice( diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index f2534f3d3..6c53c9fbb 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -57,7 +57,7 @@ public: void clearPermanent(not_null peer); void requestLinks(not_null peer); - [[nodiscard]] Links links(not_null peer) const; + [[nodiscard]] const Links &links(not_null peer) const; void requestMoreLinks( not_null peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp new file mode 100644 index 000000000..03b58acdc --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -0,0 +1,91 @@ +/* +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 "boxes/peers/edit_peer_invite_links.h" + +#include "data/data_changes.h" +#include "data/data_peer.h" +#include "main/main_session.h" +#include "api/api_invite_links.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/widgets/popup_menu.h" +#include "ui/controls/invite_link_label.h" +#include "ui/toast/toast.h" +#include "lang/lang_keys.h" +#include "apiwrap.h" +#include "styles/style_info.h" + +#include + +void AddPermanentLinkBlock( + not_null container, + not_null peer) { + const auto computePermanentLink = [=] { + const auto &links = peer->session().api().inviteLinks().links( + peer).links; + const auto link = links.empty() ? nullptr : &links.front(); + return (link && link->permanent && !link->revoked) ? link : nullptr; + }; + auto value = peer->session().changes().peerFlagsValue( + peer, + Data::PeerUpdate::Flag::InviteLinks + ) | rpl::map([=] { + const auto link = computePermanentLink(); + return link + ? std::make_tuple(link->link, link->usage) + : std::make_tuple(QString(), 0); + }) | rpl::start_spawning(container->lifetime()); + + const auto copyLink = [=] { + if (const auto link = computePermanentLink()) { + QGuiApplication::clipboard()->setText(link->link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + } + }; + const auto shareLink = [=] { + if (const auto link = computePermanentLink()) { + QGuiApplication::clipboard()->setText(link->link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + } + }; + const auto revokeLink = [=] { + if (const auto link = computePermanentLink()) { + QGuiApplication::clipboard()->setText(link->link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + } + }; + + auto link = rpl::duplicate( + value + ) | rpl::map([=](QString link, int usage) { + const auto prefix = qstr("https://"); + return link.startsWith(prefix) ? link.mid(prefix.size()) : link; + }); + const auto createMenu = [=] { + auto result = base::make_unique_q(container); + result->addAction( + tr::lng_group_invite_context_copy(tr::now), + copyLink); + result->addAction( + tr::lng_group_invite_context_share(tr::now), + shareLink); + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + return result; + }; + const auto label = container->lifetime().make_state( + container, + std::move(link), + createMenu); + container->add( + label->take(), + st::inviteLinkFieldPadding); + + label->clicks( + ) | rpl::start_with_next(copyLink, label->lifetime()); +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h new file mode 100644 index 000000000..358f82f5f --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h @@ -0,0 +1,18 @@ +/* +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 + +class PeerData; + +namespace Ui { +class VerticalLayout; +} // namespace Ui + +void AddPermanentLinkBlock( + not_null container, + not_null peer); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 170bb5dc9..117419c56 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" +#include "boxes/peers/edit_peer_info_box.h" // CreateButton. +#include "boxes/peers/edit_peer_invite_links.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "core/application.h" #include "data/data_channel.h" @@ -32,15 +34,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" +#include "ui/widgets/popup_menu.h" #include "ui/widgets/box_content_divider.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/special_fields.h" #include "window/window_session_controller.h" +#include "settings/settings_common.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" +#include "styles/style_settings.h" #include #include @@ -101,8 +106,6 @@ private: object_ptr createUsernameEdit(); object_ptr createInviteLinkBlock(); - void observeInviteLink(); - void privacyChanged(Privacy value); void checkUsernameAvailability(); @@ -114,8 +117,6 @@ private: rpl::producer &&text, not_null st); - bool canEditInviteLink() const; - void refreshInviteLinkBlock(); void createInviteLink(); void revokeInviteLink(const QString &link); @@ -178,6 +179,18 @@ void Controller::createContent() { _wrap->add(createInviteLinkBlock()); _wrap->add(createUsernameEdit()); + using namespace Settings; + AddSkip(_wrap.get()); + _wrap->add(EditPeerInfoBox::CreateButton( + _wrap.get(), + tr::lng_group_invite_manage(), + rpl::single(QString()), + [=] { /*ShowEditInviteLinks(_navigation, _peer);*/ }, + st::manageGroupButton, + &st::infoIconInviteLinks)); + AddSkip(_wrap.get()); + AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); + if (_controls.privacy->value() == Privacy::NoUsername) { checkUsernameAvailability(); } @@ -292,21 +305,23 @@ object_ptr Controller::createUsernameEdit() { auto result = object_ptr>( _wrap, - object_ptr(_wrap), - st::editPeerUsernameMargins); + object_ptr(_wrap)); _controls.usernameWrap = result.data(); const auto container = result->entity(); - container->add(object_ptr>( - container, + + using namespace Settings; + AddSkip(container); + container->add( object_ptr( container, tr::lng_create_group_link(), - st::editPeerSectionLabel), - st::editPeerUsernameTitleLabelMargins)); + st::settingsSubsectionTitle), + st::settingsSubsectionTitlePadding); - const auto placeholder = container->add(object_ptr( - container)); + const auto placeholder = container->add( + object_ptr(container), + st::editPeerUsernameFieldMargins); placeholder->setAttribute(Qt::WA_TransparentForMouseEvents); _controls.usernameInput = Ui::AttachParentChild( container, @@ -328,13 +343,9 @@ object_ptr Controller::createUsernameEdit() { }, placeholder->lifetime()); _controls.usernameInput->move(placeholder->pos()); - container->add(object_ptr>( + AddDividerText( container, - object_ptr( - container, - tr::lng_create_channel_link_about(), - st::editPeerPrivacyLabel), - st::editPeerUsernameAboutLabelMargins)); + tr::lng_create_channel_link_about()); QObject::connect( _controls.usernameInput, @@ -348,6 +359,11 @@ object_ptr Controller::createUsernameEdit() { } void Controller::privacyChanged(Privacy value) { + const auto toggleInviteLink = [&] { + _controls.inviteLinkWrap->toggle( + (value != Privacy::HasUsername), + anim::type::instant); + }; const auto toggleEditUsername = [&] { _controls.usernameWrap->toggle( (value == Privacy::HasUsername), @@ -358,14 +374,14 @@ void Controller::privacyChanged(Privacy value) { // Otherwise box will change own Y position. if (value == Privacy::HasUsername) { - refreshInviteLinkBlock(); + toggleInviteLink(); toggleEditUsername(); _controls.usernameResult = nullptr; checkUsernameAvailability(); } else { toggleEditUsername(); - refreshInviteLinkBlock(); + toggleInviteLink(); } }; if (value == Privacy::HasUsername) { @@ -538,100 +554,38 @@ void Controller::revokeInviteLink(const QString &link) { Ui::show(std::move(box), Ui::LayerOption::KeepOther); } -bool Controller::canEditInviteLink() const { - if (const auto channel = _peer->asChannel()) { - return channel->canHaveInviteLink(); - } else if (const auto chat = _peer->asChat()) { - return chat->canHaveInviteLink(); - } - return false; -} - -void Controller::observeInviteLink() { - if (!_controls.inviteLinkWrap) { - return; - } - _peer->session().changes().peerFlagsValue( - _peer, - Data::PeerUpdate::Flag::InviteLinks - ) | rpl::start_with_next([=] { - refreshInviteLinkBlock(); - }, _controls.inviteLinkWrap->lifetime()); -} - object_ptr Controller::createInviteLinkBlock() { Expects(_wrap != nullptr); - if (!canEditInviteLink()) { - return nullptr; - } - auto result = object_ptr>( _wrap, - object_ptr(_wrap), - st::editPeerInvitesMargins); + object_ptr(_wrap)); _controls.inviteLinkWrap = result.data(); const auto container = result->entity(); - container->add(object_ptr( - container, - tr::lng_profile_invite_link_section(), - st::editPeerSectionLabel)); - container->add(object_ptr( - container, - st::editPeerInviteLinkBoxBottomSkip)); - _controls.inviteLink = container->add(object_ptr( - container, - st::editPeerInviteLink)); - _controls.inviteLink->setSelectable(true); - _controls.inviteLink->setContextCopyText(QString()); - _controls.inviteLink->setBreakEverywhere(true); - _controls.inviteLink->setClickHandlerFilter([=](auto&&...) { - QGuiApplication::clipboard()->setText(inviteLinkText()); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); - return false; - }); + using namespace Settings; + AddSkip(container); + container->add( + object_ptr( + container, + tr::lng_create_permanent_link_title(), + st::settingsSubsectionTitle), + st::settingsSubsectionTitlePadding); - container->add(object_ptr( - container, - st::editPeerInviteLinkSkip)); - container->add(object_ptr( - container, - tr::lng_group_invite_create_new(tr::now), - st::editPeerInviteLinkButton) - )->addClickHandler([=] { revokeInviteLink(inviteLinkText()); }); + AddPermanentLinkBlock(container, _peer); - observeInviteLink(); + AddSkip(container); + + AddDividerText( + container, + ((_peer->isMegagroup() || _peer->asChat()) + ? tr::lng_group_invite_about_permanent_group() + : tr::lng_group_invite_about_permanent_channel())); return result; } -void Controller::refreshInviteLinkBlock() { - const auto link = inviteLinkText(); - auto text = TextWithEntities(); - if (!link.isEmpty()) { - text.text = link; - const auto remove = qstr("https://"); - if (text.text.startsWith(remove)) { - text.text.remove(0, remove.size()); - } - text.entities.push_back({ - EntityType::CustomUrl, - 0, - text.text.size(), - link }); - } - _controls.inviteLink->setMarkedText(text); - - // Hack to expand FlatLabel width to naturalWidth again. - _controls.inviteLinkWrap->resizeToWidth(st::boxWideWidth); - - _controls.inviteLinkWrap->toggle( - inviteLinkShown() && !link.isEmpty(), - anim::type::instant); -} - bool Controller::inviteLinkShown() { return !_controls.privacy || (_controls.privacy->value() == Privacy::NoUsername); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 72601f186..5dce0cf3e 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -693,8 +693,8 @@ editPeerSectionLabel: FlatLabel(boxTitle) { linkFontOver: font(15px semibold underline); } } -editPeerUsernameTitleLabelMargins: margins(0px, 0px, 0px, 10px); -editPeerUsernameAboutLabelMargins: margins(0px, 15px, 34px, 15px); +editPeerUsernameTitleLabelMargins: margins(22px, 17px, 22px, 10px); +editPeerUsernameFieldMargins: margins(22px, 0px, 22px, 20px); editPeerUsername: setupChannelLink; editPeerUsernameSkip: 8px; editPeerInviteLink: FlatLabel(defaultFlatLabel) { @@ -702,7 +702,6 @@ editPeerInviteLink: FlatLabel(defaultFlatLabel) { style: boxTextStyle; } editPeerInviteLinkButton: boxLinkButton; -editPeerUsernameMargins: margins(22px, 17px, 22px, 2px); editPeerUsernameGood: FlatLabel(defaultFlatLabel) { textFg: boxTextFgGood; style: boxTextStyle; @@ -832,3 +831,21 @@ separatePanelBack: IconButton(separatePanelClose) { icon: infoTopBarBackIcon; iconOver: infoTopBarBackIconOver; } + +inviteLinkField: FlatInput(defaultFlatInput) { + font: font(fsize); + + height: 44px; + textMrg: margins(14px, 12px, 36px, 9px); +} +inviteLinkThreeDots: IconButton(defaultIconButton) { + width: 36px; + height: 44px; + + icon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }}; + iconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }}; + iconPosition: point(-1px, -1px); + + rippleAreaSize: 0px; +} +inviteLinkFieldPadding: margins(22px, 7px, 22px, 9px); diff --git a/Telegram/SourceFiles/ui/controls/invite_link_label.cpp b/Telegram/SourceFiles/ui/controls/invite_link_label.cpp new file mode 100644 index 000000000..15fa86d3a --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/invite_link_label.cpp @@ -0,0 +1,102 @@ +/* +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 "ui/controls/invite_link_label.h" + +#include "ui/rp_widget.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/popup_menu.h" +#include "styles/style_info.h" + +namespace Ui { + +InviteLinkLabel::InviteLinkLabel( + not_null parent, + rpl::producer text, + Fn()> createMenu) +: _outer(std::in_place, parent) { + _outer->resize(_outer->width(), st::inviteLinkField.height); + const auto label = CreateChild( + _outer.get(), + std::move(text), + st::defaultFlatLabel); + label->setAttribute(Qt::WA_TransparentForMouseEvents); + + const auto button = CreateChild( + _outer.get(), + st::inviteLinkThreeDots); + + _outer->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto margin = st::inviteLinkField.textMrg; + label->resizeToWidth(width - margin.left() - margin.right()); + label->moveToLeft(margin.left(), margin.top()); + button->moveToRight(0, 0); + }, _outer->lifetime()); + + _outer->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(_outer.get()); + p.setPen(Qt::NoPen); + p.setBrush(st::inviteLinkField.bgColor); + { + PainterHighQualityEnabler hq(p); + p.drawRoundedRect( + _outer->rect(), + st::roundRadiusSmall, + st::roundRadiusSmall); + } + }, _outer->lifetime()); + + _outer->setCursor(style::cur_pointer); + + rpl::merge( + button->clicks() | rpl::to_empty, + _outer->events( + ) | rpl::filter([=](not_null event) { + return (event->type() == QEvent::MouseButtonPress) + && (static_cast(event.get())->button() + == Qt::RightButton); + }) | rpl::to_empty + ) | rpl::start_with_next([=] { + if (_menu) { + _menu = nullptr; + } else if ((_menu = createMenu())) { + _menu->popup(QCursor::pos()); + } + }, _outer->lifetime()); +} + +object_ptr InviteLinkLabel::take() { + return object_ptr::fromRaw(_outer.get()); +} + +rpl::producer<> InviteLinkLabel::clicks() { + return _outer->events( + ) | rpl::filter([=](not_null event) { + return (event->type() == QEvent::MouseButtonPress) + && (static_cast(event.get())->button() + == Qt::LeftButton); + }) | rpl::map([=](not_null event) { + return _outer->events( + ) | rpl::filter([=](not_null event) { + return (event->type() == QEvent::MouseButtonRelease) + && (static_cast(event.get())->button() + == Qt::LeftButton); + }) | rpl::take(1) | rpl::filter([=](not_null event) { + return (_outer->rect().contains( + static_cast(event.get())->pos())); + }); + }) | rpl::flatten_latest() | rpl::to_empty; +} + +rpl::lifetime &InviteLinkLabel::lifetime() { + return _outer->lifetime(); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/invite_link_label.h b/Telegram/SourceFiles/ui/controls/invite_link_label.h new file mode 100644 index 000000000..2af7defdb --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/invite_link_label.h @@ -0,0 +1,37 @@ +/* +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/object_ptr.h" +#include "base/unique_qptr.h" + +namespace Ui { + +class RpWidget; +class PopupMenu; + +class InviteLinkLabel final { +public: + InviteLinkLabel( + not_null parent, + rpl::producer text, + Fn()> createMenu); + + [[nodiscard]] object_ptr take(); + + [[nodiscard]] rpl::producer<> clicks(); + + [[nodiscard]] rpl::lifetime &lifetime(); + +private: + const base::unique_qptr _outer; + base::unique_qptr _menu; + +}; + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index bcdee7aae..af6fce641 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -88,6 +88,8 @@ PRIVATE ui/chat/pinned_bar.h ui/controls/emoji_button.cpp ui/controls/emoji_button.h + ui/controls/invite_link_label.cpp + ui/controls/invite_link_label.h ui/controls/send_button.cpp ui/controls/send_button.h ui/text/format_values.cpp From 7e89ed48c27f2429c469c08ca16daa82c09a98d8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 15:42:26 +0400 Subject: [PATCH 139/396] Improve permanent link edit design. --- Telegram/CMakeLists.txt | 2 ++ Telegram/Resources/langs/lang.strings | 39 ++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 9de5f9162..7810cb205 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -181,6 +181,8 @@ PRIVATE boxes/peers/edit_participants_box.h boxes/peers/edit_peer_info_box.cpp boxes/peers/edit_peer_info_box.h + boxes/peers/edit_peer_invite_links.cpp + boxes/peers/edit_peer_invite_links.h boxes/peers/edit_peer_type_box.cpp boxes/peers/edit_peer_type_box.h boxes/peers/edit_peer_history_visibility_box.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f78482907..1eb58fc27 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1165,14 +1165,45 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_members#other" = "{count} members, among them:"; "lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content."; -"lng_group_invite_create" = "Create an invite link"; -"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; -"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link."; -"lng_group_invite_create_new" = "Revoke invite link"; +"lng_group_invite_create" = "Create an invite link"; // #TODO links legacy +"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; // #TODO links legacy +"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link."; // #TODO links legacy +"lng_group_invite_create_new" = "Revoke invite link"; // #TODO links legacy "lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you."; "lng_group_invite_copied" = "Invite link copied to clipboard."; "lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already."; +"lng_group_invite_permanent" = "Permanent link"; +"lng_group_invite_copy" = "Copy Link"; +"lng_group_invite_share" = "Share Link"; +"lng_group_invite_revoke" = "Revoke Link"; +"lng_group_invite_no_joined" = "No one joined yet"; +"lng_group_invite_joined#one" = "{count} person joined"; +"lng_group_invite_joined#other" = "{count} people joined"; +"lng_group_invite_about_permanent_group" = "Anyone who has Telegram installed will be able to join your group by following this link."; +"lng_group_invite_about_permanent_channel" = "Anyone who has Telegram installed will be able to join your channel by following this link."; +"lng_group_invite_manage" = "Manage Invite Links"; +"lng_group_invite_manage_about" = "You can create additional invite links that have limited time or number of usages."; +"lng_group_invite_add" = "Create a New Link"; +"lng_group_invite_expires_at" = "This link expires {when}."; +"lng_group_invite_created_by" = "Link created by"; +"lng_group_invite_context_copy" = "Copy"; +"lng_group_invite_context_share" = "Share"; +"lng_group_invite_context_edit" = "Edit"; +"lng_group_invite_context_revoke" = "Revoke"; +"lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?"; +"lng_group_invite_link_expired" = "Expired"; +"lng_group_invite_link_revoked" = "Revoked"; +"lng_group_invite_edit_title" = "Edit Link"; +"lng_group_invite_new_title" = "New Link"; +"lng_group_invite_expire_title" = "Limit by time period"; +"lng_group_invite_expire_about" = "You can make the link expire after a certain time."; +"lng_group_invite_expire_custom" = "Set custom duration"; +"lng_group_invite_usage_title" = "Limit number of uses."; +"lng_group_invite_usage_about" = "You can make the link expire after it has been used for a certain number of times."; +"lng_group_invite_usage_custom" = "Enter custom limit"; + + "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; From 8c7030378a80804cfa8242338b6a16bb28d87feb Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 16:57:42 +0400 Subject: [PATCH 140/396] Add 'Copy Link' and 'Share Link' buttons. --- .../Resources/icons/info/edit/links_copy.png | Bin 0 -> 397 bytes .../icons/info/edit/links_copy@2x.png | Bin 0 -> 682 bytes .../icons/info/edit/links_copy@3x.png | Bin 0 -> 1069 bytes .../Resources/icons/info/edit/links_share.png | Bin 0 -> 464 bytes .../icons/info/edit/links_share@2x.png | Bin 0 -> 859 bytes .../icons/info/edit/links_share@3x.png | Bin 0 -> 1349 bytes .../boxes/peers/edit_peer_invite_links.cpp | 7 +++ Telegram/SourceFiles/info/info.style | 18 ++++++- .../ui/controls/invite_link_buttons.cpp | 50 ++++++++++++++++++ .../ui/controls/invite_link_buttons.h | 19 +++++++ Telegram/cmake/td_ui.cmake | 2 + 11 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 Telegram/Resources/icons/info/edit/links_copy.png create mode 100644 Telegram/Resources/icons/info/edit/links_copy@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_copy@3x.png create mode 100644 Telegram/Resources/icons/info/edit/links_share.png create mode 100644 Telegram/Resources/icons/info/edit/links_share@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_share@3x.png create mode 100644 Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp create mode 100644 Telegram/SourceFiles/ui/controls/invite_link_buttons.h diff --git a/Telegram/Resources/icons/info/edit/links_copy.png b/Telegram/Resources/icons/info/edit/links_copy.png new file mode 100644 index 0000000000000000000000000000000000000000..15e6cde2856a206c2184f302134e84652b5c38de GIT binary patch literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp6?zI(bjhD30_ z4e{n{QQ*;?99hkIeO}A$O7T4{o$Lj=*Ed`i+C8D=`~uF|lN2;-bLIKF85x$`%&plK zx{phnG1NhHMOcCa+l=Ru-)h@G+b{~|U$(q{<>LKW8hZP3rF$OFXfa-R?cl_lp6bj0 zbiCg6$iz3J=j0>rpGx)nESf#PIW%;;AI!gz%2e{av0=`gsP5y1Eqi|){{7~EbH>aD zk%>!IK59zdpWwZEC&QK{?5cczK?-YOb71^YCWw z-sdqfq2fn4w{Y+IzEQ(`_mlmNCs)*MW%>Oq_|6ypBkC1N<@wI43%FJ=+3Q?4nkdu! QG8z;Vp00i_>zopr0N=rojQ{`u literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_copy@2x.png b/Telegram/Resources/icons/info/edit/links_copy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9b54334978062b145f6a7dcbdaf0af617f1d81a8 GIT binary patch literal 682 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz$D`7;uuoF z_%_0EU6X-;u6^NYL5t?Ry^ZY;_+lC>r1Tp*!!NoBi@ovmsaOBFTGu%%b;Z~7CW;3x z_&wiS8N6U-kk{&_|inuyA2!B_IV7O2^_x9VT)e_dioP`_>I)1qilG#L$C@Q#oOLl4G z&tlp%+qrnwHJ=O9nsu13_dm0%x8dAf^tY{7?%TY#=@}NGv&0k?&h9zV?Ij}6|Jo#7 zh;_-#=@o&UOpglZKJ!W8vY6{v-g5qWdGaA~mHXE>^t1o2Os=jcb2d6+s0O-@$ckiF2>24H5U&)Evnr3yuzGIe%<#BE(;mHcXtkc7qQ#m z=^3w`#&qTW{`(@b%>Q!Og@SI2^?#FX@YH9G-eRi2x*>wciQyQR`fQDc;BAI$7BTdQ z`k4hXEYC@faAlOxo|ChP!SCj=4P8tIVHP**7}M0nPZ;jnW(7(op00i_>zopr0A~6G A$N&HU literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_copy@3x.png b/Telegram/Resources/icons/info/edit/links_copy@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5c702a8e316f1a3a9ed6ed8b000b19264d0c2d65 GIT binary patch literal 1069 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9PbBIKHbyBF{Fa= z?QFxoM+PEoygDWqE-Febn`y03$dl*5%HG&EscMNO%hd}P0ty(oTpXFj8`_R#zvmMY zs#A(9s*bjVK!@$H~%`h#URe<3J;|3e;28IKC4NoH(Ss2O~bk1`rFl=B>s0_o< zS}(gIL~Ggg*E3(1WQDDsdh`AFXGJ?DvftXjZ9ja_M@=|*_0Mv z_S(-@|Ia!$^y#*4xA;8!?6SYVex17ib#~7>p+#&TUKcj*S$6dI@7*)cKY#Z2?ORi( z|DItdJa3$>G5u_BZ@<`mVceqbqmR-iR-D~m_wwWG{nnhT`_BuWUo>-N;j|+MxBY9G zz2$A0wY3CK&hpC}uV$IfP=3sie10MOf!HjyxVLB0jHB0IPd3+i|L-MZ!S$byo_v2M zo|Cp*wqK5+e6lFZgXweSEk6He2-?f_s%-Z`?MWXia?U+3{t$7oc@;y{+OP}$<;G$? z|EDC}+WP8c*}L6y9|(PUk+nQV@AkujjO(wr+RpVmD3jUz@3US1`RAXlPP~X+&pgS~ zQ*F!b+}$yHOZ)9azgO1&Q>krD{>b#f_)F-s_>({XJn-reKagRv>(`e{`|B)&X1tu_ zwuj+Q)Y^@evLX$awr}|T`PbQ*k|LT@Ys}x7mme~D_wT_!hjW7UoOd!ziq6f_i@n02 zlJK%^+S;imoSRZl-i!Kbr%?U9>pv$GgRvS3k-9qUz{iR`(s>$X*H;{13}skoKGk_6 z({EXsS+^cr{9m@IKKbVUopYYOP3=8CF`V~#&n*7!QLFrqH2n~6s!^Jjz{mgealKYsk+0Q35;6rPY> z@cN8%?7n&P4lOZpv%B18f1!h+X?@G0e=eG@-Zw2V-R$vkh5sQ}qbMnEMvrqEM>!o% zW_Y$SsH9JM#Ihh|i;6SDBxA2arU0X;Ns0_7C6`(-YDnsy6li$T6EufmO3$K) zO+N-NH*X^r1I|xjRVA4w%n9rgZkTj&5)UC`fAY57ZhooQkun#QRXtt(T-G@y GGywp8n#Q~U literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_share.png b/Telegram/Resources/icons/info/edit/links_share.png new file mode 100644 index 0000000000000000000000000000000000000000..78ca22f8d3db5e4e46af6e9a432e7d8514ba09a3 GIT binary patch literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{X65PP7!twx zcIx?4t_~8%p4Xg^-NDE`Ju&%Z+!POu$jBQzS$1+ut4gbCZ%@>W+LWz8Y~aUB=8k{t`)eqx98S2*F_qOcCFK2dMNzk z+H2o!A8gQh?KQ9Zk;Fgt21T3m*4%}>4Ua7XLPZ~0v}MlP@J%RDVqT*K`+?6kERGvi zRt3hD0`<2pU=I*sowY3Tq)fS?MrQrTEw{s_rG`vXoutxP#wpd-n58#SC_FIqYT?fR zZngK9pX%o96gKs}y=1<2^CAs}(?%YXSdJCm>Dj%``^?Tus?19=e+eGYd$EhJNg;>j z+VKm8bI#uI%6M-tsc^p7fG1i1V3AVJ?R#^J8JyTGBK`=z*~RB`d5;p``utBnKl^NW fJ_GDSn|BAKdzSJ?8-@gxfkM;M)z4*}Q$iB}W|6G( literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_share@2x.png b/Telegram/Resources/icons/info/edit/links_share@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..451f6332263d6a064fe22cb72952e7092e19b0e1 GIT binary patch literal 859 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz;x8p#WAFU z@$F3i*Orbl$B)nNc7L#D&RorKb$9bAOH}G7)q55d35cunJ5(~vYns$CNm{O9%Ab>g zGw%0I_`9uptFiUx?DVX+M%JHy*2K;Ke&!q>&~~o&yzap56Yn!WXV}41;@7~>IG3BD zfVF`2#T*8CCM$7<`7A#L9ymKZXZay;VJE|nV4#YyHiwlVOs~I6u`n^7jdhy6=t}?3 z8ot1tF?t3veAzGg|2_z0ns)jrFT<}LaqA7Fdb3y?Z5jAEI$fN&IdYgPPAE=3Iic-< z83W6r2%SkS4&n_*lN$A=bFT|q?S4u1{8X=25tg@%7BYM{GE5#6ShOs^>}iy+QhxPS zEl!SG3J2ijwH<>-(?1F1;ggg1NwIZh?tZt?ZNK(zDO1`EjH$KKWc(VK?9Y z=#m}#u4Xwd(c1)G-8(UZ1&6|GaUyfPx!T^q_wb!|-4gyB!Pw9<=;a*9|tOyQNlmhOay%@) gjzQ{5BmfB*mh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_share@3x.png b/Telegram/Resources/icons/info/edit/links_share@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..af03170f6156e1922724b2ade0b6efb4cf1080cb GIT binary patch literal 1349 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9Ph`#z_Q-c#WAFU z@$GD1jgml#HC=XHFv82m8Z=Mo@Jf(?9ch1f8Q0Sznf_-w^T&3{*nWWki$-) z->wcU4dtwY3QPxB4txw^RATU9kkJ>V1^0j*Q)_GMs~0ak=FXY3V$~|G-&SQi)c4ug zs{Pxxef!Dlaq;mhmn~D1dL}CUp;Gs#=f zOV#|)b2j^f9L+NG|Ku}89DDv;{p*)67Iu9CmPh*;;$3-ocy@R?{ozayJbd_Y-b8`< zOc95U9BGkOaarClWr~QFuI|!{7XxqIxUt~#=i>K<^A5IMQd3vID747kAuCHuQeHhX zJ-z*h&-Ha?rlze^9@H^bR#tA=u)%?!pTErT|MK}WXG%_C`ObCX+__~-m#RKG|H10k zn>RX6O}|+ryg5wl?EL)w{jGNVKQUweeD(h0i-TKsv!tY^UVQiN9lyq|_d0rdK~7Fh zdYtbB6V^PxeDr8*-aW?8)22^ahKGkw6A0k{aC7VBV)L*4!W;JO>r2X+$sMueFh@p4#)g)Ls)URTkLUB=pT4B8 zpsU-fr>`G4hxr*FAK$Nr7yGX2H+(lS-}>)g-Q5fOKikCTvmNW5mo_JH)#lB{QzuUr zE_?9%_wHT0cL$rBo4@+@?OOF|c}Z<;?NB#2H;=#HzJ6V~bLY+pamn8&@6VNxwcZyS z7$|t@Tz!4Le{^)TiII_$tE=mjdGqotkNvMz&*6LazP-A-TKD&*gO9WiSH5_5a?P4G zGb~x|*xA~CWjcTUJb$LIS_C7{{@uH8^M0u0`Ni^q>H62Nr4BFtoPYc{IV&qm>2cJ& z4G&n98y=RImbPBq@4V&<=Y!4n9NvETFk#i|)yi>f|K>Ba-M@MBrP_}w?S}m<=H_fh zwzjh0>i`U>eXPIDBzbByHWA1W*y?>FVdQ I&MBb@0Es3?w*UYD literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 03b58acdc..a7e2fad86 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/wrap/vertical_layout.h" +#include "ui/wrap/padding_wrap.h" #include "ui/widgets/popup_menu.h" #include "ui/controls/invite_link_label.h" +#include "ui/controls/invite_link_buttons.h" #include "ui/toast/toast.h" #include "lang/lang_keys.h" #include "apiwrap.h" @@ -88,4 +90,9 @@ void AddPermanentLinkBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); + + AddCopyShareLinkButtons( + container, + copyLink, + shareLink); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 5dce0cf3e..534caccde 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -848,4 +848,20 @@ inviteLinkThreeDots: IconButton(defaultIconButton) { rippleAreaSize: 0px; } -inviteLinkFieldPadding: margins(22px, 7px, 22px, 9px); +inviteLinkFieldPadding: margins(22px, 7px, 22px, 14px); + +inviteLinkButton: RoundButton(defaultActiveButton) { + height: 36px; + textTop: 9px; +} +inviteLinkButtonsPadding: margins(22px, 0px, 22px, 0px); +inviteLinkButtonsSkip: 10px; +inviteLinkCopy: RoundButton(inviteLinkButton) { + icon: icon {{ "info/edit/links_copy", activeButtonFg }}; + iconOver: icon {{ "info/edit/links_copy", activeButtonFgOver }}; + iconPosition: point(-1px, 2px); +} +inviteLinkShare: RoundButton(inviteLinkCopy) { + icon: icon {{ "info/edit/links_share", activeButtonFg }}; + iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }}; +} diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp new file mode 100644 index 000000000..233ee0f51 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -0,0 +1,50 @@ +/* +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 "ui/controls/invite_link_buttons.h" + +#include "ui/widgets/buttons.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/padding_wrap.h" +#include "lang/lang_keys.h" +#include "styles/style_info.h" + +namespace Ui { + +void AddCopyShareLinkButtons( + not_null container, + Fn copyLink, + Fn shareLink) { + const auto wrap = container->add( + object_ptr( + container, + st::inviteLinkButton.height), + st::inviteLinkButtonsPadding); + const auto copy = CreateChild( + wrap, + tr::lng_group_invite_copy(), + st::inviteLinkCopy); + copy->setTextTransform(RoundButton::TextTransform::NoTransform); + copy->setClickedCallback(copyLink); + const auto share = CreateChild( + wrap, + tr::lng_group_invite_share(), + st::inviteLinkShare); + share->setTextTransform(RoundButton::TextTransform::NoTransform); + share->setClickedCallback(shareLink); + + wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto buttonWidth = (width - st::inviteLinkButtonsSkip) / 2; + copy->setFullWidth(buttonWidth); + share->setFullWidth(buttonWidth); + copy->moveToLeft(0, 0, width); + share->moveToRight(0, 0, width); + }, wrap->lifetime()); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h new file mode 100644 index 000000000..aa637dbb9 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h @@ -0,0 +1,19 @@ +/* +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 + +namespace Ui { + +class VerticalLayout; + +void AddCopyShareLinkButtons( + not_null container, + Fn copyLink, + Fn shareLink); + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index af6fce641..fd12d7f3a 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -88,6 +88,8 @@ PRIVATE ui/chat/pinned_bar.h ui/controls/emoji_button.cpp ui/controls/emoji_button.h + ui/controls/invite_link_buttons.cpp + ui/controls/invite_link_buttons.h ui/controls/invite_link_label.cpp ui/controls/invite_link_label.h ui/controls/send_button.cpp From be1afb4781a1a4c11744b1e3fe1afbddeff6600a Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 19:27:34 +0400 Subject: [PATCH 141/396] Show recently joined by permanent link userpics. --- .../Resources/icons/info/edit/links_link.png | Bin 0 -> 606 bytes .../icons/info/edit/links_link@2x.png | Bin 0 -> 1193 bytes .../icons/info/edit/links_link@3x.png | Bin 0 -> 1905 bytes .../icons/info/edit/roundbtn_plus.png | Bin 0 -> 289 bytes .../icons/info/edit/roundbtn_plus@2x.png | Bin 0 -> 460 bytes .../icons/info/edit/roundbtn_plus@3x.png | Bin 0 -> 752 bytes Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/api/api_invite_links.cpp | 76 ++++++++++++++- Telegram/SourceFiles/api/api_invite_links.h | 35 ++++++- .../boxes/peers/edit_peer_invite_links.cpp | 87 +++++++++++++++++- Telegram/SourceFiles/info/info.style | 10 ++ .../ui/controls/invite_link_buttons.cpp | 87 ++++++++++++++++++ .../ui/controls/invite_link_buttons.h | 11 +++ 13 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/links_link.png create mode 100644 Telegram/Resources/icons/info/edit/links_link@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_link@3x.png create mode 100644 Telegram/Resources/icons/info/edit/roundbtn_plus.png create mode 100644 Telegram/Resources/icons/info/edit/roundbtn_plus@2x.png create mode 100644 Telegram/Resources/icons/info/edit/roundbtn_plus@3x.png diff --git a/Telegram/Resources/icons/info/edit/links_link.png b/Telegram/Resources/icons/info/edit/links_link.png new file mode 100644 index 0000000000000000000000000000000000000000..334eab0a28935a0fa08bc7ed13a3bb82aa58aa39 GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{cEZ!eF(iWX zZRp-phYUn~?dt>X+ubu6cPJ5L4;NopBDCI8 zJa~(IRxo$0l*i;dEpsLp*Jp7kdVD)`D?aT6|ARRW>wqweYs%@TE|PzR8{HNw##|SD z{yCDxQDNKdwGzE`tCA2`@_F={@ZW& zs!iq;?RI6$*?zmkMsDVb0*fARCCSFR{r)ckzwWxLCmgXh?Ekk#>GH2rSgxcl%M4n6 zne*VPt6IVqGJFet=hhk@Q)Za|zHImO@a<8$lNdd&zn+@4Rf_pg+qSs%mz5P3&t>Mb z3*V$&&~P@*m`!`C*Or?(2};KcFDWYo_v*VIa6Fc_`J{e(q22s;hs!UgoUfec-s+^d zW&gyuxAoG)X2k1=@qFRo6S%$2lJ)i1UAt_~{g~OXrr=HTlbuzZ3{`vMc7%Ov zd7RF)BP7hC&hXP8Nfx{IH_Znh9Q{|LcI=OJ0VV4@vAd%V5XGwtp2`-M_#ikWr-;Y{rU Y_N{Tr;zbb!3qWz}>FVdQ&MBb@0P*ha)Bpeg literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_link@2x.png b/Telegram/Resources/icons/info/edit/links_link@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6c1174a8a9ad3d007d61891558761cef922f2006 GIT binary patch literal 1193 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz#{DF;uuoF z_%_1U`-_9XJ?r?uKt~;szzrJ$1&+Aove-viZryS0+_776TQ*#_S6ETd+S~d_{2voH zx8N0L?v0`>JDs>41CQ786~D=wboyvck^URLmYhj9&)S_ockYZ|m!aDqqp(ZIl>W2k zac&SxuwX9ZmdIl`6VK(zLk=ZS#tKQZ&gi=O5nVC^E9TPzIft<$DQKUd=>j{ZP~op`TqU;N_pqM-Mc5Z zb<36`t}dTgN=i#Fo;};E91;OWy;_q`SM)zyo?efzel)@jlt9Y$kg<1e2+Ih{X$-o(^Ylw--VWo_rqpH~$U z6BA?l-KqdI-@vfJYiW>Z^NAEAA;#UicZ+`KReB^J?h&)&F!$HMbj&TW31`msyB8$?mb0k^8pi zbKKtI4@g#b_X-pP}Nr}tTvb)@gLbLjnCP;uk)tCo-te24!1 ztDFAr)vHyXcUHtlMi#zY@wmO>nmfa#Xw$>Z+rwq&$^Yo>V@a{|9tzlt$W3`ZDzZ6?-p*?$y>{j5c;9wnlpo6Y1Xe*YLeOz?B!Yyy=@2o{oA*snSK3K_x(3-MtXk# z{CV;O{*L6Vtk;STHs_N!W#!phT52+CvCAF!_3IbMV~2wIvuAfR+Z^lhW0*5vgQNb{ zj>Cr!FY0=Ks{8M;1)b$Z3zpk$CPMLYr z>zv*%5vMHUI!<<-Q|=@ybN9xLh=lnI4F7+KiNLzHSS0tdb6Hd(ObK>$NI_%7T*KSGrs?p0b!WOBDT9IB@CvmGze&n*TktdV5Y) z)hu5hAA>0y15Q4CI4L$JX2ase%4M~+YyU4-bnltkFrj#5SJl<*rcg)SKUahlCM2d@ lj%t{ATCZW77*U*mj5?2lcAh@prVlD6JYD@<);T3K0RVT58Rh^0 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_link@3x.png b/Telegram/Resources/icons/info/edit/links_link@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f4d797b7f1d6c644f0242fddb9641938588e199d GIT binary patch literal 1905 zcmbVNX*Aml7XK$ggF#SF$C{Ff<@HG`wG?BGMC!$sM@b|0B4R08)mm~oX)QzX>{J^| zt!YrT9zwNRO53MOT8h@bRBS~MuM=NpKFv9E=ELv)?()0$ocrb86ekCql!TH5002^W zJB*8vy^jGE6WW=A7-J!UqFiv+0Q;@VvM|5|xZ|%72!O88hXSCaU;uI~5kg4_01#n< z01+XBj&n@#zpLI%k^lI|!Wy)f0|1Ck;4xOe-2km!cC>SKJgJr#O!|sFn;YR%&CpJ+ z_G!~6NtCt>KZc*i*nR0u=Y4L5k=kINPx{W4*@Y@R{ll686+K7RAr&N!T7Kz2c*P0`it-3Iza?ehTEf z{SEXha1Q)p;gd)K;0sB9({$o2=miv394O-rPL_omzEkUgz%(ss5B^RUhhym<&29{R1YH225UV&tr(Gs+F>kRbaYfKFE5|@^XL$JBoTei)bwZb_yYtd z@rT}%rY76q;F_SIAlRjPib2a|4-eT?NMlC_m6hGE?EkIK%434Z;~6?oI~hqyN%L=t zjhYYG=E;rqT(slDu=xSBQA}d6u_kt8ezac~2g8J-mf4hnn7S{5_+1*_` zHz{a`nYrYgqXg-UKQt6;o1FCY@bU^5<)C9DBO~Xy^u+cEPwz_KiX=keKs-_;3!ikH$ zcx5iXva+)6y9wg3tx_6fE;cW&K_9~>CUyr-zz;XyVQnnp_{@m3T!BFFu@{m@J_XiC zqithj_XozO#Zb)%1>u(GTxD&ghd^k$Z44ahj zF)=angzXA+>CnRx>_RnwdeTnV6Ij=22c;s@pyuZ0QP)?`lVnP21KFOQp6oT+!F-dn z6v$*l718kFyAsCU-hOB(W_&ianS;lXNJRp{ew6>t$a+9Pz{~5%_^Wv%urEKkyj;$k zV0QMXD=9^W)=$Y+(QyeLBvNGP;#ie2?~WrNP5kw?DtiJsC@ILwl1+{|2CNBlx^aG%d=GU3*H{Fi-)X9gM~k(P%UlXrLH%H5}1pfc2@apIcj79q~P~ z{&Mt;#v~hhs{R9M^R2gqBy ztOfi{8+ck=7UDcO^ASsaZtX@&(Y~*b&$Bwx>#4nYeQM4kyME$ME&mqW)ai`D?6R`! zgp-UvQcKJ4ijMx6iHV8t>rlxEiAqBc>wxo6Gxi7StCryRA0$H;;1*t5{q8pJbF5!* zaf%bqT;%IiyE~w(hsx1rs(lYNGauH=`?F35zjm|8`@I9F6fIwo=e~Ct&Dns`uL>^o zBKqB!KS@%H#t>zBIO>k&)^UxgKguKdFrW{o_6HAYv>ZLWf{3OsZ)b(s5%}zoGHI zrbs4X{=6_<@sC-AF2&01p>YKb%&okC56ETztom6gA}f5OZp6?x;Yem>s@Kt8R0eB2%{N{Ya4nwT49J^YiTz?9P99E9tYjA@Iy5rmJe1JJaQ*octHAzwt#Z0BovEey!2&yZaK}ft=^*>gTe~DWM4f D{v%i~ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/roundbtn_plus@2x.png b/Telegram/Resources/icons/info/edit/roundbtn_plus@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f2772af06d54919c3a6f59ad68860629bc761ec7 GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blLz^X6EVQ7*fIb zcE&}%W(6MC#QNv|>zn?yXN#Nmwde5%_`Fk8Je-(QUEOszOEY!5rghw=iSmug3@jbm z4xAm#0SXLEM?@E}9ASJQu!Kc}v5*yr87jB~m^OPQL{8hZ?sfXFs%IbUT`wo_X*cg*q=@bgo+M3kpe3S3j3^P6-CxncH525KNM8)7|V*d%xeD9|(Gd4#~dupU7?(%^B zl<7Aw2gVP90S!zEOa*&n7ck6VkjZD)U{GT?ew&f2f%Cw}GRmYHzH{bmzx}kpLTCBq zm+x1tUv2h!RlWcGS5gP``&Vae-rM)O?s-%EY&rG+6aUBOx-rh4-TKkutkjpsaptqD zEQ@vX4{gwACFGuD*#i5ZbvMshUpG#B|EISquXpoxgHvwDc4RKGS)kA@lbU?_f>rJA zyYFr$=B=H7{(0*D``gp*9on#c!ELsEe+7jLY~;cZ&%4K6#4lm~(0lLqW&YcD<*`hS ztzNOW=W;{L4ciBkAKSAWjsGsZvE%3?Z6=_=)n)r$rgJsqYCA9*Fk0N>3t;eJ=-bXL z!XVu6_$>qL0hR|pG_4mr``)j8Rc`*~WAB%XOgMIYWHwX3E=VCEXl1%hQ=K@N*9MFcx2zY$s5 z;Iee%vIe&snqG{ZUdg_Uy&Hn3FbPdPHj7Crak&PoidL^FtH~k1KrW9Escf!{mbr^W cC@^qEyz4EmhSsecond.push_back(std::move(done)); @@ -250,6 +251,58 @@ void InviteLinks::requestLinks(not_null peer) { _firstSliceRequests.emplace(peer, requestId); } +JoinedByLinkSlice InviteLinks::lookupJoinedFirstSlice(LinkKey key) const { + const auto i = _firstJoined.find(key); + return (i != end(_firstJoined)) ? i->second : JoinedByLinkSlice(); +} + +rpl::producer InviteLinks::joinedFirstSliceValue( + not_null peer, + const QString &link, + int fullCount) { + const auto key = LinkKey{ peer, link }; + auto current = lookupJoinedFirstSlice(key); + if (current.count == fullCount + && (!fullCount || !current.users.empty())) { + return rpl::single(current); + } + current.count = fullCount; + const auto remove = int(current.users.size()) - current.count; + if (remove > 0) { + current.users.erase(end(current.users) - remove, end(current.users)); + } + requestJoinedFirstSlice(key); + using namespace rpl::mappers; + return rpl::single( + current + ) | rpl::then(_joinedFirstSliceLoaded.events( + ) | rpl::filter( + _1 == key + ) | rpl::map([=] { + return lookupJoinedFirstSlice(key); + })); +} + +void InviteLinks::requestJoinedFirstSlice(LinkKey key) { + if (_firstJoinedRequests.contains(key)) { + return; + } + const auto requestId = _api->request(MTPmessages_GetChatInviteImporters( + key.peer->input, + MTP_string(key.link), + MTP_int(0), // offset_date + MTP_inputUserEmpty(), // offset_user + MTP_int(kJoinedFirstPage) + )).done([=](const MTPmessages_ChatInviteImporters &result) { + _firstJoinedRequests.remove(key); + _firstJoined[key] = parseSlice(key.peer, result); + _joinedFirstSliceLoaded.fire_copy(key); + }).fail([=](const RPCError &error) { + _firstJoinedRequests.remove(key); + }).send(); + _firstJoinedRequests.emplace(key, requestId); +} + void InviteLinks::setPermanent( not_null peer, const MTPExportedChatInvite &invite) { @@ -320,6 +373,27 @@ auto InviteLinks::parseSlice( return result; } +JoinedByLinkSlice InviteLinks::parseSlice( + not_null peer, + const MTPmessages_ChatInviteImporters &slice) const { + auto result = JoinedByLinkSlice(); + slice.match([&](const MTPDmessages_chatInviteImporters &data) { + auto &owner = peer->session().data(); + owner.processUsers(data.vusers()); + result.count = data.vcount().v; + result.users.reserve(data.vimporters().v.size()); + for (const auto importer : data.vimporters().v) { + importer.match([&](const MTPDchatInviteImporter &data) { + result.users.push_back({ + .user = owner.user(data.vuser_id().v), + .date = data.vdate().v, + }); + }); + } + }); + return result; +} + auto InviteLinks::parse( not_null peer, const MTPExportedChatInvite &invite) const -> Link { diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 6c53c9fbb..8d78a8f18 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -28,6 +28,16 @@ struct PeerInviteLinks { int count = 0; }; +struct JoinedByLinkUser { + not_null user; + TimeId date = 0; +}; + +struct JoinedByLinkSlice { + std::vector users; + int count = 0; +}; + class InviteLinks final { public: explicit InviteLinks(not_null api); @@ -59,21 +69,29 @@ public: void requestLinks(not_null peer); [[nodiscard]] const Links &links(not_null peer) const; + [[nodiscard]] rpl::producer joinedFirstSliceValue( + not_null peer, + const QString &link, + int fullCount); + void requestMoreLinks( not_null peer, const QString &last, Fn done); private: - struct EditKey { + struct LinkKey { not_null peer; QString link; - friend inline bool operator<(const EditKey &a, const EditKey &b) { + friend inline bool operator<(const LinkKey &a, const LinkKey &b) { return (a.peer == b.peer) ? (a.link < b.link) : (a.peer < b.peer); } + friend inline bool operator==(const LinkKey &a, const LinkKey &b) { + return (a.peer == b.peer) && (a.link == b.link); + } }; [[nodiscard]] Links parseSlice( @@ -82,6 +100,9 @@ private: [[nodiscard]] Link parse( not_null peer, const MTPExportedChatInvite &invite) const; + [[nodiscard]] JoinedByLinkSlice parseSlice( + not_null peer, + const MTPmessages_ChatInviteImporters &slice) const; [[nodiscard]] Link *lookupPermanent(not_null peer); [[nodiscard]] Link *lookupPermanent(Links &links); [[nodiscard]] const Link *lookupPermanent(const Links &links) const; @@ -108,15 +129,23 @@ private: TimeId expireDate = 0, int usageLimit = 0); + void requestJoinedFirstSlice(LinkKey key); + [[nodiscard]] JoinedByLinkSlice lookupJoinedFirstSlice( + LinkKey key) const; + const not_null _api; base::flat_map, Links> _firstSlices; base::flat_map, mtpRequestId> _firstSliceRequests; + base::flat_map _firstJoined; + base::flat_map _firstJoinedRequests; + rpl::event_stream _joinedFirstSliceLoaded; + base::flat_map< not_null, std::vector>> _createCallbacks; - base::flat_map>> _editCallbacks; + base::flat_map>> _editCallbacks; }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index a7e2fad86..482a9b91b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -8,15 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_invite_links.h" #include "data/data_changes.h" -#include "data/data_peer.h" +#include "data/data_user.h" #include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" +#include "ui/abstract_button.h" #include "ui/widgets/popup_menu.h" #include "ui/controls/invite_link_label.h" #include "ui/controls/invite_link_buttons.h" #include "ui/toast/toast.h" +#include "history/view/history_view_group_call_tracker.h" // GenerateUs... #include "lang/lang_keys.h" #include "apiwrap.h" #include "styles/style_info.h" @@ -40,7 +42,8 @@ void AddPermanentLinkBlock( return link ? std::make_tuple(link->link, link->usage) : std::make_tuple(QString(), 0); - }) | rpl::start_spawning(container->lifetime()); + }) | rpl::distinct_until_changed( + ) | rpl::start_spawning(container->lifetime()); const auto copyLink = [=] { if (const auto link = computePermanentLink()) { @@ -95,4 +98,84 @@ void AddPermanentLinkBlock( container, copyLink, shareLink); + + struct JoinedState { + QImage cachedUserpics; + std::vector list; + int count = 0; + bool allUserpicsLoaded = false; + rpl::variable content; + rpl::lifetime lifetime; + }; + const auto state = container->lifetime().make_state(); + const auto push = [=] { + HistoryView::GenerateUserpicsInRow( + state->cachedUserpics, + state->list, + st::inviteLinkUserpics, + 0); + state->allUserpicsLoaded = ranges::all_of( + state->list, + [](const HistoryView::UserpicInRow &element) { + return !element.peer->hasUserpic() || element.view->image(); + }); + state->content = Ui::JoinedCountContent{ + .count = state->count, + .userpics = state->cachedUserpics + }; + }; + std::move( + value + ) | rpl::map([=](QString link, int usage) { + return peer->session().api().inviteLinks().joinedFirstSliceValue( + peer, + link, + usage); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](const Api::JoinedByLinkSlice &slice) { + auto list = std::vector(); + list.reserve(slice.users.size()); + for (const auto &item : slice.users) { + const auto i = ranges::find( + state->list, + item.user, + &HistoryView::UserpicInRow::peer); + if (i != end(state->list)) { + list.push_back(std::move(*i)); + } else { + list.push_back({ item.user }); + } + } + state->count = slice.count; + state->list = std::move(list); + push(); + }, state->lifetime); + + peer->session().downloaderTaskFinished( + ) | rpl::filter([=] { + return !state->allUserpicsLoaded; + }) | rpl::start_with_next([=] { + auto pushing = false; + state->allUserpicsLoaded = true; + for (const auto &element : state->list) { + if (!element.peer->hasUserpic()) { + continue; + } else if (element.peer->userpicUniqueKey(element.view) + != element.uniqueKey) { + pushing = true; + } else if (!element.view->image()) { + state->allUserpicsLoaded = false; + } + } + if (pushing) { + push(); + } + }, state->lifetime); + + Ui::AddJoinedCountButton( + container, + state->content.value(), + st::inviteLinkJoinedRowPadding + )->setClickedCallback([=] { + }); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 534caccde..fd9997ef6 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -9,6 +9,7 @@ using "ui/basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; +using "ui/chat/chat.style"; // GroupCallUserpics. InfoToggle { color: color; @@ -865,3 +866,12 @@ inviteLinkShare: RoundButton(inviteLinkCopy) { icon: icon {{ "info/edit/links_share", activeButtonFg }}; iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }}; } +inviteLinkUserpics: GroupCallUserpics { + size: 28px; + shift: 6px; + stroke: 2px; + align: align(left); +} +inviteLinkUserpicsSkip: 8px; +inviteLinkJoinedFont: font(14px); +inviteLinkJoinedRowPadding: margins(0px, 18px, 0px, 0px); diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp index 233ee0f51..8b8fc3cd9 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -11,9 +11,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" #include "lang/lang_keys.h" +#include "styles/style_chat.h" #include "styles/style_info.h" namespace Ui { +namespace { + +class JoinedCountButton final : public AbstractButton { +public: + using AbstractButton::AbstractButton; + + void onStateChanged(State was, StateChangeSource source) override { + update(); + } +}; + +} // namespace void AddCopyShareLinkButtons( not_null container, @@ -47,4 +60,78 @@ void AddCopyShareLinkButtons( }, wrap->lifetime()); } +not_null AddJoinedCountButton( + not_null container, + rpl::producer content, + style::margins padding) { + struct State { + JoinedCountContent content; + QString phrase; + int addedWidth = 0; + }; + const auto wrap = container->add( + object_ptr( + container, + st::inviteLinkUserpics.size), + padding); + const auto result = CreateChild(wrap); + const auto state = result->lifetime().make_state(); + std::move( + content + ) | rpl::start_with_next([=](JoinedCountContent &&content) { + state->content = std::move(content); + result->setAttribute( + Qt::WA_TransparentForMouseEvents, + !state->content.count); + const auto &st = st::inviteLinkUserpics; + const auto imageWidth = !state->content.userpics.isNull() + ? state->content.userpics.width() / style::DevicePixelRatio() + : !state->content.count + ? 0 + : ((std::min(state->content.count, 3) - 1) * (st.size - st.shift) + + st.size); + state->addedWidth = imageWidth + ? (imageWidth + st::inviteLinkUserpicsSkip) + : 0; + state->phrase = state->content.count + ? tr::lng_group_invite_joined( + tr::now, + lt_count_decimal, + state->content.count) + : tr::lng_group_invite_no_joined(tr::now); + const auto fullWidth = st::inviteLinkJoinedFont->width(state->phrase) + + state->addedWidth; + result->resize(fullWidth, st.size); + result->move((wrap->width() - fullWidth) / 2, 0); + result->update(); + }, result->lifetime()); + + result->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(result); + if (!state->content.userpics.isNull()) { + p.drawImage(0, 0, state->content.userpics); + } + const auto &font = st::inviteLinkJoinedFont; + p.setPen(state->content.count + ? st::defaultLinkButton.color + : st::windowSubTextFg); + p.setFont((result->isOver() || result->isDown()) + ? font->underline() + : font); + const auto top = (result->height() - font->height) / 2; + p.drawText( + state->addedWidth, + top + font->ascent, + state->phrase); + }, result->lifetime()); + + wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + result->move((width - result->width()) / 2, 0); + }, wrap->lifetime()); + + return result; +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h index aa637dbb9..c21195b76 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { +class AbstractButton; class VerticalLayout; void AddCopyShareLinkButtons( @@ -16,4 +17,14 @@ void AddCopyShareLinkButtons( Fn copyLink, Fn shareLink); +struct JoinedCountContent { + int count = 0; + QImage userpics; +}; + +not_null AddJoinedCountButton( + not_null container, + rpl::producer content, + style::margins padding); + } // namespace Ui From c7b1a37722c3500a006460bb2b54e319fa39efb6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 20:13:45 +0400 Subject: [PATCH 142/396] Implement revoke of permanent link. --- Telegram/SourceFiles/api/api_invite_links.cpp | 23 ++++++++---- .../boxes/peers/edit_peer_invite_links.cpp | 35 +++++++++++++------ Telegram/SourceFiles/info/info.style | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 83d4c4838..5651afa3d 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -72,7 +72,10 @@ void InviteLinks::performCreate( using Flag = MTPmessages_ExportChatInvite::Flag; _api->request(MTPmessages_ExportChatInvite( - MTP_flags((expireDate ? Flag::f_expire_date : Flag(0)) + MTP_flags((revokeLegacyPermanent + ? Flag::f_legacy_revoke_permanent + : Flag(0)) + | (expireDate ? Flag::f_expire_date : Flag(0)) | (usageLimit ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_int(expireDate), @@ -118,14 +121,19 @@ auto InviteLinks::prepend( i = _firstSlices.emplace(peer).first; } auto &links = i->second; + const auto permanent = lookupPermanent(links); if (link.permanent) { - if (const auto permanent = lookupPermanent(links)) { + if (permanent) { permanent->revoked = true; } editPermanentLink(peer, link.link); } ++links.count; - links.links.insert(begin(links.links), link); + if (permanent && !link.permanent) { + links.links.insert(begin(links.links) + 1, link); + } else { + links.links.insert(begin(links.links), link); + } notify(peer); return link; } @@ -153,10 +161,6 @@ void InviteLinks::performEdit( } return; } - auto &callbacks = _editCallbacks[key]; - if (done) { - callbacks.push_back(std::move(done)); - } if (const auto permanent = revoke ? lookupPermanent(peer) : nullptr) { if (permanent->link == link) { @@ -167,6 +171,11 @@ void InviteLinks::performEdit( } } + auto &callbacks = _editCallbacks[key]; + if (done) { + callbacks.push_back(std::move(done)); + } + using Flag = MTPmessages_EditExportedChatInvite::Flag; const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( MTP_flags((revoke ? Flag::f_revoked : Flag(0)) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 482a9b91b..bb8b31883 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... #include "lang/lang_keys.h" +#include "boxes/confirm_box.h" #include "apiwrap.h" #include "styles/style_info.h" @@ -45,24 +46,38 @@ void AddPermanentLinkBlock( }) | rpl::distinct_until_changed( ) | rpl::start_spawning(container->lifetime()); - const auto copyLink = [=] { + const auto weak = Ui::MakeWeak(container); + const auto copyLink = crl::guard(weak, [=] { if (const auto link = computePermanentLink()) { QGuiApplication::clipboard()->setText(link->link); Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); } - }; - const auto shareLink = [=] { + }); + const auto shareLink = crl::guard(weak, [=] { if (const auto link = computePermanentLink()) { QGuiApplication::clipboard()->setText(link->link); Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); } - }; - const auto revokeLink = [=] { - if (const auto link = computePermanentLink()) { - QGuiApplication::clipboard()->setText(link->link); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); - } - }; + }); + const auto revokeLink = crl::guard(weak, [=] { + const auto box = std::make_shared>(); + const auto done = crl::guard(weak, [=] { + if (const auto link = computePermanentLink()) { + const auto close = [=](auto&&) { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().revoke( + peer, + link->link, + close); + } + }); + *box = Ui::show( + Box(tr::lng_group_invite_about_new(tr::now), done), + Ui::LayerOption::KeepOther); + }); auto link = rpl::duplicate( value diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index fd9997ef6..465ce7260 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -874,4 +874,4 @@ inviteLinkUserpics: GroupCallUserpics { } inviteLinkUserpicsSkip: 8px; inviteLinkJoinedFont: font(14px); -inviteLinkJoinedRowPadding: margins(0px, 18px, 0px, 0px); +inviteLinkJoinedRowPadding: margins(0px, 18px, 0px, 8px); From 7132ab5bf4a20ee3288de0ed9497a14adf17906b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 20:19:27 +0400 Subject: [PATCH 143/396] Fix long content in group type box. --- Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 117419c56..734108fd5 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -616,11 +616,11 @@ void EditPeerTypeBox::setInnerFocus() { void EditPeerTypeBox::prepare() { _peer->updateFull(); - const auto content = Ui::CreateChild(this); + auto content = object_ptr(this); const auto controller = Ui::CreateChild( this, - content, + content.data(), _peer, _useLocationPhrases, _privacySavedValue, @@ -657,5 +657,6 @@ void EditPeerTypeBox::prepare() { } addButton(tr::lng_cancel(), [=] { closeBox(); }); - setDimensionsToContent(st::boxWideWidth, content); + setDimensionsToContent(st::boxWideWidth, content.data()); + setInnerWidget(std::move(content)); } From 542abb26b9620c594d3b4cc9f1c82d7e7ce0c8f0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 20:55:22 +0400 Subject: [PATCH 144/396] Allow sharing link to chats. --- .../boxes/peers/edit_peer_info_box.cpp | 2 +- .../boxes/peers/edit_peer_invite_links.cpp | 61 ++++++++++++++++++- Telegram/SourceFiles/data/data_changes.h | 3 +- Telegram/SourceFiles/mainwidget.cpp | 38 +++++++----- 4 files changed, 85 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index a3ae08f6e..7c3c9f0c1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1003,7 +1003,7 @@ void Controller::fillManageSection() { }); }) | rpl::flatten_latest( ) | ToPositiveNumberString(), - [=] { ShowEditInviteLinks(_navigation, _peer); }, + [=] { }, st::infoIconInviteLinks); } if (canViewAdmins) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index bb8b31883..d963db7bc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_user.h" +#include "data/data_drafts.h" #include "main/main_session.h" +#include "data/data_session.h" #include "api/api_invite_links.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" @@ -19,13 +21,69 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/controls/invite_link_buttons.h" #include "ui/toast/toast.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... +#include "history/history.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" +#include "boxes/peer_list_box.h" +#include "boxes/peer_list_controllers.h" #include "apiwrap.h" +#include "api/api_common.h" #include "styles/style_info.h" #include +namespace { + +void ShareLinkBox(not_null peer, const QString &link) { + const auto weak = std::make_shared>(); + auto callback = [ + weak, + link + ](not_null peer) mutable { + const auto history = peer->owner().history(peer); + if (peer->isSelf()) { + const auto api = &peer->session().api(); + auto message = Api::MessageToSend(history); + message.action.clearDraft = false; + message.action.generateLocal = false; + message.textWithTags = { link }; + api->sendMessage(std::move(message)); + Ui::Toast::Show(tr::lng_share_done(tr::now)); + } else { + auto textWithTags = TextWithTags{ + link + '\n', + TextWithTags::Tags() + }; + auto cursor = MessageCursor{ + link.size() + 1, + link.size() + 1, + QFIXED_MAX + }; + history->setLocalDraft( + std::make_unique(textWithTags, 0, cursor, false)); + history->clearLocalEditDraft(); + history->session().changes().historyUpdated( + history, + Data::HistoryUpdate::Flag::LocalDraftSet); + } + if (const auto strong = *weak) { + strong->closeBox(); + } + }; + auto initBox = [](not_null box) { + box->addButton(tr::lng_cancel(), [box] { + box->closeBox(); + }); + }; + *weak = Ui::show(Box( + std::make_unique( + &peer->session(), + std::move(callback)), + std::move(initBox)), Ui::LayerOption::KeepOther); +} + +} // namespace + void AddPermanentLinkBlock( not_null container, not_null peer) { @@ -55,8 +113,7 @@ void AddPermanentLinkBlock( }); const auto shareLink = crl::guard(weak, [=] { if (const auto link = computePermanentLink()) { - QGuiApplication::clipboard()->setText(link->link); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + ShareLinkBox(peer, link->link); } }); const auto revokeLink = crl::guard(weak, [=] { diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index cf00ebec3..0a162906f 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -116,8 +116,9 @@ struct HistoryUpdate { OutboxRead = (1 << 10), BotKeyboard = (1 << 11), CloudDraft = (1 << 12), + LocalDraftSet = (1 << 13), - LastUsedBit = (1 << 12), + LastUsedBit = (1 << 13), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e7c258519..bbf183590 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -301,14 +301,27 @@ MainWidget::MainWidget( session().changes().historyUpdates( Data::HistoryUpdate::Flag::MessageSent + | Data::HistoryUpdate::Flag::LocalDraftSet ) | rpl::start_with_next([=](const Data::HistoryUpdate &update) { const auto history = update.history; - history->forgetScrollState(); - if (const auto from = history->peer->migrateFrom()) { - if (const auto migrated = history->owner().historyLoaded(from)) { - migrated->forgetScrollState(); + if (update.flags & Data::HistoryUpdate::Flag::MessageSent) { + history->forgetScrollState(); + if (const auto from = history->peer->migrateFrom()) { + auto &owner = history->owner(); + if (const auto migrated = owner.historyLoaded(from)) { + migrated->forgetScrollState(); + } } } + if (update.flags & Data::HistoryUpdate::Flag::LocalDraftSet) { + const auto opened = (_history->peer() == history->peer.get()); + if (opened) { + _history->applyDraft(); + } else { + Ui::showPeerHistory(history, ShowAtUnreadMsgId); + } + Ui::hideLayer(); + } }, lifetime()); // MSVC BUG + REGRESSION rpl::mappers::tuple :( @@ -538,11 +551,9 @@ bool MainWidget::shareUrl( history->setLocalDraft( std::make_unique(textWithTags, 0, cursor, false)); history->clearLocalEditDraft(); - if (_history->peer() == peer) { - _history->applyDraft(); - } else { - Ui::showPeerHistory(peer, ShowAtUnreadMsgId); - } + history->session().changes().historyUpdated( + history, + Data::HistoryUpdate::Flag::LocalDraftSet); return true; } @@ -567,12 +578,9 @@ bool MainWidget::inlineSwitchChosen(PeerId peerId, const QString &botAndQuery) { MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX }; h->setLocalDraft(std::make_unique(textWithTags, 0, cursor, false)); h->clearLocalEditDraft(); - const auto opened = _history->peer() && (_history->peer() == peer); - if (opened) { - _history->applyDraft(); - } else { - Ui::showPeerHistory(peer, ShowAtUnreadMsgId); - } + h->session().changes().historyUpdated( + h, + Data::HistoryUpdate::Flag::LocalDraftSet); return true; } From 5e10d97abeec40921f277d0772c2970833598bb8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 21:07:25 +0400 Subject: [PATCH 145/396] Hide 'No one joined yet.' message. --- .../ui/controls/invite_link_buttons.cpp | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp index 8b8fc3cd9..11a904057 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" +#include "ui/wrap/slide_wrap.h" #include "lang/lang_keys.h" #include "styles/style_chat.h" #include "styles/style_info.h" @@ -70,16 +71,23 @@ not_null AddJoinedCountButton( int addedWidth = 0; }; const auto wrap = container->add( - object_ptr( + object_ptr>( container, - st::inviteLinkUserpics.size), - padding); - const auto result = CreateChild(wrap); + object_ptr( + container, + st::inviteLinkUserpics.size), + QMargins(padding.left(), padding.top(), padding.right(), 0)), + QMargins(0, 0, 0, padding.bottom())); + const auto result = CreateChild(wrap->entity()); const auto state = result->lifetime().make_state(); std::move( content ) | rpl::start_with_next([=](JoinedCountContent &&content) { state->content = std::move(content); + wrap->toggle(state->content.count > 0, anim::type::instant); + if (state->content.count <= 0) { + return; + } result->setAttribute( Qt::WA_TransparentForMouseEvents, !state->content.count); @@ -93,12 +101,10 @@ not_null AddJoinedCountButton( state->addedWidth = imageWidth ? (imageWidth + st::inviteLinkUserpicsSkip) : 0; - state->phrase = state->content.count - ? tr::lng_group_invite_joined( - tr::now, - lt_count_decimal, - state->content.count) - : tr::lng_group_invite_no_joined(tr::now); + state->phrase = tr::lng_group_invite_joined( + tr::now, + lt_count_decimal, + state->content.count); const auto fullWidth = st::inviteLinkJoinedFont->width(state->phrase) + state->addedWidth; result->resize(fullWidth, st.size); @@ -113,9 +119,7 @@ not_null AddJoinedCountButton( p.drawImage(0, 0, state->content.userpics); } const auto &font = st::inviteLinkJoinedFont; - p.setPen(state->content.count - ? st::defaultLinkButton.color - : st::windowSubTextFg); + p.setPen(st::defaultLinkButton.color); p.setFont((result->isOver() || result->isDown()) ? font->underline() : font); From 3862b3b90e795012ed7c3c99bb56a7d47f723870 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jan 2021 21:41:37 +0400 Subject: [PATCH 146/396] Make sharing invite links using ShareBox. --- .../boxes/peers/edit_peer_invite_links.cpp | 118 ++++++++++++------ 1 file changed, 77 insertions(+), 41 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index d963db7bc..eb5b5569b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -10,8 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_user.h" #include "data/data_drafts.h" -#include "main/main_session.h" #include "data/data_session.h" +#include "data/data_histories.h" +#include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" @@ -19,14 +20,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/controls/invite_link_label.h" #include "ui/controls/invite_link_buttons.h" +#include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... +#include "history/history_message.h" // GetErrorTextForSending. #include "history/history.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" #include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" #include "apiwrap.h" +#include "mainwindow.h" +#include "boxes/share_box.h" +#include "window/window_session_controller.h" #include "api/api_common.h" #include "styles/style_info.h" @@ -35,51 +41,81 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { void ShareLinkBox(not_null peer, const QString &link) { - const auto weak = std::make_shared>(); - auto callback = [ - weak, - link - ](not_null peer) mutable { - const auto history = peer->owner().history(peer); - if (peer->isSelf()) { - const auto api = &peer->session().api(); - auto message = Api::MessageToSend(history); + const auto session = &peer->session(); + const auto sending = std::make_shared(); + const auto box = std::make_shared>(); + + auto copyCallback = [=] { + QGuiApplication::clipboard()->setText(link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + }; + auto submitCallback = [=]( + std::vector> &&result, + TextWithTags &&comment, + Api::SendOptions options) { + if (*sending || result.empty()) { + return; + } + + const auto error = [&] { + for (const auto peer : result) { + const auto error = GetErrorTextForSending( + peer, + {}, + comment); + if (!error.isEmpty()) { + return std::make_pair(error, peer); + } + } + return std::make_pair(QString(), result.front()); + }(); + if (!error.first.isEmpty()) { + auto text = TextWithEntities(); + if (result.size() > 1) { + text.append( + Ui::Text::Bold(error.second->name) + ).append("\n\n"); + } + text.append(error.first); + Ui::show( + Box(text), + Ui::LayerOption::KeepOther); + return; + } + + *sending = true; + if (!comment.text.isEmpty()) { + comment.text = link + "\n" + comment.text; + const auto add = link.size() + 1; + for (auto &tag : comment.tags) { + tag.offset += add; + } + } + const auto owner = &peer->owner(); + auto &api = peer->session().api(); + auto &histories = owner->histories(); + const auto requestType = Data::Histories::RequestType::Send; + for (const auto peer : result) { + const auto history = owner->history(peer); + auto message = ApiWrap::MessageToSend(history); + message.textWithTags = comment; + message.action.options = options; message.action.clearDraft = false; - message.action.generateLocal = false; - message.textWithTags = { link }; - api->sendMessage(std::move(message)); - Ui::Toast::Show(tr::lng_share_done(tr::now)); - } else { - auto textWithTags = TextWithTags{ - link + '\n', - TextWithTags::Tags() - }; - auto cursor = MessageCursor{ - link.size() + 1, - link.size() + 1, - QFIXED_MAX - }; - history->setLocalDraft( - std::make_unique(textWithTags, 0, cursor, false)); - history->clearLocalEditDraft(); - history->session().changes().historyUpdated( - history, - Data::HistoryUpdate::Flag::LocalDraftSet); + api.sendMessage(std::move(message)); } - if (const auto strong = *weak) { - strong->closeBox(); + Ui::Toast::Show(tr::lng_share_done(tr::now)); + if (*box) { + (*box)->closeBox(); } }; - auto initBox = [](not_null box) { - box->addButton(tr::lng_cancel(), [box] { - box->closeBox(); - }); + auto filterCallback = [](PeerData *peer) { + return peer->canWrite(); }; - *weak = Ui::show(Box( - std::make_unique( - &peer->session(), - std::move(callback)), - std::move(initBox)), Ui::LayerOption::KeepOther); + *box = Ui::show(Box( + App::wnd()->sessionController(), + std::move(copyCallback), + std::move(submitCallback), + std::move(filterCallback))); } } // namespace From 7fa342b4875e7d538d235b837e0a2af0a0a074b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 16 Jan 2021 14:36:48 +0400 Subject: [PATCH 147/396] Update API scheme. --- Telegram/Resources/tl/api.tl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 1d2d02630..2f3bb923e 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -407,7 +407,7 @@ encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat; encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat; encryptedChatRequested#62718a82 flags:# folder_id:flags.0?int id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat; encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat; -encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat; +encryptedChatDiscarded#1e1c7c45 flags:# history_deleted:flags.0?true id:int = EncryptedChat; inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat; @@ -535,7 +535,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#a9a847ea flags:# revoked:flags.0?true expired:flags.4?true permanent:flags.5?true link:string admin_id:int date:int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#6e24fc9d flags:# revoked:flags.0?true permanent:flags.5?true expired:flags.6?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; @@ -1361,7 +1361,7 @@ messages.createChat#9cb126e users:Vector title:string = Updates; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; -messages.discardEncryption#edd923c5 chat_id:int = Bool; +messages.discardEncryption#f393aea0 flags:# delete_history:flags.0?true chat_id:int = Bool; messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool; messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool; messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; @@ -1463,9 +1463,13 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6d9cae03 flags:# peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvites#6d9cae03 flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; +messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.deleteChat#83247d11 chat_id:int = Bool; +messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; From 40e90af76df92ad761232c66b0596b43ce16a226 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 16 Jan 2021 16:33:09 +0400 Subject: [PATCH 148/396] Detect tablet mode on Windows 10. --- Telegram/CMakeLists.txt | 1 - .../platform/win/main_window_win.cpp | 68 +++++++++++++++-- .../platform/win/main_window_win.h | 12 ++- .../win/notifications_manager_win.cpp | 74 +++---------------- .../SourceFiles/platform/win/windows_dlls.cpp | 10 --- .../SourceFiles/platform/win/windows_dlls.h | 19 ----- .../platform/win/windows_event_filter.cpp | 5 +- .../platform/win/wrapper_wrl_implements_h.h | 14 ---- Telegram/SourceFiles/window/main_window.cpp | 4 + Telegram/SourceFiles/window/main_window.h | 4 + 10 files changed, 89 insertions(+), 122 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/win/wrapper_wrl_implements_h.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 7810cb205..fccf3ddcd 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -902,7 +902,6 @@ PRIVATE platform/win/windows_dlls.h platform/win/windows_event_filter.cpp platform/win/windows_event_filter.h - platform/win/wrapper_wrl_implements_h.h platform/platform_audio.h platform/platform_file_utilities.h platform/platform_launcher.h diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index c93530ba4..24e3a0f2d 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_manager.h" #include "mainwindow.h" #include "base/crc32hash.h" +#include "base/platform/win/base_windows_wrl.h" #include "core/application.h" #include "lang/lang_keys.h" #include "storage/localstorage.h" @@ -27,24 +28,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include #include #include #include #include -#include -#include +#include +#include #include #include HICON qt_pixmapToWinHICON(const QPixmap &); -using namespace Microsoft::WRL; - Q_DECLARE_METATYPE(QMargins); +namespace ViewManagement = ABI::Windows::UI::ViewManagement; + namespace Platform { namespace { @@ -55,6 +57,8 @@ namespace { // icon click (both left or right button) was made from the active app. constexpr auto kKeepActiveForTrayIcon = crl::time(500); +using namespace Microsoft::WRL; + HICON createHIconFromQIcon(const QIcon &icon, int xSize, int ySize) { if (!icon.isNull()) { const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); @@ -99,21 +103,24 @@ HWND createTaskbarHider() { } ComPtr taskbarList; - bool handleSessionNotification = false; +uint32 kTaskbarCreatedMsgId = 0; } // namespace -UINT MainWindow::_taskbarCreatedMsgId = 0; +struct MainWindow::Private { + ComPtr viewSettings; +}; MainWindow::MainWindow(not_null controller) : Window::MainWindow(controller) +, _private(std::make_unique()) , ps_tbHider_hWnd(createTaskbarHider()) { QCoreApplication::instance()->installNativeEventFilter( EventFilter::CreateInstance(this)); - if (!_taskbarCreatedMsgId) { - _taskbarCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated"); + if (!kTaskbarCreatedMsgId) { + kTaskbarCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated"); } subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { if (_shadow && update.paletteChanged()) { @@ -168,6 +175,10 @@ void MainWindow::setupNativeWindowFrame() { }, lifetime()); } +uint32 MainWindow::TaskbarCreatedMsgId() { + return kTaskbarCreatedMsgId; +} + void MainWindow::TaskbarCreated() { HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&taskbarList)); if (!SUCCEEDED(hr)) { @@ -291,6 +302,32 @@ void MainWindow::workmodeUpdated(DBIWorkMode mode) { } } +bool MainWindow::hasTabletView() const { + if (!_private->viewSettings) { + return false; + } + auto mode = ViewManagement::UserInteractionMode(); + _private->viewSettings->get_UserInteractionMode(&mode); + return (mode == ViewManagement::UserInteractionMode_Touch); +} + +bool MainWindow::initSizeFromSystem() { + if (!hasTabletView()) { + return false; + } + const auto screen = [&] { + if (const auto result = windowHandle()->screen()) { + return result; + } + return QGuiApplication::primaryScreen(); + }(); + if (!screen) { + return false; + } + setGeometry(screen->geometry()); + return true; +} + void MainWindow::updateWindowIcon() { updateIconCounters(); } @@ -362,6 +399,20 @@ void MainWindow::initHook() { Dlls::WTSRegisterSessionNotification(ps_hWnd, NOTIFY_FOR_THIS_SESSION); } + using namespace base::Platform; + auto factory = ComPtr(); + if (SupportsWRL()) { + GetActivationFactory( + StringReferenceWrapper( + RuntimeClass_Windows_UI_ViewManagement_UIViewSettings).Get(), + &factory); + if (factory) { + factory->GetForWindow( + ps_hWnd, + IID_PPV_ARGS(&_private->viewSettings)); + } + } + psInitSysMenu(); } @@ -662,6 +713,7 @@ MainWindow::~MainWindow() { if (handleSessionNotification) { Dlls::WTSUnRegisterSessionNotification(ps_hWnd); } + _private->viewSettings.Reset(); if (taskbarList) { taskbarList.Reset(); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 61042afcd..a60f2b941 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -40,9 +40,7 @@ public: virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; - static UINT TaskbarCreatedMsgId() { - return _taskbarCreatedMsgId; - } + [[nodiscard]] static uint32 TaskbarCreatedMsgId(); static void TaskbarCreated(); // Custom shadows. @@ -59,6 +57,8 @@ public: return _deltaTop; } + [[nodiscard]] bool hasTabletView() const; + void psShowTrayMenu(); ~MainWindow(); @@ -87,9 +87,13 @@ protected: void workmodeUpdated(DBIWorkMode mode) override; + bool initSizeFromSystem() override; + QTimer psUpdatedPositionTimer; private: + struct Private; + void setupNativeWindowFrame(); void updateIconCounters(); QMargins computeCustomMargins(); @@ -97,7 +101,7 @@ private: void psDestroyIcons(); void fixMaximizedWindow(); - static UINT _taskbarCreatedMsgId; + const std::unique_ptr _private; std::optional _shadow; diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index fe0b73b72..c4ac40370 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/win/notifications_manager_win.h" #include "window/notifications_utilities.h" +#include "base/platform/win/base_windows_wrl.h" +#include "base/platform/base_platform_info.h" #include "platform/win/windows_app_user_model_id.h" #include "platform/win/windows_event_filter.h" #include "platform/win/windows_dlls.h" @@ -20,16 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include -#include -#include - #ifndef __MINGW32__ -#include "platform/win/wrapper_wrl_implements_h.h" +#include "base/platform/win/wrl/wrl_implements_h.h" #include -#include -#include - HICON qt_pixmapToWinHICON(const QPixmap &); using namespace Microsoft::WRL; @@ -44,68 +40,16 @@ namespace Notifications { #ifndef __MINGW32__ namespace { -class StringReferenceWrapper { -public: - StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { - HRESULT hr = Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - ~StringReferenceWrapper() { - Dlls::WindowsDeleteString(_hstring); - } - - template - StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() { - UINT32 length = N - 1; - HRESULT hr = Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - template - StringReferenceWrapper(_In_reads_(_) wchar_t(&stringRef)[_]) throw() { - UINT32 length; - HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - - Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - } - - HSTRING Get() const throw() { - return _hstring; - } - -private: - HSTRING _hstring; - HSTRING_HEADER _header; - -}; - -template -_Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { - return Dlls::RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); -} - -template -inline HRESULT wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() { - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); -} +using base::Platform::GetActivationFactory; +using base::Platform::StringReferenceWrapper; bool init() { - if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS8) { + if (!IsWindows8OrGreater()) { return false; } if ((Dlls::SetCurrentProcessExplicitAppUserModelID == nullptr) || (Dlls::PropVariantToString == nullptr) - || (Dlls::RoGetActivationFactory == nullptr) - || (Dlls::WindowsCreateStringReference == nullptr) - || (Dlls::WindowsDeleteString == nullptr)) { + || !base::Platform::SupportsWRL()) { return false; } @@ -395,7 +339,7 @@ Manager::Private::Private(Manager *instance, Type type) } bool Manager::Private::init() { - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager))) { + if (!SUCCEEDED(GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager))) { return false; } @@ -404,7 +348,7 @@ bool Manager::Private::init() { return false; } - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) { + if (!SUCCEEDED(GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) { return false; } return true; diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.cpp b/Telegram/SourceFiles/platform/win/windows_dlls.cpp index bac70f738..e88fad409 100644 --- a/Telegram/SourceFiles/platform/win/windows_dlls.cpp +++ b/Telegram/SourceFiles/platform/win/windows_dlls.cpp @@ -63,9 +63,6 @@ f_WTSUnRegisterSessionNotification WTSUnRegisterSessionNotification; f_SHQueryUserNotificationState SHQueryUserNotificationState; f_SHChangeNotify SHChangeNotify; f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; -f_RoGetActivationFactory RoGetActivationFactory; -f_WindowsCreateStringReference WindowsCreateStringReference; -f_WindowsDeleteString WindowsDeleteString; f_PropVariantToString PropVariantToString; f_PSStringFromPropertyKey PSStringFromPropertyKey; f_DwmIsCompositionEnabled DwmIsCompositionEnabled; @@ -112,13 +109,6 @@ void start() { LoadMethod(LibPropSys, "PropVariantToString", PropVariantToString); LoadMethod(LibPropSys, "PSStringFromPropertyKey", PSStringFromPropertyKey); - if (IsWindows8OrGreater()) { - const auto LibComBase = SafeLoadLibrary(u"combase.dll"_q); - LoadMethod(LibComBase, "RoGetActivationFactory", RoGetActivationFactory); - LoadMethod(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference); - LoadMethod(LibComBase, "WindowsDeleteString", WindowsDeleteString); - } - const auto LibDwmApi = SafeLoadLibrary(u"dwmapi.dll"_q); LoadMethod(LibDwmApi, "DwmIsCompositionEnabled", DwmIsCompositionEnabled); LoadMethod(LibDwmApi, "DwmSetWindowAttribute", DwmSetWindowAttribute); diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.h b/Telegram/SourceFiles/platform/win/windows_dlls.h index b01fbd9b4..7f34e59e4 100644 --- a/Telegram/SourceFiles/platform/win/windows_dlls.h +++ b/Telegram/SourceFiles/platform/win/windows_dlls.h @@ -126,25 +126,6 @@ using f_PSStringFromPropertyKey = HRESULT(FAR STDAPICALLTYPE*)( _In_ UINT cch); extern f_PSStringFromPropertyKey PSStringFromPropertyKey; -// COMBASE.DLL - -using f_RoGetActivationFactory = HRESULT(FAR STDAPICALLTYPE*)( - _In_ HSTRING activatableClassId, - _In_ REFIID iid, - _COM_Outptr_ void ** factory); -extern f_RoGetActivationFactory RoGetActivationFactory; - -using f_WindowsCreateStringReference = HRESULT(FAR STDAPICALLTYPE*)( - _In_reads_opt_(length + 1) PCWSTR sourceString, - UINT32 length, - _Out_ HSTRING_HEADER * hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); -extern f_WindowsCreateStringReference WindowsCreateStringReference; - -using f_WindowsDeleteString = HRESULT(FAR STDAPICALLTYPE*)( - _In_opt_ HSTRING string); -extern f_WindowsDeleteString WindowsDeleteString; - // DWMAPI.DLL using f_DwmIsCompositionEnabled = HRESULT(FAR STDAPICALLTYPE*)( diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp index bdd55b957..3e1b9e799 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp @@ -268,7 +268,10 @@ bool EventFilter::mainWindowEvent( case WM_WINDOWPOSCHANGED: { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); - if (GetWindowPlacement(hWnd, &wp) && (wp.showCmd == SW_SHOWMAXIMIZED || wp.showCmd == SW_SHOWMINIMIZED)) { + if (_window->hasTabletView() + || (GetWindowPlacement(hWnd, &wp) + && (wp.showCmd == SW_SHOWMAXIMIZED + || wp.showCmd == SW_SHOWMINIMIZED))) { _window->shadowsUpdate(Change::Hidden); } else { _window->shadowsUpdate(Change::Moved | Change::Resized, (WINDOWPOS*)lParam); diff --git a/Telegram/SourceFiles/platform/win/wrapper_wrl_implements_h.h b/Telegram/SourceFiles/platform/win/wrapper_wrl_implements_h.h deleted file mode 100644 index 4f7ccd7f7..000000000 --- a/Telegram/SourceFiles/platform/win/wrapper_wrl_implements_h.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -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 - -#pragma warning(push) -// class has virtual functions, but destructor is not virtual -#pragma warning(disable:4265) -#include -#pragma warning(pop) diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 4760636a9..9bbbdadaf 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -420,6 +420,10 @@ void MainWindow::recountGeometryConstraints() { void MainWindow::initSize() { updateMinimumSize(); + if (initSizeFromSystem()) { + return; + } + auto position = cWindowPos(); DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index c209138af..a6ab3749e 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -186,6 +186,10 @@ protected: virtual void firstShadowsUpdate() { } + virtual bool initSizeFromSystem() { + return false; + } + // This one is overriden in Windows for historical reasons. virtual int32 screenNameChecksum(const QString &name) const; From 01ecf0ca9370fb1b0a897967160357aa7b46c5cf Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 18 Jan 2021 20:13:58 +0400 Subject: [PATCH 149/396] Show invite links list with context menu. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/api/api_invite_links.cpp | 7 + Telegram/SourceFiles/api/api_invite_links.h | 6 +- .../boxes/filters/edit_filter_chats_list.cpp | 37 +- Telegram/SourceFiles/boxes/peer_list_box.h | 36 +- .../boxes/peers/edit_peer_info_box.cpp | 3 +- .../boxes/peers/edit_peer_invite_links.cpp | 510 +++++++++++++++++- .../boxes/peers/edit_peer_invite_links.h | 6 + .../boxes/peers/edit_peer_type_box.cpp | 2 +- Telegram/SourceFiles/info/info.style | 46 +- 10 files changed, 600 insertions(+), 54 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 37509fd5d..de3ccf75b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1184,6 +1184,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_about_permanent_channel" = "Anyone who has Telegram installed will be able to join your channel by following this link."; "lng_group_invite_manage" = "Manage Invite Links"; "lng_group_invite_manage_about" = "You can create additional invite links that have limited time or number of usages."; +"lng_group_invite_title" = "Invite links"; "lng_group_invite_add" = "Create a New Link"; "lng_group_invite_expires_at" = "This link expires {when}."; "lng_group_invite_created_by" = "Link created by"; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 5651afa3d..c01f54d55 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -218,6 +218,12 @@ void InviteLinks::revoke( performEdit(peer, link, std::move(done), true); } +void InviteLinks::revokePermanent( + not_null peer, + Fn done) { + performCreate(peer, std::move(done), true); +} + void InviteLinks::requestLinks(not_null peer) { if (_firstSliceRequests.contains(peer)) { return; @@ -411,6 +417,7 @@ auto InviteLinks::parse( .link = qs(data.vlink()), .admin = peer->session().data().user(data.vadmin_id().v), .date = data.vdate().v, + .startDate = data.vstart_date().value_or_empty(), .expireDate = data.vexpire_date().value_or_empty(), .usageLimit = data.vusage_limit().value_or_empty(), .usage = data.vusage().value_or_empty(), diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 8d78a8f18..096038c06 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -14,7 +14,8 @@ namespace Api { struct InviteLink { QString link; not_null admin; - TimeId date; + TimeId date = 0; + TimeId startDate = 0; TimeId expireDate = 0; int usageLimit = 0; int usage = 0; @@ -60,6 +61,9 @@ public: not_null peer, const QString &link, Fn done = nullptr); + void revokePermanent( + not_null peer, + Fn done = nullptr); void setPermanent( not_null peer, diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index fd8c9cc6d..e0db55baa 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -194,39 +194,6 @@ PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() { }; } -void TypeDelegate::peerListSetTitle(rpl::producer title) { -} - -void TypeDelegate::peerListSetAdditionalTitle(rpl::producer title) { -} - -bool TypeDelegate::peerListIsRowChecked(not_null row) { - return false; -} - -int TypeDelegate::peerListSelectedRowsCount() { - return 0; -} - -void TypeDelegate::peerListScrollToTop() { -} - -void TypeDelegate::peerListAddSelectedPeerInBunch(not_null peer) { - Unexpected("Item selection in Info::Profile::Members."); -} - -void TypeDelegate::peerListAddSelectedRowInBunch(not_null row) { - Unexpected("Item selection in Info::Profile::Members."); -} - -void TypeDelegate::peerListFinishSelectedRowsBunch() { -} - -void TypeDelegate::peerListSetDescription( - object_ptr description) { - description.destroy(); -} - TypeController::TypeController( not_null session, Flags options, @@ -412,7 +379,9 @@ object_ptr EditFilterChatsListController::prepareTypesList() { container->add(object_ptr( container, st::membersMarginTop)); - const auto delegate = container->lifetime().make_state(); + const auto delegate = container->lifetime().make_state< + PeerListContentDelegateSimple + >(); const auto controller = container->lifetime().make_state( &session(), _options, diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index eed2af665..b9ce4f203 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -302,7 +302,7 @@ public: virtual void peerListShowRowMenu( not_null row, - Fn)> destroyed) = 0; + Fn)> destroyed = nullptr) = 0; virtual int peerListSelectedRowsCount() = 0; virtual std::unique_ptr peerListSaveState() const = 0; virtual void peerListRestoreState( @@ -837,7 +837,7 @@ public: } void peerListShowRowMenu( not_null row, - Fn)> destroyed) override { + Fn)> destroyed = nullptr) override { _content->showRowMenu(row, std::move(destroyed)); } @@ -851,6 +851,38 @@ private: }; +class PeerListContentDelegateSimple : public PeerListContentDelegate { +public: + void peerListSetTitle(rpl::producer title) override { + } + void peerListSetAdditionalTitle(rpl::producer title) override { + } + bool peerListIsRowChecked(not_null row) override { + return false; + } + int peerListSelectedRowsCount() override { + return 0; + } + void peerListScrollToTop() override { + } + void peerListAddSelectedPeerInBunch( + not_null peer) override { + Unexpected("...DelegateSimple::peerListAddSelectedPeerInBunch"); + } + void peerListAddSelectedRowInBunch(not_null row) override { + Unexpected("...DelegateSimple::peerListAddSelectedRowInBunch"); + } + void peerListFinishSelectedRowsBunch() override { + Unexpected("...DelegateSimple::peerListFinishSelectedRowsBunch"); + } + void peerListSetDescription( + object_ptr description) override { + description.destroy(); + } + +}; + + class PeerListBox : public Ui::BoxContent , public PeerListContentDelegate { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 7c3c9f0c1..9ebdc480c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_type_box.h" #include "boxes/peers/edit_peer_history_visibility_box.h" #include "boxes/peers/edit_peer_permissions_box.h" +#include "boxes/peers/edit_peer_invite_links.h" #include "boxes/peers/edit_linked_chat_box.h" #include "boxes/stickers_box.h" #include "chat_helpers/emoji_suggestions_widget.h" @@ -1003,7 +1004,7 @@ void Controller::fillManageSection() { }); }) | rpl::flatten_latest( ) | ToPositiveNumberString(), - [=] { }, + [=] { Ui::show(Box(ManageInviteLinksBox, _peer)); }, st::infoIconInviteLinks); } if (canViewAdmins) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index eb5b5569b..28815f842 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -15,8 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/wrap/vertical_layout.h" +#include "ui/wrap/slide_wrap.h" #include "ui/wrap/padding_wrap.h" #include "ui/abstract_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "ui/controls/invite_link_label.h" #include "ui/controls/invite_link_buttons.h" @@ -29,17 +31,152 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" +#include "settings/settings_common.h" // AddDivider. #include "apiwrap.h" #include "mainwindow.h" #include "boxes/share_box.h" +#include "base/weak_ptr.h" +#include "base/unixtime.h" #include "window/window_session_controller.h" #include "api/api_common.h" #include "styles/style_info.h" +#include + #include namespace { +constexpr auto kPreloadPages = 2; +constexpr auto kExpireSoonNominator = 3; +constexpr auto kExpireSoonDenominator = 4; + +enum class Color { + Permanent, + Expiring, + ExpireSoon, + Expired, + Revoked, + + Count, +}; + +using InviteLinkData = Api::InviteLink; +using InviteLinksSlice = Api::PeerInviteLinks; + +struct InviteLinkAction { + enum class Type { + Copy, + Share, + Edit, + Revoke, + Delete, + }; + QString link; + Type type = Type::Copy; +}; + +class Row; + +class RowDelegate { +public: + virtual void rowUpdateRow(not_null row) = 0; + virtual void rowPaintIcon( + QPainter &p, + int x, + int y, + int size, + float64 progress, + Color color) = 0; +}; + +class Row final : public PeerListRow { +public: + Row( + not_null delegate, + const InviteLinkData &data, + TimeId now); + + void update(const InviteLinkData &data); + + [[nodiscard]] InviteLinkData data() const; + + QString generateName() override; + QString generateShortName() override; + PaintRoundImageCallback generatePaintUserpicCallback() override; + + QSize actionSize() const override; + QMargins actionMargins() const override; + void paintAction( + Painter &p, + int x, + int y, + int outerWidth, + bool selected, + bool actionSelected) override; + +private: + const not_null _delegate; + InviteLinkData _data; + QString _status; + float64 _progressTillExpire = 0.; + Color _color = Color::Permanent; + +}; + +[[nodiscard]] uint64 ComputeRowId(const QString &link) { + return XXH64(link.data(), link.size() * sizeof(ushort), 0); +} + +[[nodiscard]] uint64 ComputeRowId(const InviteLinkData &data) { + return ComputeRowId(data.link); +} + +[[nodiscard]] float64 ComputeProgress( + const InviteLinkData &link, + TimeId now) { + const auto startDate = link.startDate ? link.startDate : link.date; + if (link.expireDate <= startDate && link.usageLimit <= 0) { + return -1; + } + const auto expireProgress = (link.expireDate <= startDate + || now <= startDate) + ? 0. + : (link.expireDate <= now) + ? 1. + : (now - startDate) / float64(link.expireDate - startDate); + const auto usageProgress = (link.usageLimit <= 0 || link.usage <= 0) + ? 0. + : (link.usageLimit <= link.usage) + ? 1. + : link.usage / float64(link.usageLimit); + return std::max(expireProgress, usageProgress); +} + +[[nodiscard]] Color ComputeColor( + const InviteLinkData &link, + float64 progress) { + const auto startDate = link.startDate ? link.startDate : link.date; + return link.revoked + ? Color::Revoked + : (progress >= 1.) + ? Color::Expired + : (progress >= 3 / 4.) + ? Color::ExpireSoon + : (progress >= 0.) + ? Color::Expiring + : Color::Permanent; +} + +[[nodiscard]] QString ComputeStatus(const InviteLinkData &link) { + return "nothing"; +} + +void CopyLink(const QString &link) { + QGuiApplication::clipboard()->setText(link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); +} + void ShareLinkBox(not_null peer, const QString &link) { const auto session = &peer->session(); const auto sending = std::make_shared(); @@ -118,6 +255,323 @@ void ShareLinkBox(not_null peer, const QString &link) { std::move(filterCallback))); } +not_null AddCreateLinkButton( + not_null container) { + const auto result = container->add( + object_ptr( + container, + tr::lng_group_invite_add(), + st::inviteLinkCreate), + style::margins(0, st::inviteLinkCreateSkip, 0, 0)); + const auto icon = Ui::CreateChild(result); + icon->setAttribute(Qt::WA_TransparentForMouseEvents); + const auto size = st::inviteLinkCreateIconSize; + icon->resize(size, size); + result->heightValue( + ) | rpl::start_with_next([=](int height) { + const auto &st = st::inviteLinkList.item; + icon->move( + st.photoPosition.x() + (st.photoSize - size) / 2, + (height - size) / 2); + }, icon->lifetime()); + icon->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(icon); + p.setPen(Qt::NoPen); + p.setBrush(st::windowBgActive); + const auto rect = icon->rect(); + auto hq = PainterHighQualityEnabler(p); + p.drawEllipse(rect); + st::inviteLinkCreateIcon.paintInCenter(p, rect); + }, icon->lifetime()); + return result; +} + +void EditLinkBox( + not_null box, + not_null peer, + const InviteLinkData &data) { + box->setTitle(data.link.isEmpty() + ? tr::lng_group_invite_new_title() + : tr::lng_group_invite_edit_title()); +} + +void CreateLinkBox( + not_null box, + not_null peer) { + EditLinkBox( + box, + peer, + InviteLinkData{ .admin = peer->session().user() }); +} + +Row::Row( + not_null delegate, + const InviteLinkData &data, + TimeId now) +: PeerListRow(ComputeRowId(data)) +, _delegate(delegate) +, _data(data) +, _progressTillExpire(ComputeProgress(data, now)) +, _color(ComputeColor(data, _progressTillExpire)) { + setCustomStatus(ComputeStatus(data)); +} + +void Row::update(const InviteLinkData &data) { + _data = data; + _progressTillExpire = ComputeProgress(data, base::unixtime::now()); + _color = ComputeColor(data, _progressTillExpire); + setCustomStatus(ComputeStatus(data)); + _delegate->rowUpdateRow(this); +} + +InviteLinkData Row::data() const { + return _data; +} + +QString Row::generateName() { + auto result = _data.link; + return result.replace(qstr("https://"), QString()); +} + +QString Row::generateShortName() { + return generateName(); +} + +PaintRoundImageCallback Row::generatePaintUserpicCallback() { + return [=]( + Painter &p, + int x, + int y, + int outerWidth, + int size) { + _delegate->rowPaintIcon(p, x, y, size, _progressTillExpire, _color); + }; +} + +QSize Row::actionSize() const { + return QSize( + st::inviteLinkThreeDotsIcon.width(), + st::inviteLinkThreeDotsIcon.height()); +} + +QMargins Row::actionMargins() const { + return QMargins( + 0, + (st::inviteLinkList.item.height - actionSize().height()) / 2, + st::inviteLinkThreeDotsSkip, + 0); +} + +void Row::paintAction( + Painter &p, + int x, + int y, + int outerWidth, + bool selected, + bool actionSelected) { + (actionSelected + ? st::inviteLinkThreeDotsIconOver + : st::inviteLinkThreeDotsIcon).paint(p, x, y, outerWidth); +} + +class Controller final + : public PeerListController + , public RowDelegate + , public base::has_weak_ptr { +public: + explicit Controller(not_null peer); + + void prepare() override; + void rowClicked(not_null row) override; + void rowActionClicked(not_null row) override; + base::unique_qptr rowContextMenu( + QWidget *parent, + not_null row) override; + Main::Session &session() const override; + + void rowUpdateRow(not_null row) override; + void rowPaintIcon( + QPainter &p, + int x, + int y, + int size, + float64 progress, + Color color) override; + +private: + void appendRow(const InviteLinkData &data, TimeId now); + [[nodiscard]] base::unique_qptr createRowContextMenu( + QWidget *parent, + not_null row); + + not_null _peer; + base::unique_qptr _menu; + + std::array _icons; + rpl::lifetime _lifetime; + +}; + +Controller::Controller(not_null peer) +: _peer(peer) { + style::PaletteChanged( + ) | rpl::start_with_next([=] { + for (auto &image : _icons) { + image = QImage(); + } + }, _lifetime); +} + +void Controller::prepare() { + const auto now = base::unixtime::now(); + const auto &links = _peer->session().api().inviteLinks().links(_peer); + for (const auto &link : links.links) { + if (!link.permanent || link.revoked) { + appendRow(link, now); + } + } + delegate()->peerListRefreshRows(); +} + +void Controller::rowClicked(not_null row) { + // #TODO links show +} + +void Controller::rowActionClicked(not_null row) { + delegate()->peerListShowRowMenu(row, nullptr); +} + +base::unique_qptr Controller::rowContextMenu( + QWidget *parent, + not_null row) { + auto result = createRowContextMenu(parent, row); + + if (result) { + // First clear _menu value, so that we don't check row positions yet. + base::take(_menu); + + // Here unique_qptr is used like a shared pointer, where + // not the last destroyed pointer destroys the object, but the first. + _menu = base::unique_qptr(result.get()); + } + + return result; +} + +base::unique_qptr Controller::createRowContextMenu( + QWidget *parent, + not_null row) { + const auto real = static_cast(row.get()); + const auto data = real->data(); + const auto link = data.link; + auto result = base::make_unique_q(parent); + if (data.revoked) { + //result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] { + // // #TODO links delete + //}); + } else { + result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] { + CopyLink(link); + }); + result->addAction(tr::lng_group_invite_context_share(tr::now), [=] { + ShareLinkBox(_peer, link); + }); + result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] { + Ui::show( + Box(EditLinkBox, _peer, data), + Ui::LayerOption::KeepOther); + }); + result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] { + const auto box = std::make_shared>(); + const auto revoke = crl::guard(this, [=] { + const auto done = crl::guard(this, [=](InviteLinkData data) { + // #TODO links add to revoked, remove from list + if (*box) { + (*box)->closeBox(); + } + }); + _peer->session().api().inviteLinks().revoke( + _peer, + link, + done); + }); + *box = Ui::show( + Box( + tr::lng_group_invite_revoke_about(tr::now), + revoke), + Ui::LayerOption::KeepOther); + }); + } + return result; +} + +Main::Session &Controller::session() const { + return _peer->session(); +} + +void Controller::appendRow(const InviteLinkData &data, TimeId now) { + delegate()->peerListAppendRow(std::make_unique(this, data, now)); +} + +void Controller::rowUpdateRow(not_null row) { + delegate()->peerListUpdateRow(row); +} + +void Controller::rowPaintIcon( + QPainter &p, + int x, + int y, + int size, + float64 progress, + Color color) { + const auto skip = st::inviteLinkIconSkip; + const auto inner = size - 2 * skip; + const auto bg = [&] { + switch (color) { + case Color::Permanent: return &st::msgFile1Bg; + case Color::Expiring: return &st::msgFile2Bg; + case Color::ExpireSoon: return &st::msgFile4Bg; + case Color::Expired: return &st::msgFile3Bg; + case Color::Revoked: return &st::windowSubTextFg; + } + Unexpected("Color in Controller::rowPaintIcon."); + }(); + auto &icon = _icons[int(color)]; + if (icon.isNull()) { + icon = QImage( + QSize(inner, inner) * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + icon.fill(Qt::transparent); + icon.setDevicePixelRatio(style::DevicePixelRatio()); + + auto p = QPainter(&icon); + p.setPen(Qt::NoPen); + p.setBrush(*bg); + auto hq = PainterHighQualityEnabler(p); + p.drawEllipse(0, 0, inner, inner); + st::inviteLinkIcon.paintInCenter(p, { 0, 0, inner, inner }); + } + p.drawImage(x + skip, y + skip, icon); + if (progress >= 0. && progress < 1.) { + const auto kFullArcLength = 360 * 16; + const auto stroke = st::inviteLinkIconStroke; + auto hq = PainterHighQualityEnabler(p); + auto pen = QPen((*bg)->c); + pen.setWidth(stroke); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + + const auto margins = 1.5 * stroke; + p.drawArc(QRectF(x + skip, y + skip, inner, inner).marginsAdded({ + margins, + margins, + margins, + margins, + }), 0, kFullArcLength * progress); + } +} + } // namespace void AddPermanentLinkBlock( @@ -143,8 +597,7 @@ void AddPermanentLinkBlock( const auto weak = Ui::MakeWeak(container); const auto copyLink = crl::guard(weak, [=] { if (const auto link = computePermanentLink()) { - QGuiApplication::clipboard()->setText(link->link); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + CopyLink(link->link); } }); const auto shareLink = crl::guard(weak, [=] { @@ -155,17 +608,12 @@ void AddPermanentLinkBlock( const auto revokeLink = crl::guard(weak, [=] { const auto box = std::make_shared>(); const auto done = crl::guard(weak, [=] { - if (const auto link = computePermanentLink()) { - const auto close = [=](auto&&) { - if (*box) { - (*box)->closeBox(); - } - }; - peer->session().api().inviteLinks().revoke( - peer, - link->link, - close); - } + const auto close = [=](auto&&) { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().revokePermanent(peer, close); }); *box = Ui::show( Box(tr::lng_group_invite_about_new(tr::now), done), @@ -286,4 +734,40 @@ void AddPermanentLinkBlock( st::inviteLinkJoinedRowPadding )->setClickedCallback([=] { }); + + container->add(object_ptr>( + container, + object_ptr( + container, + st::inviteLinkJoinedRowPadding.bottom())) + )->setDuration(0)->toggleOn(state->content.value( + ) | rpl::map([=](const Ui::JoinedCountContent &content) { + return (content.count <= 0); + })); +} + +void ManageInviteLinksBox( + not_null box, + not_null peer) { + box->setTitle(tr::lng_group_invite_title()); + + const auto container = box->verticalLayout(); + AddPermanentLinkBlock(container, peer); + Settings::AddDivider(container); + + const auto add = AddCreateLinkButton(container); + add->setClickedCallback([=] { + box->getDelegate()->show(Box(CreateLinkBox, peer)); + }); + + const auto delegate = box->lifetime().make_state< + PeerListContentDelegateSimple + >(); + const auto controller = box->lifetime().make_state(peer); + controller->setStyleOverrides(&st::inviteLinkList); + const auto content = container->add(object_ptr( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h index 358f82f5f..365071e3a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/layers/generic_box.h" + class PeerData; namespace Ui { @@ -16,3 +18,7 @@ class VerticalLayout; void AddPermanentLinkBlock( not_null container, not_null peer); + +void ManageInviteLinksBox( + not_null box, + not_null peer); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 734108fd5..a6a261173 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -185,7 +185,7 @@ void Controller::createContent() { _wrap.get(), tr::lng_group_invite_manage(), rpl::single(QString()), - [=] { /*ShowEditInviteLinks(_navigation, _peer);*/ }, + [=] { Ui::show(Box(ManageInviteLinksBox, _peer)); }, st::manageGroupButton, &st::infoIconInviteLinks)); AddSkip(_wrap.get()); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 465ce7260..818d02295 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -839,12 +839,14 @@ inviteLinkField: FlatInput(defaultFlatInput) { height: 44px; textMrg: margins(14px, 12px, 36px, 9px); } +inviteLinkThreeDotsIcon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }}; +inviteLinkThreeDotsIconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }}; inviteLinkThreeDots: IconButton(defaultIconButton) { width: 36px; height: 44px; - icon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }}; - iconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }}; + icon: inviteLinkThreeDotsIcon; + iconOver: inviteLinkThreeDotsIconOver; iconPosition: point(-1px, -1px); rippleAreaSize: 0px; @@ -875,3 +877,43 @@ inviteLinkUserpics: GroupCallUserpics { inviteLinkUserpicsSkip: 8px; inviteLinkJoinedFont: font(14px); inviteLinkJoinedRowPadding: margins(0px, 18px, 0px, 8px); + +inviteLinkCreateSkip: 8px; +inviteLinkCreate: SettingsButton(defaultSettingsButton) { + textFg: lightButtonFg; + textFgOver: lightButtonFgOver; + textBg: windowBg; + textBgOver: windowBgOver; + + font: semiboldFont; + + height: 20px; + padding: margins(58px, 7px, 12px, 5px); + + toggle: infoProfileToggle; + toggleOver: infoProfileToggleOver; + toggleSkip: 22px; + + ripple: defaultRippleAnimation; +} +inviteLinkCreateIcon: icon {{ "info/edit/roundbtn_plus", windowFgActive }}; +inviteLinkCreateIconSize: 20px; +inviteLinkListItem: PeerListItem(defaultPeerListItem) { + button: OutlineButton(defaultPeerListButton) { + font: normalFont; + padding: margins(11px, 5px, 11px, 5px); + } + height: 52px; + photoPosition: point(8px, 6px); + namePosition: point(58px, 6px); + statusPosition: point(58px, 25px); + photoSize: 40px; +} +inviteLinkList: PeerList(defaultPeerList) { + item: inviteLinkListItem; + padding: margins(0px, 4px, 0px, 4px); +} +inviteLinkIconSkip: 7px; +inviteLinkIconStroke: 2px; +inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }}; +inviteLinkThreeDotsSkip: 8px; From 1cce383d15ce2df7d5b66c4c974e549d411de9eb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 19 Jan 2021 10:22:25 +0400 Subject: [PATCH 150/396] Add a box to create / edit invite links. --- Telegram/Resources/langs/lang.strings | 17 +- Telegram/SourceFiles/boxes/connection_box.cpp | 7 +- .../boxes/peers/edit_peer_info_box.cpp | 5 +- .../boxes/peers/edit_peer_invite_links.cpp | 319 +++++++++++++++++- .../boxes/peers/edit_peer_type_box.cpp | 33 +- .../view/history_view_schedule_box.cpp | 80 +++-- .../history/view/history_view_schedule_box.h | 13 + Telegram/SourceFiles/info/info.style | 2 + Telegram/lib_ui | 2 +- 9 files changed, 392 insertions(+), 86 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index de3ccf75b..fbe9396a3 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1166,9 +1166,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content."; "lng_group_invite_create" = "Create an invite link"; // #TODO links legacy -"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; // #TODO links legacy -"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link."; // #TODO links legacy -"lng_group_invite_create_new" = "Revoke invite link"; // #TODO links legacy "lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you."; "lng_group_invite_copied" = "Invite link copied to clipboard."; "lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already."; @@ -1186,12 +1183,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_manage_about" = "You can create additional invite links that have limited time or number of usages."; "lng_group_invite_title" = "Invite links"; "lng_group_invite_add" = "Create a New Link"; +"lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used."; "lng_group_invite_expires_at" = "This link expires {when}."; "lng_group_invite_created_by" = "Link created by"; "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; "lng_group_invite_context_edit" = "Edit"; "lng_group_invite_context_revoke" = "Revoke"; +"lng_group_invite_context_delete" = "Delete"; +"lng_group_invite_context_delete_all" = "Delete all"; +"lng_group_invite_revoked_title" = "Revoked links"; "lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?"; "lng_group_invite_link_expired" = "Expired"; "lng_group_invite_link_revoked" = "Revoked"; @@ -1199,10 +1200,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_new_title" = "New Link"; "lng_group_invite_expire_title" = "Limit by time period"; "lng_group_invite_expire_about" = "You can make the link expire after a certain time."; -"lng_group_invite_expire_custom" = "Set custom duration"; -"lng_group_invite_usage_title" = "Limit number of uses."; +"lng_group_invite_expire_never" = "No limit"; +"lng_group_invite_expire_custom" = "Custom"; +"lng_group_invite_usage_title" = "Limit number of uses"; "lng_group_invite_usage_about" = "You can make the link expire after it has been used for a certain number of times."; -"lng_group_invite_usage_custom" = "Enter custom limit"; +"lng_group_invite_expire_after" = "Expire after"; +"lng_group_invite_custom_limit" = "Enter custom limit"; +"lng_group_invite_usage_any" = "No limit"; +"lng_group_invite_usage_custom" = "Custom"; "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 0ede048a0..8b2e3a6f7 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -252,7 +252,7 @@ private: QPointer> _aboutSponsored; QPointer _host; - QPointer _port; + QPointer _port; QPointer _user; QPointer _password; QPointer _secret; @@ -928,11 +928,12 @@ void ProxyBox::setupSocketAddress(const ProxyData &data) { st::connectionHostInputField, tr::lng_connection_host_ph(), data.host); - _port = Ui::CreateChild( + _port = Ui::CreateChild( address, st::connectionPortInputField, tr::lng_connection_port_ph(), - data.port ? QString::number(data.port) : QString()); + data.port ? QString::number(data.port) : QString(), + 65535); address->widthValue( ) | rpl::start_with_next([=](int width) { _port->moveToRight(0, 0); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 9ebdc480c..5671917e0 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1004,7 +1004,10 @@ void Controller::fillManageSection() { }); }) | rpl::flatten_latest( ) | ToPositiveNumberString(), - [=] { Ui::show(Box(ManageInviteLinksBox, _peer)); }, + [=] { Ui::show( + Box(ManageInviteLinksBox, _peer), + Ui::LayerOption::KeepOther); + }, st::infoIconInviteLinks); } if (canViewAdmins) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 28815f842..37c814cb2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -20,11 +20,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/abstract_button.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/input_fields.h" #include "ui/controls/invite_link_label.h" #include "ui/controls/invite_link_buttons.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... +#include "history/view/history_view_schedule_box.h" // ChooseDateTimeBox. #include "history/history_message.h" // GetErrorTextForSending. #include "history/history.h" #include "lang/lang_keys.h" @@ -40,6 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "api/api_common.h" #include "styles/style_info.h" +#include "styles/style_layers.h" // st::boxDividerLabel +#include "styles/style_settings.h" // st::settingsDividerLabelPadding #include @@ -48,8 +53,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kPreloadPages = 2; -constexpr auto kExpireSoonNominator = 3; -constexpr auto kExpireSoonDenominator = 4; +constexpr auto kMaxLimit = std::numeric_limits::max(); +constexpr auto kHour = 3600; +constexpr auto kDay = 86400; enum class Color { Permanent, @@ -124,6 +130,27 @@ private: }; +[[nodiscard]] QString FormatExpireDate(TimeId date) { + if (date > 0) { + return langDateTime(base::unixtime::parse(date)); + } else if (-date < kDay) { + return tr::lng_group_call_duration_hours( + tr::now, + lt_count, + (-date / kHour)); + } else if (-date < 7 * kDay) { + return tr::lng_group_call_duration_days( + tr::now, + lt_count, + (-date / kDay)); + } else { + return tr::lng_local_storage_limit_weeks( + tr::now, + lt_count, + (-date / (7 * kDay))); + } +} + [[nodiscard]] uint64 ComputeRowId(const QString &link) { return XXH64(link.data(), link.size() * sizeof(ushort), 0); } @@ -291,9 +318,226 @@ void EditLinkBox( not_null box, not_null peer, const InviteLinkData &data) { - box->setTitle(data.link.isEmpty() + const auto link = data.link; + box->setTitle(link.isEmpty() ? tr::lng_group_invite_new_title() : tr::lng_group_invite_edit_title()); + + using namespace Settings; + const auto container = box->verticalLayout(); + + AddSubsectionTitle(container, tr::lng_group_invite_expire_title()); + const auto expiresWrap = container->add(object_ptr( + container)); + AddSkip(container); + + AddDividerText(container, tr::lng_group_invite_expire_about()); + AddSkip(container); + AddSubsectionTitle(container, tr::lng_group_invite_usage_title()); + const auto usagesWrap = container->add(object_ptr( + container)); + AddSkip(container); + + AddDividerText(container, tr::lng_group_invite_usage_about()); + + static const auto addButton = []( + not_null container, + const std::shared_ptr &group, + int value, + const QString &text) { + return container->add( + object_ptr( + container, + group, + value, + text), + st::inviteLinkLimitMargin); + }; + + const auto now = base::unixtime::now(); + const auto expire = data.expireDate ? data.expireDate : kMaxLimit; + const auto expireGroup = std::make_shared(expire); + const auto usage = data.usageLimit ? data.usageLimit : kMaxLimit; + const auto usageGroup = std::make_shared(usage); + + using Buttons = base::flat_map>; + struct State { + Buttons expireButtons; + Buttons usageButtons; + int expireValue = 0; + int usageValue = 0; + }; + const auto state = container->lifetime().make_state(State{ + .expireValue = expire, + .usageValue = usage + }); + const auto regenerate = [=] { + expireGroup->setValue(state->expireValue); + usageGroup->setValue(state->usageValue); + + auto expires = std::vector{ kMaxLimit, -kHour, -kDay, -kDay * 7, 0 }; + auto usages = std::vector{ kMaxLimit, 1, 10, 100, 0 }; + auto defaults = State(); + for (auto i = begin(expires); i != end(expires); ++i) { + if (*i == state->expireValue) { + break; + } else if (*i == kMaxLimit) { + continue; + } else if (!*i || (now - *i >= state->expireValue)) { + expires.insert(i, state->expireValue); + break; + } + } + for (auto i = begin(usages); i != end(usages); ++i) { + if (*i == state->usageValue) { + break; + } else if (*i == kMaxLimit) { + continue; + } else if (!*i || *i > state->usageValue) { + usages.insert(i, state->usageValue); + break; + } + } + state->expireButtons.clear(); + state->usageButtons.clear(); + for (const auto limit : expires) { + const auto text = (limit == kMaxLimit) + ? tr::lng_group_invite_expire_never(tr::now) + : !limit + ? tr::lng_group_invite_expire_custom(tr::now) + : FormatExpireDate(limit); + state->expireButtons.emplace( + limit, + addButton(expiresWrap, expireGroup, limit, text)); + } + for (const auto limit : usages) { + const auto text = (limit == kMaxLimit) + ? tr::lng_group_invite_usage_any(tr::now) + : !limit + ? tr::lng_group_invite_usage_custom(tr::now) + : QString("%L1").arg(limit); + state->usageButtons.emplace( + limit, + addButton(usagesWrap, usageGroup, limit, text)); + } + }; + + const auto guard = Ui::MakeWeak(box); + expireGroup->setChangedCallback([=](int value) { + if (value) { + state->expireValue = value; + return; + } + expireGroup->setValue(state->expireValue); + box->getDelegate()->show(Box([=](not_null box) { + const auto save = [=](TimeId result) { + if (!result) { + return; + } + if (guard) { + state->expireValue = result; + regenerate(); + } + box->closeBox(); + }; + const auto now = base::unixtime::now(); + const auto time = (state->expireValue == kMaxLimit) + ? (now + kDay) + : (state->expireValue > now) + ? state->expireValue + : (state->expireValue < 0) + ? (now - state->expireValue) + : (now + kDay); + HistoryView::ChooseDateTimeBox( + box, + tr::lng_group_invite_expire_after(), + tr::lng_settings_save(), + save, + time); + })); + }); + usageGroup->setChangedCallback([=](int value) { + if (value) { + state->usageValue = value; + return; + } + usageGroup->setValue(state->usageValue); + box->getDelegate()->show(Box([=](not_null box) { + const auto height = st::boxPadding.bottom() + + st::defaultInputField.heightMin + + st::boxPadding.bottom(); + box->setTitle(tr::lng_group_invite_expire_after()); + const auto wrap = box->addRow(object_ptr( + box, + height)); + const auto input = Ui::CreateChild( + wrap, + st::defaultInputField, + tr::lng_group_invite_custom_limit(), + (state->usageValue == kMaxLimit + ? QString() + : QString::number(state->usageValue)), + 200'000); + wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + input->resize(width, input->height()); + input->moveToLeft(0, st::boxPadding.bottom()); + }, input->lifetime()); + box->setFocusCallback([=] { + input->setFocusFast(); + }); + + const auto save = [=] { + const auto value = input->getLastText().toInt(); + if (value <= 0) { + input->showError(); + return; + } + if (guard) { + state->usageValue = value; + regenerate(); + } + box->closeBox(); + }; + QObject::connect(input, &Ui::NumberInput::submitted, save); + box->addButton(tr::lng_settings_save(), save); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + }); + + regenerate(); + + const auto &saveLabel = link.isEmpty() + ? tr::lng_formatting_link_create + : tr::lng_settings_save; + box->addButton(saveLabel(), [=] { + const auto expireDate = (state->expireValue == kMaxLimit) + ? 0 + : (state->expireValue < 0) + ? (base::unixtime::now() - state->expireValue) + : state->expireValue; + const auto usageLimit = (state->usageValue == kMaxLimit) + ? 0 + : state->usageValue; + const auto done = [=](const Api::InviteLink &result) { + box->closeBox(); + }; + if (link.isEmpty()) { + peer->session().api().inviteLinks().create( + peer, + done, + expireDate, + usageLimit); + } else { + peer->session().api().inviteLinks().edit( + peer, + link, + expireDate, + usageLimit, + done); + } + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } void CreateLinkBox( @@ -380,7 +624,7 @@ class Controller final , public RowDelegate , public base::has_weak_ptr { public: - explicit Controller(not_null peer); + Controller(not_null peer, bool revoked); void prepare() override; void rowClicked(not_null row) override; @@ -406,6 +650,7 @@ private: not_null row); not_null _peer; + bool _revoked = false; base::unique_qptr _menu; std::array _icons; @@ -413,8 +658,9 @@ private: }; -Controller::Controller(not_null peer) -: _peer(peer) { +Controller::Controller(not_null peer, bool revoked) +: _peer(peer) +, _revoked(revoked) { style::PaletteChanged( ) | rpl::start_with_next([=] { for (auto &image : _icons) { @@ -559,6 +805,7 @@ void Controller::rowPaintIcon( auto hq = PainterHighQualityEnabler(p); auto pen = QPen((*bg)->c); pen.setWidth(stroke); + pen.setCapStyle(Qt::RoundCap); p.setPen(pen); p.setBrush(Qt::NoBrush); @@ -568,7 +815,7 @@ void Controller::rowPaintIcon( margins, margins, margins, - }), 0, kFullArcLength * progress); + }), (kFullArcLength / 4), kFullArcLength * (1. - progress)); } } @@ -746,6 +993,26 @@ void AddPermanentLinkBlock( })); } +not_null AddLinksList( + not_null container, + not_null peer, + bool revoked) { + const auto delegate = container->lifetime().make_state< + PeerListContentDelegateSimple + >(); + const auto controller = container->lifetime().make_state( + peer, + revoked); + controller->setStyleOverrides(&st::inviteLinkList); + const auto content = container->add(object_ptr( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); + + return content; +} + void ManageInviteLinksBox( not_null box, not_null peer) { @@ -760,14 +1027,34 @@ void ManageInviteLinksBox( box->getDelegate()->show(Box(CreateLinkBox, peer)); }); - const auto delegate = box->lifetime().make_state< - PeerListContentDelegateSimple - >(); - const auto controller = box->lifetime().make_state(peer); - controller->setStyleOverrides(&st::inviteLinkList); - const auto content = container->add(object_ptr( + const auto list = AddLinksList(container, peer, false); + const auto dividerAbout = container->add(object_ptr>( container, - controller)); - delegate->setContent(content); - controller->setDelegate(delegate); + object_ptr( + container, + object_ptr( + container, + tr::lng_group_invite_add_about(), + st::boxDividerLabel), + st::settingsDividerLabelPadding))); + const auto divider = container->add(object_ptr>( + container, + object_ptr(container))); + const auto header = container->add(object_ptr>( + container, + object_ptr( + container, + tr::lng_group_invite_revoked_title(), + st::settingsSubsectionTitle), + st::inviteLinkRevokedTitlePadding)); + const auto revoked = AddLinksList(container, peer, true); + + rpl::combine( + list->heightValue(), + revoked->heightValue() + ) | rpl::start_with_next([=](int list, int revoked) { + dividerAbout->toggle(!list, anim::type::instant); + divider->toggle(list > 0 && revoked > 0, anim::type::instant); + header->toggle(revoked > 0, anim::type::instant); + }, header->lifetime()); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index a6a261173..6cdda9579 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -117,9 +117,6 @@ private: rpl::producer &&text, not_null st); - void createInviteLink(); - void revokeInviteLink(const QString &link); - void fillPrivaciesButtons( not_null parent, std::optional savedValue = std::nullopt); @@ -185,7 +182,10 @@ void Controller::createContent() { _wrap.get(), tr::lng_group_invite_manage(), rpl::single(QString()), - [=] { Ui::show(Box(ManageInviteLinksBox, _peer)); }, + [=] { Ui::show( + Box(ManageInviteLinksBox, _peer), + Ui::LayerOption::KeepOther); + }, st::manageGroupButton, &st::infoIconInviteLinks)); AddSkip(_wrap.get()); @@ -529,31 +529,6 @@ void Controller::showUsernameResult( _usernameResultTexts.fire(std::move(text)); } -void Controller::createInviteLink() { - const auto callback = crl::guard(this, [=](Fn &&close) { - close(); - _peer->session().api().inviteLinks().create(_peer->migrateToOrMe()); - }); - auto box = Box( - (_isGroup - ? tr::lng_group_invite_about - : tr::lng_group_invite_about_channel)(tr::now), - std::move(callback)); - Ui::show(std::move(box), Ui::LayerOption::KeepOther); -} - -void Controller::revokeInviteLink(const QString &link) { - const auto callback = crl::guard(this, [=](Fn &&close) { - close(); - const auto peer = _peer->migrateToOrMe(); - peer->session().api().inviteLinks().revoke(peer, link); - }); - auto box = Box( - tr::lng_group_invite_about_new(tr::now), - std::move(callback)); - Ui::show(std::move(box), Ui::LayerOption::KeepOther); -} - object_ptr Controller::createInviteLinkBlock() { Expects(_wrap != nullptr); diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index 52b05ae4a..c832129a4 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -596,19 +596,18 @@ TimeId DefaultScheduleTime() { bool CanScheduleUntilOnline(not_null peer) { return !peer->isSelf() - && peer->isUser() - && !peer->asUser()->isBot() - && (peer->asUser()->onlineTill > 0); + && peer->isUser() + && !peer->asUser()->isBot() + && (peer->asUser()->onlineTill > 0); } -void ScheduleBox( +ChooseDateTimeBoxDescriptor ChooseDateTimeBox( not_null box, - SendMenu::Type type, - Fn done, + rpl::producer title, + rpl::producer submit, + Fn done, TimeId time) { - box->setTitle((type == SendMenu::Type::Reminder) - ? tr::lng_remind_title() - : tr::lng_schedule_title()); + box->setTitle(std::move(title)); box->setWidth(st::boxWideWidth); const auto date = Ui::CreateChild>( @@ -716,46 +715,67 @@ void ScheduleBox( } return result; }; - const auto save = [=](bool silent, bool untilOnline = false) { + const auto save = [=] { + if (const auto result = collect()) { + done(result); + } + }; + timeInput->submitRequests( + ) | rpl::start_with_next( + save, + timeInput->lifetime()); + + auto result = ChooseDateTimeBoxDescriptor(); + box->setFocusCallback([=] { timeInput->setFocusFast(); }); + result.submit = box->addButton(std::move(submit), save); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + + return result; +} + +void ScheduleBox( + not_null box, + SendMenu::Type type, + Fn done, + TimeId time) { + const auto save = [=](bool silent, TimeId scheduleDate) { + if (!scheduleDate) { + return; + } // Pro tip: Hold Ctrl key to send a silent scheduled message! auto ctrl = (QGuiApplication::keyboardModifiers() == Qt::ControlModifier); auto result = Api::SendOptions(); result.silent = silent || ctrl; - result.scheduled = untilOnline - ? Data::ScheduledMessages::kScheduledUntilOnlineTimestamp - : collect(); - if (!result.scheduled) { - return; - } - - auto copy = done; + result.scheduled = scheduleDate; + const auto copy = done; box->closeBox(); copy(result); }; - timeInput->submitRequests( - ) | rpl::start_with_next([=] { - save(false); - }, timeInput->lifetime()); + auto descriptor = ChooseDateTimeBox( + box, + (type == SendMenu::Type::Reminder + ? tr::lng_remind_title() + : tr::lng_schedule_title()), + tr::lng_schedule_button(), + [=](TimeId result) { save(false, result); }, + time); - box->setFocusCallback([=] { timeInput->setFocusFast(); }); - const auto submit = box->addButton(tr::lng_schedule_button(), [=] { - save(false); - }); SendMenu::SetupMenuAndShortcuts( - submit.data(), + descriptor.submit.data(), [=] { return SendMenu::Type::SilentOnly; }, - [=] { save(true); }, + [=] { save(true, descriptor.collect()); }, nullptr); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); if (type == SendMenu::Type::ScheduledToUser) { const auto sendUntilOnline = box->addTopButton(st::infoTopBarMenu); + const auto timestamp + = Data::ScheduledMessages::kScheduledUntilOnlineTimestamp; FillSendUntilOnlineMenu( sendUntilOnline.data(), - [=] { save(false, true); }); + [=] { save(false, timestamp); }); } - } } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.h b/Telegram/SourceFiles/history/view/history_view_schedule_box.h index da640faaa..2ad25e4ee 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.h +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.h @@ -21,6 +21,19 @@ namespace HistoryView { [[nodiscard]] TimeId DefaultScheduleTime(); [[nodiscard]] bool CanScheduleUntilOnline(not_null peer); + +struct ChooseDateTimeBoxDescriptor { + QPointer submit; + Fn collect; +}; + +ChooseDateTimeBoxDescriptor ChooseDateTimeBox( + not_null box, + rpl::producer title, + rpl::producer submit, + Fn done, + TimeId time); + void ScheduleBox( not_null box, SendMenu::Type type, diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 818d02295..f02207cda 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -917,3 +917,5 @@ inviteLinkIconSkip: 7px; inviteLinkIconStroke: 2px; inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }}; inviteLinkThreeDotsSkip: 8px; +inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 9px); +inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 912f0b48a..a5fb99372 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 912f0b48a648e7f12e7bcba82a0f0ec326aab8dd +Subproject commit a5fb99372184d5f3c00e5e851aaa16d8d28d2ce1 From 97fb310f540fbd14419de4d2160b31dd2643eb0f Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 19 Jan 2021 11:14:50 +0400 Subject: [PATCH 151/396] Move CalendarBox and ChooseDateTimeBox to td_ui. --- Telegram/CMakeLists.txt | 2 - Telegram/SourceFiles/api/api_invite_links.cpp | 5 +- Telegram/SourceFiles/api/api_invite_links.h | 1 + Telegram/SourceFiles/boxes/boxes.style | 29 + .../boxes/peers/edit_participant_box.cpp | 4 +- .../boxes/peers/edit_participant_box.h | 4 +- .../boxes/peers/edit_peer_invite_links.cpp | 306 ++------ .../export/view/export_view_settings.cpp | 8 +- .../view/history_view_schedule_box.cpp | 680 +---------------- .../history/view/history_view_schedule_box.h | 12 - .../{ => ui}/boxes/calendar_box.cpp | 15 +- .../SourceFiles/{ => ui}/boxes/calendar_box.h | 15 +- .../SourceFiles/ui/boxes/choose_date_time.cpp | 702 ++++++++++++++++++ .../SourceFiles/ui/boxes/choose_date_time.h | 28 + .../SourceFiles/ui/boxes/edit_invite_link.cpp | 290 ++++++++ .../SourceFiles/ui/boxes/edit_invite_link.h | 29 + Telegram/SourceFiles/ui/chat/chat.style | 29 - .../window/window_session_controller.cpp | 4 +- Telegram/cmake/td_ui.cmake | 6 + 19 files changed, 1161 insertions(+), 1008 deletions(-) rename Telegram/SourceFiles/{ => ui}/boxes/calendar_box.cpp (97%) rename Telegram/SourceFiles/{ => ui}/boxes/calendar_box.h (88%) create mode 100644 Telegram/SourceFiles/ui/boxes/choose_date_time.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/choose_date_time.h create mode 100644 Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/edit_invite_link.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index fccf3ddcd..2a14bab17 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -203,8 +203,6 @@ PRIVATE boxes/background_box.h boxes/background_preview_box.cpp boxes/background_preview_box.h - boxes/calendar_box.cpp - boxes/calendar_box.h boxes/change_phone_box.cpp boxes/change_phone_box.h boxes/confirm_box.cpp diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index c01f54d55..34ad40cf9 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -431,9 +431,12 @@ auto InviteLinks::parse( void InviteLinks::requestMoreLinks( not_null peer, const QString &last, + bool revoked, Fn done) { + using Flag = MTPmessages_GetExportedChatInvites::Flag; _api->request(MTPmessages_GetExportedChatInvites( - MTP_flags(MTPmessages_GetExportedChatInvites::Flag::f_offset_link), + MTP_flags(Flag::f_offset_link + | (revoked ? Flag::f_revoked : Flag(0))), peer->input, MTPInputUser(), // admin_id, MTP_string(last), diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 096038c06..cec3a9c03 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -81,6 +81,7 @@ public: void requestMoreLinks( not_null peer, const QString &last, + bool revoked, Fn done); private: diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 82d7dfda6..53d827c6c 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -905,3 +905,32 @@ pollResultsShowMore: SettingsButton(defaultSettingsButton) { ripple: defaultRippleAnimation; } + +scheduleHeight: 95px; +scheduleDateTop: 38px; +scheduleDateField: InputField(defaultInputField) { + textMargins: margins(2px, 0px, 2px, 0px); + placeholderScale: 0.; + heightMin: 30px; + textAlign: align(top); + font: font(14px); +} +scheduleTimeField: InputField(scheduleDateField) { + border: 0px; + borderActive: 0px; + heightMin: 28px; + placeholderFont: font(14px); + placeholderFgActive: placeholderFgActive; +} +scheduleDateWidth: 136px; +scheduleTimeWidth: 72px; +scheduleAtSkip: 24px; +scheduleAtTop: 42px; +scheduleAtLabel: FlatLabel(defaultFlatLabel) { +} +scheduleTimeSeparator: FlatLabel(defaultFlatLabel) { + style: TextStyle(defaultTextStyle) { + font: font(14px); + } +} +scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 2ef65dcef..df67a6255 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -20,10 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/text/text_options.h" +#include "ui/boxes/calendar_box.h" #include "ui/special_buttons.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "settings/settings_privacy_security.h" -#include "boxes/calendar_box.h" #include "boxes/confirm_box.h" #include "boxes/passcode_box.h" #include "boxes/peers/edit_peer_permissions_box.h" @@ -691,7 +691,7 @@ void EditRestrictedBox::showRestrictUntil() { : base::unixtime::parse(getRealUntilValue()).date(); auto month = highlighted; _restrictUntilBox = Ui::show( - Box( + Box( month, highlighted, [this](const QDate &date) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 689b08a7b..371d7b98b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -18,6 +18,7 @@ class LinkButton; class Checkbox; class Radiobutton; class RadiobuttonGroup; +class CalendarBox; template class SlideWrap; } // namespace Ui @@ -26,7 +27,6 @@ namespace Core { struct CloudPasswordResult; } // namespace Core -class CalendarBox; class PasscodeBox; class EditParticipantBox : public Ui::BoxContent { @@ -162,7 +162,7 @@ private: std::shared_ptr _untilGroup; std::vector> _untilVariants; - QPointer _restrictUntilBox; + QPointer _restrictUntilBox; static constexpr auto kUntilOneDay = -1; static constexpr auto kUntilOneWeek = -2; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 37c814cb2..10b344318 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "main/main_session.h" #include "api/api_invite_links.h" +#include "ui/boxes/edit_invite_link.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/padding_wrap.h" @@ -27,7 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... -#include "history/view/history_view_schedule_box.h" // ChooseDateTimeBox. #include "history/history_message.h" // GetErrorTextForSending. #include "history/history.h" #include "lang/lang_keys.h" @@ -53,9 +53,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kPreloadPages = 2; -constexpr auto kMaxLimit = std::numeric_limits::max(); -constexpr auto kHour = 3600; -constexpr auto kDay = 86400; enum class Color { Permanent, @@ -130,27 +127,6 @@ private: }; -[[nodiscard]] QString FormatExpireDate(TimeId date) { - if (date > 0) { - return langDateTime(base::unixtime::parse(date)); - } else if (-date < kDay) { - return tr::lng_group_call_duration_hours( - tr::now, - lt_count, - (-date / kHour)); - } else if (-date < 7 * kDay) { - return tr::lng_group_call_duration_days( - tr::now, - lt_count, - (-date / kDay)); - } else { - return tr::lng_local_storage_limit_weeks( - tr::now, - lt_count, - (-date / (7 * kDay))); - } -} - [[nodiscard]] uint64 ComputeRowId(const QString &link) { return XXH64(link.data(), link.size() * sizeof(ushort), 0); } @@ -282,6 +258,45 @@ void ShareLinkBox(not_null peer, const QString &link) { std::move(filterCallback))); } +void EditLink(not_null peer, const InviteLinkData &data) { + const auto creating = data.link.isEmpty(); + const auto box = std::make_shared>(); + using Fields = Ui::InviteLinkFields; + const auto done = [=](Fields result) { + const auto finish = [=](Api::InviteLink finished) { + if (*box) { + (*box)->closeBox(); + } + }; + if (creating) { + peer->session().api().inviteLinks().create( + peer, + finish, + result.expireDate, + result.usageLimit); + } else { + peer->session().api().inviteLinks().edit( + peer, + result.link, + result.expireDate, + result.usageLimit, + finish); + } + }; + *box = Ui::show( + (creating + ? Box(Ui::CreateInviteLinkBox, done) + : Box( + Ui::EditInviteLinkBox, + Fields{ + .link = data.link, + .expireDate = data.expireDate, + .usageLimit = data.usageLimit + }, + done)), + Ui::LayerOption::KeepOther); +} + not_null AddCreateLinkButton( not_null container) { const auto result = container->add( @@ -314,241 +329,6 @@ not_null AddCreateLinkButton( return result; } -void EditLinkBox( - not_null box, - not_null peer, - const InviteLinkData &data) { - const auto link = data.link; - box->setTitle(link.isEmpty() - ? tr::lng_group_invite_new_title() - : tr::lng_group_invite_edit_title()); - - using namespace Settings; - const auto container = box->verticalLayout(); - - AddSubsectionTitle(container, tr::lng_group_invite_expire_title()); - const auto expiresWrap = container->add(object_ptr( - container)); - AddSkip(container); - - AddDividerText(container, tr::lng_group_invite_expire_about()); - AddSkip(container); - AddSubsectionTitle(container, tr::lng_group_invite_usage_title()); - const auto usagesWrap = container->add(object_ptr( - container)); - AddSkip(container); - - AddDividerText(container, tr::lng_group_invite_usage_about()); - - static const auto addButton = []( - not_null container, - const std::shared_ptr &group, - int value, - const QString &text) { - return container->add( - object_ptr( - container, - group, - value, - text), - st::inviteLinkLimitMargin); - }; - - const auto now = base::unixtime::now(); - const auto expire = data.expireDate ? data.expireDate : kMaxLimit; - const auto expireGroup = std::make_shared(expire); - const auto usage = data.usageLimit ? data.usageLimit : kMaxLimit; - const auto usageGroup = std::make_shared(usage); - - using Buttons = base::flat_map>; - struct State { - Buttons expireButtons; - Buttons usageButtons; - int expireValue = 0; - int usageValue = 0; - }; - const auto state = container->lifetime().make_state(State{ - .expireValue = expire, - .usageValue = usage - }); - const auto regenerate = [=] { - expireGroup->setValue(state->expireValue); - usageGroup->setValue(state->usageValue); - - auto expires = std::vector{ kMaxLimit, -kHour, -kDay, -kDay * 7, 0 }; - auto usages = std::vector{ kMaxLimit, 1, 10, 100, 0 }; - auto defaults = State(); - for (auto i = begin(expires); i != end(expires); ++i) { - if (*i == state->expireValue) { - break; - } else if (*i == kMaxLimit) { - continue; - } else if (!*i || (now - *i >= state->expireValue)) { - expires.insert(i, state->expireValue); - break; - } - } - for (auto i = begin(usages); i != end(usages); ++i) { - if (*i == state->usageValue) { - break; - } else if (*i == kMaxLimit) { - continue; - } else if (!*i || *i > state->usageValue) { - usages.insert(i, state->usageValue); - break; - } - } - state->expireButtons.clear(); - state->usageButtons.clear(); - for (const auto limit : expires) { - const auto text = (limit == kMaxLimit) - ? tr::lng_group_invite_expire_never(tr::now) - : !limit - ? tr::lng_group_invite_expire_custom(tr::now) - : FormatExpireDate(limit); - state->expireButtons.emplace( - limit, - addButton(expiresWrap, expireGroup, limit, text)); - } - for (const auto limit : usages) { - const auto text = (limit == kMaxLimit) - ? tr::lng_group_invite_usage_any(tr::now) - : !limit - ? tr::lng_group_invite_usage_custom(tr::now) - : QString("%L1").arg(limit); - state->usageButtons.emplace( - limit, - addButton(usagesWrap, usageGroup, limit, text)); - } - }; - - const auto guard = Ui::MakeWeak(box); - expireGroup->setChangedCallback([=](int value) { - if (value) { - state->expireValue = value; - return; - } - expireGroup->setValue(state->expireValue); - box->getDelegate()->show(Box([=](not_null box) { - const auto save = [=](TimeId result) { - if (!result) { - return; - } - if (guard) { - state->expireValue = result; - regenerate(); - } - box->closeBox(); - }; - const auto now = base::unixtime::now(); - const auto time = (state->expireValue == kMaxLimit) - ? (now + kDay) - : (state->expireValue > now) - ? state->expireValue - : (state->expireValue < 0) - ? (now - state->expireValue) - : (now + kDay); - HistoryView::ChooseDateTimeBox( - box, - tr::lng_group_invite_expire_after(), - tr::lng_settings_save(), - save, - time); - })); - }); - usageGroup->setChangedCallback([=](int value) { - if (value) { - state->usageValue = value; - return; - } - usageGroup->setValue(state->usageValue); - box->getDelegate()->show(Box([=](not_null box) { - const auto height = st::boxPadding.bottom() - + st::defaultInputField.heightMin - + st::boxPadding.bottom(); - box->setTitle(tr::lng_group_invite_expire_after()); - const auto wrap = box->addRow(object_ptr( - box, - height)); - const auto input = Ui::CreateChild( - wrap, - st::defaultInputField, - tr::lng_group_invite_custom_limit(), - (state->usageValue == kMaxLimit - ? QString() - : QString::number(state->usageValue)), - 200'000); - wrap->widthValue( - ) | rpl::start_with_next([=](int width) { - input->resize(width, input->height()); - input->moveToLeft(0, st::boxPadding.bottom()); - }, input->lifetime()); - box->setFocusCallback([=] { - input->setFocusFast(); - }); - - const auto save = [=] { - const auto value = input->getLastText().toInt(); - if (value <= 0) { - input->showError(); - return; - } - if (guard) { - state->usageValue = value; - regenerate(); - } - box->closeBox(); - }; - QObject::connect(input, &Ui::NumberInput::submitted, save); - box->addButton(tr::lng_settings_save(), save); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); - })); - }); - - regenerate(); - - const auto &saveLabel = link.isEmpty() - ? tr::lng_formatting_link_create - : tr::lng_settings_save; - box->addButton(saveLabel(), [=] { - const auto expireDate = (state->expireValue == kMaxLimit) - ? 0 - : (state->expireValue < 0) - ? (base::unixtime::now() - state->expireValue) - : state->expireValue; - const auto usageLimit = (state->usageValue == kMaxLimit) - ? 0 - : state->usageValue; - const auto done = [=](const Api::InviteLink &result) { - box->closeBox(); - }; - if (link.isEmpty()) { - peer->session().api().inviteLinks().create( - peer, - done, - expireDate, - usageLimit); - } else { - peer->session().api().inviteLinks().edit( - peer, - link, - expireDate, - usageLimit, - done); - } - }); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); -} - -void CreateLinkBox( - not_null box, - not_null peer) { - EditLinkBox( - box, - peer, - InviteLinkData{ .admin = peer->session().user() }); -} - Row::Row( not_null delegate, const InviteLinkData &data, @@ -724,9 +504,7 @@ base::unique_qptr Controller::createRowContextMenu( ShareLinkBox(_peer, link); }); result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] { - Ui::show( - Box(EditLinkBox, _peer, data), - Ui::LayerOption::KeepOther); + EditLink(_peer, data); }); result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] { const auto box = std::make_shared>(); @@ -1024,7 +802,7 @@ void ManageInviteLinksBox( const auto add = AddCreateLinkButton(container); add->setClickedCallback([=] { - box->getDelegate()->show(Box(CreateLinkBox, peer)); + EditLink(peer, InviteLinkData{ .admin = peer->session().user() }); }); const auto list = AddLinksList(container, peer, false); diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index 7fefa6639..956cfb780 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -21,9 +21,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" +#include "ui/boxes/calendar_box.h" #include "platform/platform_specific.h" #include "core/file_utilities.h" -#include "boxes/calendar_box.h" #include "base/unixtime.h" #include "base/qt_adapters.h" #include "main/main_session.h" @@ -463,8 +463,8 @@ void SettingsWidget::editDateLimit( ? base::unixtime::parse(min).date() : QDate::currentDate(); const auto month = highlighted; - const auto shared = std::make_shared>(); - const auto finalize = [=](not_null box) { + const auto shared = std::make_shared>(); + const auto finalize = [=](not_null box) { box->setMaxDate(max ? base::unixtime::parse(max).date() : QDate::currentDate()); @@ -484,7 +484,7 @@ void SettingsWidget::editDateLimit( weak->closeBox(); } }); - auto box = Box( + auto box = Box( month, highlighted, callback, diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index c832129a4..e22bffb1c 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -14,12 +14,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "base/event_filter.h" #include "base/unixtime.h" -#include "boxes/calendar_box.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "ui/wrap/padding_wrap.h" +#include "ui/boxes/choose_date_time.h" #include "chat_helpers/send_context_menu.h" #include "styles/style_info.h" #include "styles/style_layers.h" @@ -30,550 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { namespace { -constexpr auto kMinimalSchedule = TimeId(10); - -tr::phrase<> MonthDay(int index) { - switch (index) { - case 1: return tr::lng_month_day1; - case 2: return tr::lng_month_day2; - case 3: return tr::lng_month_day3; - case 4: return tr::lng_month_day4; - case 5: return tr::lng_month_day5; - case 6: return tr::lng_month_day6; - case 7: return tr::lng_month_day7; - case 8: return tr::lng_month_day8; - case 9: return tr::lng_month_day9; - case 10: return tr::lng_month_day10; - case 11: return tr::lng_month_day11; - case 12: return tr::lng_month_day12; - } - Unexpected("Index in MonthDay."); -} - -QString DayString(const QDate &date) { - return tr::lng_month_day( - tr::now, - lt_month, - MonthDay(date.month())(tr::now), - lt_day, - QString::number(date.day())); -} - -QString TimeString(TimeId time) { - const auto parsed = base::unixtime::parse(time).time(); - return QString("%1:%2" - ).arg(parsed.hour() - ).arg(parsed.minute(), 2, 10, QLatin1Char('0')); -} - -int ProcessWheelEvent(not_null e) { - // Only a mouse wheel is accepted. - constexpr auto step = static_cast(QWheelEvent::DefaultDeltasPerStep); - const auto delta = e->angleDelta().y(); - const auto absDelta = std::abs(delta); - if (absDelta != step) { - return 0; - } - return (delta / absDelta); -} - -class TimePart final : public Ui::MaskedInputField { -public: - using MaskedInputField::MaskedInputField; - - void setMaxValue(int value); - void setWheelStep(int value); - - rpl::producer<> erasePrevious() const; - rpl::producer putNext() const; - -protected: - void keyPressEvent(QKeyEvent *e) override; - void wheelEvent(QWheelEvent *e) override; - - void correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) override; - -private: - int _maxValue = 0; - int _maxDigits = 0; - int _wheelStep = 0; - rpl::event_stream<> _erasePrevious; - rpl::event_stream _putNext; - -}; - -int Number(not_null field) { - const auto text = field->getLastText(); - auto ref = text.midRef(0); - while (!ref.isEmpty() && ref.at(0) == '0') { - ref = ref.mid(1); - } - return ref.toInt(); -} - -class TimeInput final : public Ui::RpWidget { -public: - TimeInput(QWidget *parent, const QString &value); - - bool setFocusFast(); - rpl::producer value() const; - rpl::producer<> submitRequests() const; - QString valueCurrent() const; - void showError(); - - int resizeGetHeight(int width) override; - -protected: - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - -private: - void setInnerFocus(); - void putNext(const object_ptr &field, QChar ch); - void erasePrevious(const object_ptr &field); - void finishInnerAnimating(); - void setErrorShown(bool error); - void setFocused(bool focused); - void startBorderAnimation(); - template - bool insideSeparator(QPoint position, const Widget &widget) const; - - int hour() const; - int minute() const; - - object_ptr _hour; - object_ptr> _separator1; - object_ptr _minute; - rpl::variable _value; - rpl::event_stream<> _submitRequests; - - style::cursor _cursor = style::cur_default; - Ui::Animations::Simple _a_borderShown; - int _borderAnimationStart = 0; - Ui::Animations::Simple _a_borderOpacity; - bool _borderVisible = false; - - Ui::Animations::Simple _a_error; - bool _error = false; - Ui::Animations::Simple _a_focused; - bool _focused = false; - -}; - -QTime ValidateTime(const QString &value) { - const auto match = QRegularExpression( - "^(\\d{1,2})\\:(\\d\\d)$").match(value); - if (!match.hasMatch()) { - return QTime(); - } - const auto readInt = [](const QString &value) { - auto ref = value.midRef(0); - while (!ref.isEmpty() && ref.at(0) == '0') { - ref = ref.mid(1); - } - return ref.toInt(); - }; - return QTime(readInt(match.captured(1)), readInt(match.captured(2))); -} - -QString GetHour(const QString &value) { - if (const auto time = ValidateTime(value); time.isValid()) { - return QString::number(time.hour()); - } - return QString(); -} - -QString GetMinute(const QString &value) { - if (const auto time = ValidateTime(value); time.isValid()) { - return QString("%1").arg(time.minute(), 2, 10, QChar('0')); - } - return QString(); -} - -void TimePart::setMaxValue(int value) { - _maxValue = value; - _maxDigits = 0; - while (value > 0) { - ++_maxDigits; - value /= 10; - } -} - -void TimePart::setWheelStep(int value) { - _wheelStep = value; -} - -rpl::producer<> TimePart::erasePrevious() const { - return _erasePrevious.events(); -} - -rpl::producer TimePart::putNext() const { - return _putNext.events(); -} - -void TimePart::keyPressEvent(QKeyEvent *e) { - const auto isBackspace = (e->key() == Qt::Key_Backspace); - const auto isBeginning = (cursorPosition() == 0); - if (isBackspace && isBeginning && !hasSelectedText()) { - _erasePrevious.fire({}); - } else { - MaskedInputField::keyPressEvent(e); - } -} - -void TimePart::wheelEvent(QWheelEvent *e) { - const auto direction = ProcessWheelEvent(e); - auto time = Number(this) + (direction * _wheelStep); - const auto max = _maxValue + 1; - if (time < 0) { - time += max; - } else if (time >= max) { - time -= max; - } - setText(QString::number(time)); -} - -void TimePart::correctValue( - const QString &was, - int wasCursor, - QString &now, - int &nowCursor) { - auto newText = QString(); - auto newCursor = -1; - const auto oldCursor = nowCursor; - const auto oldLength = now.size(); - auto accumulated = 0; - auto limit = 0; - for (; limit != oldLength; ++limit) { - if (now[limit].isDigit()) { - accumulated *= 10; - accumulated += (now[limit].unicode() - '0'); - if (accumulated > _maxValue || limit == _maxDigits) { - break; - } - } - } - for (auto i = 0; i != limit;) { - if (now[i].isDigit()) { - newText += now[i]; - } - if (++i == oldCursor) { - newCursor = newText.size(); - } - } - if (newCursor < 0) { - newCursor = newText.size(); - } - if (newText != now) { - now = newText; - setText(now); - startPlaceholderAnimation(); - } - if (newCursor != nowCursor) { - nowCursor = newCursor; - setCursorPosition(nowCursor); - } - if (accumulated > _maxValue - || (limit == _maxDigits && oldLength > _maxDigits)) { - if (oldCursor > limit) { - _putNext.fire('0' + (accumulated % 10)); - } else { - _putNext.fire(0); - } - } -} - -TimeInput::TimeInput(QWidget *parent, const QString &value) -: RpWidget(parent) -, _hour( - this, - st::scheduleTimeField, - rpl::never(), - GetHour(value)) -, _separator1( - this, - object_ptr( - this, - QString(":"), - st::scheduleTimeSeparator), - st::scheduleTimeSeparatorPadding) -, _minute( - this, - st::scheduleTimeField, - rpl::never(), - GetMinute(value)) -, _value(valueCurrent()) { - const auto focused = [=](const object_ptr &field) { - return [this, pointer = Ui::MakeWeak(field.data())]{ - _borderAnimationStart = pointer->borderAnimationStart() - + pointer->x() - - _hour->x(); - setFocused(true); - }; - }; - const auto blurred = [=] { - setFocused(false); - }; - const auto changed = [=] { - _value = valueCurrent(); - }; - connect(_hour, &Ui::MaskedInputField::focused, focused(_hour)); - connect(_minute, &Ui::MaskedInputField::focused, focused(_minute)); - connect(_hour, &Ui::MaskedInputField::blurred, blurred); - connect(_minute, &Ui::MaskedInputField::blurred, blurred); - connect(_hour, &Ui::MaskedInputField::changed, changed); - connect(_minute, &Ui::MaskedInputField::changed, changed); - _hour->setMaxValue(23); - _hour->setWheelStep(1); - _hour->putNext() | rpl::start_with_next([=](QChar ch) { - putNext(_minute, ch); - }, lifetime()); - _minute->setMaxValue(59); - _minute->setWheelStep(10); - _minute->erasePrevious() | rpl::start_with_next([=] { - erasePrevious(_hour); - }, lifetime()); - _separator1->setAttribute(Qt::WA_TransparentForMouseEvents); - setMouseTracking(true); - - _value.changes( - ) | rpl::start_with_next([=] { - setErrorShown(false); - }, lifetime()); - - const auto submitHour = [=] { - if (hour()) { - _minute->setFocus(); - } - }; - const auto submitMinute = [=] { - if (minute()) { - if (hour()) { - _submitRequests.fire({}); - } else { - _hour->setFocus(); - } - } - }; - connect( - _hour, - &Ui::MaskedInputField::submitted, - submitHour); - connect( - _minute, - &Ui::MaskedInputField::submitted, - submitMinute); -} - -void TimeInput::putNext(const object_ptr &field, QChar ch) { - field->setCursorPosition(0); - if (ch.unicode()) { - field->setText(ch + field->getLastText()); - field->setCursorPosition(1); - } - field->setFocus(); -} - -void TimeInput::erasePrevious(const object_ptr &field) { - const auto text = field->getLastText(); - if (!text.isEmpty()) { - field->setCursorPosition(text.size() - 1); - field->setText(text.mid(0, text.size() - 1)); - } - field->setFocus(); -} - -bool TimeInput::setFocusFast() { - if (hour()) { - _minute->setFocusFast(); - } else { - _hour->setFocusFast(); - } - return true; -} - -int TimeInput::hour() const { - return Number(_hour); -} - -int TimeInput::minute() const { - return Number(_minute); -} - -QString TimeInput::valueCurrent() const { - const auto result = QString("%1:%2" - ).arg(hour() - ).arg(minute(), 2, 10, QChar('0')); - return ValidateTime(result).isValid() ? result : QString(); -} - -rpl::producer TimeInput::value() const { - return _value.value(); -} - -rpl::producer<> TimeInput::submitRequests() const { - return _submitRequests.events(); -} - -void TimeInput::paintEvent(QPaintEvent *e) { - Painter p(this); - - const auto &_st = st::scheduleDateField; - const auto height = _st.heightMin; - if (_st.border) { - p.fillRect(0, height - _st.border, width(), _st.border, _st.borderFg); - } - auto errorDegree = _a_error.value(_error ? 1. : 0.); - auto focusedDegree = _a_focused.value(_focused ? 1. : 0.); - auto borderShownDegree = _a_borderShown.value(1.); - auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); - if (_st.borderActive && (borderOpacity > 0.)) { - auto borderStart = std::clamp(_borderAnimationStart, 0, width()); - auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); - auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); - if (borderTo > borderFrom) { - auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree); - p.setOpacity(borderOpacity); - p.fillRect(borderFrom, height - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg); - p.setOpacity(1); - } - } -} - -template -bool TimeInput::insideSeparator(QPoint position, const Widget &widget) const { - const auto x = position.x(); - const auto y = position.y(); - return (x >= widget->x() && x < widget->x() + widget->width()) - && (y >= _hour->y() && y < _hour->y() + _hour->height()); -} - -void TimeInput::mouseMoveEvent(QMouseEvent *e) { - const auto cursor = insideSeparator(e->pos(), _separator1) - ? style::cur_text - : style::cur_default; - if (_cursor != cursor) { - _cursor = cursor; - setCursor(_cursor); - } -} - -void TimeInput::mousePressEvent(QMouseEvent *e) { - const auto x = e->pos().x(); - const auto focus1 = [&] { - if (_hour->getLastText().size() > 1) { - _minute->setFocus(); - } else { - _hour->setFocus(); - } - }; - if (insideSeparator(e->pos(), _separator1)) { - focus1(); - _borderAnimationStart = x - _hour->x(); - } -} - -int TimeInput::resizeGetHeight(int width) { - const auto &_st = st::scheduleTimeField; - const auto &font = _st.placeholderFont; - const auto addToWidth = st::scheduleTimeSeparatorPadding.left(); - const auto hourWidth = _st.textMargins.left() - + _st.placeholderMargins.left() - + font->width(QString("23")) - + _st.placeholderMargins.right() - + _st.textMargins.right() - + addToWidth; - const auto minuteWidth = _st.textMargins.left() - + _st.placeholderMargins.left() - + font->width(QString("59")) - + _st.placeholderMargins.right() - + _st.textMargins.right() - + addToWidth; - const auto full = hourWidth - - addToWidth - + _separator1->width() - + minuteWidth - - addToWidth; - auto left = (width - full) / 2; - auto top = 0; - _hour->setGeometry(left, top, hourWidth, _hour->height()); - left += hourWidth - addToWidth; - _separator1->resizeToNaturalWidth(width); - _separator1->move(left, top); - left += _separator1->width(); - _minute->setGeometry(left, top, minuteWidth, _minute->height()); - return st::scheduleDateField.heightMin; -} - -void TimeInput::showError() { - setErrorShown(true); - if (!_focused) { - setInnerFocus(); - } -} - -void TimeInput::setInnerFocus() { - if (hour()) { - _minute->setFocus(); - } else { - _hour->setFocus(); - } -} - -void TimeInput::setErrorShown(bool error) { - if (_error != error) { - _error = error; - _a_error.start( - [=] { update(); }, - _error ? 0. : 1., - _error ? 1. : 0., - st::scheduleDateField.duration); - startBorderAnimation(); - } -} - -void TimeInput::setFocused(bool focused) { - if (_focused != focused) { - _focused = focused; - _a_focused.start( - [=] { update(); }, - _focused ? 0. : 1., - _focused ? 1. : 0., - st::scheduleDateField.duration); - startBorderAnimation(); - } -} - -void TimeInput::finishInnerAnimating() { - _hour->finishAnimating(); - _minute->finishAnimating(); - _a_borderOpacity.stop(); - _a_borderShown.stop(); - _a_error.stop(); -} - -void TimeInput::startBorderAnimation() { - auto borderVisible = (_error || _focused); - if (_borderVisible != borderVisible) { - _borderVisible = borderVisible; - const auto duration = st::scheduleDateField.duration; - if (_borderVisible) { - if (_a_borderOpacity.animating()) { - _a_borderOpacity.start([=] { update(); }, 0., 1., duration); - } else { - _a_borderShown.start([=] { update(); }, 0., 1., duration); - } - } else { - _a_borderOpacity.start([=] { update(); }, 1., 0., duration); - } - } -} - void FillSendUntilOnlineMenu( not_null button, Fn callback) { @@ -601,138 +57,6 @@ bool CanScheduleUntilOnline(not_null peer) { && (peer->asUser()->onlineTill > 0); } -ChooseDateTimeBoxDescriptor ChooseDateTimeBox( - not_null box, - rpl::producer title, - rpl::producer submit, - Fn done, - TimeId time) { - box->setTitle(std::move(title)); - box->setWidth(st::boxWideWidth); - - const auto date = Ui::CreateChild>( - box.get(), - base::unixtime::parse(time).date()); - const auto content = box->addRow( - object_ptr(box, st::scheduleHeight)); - const auto dayInput = Ui::CreateChild( - content, - st::scheduleDateField); - const auto timeInput = Ui::CreateChild( - content, - TimeString(time)); - const auto at = Ui::CreateChild( - content, - tr::lng_schedule_at(), - st::scheduleAtLabel); - - date->value( - ) | rpl::start_with_next([=](QDate date) { - dayInput->setText(DayString(date)); - timeInput->setFocusFast(); - }, dayInput->lifetime()); - - const auto minDate = QDate::currentDate(); - const auto maxDate = minDate.addYears(1).addDays(-1); - - const auto &dayViewport = dayInput->rawTextEdit()->viewport(); - base::install_event_filter(dayViewport, [=](not_null event) { - if (event->type() == QEvent::Wheel) { - const auto e = static_cast(event.get()); - const auto direction = ProcessWheelEvent(e); - if (!direction) { - return base::EventFilterResult::Continue; - } - const auto d = date->current().addDays(direction); - *date = std::clamp(d, minDate, maxDate); - return base::EventFilterResult::Cancel; - } - return base::EventFilterResult::Continue; - }); - - content->widthValue( - ) | rpl::start_with_next([=](int width) { - const auto paddings = width - - at->width() - - 2 * st::scheduleAtSkip - - st::scheduleDateWidth - - st::scheduleTimeWidth; - const auto left = paddings / 2; - dayInput->resizeToWidth(st::scheduleDateWidth); - dayInput->moveToLeft(left, st::scheduleDateTop, width); - at->moveToLeft( - left + st::scheduleDateWidth + st::scheduleAtSkip, - st::scheduleAtTop, - width); - timeInput->resizeToWidth(st::scheduleTimeWidth); - timeInput->moveToLeft( - width - left - st::scheduleTimeWidth, - st::scheduleDateTop, - width); - }, content->lifetime()); - - const auto calendar = - content->lifetime().make_state>(); - QObject::connect(dayInput, &Ui::InputField::focused, [=] { - if (*calendar) { - return; - } - const auto chosen = [=](QDate chosen) { - *date = chosen; - (*calendar)->closeBox(); - }; - const auto finalize = [=](not_null box) { - box->setMinDate(minDate); - box->setMaxDate(maxDate); - }; - *calendar = box->getDelegate()->show(Box( - date->current(), - date->current(), - crl::guard(box, chosen), - finalize)); - (*calendar)->boxClosing( - ) | rpl::start_with_next(crl::guard(timeInput, [=] { - timeInput->setFocusFast(); - }), (*calendar)->lifetime()); - }); - - const auto collect = [=] { - const auto timeValue = timeInput->valueCurrent().split(':'); - if (timeValue.size() != 2) { - timeInput->showError(); - return 0; - } - const auto time = QTime(timeValue[0].toInt(), timeValue[1].toInt()); - if (!time.isValid()) { - timeInput->showError(); - return 0; - } - const auto result = base::unixtime::serialize( - QDateTime(date->current(), time)); - if (result <= base::unixtime::now() + kMinimalSchedule) { - timeInput->showError(); - return 0; - } - return result; - }; - const auto save = [=] { - if (const auto result = collect()) { - done(result); - } - }; - timeInput->submitRequests( - ) | rpl::start_with_next( - save, - timeInput->lifetime()); - - auto result = ChooseDateTimeBoxDescriptor(); - box->setFocusCallback([=] { timeInput->setFocusFast(); }); - result.submit = box->addButton(std::move(submit), save); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); - - return result; -} - void ScheduleBox( not_null box, SendMenu::Type type, @@ -752,7 +76,7 @@ void ScheduleBox( box->closeBox(); copy(result); }; - auto descriptor = ChooseDateTimeBox( + auto descriptor = Ui::ChooseDateTimeBox( box, (type == SendMenu::Type::Reminder ? tr::lng_remind_title() diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.h b/Telegram/SourceFiles/history/view/history_view_schedule_box.h index 2ad25e4ee..30ee8bd49 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.h +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.h @@ -22,18 +22,6 @@ namespace HistoryView { [[nodiscard]] TimeId DefaultScheduleTime(); [[nodiscard]] bool CanScheduleUntilOnline(not_null peer); -struct ChooseDateTimeBoxDescriptor { - QPointer submit; - Fn collect; -}; - -ChooseDateTimeBoxDescriptor ChooseDateTimeBox( - not_null box, - rpl::producer title, - rpl::producer submit, - Fn done, - TimeId time); - void ScheduleBox( not_null box, SendMenu::Type type, diff --git a/Telegram/SourceFiles/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp similarity index 97% rename from Telegram/SourceFiles/boxes/calendar_box.cpp rename to Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 6019df5c4..8c4095f92 100644 --- a/Telegram/SourceFiles/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -5,15 +5,16 @@ 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 "boxes/calendar_box.h" +#include "ui/boxes/calendar_box.h" #include "ui/widgets/buttons.h" -#include "lang/lang_keys.h" #include "ui/effects/ripple_animation.h" #include "ui/ui_utility.h" +#include "lang/lang_keys.h" #include "styles/style_boxes.h" #include "styles/style_dialogs.h" +namespace Ui { namespace { constexpr auto kDaysInWeek = 7; @@ -229,7 +230,7 @@ private: const style::CalendarSizes &_st; not_null _context; - std::map> _ripples; + std::map> _ripples; Fn _dateChosenCallback; @@ -257,7 +258,7 @@ void CalendarBox::Inner::monthChanged(QDate month) { _ripples.clear(); resizeToCurrent(); update(); - Ui::SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); + SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); } void CalendarBox::Inner::resizeToCurrent() { @@ -389,9 +390,9 @@ void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { auto cell = QRect(rowsLeft() + col * _st.cellSize.width(), rowsTop() + row * _st.cellSize.height(), _st.cellSize.width(), _st.cellSize.height()); auto it = _ripples.find(_selected); if (it == _ripples.cend()) { - auto mask = Ui::RippleAnimation::ellipseMask(QSize(_st.cellInner, _st.cellInner)); + auto mask = RippleAnimation::ellipseMask(QSize(_st.cellInner, _st.cellInner)); auto update = [this, cell] { rtlupdate(cell); }; - it = _ripples.emplace(_selected, std::make_unique(st::defaultRippleAnimation, std::move(mask), std::move(update))).first; + it = _ripples.emplace(_selected, std::make_unique(st::defaultRippleAnimation, std::move(mask), std::move(update))).first; } auto ripplePosition = QPoint(cell.x() + (_st.cellSize.width() - _st.cellInner) / 2, cell.y() + (_st.cellSize.height() - _st.cellInner) / 2); it->second->add(e->pos() - ripplePosition); @@ -611,3 +612,5 @@ void CalendarBox::wheelEvent(QWheelEvent *e) { } CalendarBox::~CalendarBox() = default; + +} // namespace Ui diff --git a/Telegram/SourceFiles/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h similarity index 88% rename from Telegram/SourceFiles/boxes/calendar_box.h rename to Telegram/SourceFiles/ui/boxes/calendar_box.h index 391f25bed..b5e398b6b 100644 --- a/Telegram/SourceFiles/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -7,17 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "abstract_box.h" +#include "ui/layers/box_content.h" +#include "base/observer.h" namespace style { struct CalendarSizes; } // namespace style namespace Ui { -class IconButton; -} // namespace Ui -class CalendarBox : public Ui::BoxContent, private base::Subscriber { +class IconButton; + +class CalendarBox : public BoxContent, private base::Subscriber { public: CalendarBox( QWidget*, @@ -67,10 +68,12 @@ private: class Title; object_ptr _title; - object_ptr<Ui::IconButton> _previous; - object_ptr<Ui::IconButton> _next; + object_ptr<IconButton> _previous; + object_ptr<IconButton> _next; Fn<void(QDate date)> _callback; FnMut<void(not_null<CalendarBox*>)> _finalize; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp new file mode 100644 index 000000000..a9d413e97 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp @@ -0,0 +1,702 @@ +/* +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 "ui/boxes/choose_date_time.h" + +#include "base/unixtime.h" +#include "base/event_filter.h" +#include "ui/boxes/calendar_box.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" +#include "lang/lang_keys.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" + +#include <QtCore/QRegularExpression> + +namespace Ui { +namespace { + +constexpr auto kMinimalSchedule = TimeId(10); + +tr::phrase<> MonthDay(int index) { + switch (index) { + case 1: return tr::lng_month_day1; + case 2: return tr::lng_month_day2; + case 3: return tr::lng_month_day3; + case 4: return tr::lng_month_day4; + case 5: return tr::lng_month_day5; + case 6: return tr::lng_month_day6; + case 7: return tr::lng_month_day7; + case 8: return tr::lng_month_day8; + case 9: return tr::lng_month_day9; + case 10: return tr::lng_month_day10; + case 11: return tr::lng_month_day11; + case 12: return tr::lng_month_day12; + } + Unexpected("Index in MonthDay."); +} + +QString DayString(const QDate &date) { + return tr::lng_month_day( + tr::now, + lt_month, + MonthDay(date.month())(tr::now), + lt_day, + QString::number(date.day())); +} + +QString TimeString(TimeId time) { + const auto parsed = base::unixtime::parse(time).time(); + return QString("%1:%2" + ).arg(parsed.hour() + ).arg(parsed.minute(), 2, 10, QLatin1Char('0')); +} + +int ProcessWheelEvent(not_null<QWheelEvent*> e) { + // Only a mouse wheel is accepted. + constexpr auto step = static_cast<int>(QWheelEvent::DefaultDeltasPerStep); + const auto delta = e->angleDelta().y(); + const auto absDelta = std::abs(delta); + if (absDelta != step) { + return 0; + } + return (delta / absDelta); +} + +class TimePart final : public MaskedInputField { +public: + using MaskedInputField::MaskedInputField; + + void setMaxValue(int value); + void setWheelStep(int value); + + rpl::producer<> erasePrevious() const; + rpl::producer<QChar> putNext() const; + +protected: + void keyPressEvent(QKeyEvent *e) override; + void wheelEvent(QWheelEvent *e) override; + + void correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) override; + +private: + int _maxValue = 0; + int _maxDigits = 0; + int _wheelStep = 0; + rpl::event_stream<> _erasePrevious; + rpl::event_stream<QChar> _putNext; + +}; + +int Number(not_null<TimePart*> field) { + const auto text = field->getLastText(); + auto ref = text.midRef(0); + while (!ref.isEmpty() && ref.at(0) == '0') { + ref = ref.mid(1); + } + return ref.toInt(); +} + +class TimeInput final : public RpWidget { +public: + TimeInput(QWidget *parent, const QString &value); + + bool setFocusFast(); + rpl::producer<QString> value() const; + rpl::producer<> submitRequests() const; + QString valueCurrent() const; + void showError(); + + int resizeGetHeight(int width) override; + +protected: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + +private: + void setInnerFocus(); + void putNext(const object_ptr<TimePart> &field, QChar ch); + void erasePrevious(const object_ptr<TimePart> &field); + void finishInnerAnimating(); + void setErrorShown(bool error); + void setFocused(bool focused); + void startBorderAnimation(); + template <typename Widget> + bool insideSeparator(QPoint position, const Widget &widget) const; + + int hour() const; + int minute() const; + + object_ptr<TimePart> _hour; + object_ptr<PaddingWrap<FlatLabel>> _separator1; + object_ptr<TimePart> _minute; + rpl::variable<QString> _value; + rpl::event_stream<> _submitRequests; + + style::cursor _cursor = style::cur_default; + Animations::Simple _a_borderShown; + int _borderAnimationStart = 0; + Animations::Simple _a_borderOpacity; + bool _borderVisible = false; + + Animations::Simple _a_error; + bool _error = false; + Animations::Simple _a_focused; + bool _focused = false; + +}; + +QTime ValidateTime(const QString &value) { + const auto match = QRegularExpression( + "^(\\d{1,2})\\:(\\d\\d)$").match(value); + if (!match.hasMatch()) { + return QTime(); + } + const auto readInt = [](const QString &value) { + auto ref = value.midRef(0); + while (!ref.isEmpty() && ref.at(0) == '0') { + ref = ref.mid(1); + } + return ref.toInt(); + }; + return QTime(readInt(match.captured(1)), readInt(match.captured(2))); +} + +QString GetHour(const QString &value) { + if (const auto time = ValidateTime(value); time.isValid()) { + return QString::number(time.hour()); + } + return QString(); +} + +QString GetMinute(const QString &value) { + if (const auto time = ValidateTime(value); time.isValid()) { + return QString("%1").arg(time.minute(), 2, 10, QChar('0')); + } + return QString(); +} + +void TimePart::setMaxValue(int value) { + _maxValue = value; + _maxDigits = 0; + while (value > 0) { + ++_maxDigits; + value /= 10; + } +} + +void TimePart::setWheelStep(int value) { + _wheelStep = value; +} + +rpl::producer<> TimePart::erasePrevious() const { + return _erasePrevious.events(); +} + +rpl::producer<QChar> TimePart::putNext() const { + return _putNext.events(); +} + +void TimePart::keyPressEvent(QKeyEvent *e) { + const auto isBackspace = (e->key() == Qt::Key_Backspace); + const auto isBeginning = (cursorPosition() == 0); + if (isBackspace && isBeginning && !hasSelectedText()) { + _erasePrevious.fire({}); + } else { + MaskedInputField::keyPressEvent(e); + } +} + +void TimePart::wheelEvent(QWheelEvent *e) { + const auto direction = ProcessWheelEvent(e); + auto time = Number(this) + (direction * _wheelStep); + const auto max = _maxValue + 1; + if (time < 0) { + time += max; + } else if (time >= max) { + time -= max; + } + setText(QString::number(time)); +} + +void TimePart::correctValue( + const QString &was, + int wasCursor, + QString &now, + int &nowCursor) { + auto newText = QString(); + auto newCursor = -1; + const auto oldCursor = nowCursor; + const auto oldLength = now.size(); + auto accumulated = 0; + auto limit = 0; + for (; limit != oldLength; ++limit) { + if (now[limit].isDigit()) { + accumulated *= 10; + accumulated += (now[limit].unicode() - '0'); + if (accumulated > _maxValue || limit == _maxDigits) { + break; + } + } + } + for (auto i = 0; i != limit;) { + if (now[i].isDigit()) { + newText += now[i]; + } + if (++i == oldCursor) { + newCursor = newText.size(); + } + } + if (newCursor < 0) { + newCursor = newText.size(); + } + if (newText != now) { + now = newText; + setText(now); + startPlaceholderAnimation(); + } + if (newCursor != nowCursor) { + nowCursor = newCursor; + setCursorPosition(nowCursor); + } + if (accumulated > _maxValue + || (limit == _maxDigits && oldLength > _maxDigits)) { + if (oldCursor > limit) { + _putNext.fire('0' + (accumulated % 10)); + } else { + _putNext.fire(0); + } + } +} + +TimeInput::TimeInput(QWidget *parent, const QString &value) +: RpWidget(parent) +, _hour( + this, + st::scheduleTimeField, + rpl::never<QString>(), + GetHour(value)) +, _separator1( + this, + object_ptr<FlatLabel>( + this, + QString(":"), + st::scheduleTimeSeparator), + st::scheduleTimeSeparatorPadding) +, _minute( + this, + st::scheduleTimeField, + rpl::never<QString>(), + GetMinute(value)) +, _value(valueCurrent()) { + const auto focused = [=](const object_ptr<TimePart> &field) { + return [this, pointer = MakeWeak(field.data())]{ + _borderAnimationStart = pointer->borderAnimationStart() + + pointer->x() + - _hour->x(); + setFocused(true); + }; + }; + const auto blurred = [=] { + setFocused(false); + }; + const auto changed = [=] { + _value = valueCurrent(); + }; + connect(_hour, &MaskedInputField::focused, focused(_hour)); + connect(_minute, &MaskedInputField::focused, focused(_minute)); + connect(_hour, &MaskedInputField::blurred, blurred); + connect(_minute, &MaskedInputField::blurred, blurred); + connect(_hour, &MaskedInputField::changed, changed); + connect(_minute, &MaskedInputField::changed, changed); + _hour->setMaxValue(23); + _hour->setWheelStep(1); + _hour->putNext() | rpl::start_with_next([=](QChar ch) { + putNext(_minute, ch); + }, lifetime()); + _minute->setMaxValue(59); + _minute->setWheelStep(10); + _minute->erasePrevious() | rpl::start_with_next([=] { + erasePrevious(_hour); + }, lifetime()); + _separator1->setAttribute(Qt::WA_TransparentForMouseEvents); + setMouseTracking(true); + + _value.changes( + ) | rpl::start_with_next([=] { + setErrorShown(false); + }, lifetime()); + + const auto submitHour = [=] { + if (hour()) { + _minute->setFocus(); + } + }; + const auto submitMinute = [=] { + if (minute()) { + if (hour()) { + _submitRequests.fire({}); + } else { + _hour->setFocus(); + } + } + }; + connect( + _hour, + &MaskedInputField::submitted, + submitHour); + connect( + _minute, + &MaskedInputField::submitted, + submitMinute); +} + +void TimeInput::putNext(const object_ptr<TimePart> &field, QChar ch) { + field->setCursorPosition(0); + if (ch.unicode()) { + field->setText(ch + field->getLastText()); + field->setCursorPosition(1); + } + field->setFocus(); +} + +void TimeInput::erasePrevious(const object_ptr<TimePart> &field) { + const auto text = field->getLastText(); + if (!text.isEmpty()) { + field->setCursorPosition(text.size() - 1); + field->setText(text.mid(0, text.size() - 1)); + } + field->setFocus(); +} + +bool TimeInput::setFocusFast() { + if (hour()) { + _minute->setFocusFast(); + } else { + _hour->setFocusFast(); + } + return true; +} + +int TimeInput::hour() const { + return Number(_hour); +} + +int TimeInput::minute() const { + return Number(_minute); +} + +QString TimeInput::valueCurrent() const { + const auto result = QString("%1:%2" + ).arg(hour() + ).arg(minute(), 2, 10, QChar('0')); + return ValidateTime(result).isValid() ? result : QString(); +} + +rpl::producer<QString> TimeInput::value() const { + return _value.value(); +} + +rpl::producer<> TimeInput::submitRequests() const { + return _submitRequests.events(); +} + +void TimeInput::paintEvent(QPaintEvent *e) { + Painter p(this); + + const auto &_st = st::scheduleDateField; + const auto height = _st.heightMin; + if (_st.border) { + p.fillRect(0, height - _st.border, width(), _st.border, _st.borderFg); + } + auto errorDegree = _a_error.value(_error ? 1. : 0.); + auto focusedDegree = _a_focused.value(_focused ? 1. : 0.); + auto borderShownDegree = _a_borderShown.value(1.); + auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.); + if (_st.borderActive && (borderOpacity > 0.)) { + auto borderStart = std::clamp(_borderAnimationStart, 0, width()); + auto borderFrom = qRound(borderStart * (1. - borderShownDegree)); + auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree); + if (borderTo > borderFrom) { + auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree); + p.setOpacity(borderOpacity); + p.fillRect(borderFrom, height - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg); + p.setOpacity(1); + } + } +} + +template <typename Widget> +bool TimeInput::insideSeparator(QPoint position, const Widget &widget) const { + const auto x = position.x(); + const auto y = position.y(); + return (x >= widget->x() && x < widget->x() + widget->width()) + && (y >= _hour->y() && y < _hour->y() + _hour->height()); +} + +void TimeInput::mouseMoveEvent(QMouseEvent *e) { + const auto cursor = insideSeparator(e->pos(), _separator1) + ? style::cur_text + : style::cur_default; + if (_cursor != cursor) { + _cursor = cursor; + setCursor(_cursor); + } +} + +void TimeInput::mousePressEvent(QMouseEvent *e) { + const auto x = e->pos().x(); + const auto focus1 = [&] { + if (_hour->getLastText().size() > 1) { + _minute->setFocus(); + } else { + _hour->setFocus(); + } + }; + if (insideSeparator(e->pos(), _separator1)) { + focus1(); + _borderAnimationStart = x - _hour->x(); + } +} + +int TimeInput::resizeGetHeight(int width) { + const auto &_st = st::scheduleTimeField; + const auto &font = _st.placeholderFont; + const auto addToWidth = st::scheduleTimeSeparatorPadding.left(); + const auto hourWidth = _st.textMargins.left() + + _st.placeholderMargins.left() + + font->width(QString("23")) + + _st.placeholderMargins.right() + + _st.textMargins.right() + + addToWidth; + const auto minuteWidth = _st.textMargins.left() + + _st.placeholderMargins.left() + + font->width(QString("59")) + + _st.placeholderMargins.right() + + _st.textMargins.right() + + addToWidth; + const auto full = hourWidth + - addToWidth + + _separator1->width() + + minuteWidth + - addToWidth; + auto left = (width - full) / 2; + auto top = 0; + _hour->setGeometry(left, top, hourWidth, _hour->height()); + left += hourWidth - addToWidth; + _separator1->resizeToNaturalWidth(width); + _separator1->move(left, top); + left += _separator1->width(); + _minute->setGeometry(left, top, minuteWidth, _minute->height()); + return st::scheduleDateField.heightMin; +} + +void TimeInput::showError() { + setErrorShown(true); + if (!_focused) { + setInnerFocus(); + } +} + +void TimeInput::setInnerFocus() { + if (hour()) { + _minute->setFocus(); + } else { + _hour->setFocus(); + } +} + +void TimeInput::setErrorShown(bool error) { + if (_error != error) { + _error = error; + _a_error.start( + [=] { update(); }, + _error ? 0. : 1., + _error ? 1. : 0., + st::scheduleDateField.duration); + startBorderAnimation(); + } +} + +void TimeInput::setFocused(bool focused) { + if (_focused != focused) { + _focused = focused; + _a_focused.start( + [=] { update(); }, + _focused ? 0. : 1., + _focused ? 1. : 0., + st::scheduleDateField.duration); + startBorderAnimation(); + } +} + +void TimeInput::finishInnerAnimating() { + _hour->finishAnimating(); + _minute->finishAnimating(); + _a_borderOpacity.stop(); + _a_borderShown.stop(); + _a_error.stop(); +} + +void TimeInput::startBorderAnimation() { + auto borderVisible = (_error || _focused); + if (_borderVisible != borderVisible) { + _borderVisible = borderVisible; + const auto duration = st::scheduleDateField.duration; + if (_borderVisible) { + if (_a_borderOpacity.animating()) { + _a_borderOpacity.start([=] { update(); }, 0., 1., duration); + } else { + _a_borderShown.start([=] { update(); }, 0., 1., duration); + } + } else { + _a_borderOpacity.start([=] { update(); }, 1., 0., duration); + } + } +} + +} // namespace + +ChooseDateTimeBoxDescriptor ChooseDateTimeBox( + not_null<GenericBox*> box, + rpl::producer<QString> title, + rpl::producer<QString> submit, + Fn<void(TimeId)> done, + TimeId time) { + box->setTitle(std::move(title)); + box->setWidth(st::boxWideWidth); + + const auto date = CreateChild<rpl::variable<QDate>>( + box.get(), + base::unixtime::parse(time).date()); + const auto content = box->addRow( + object_ptr<FixedHeightWidget>(box, st::scheduleHeight)); + const auto dayInput = CreateChild<InputField>( + content, + st::scheduleDateField); + const auto timeInput = CreateChild<TimeInput>( + content, + TimeString(time)); + const auto at = CreateChild<FlatLabel>( + content, + tr::lng_schedule_at(), + st::scheduleAtLabel); + + date->value( + ) | rpl::start_with_next([=](QDate date) { + dayInput->setText(DayString(date)); + timeInput->setFocusFast(); + }, dayInput->lifetime()); + + const auto minDate = QDate::currentDate(); + const auto maxDate = minDate.addYears(1).addDays(-1); + + const auto &dayViewport = dayInput->rawTextEdit()->viewport(); + base::install_event_filter(dayViewport, [=](not_null<QEvent*> event) { + if (event->type() == QEvent::Wheel) { + const auto e = static_cast<QWheelEvent*>(event.get()); + const auto direction = ProcessWheelEvent(e); + if (!direction) { + return base::EventFilterResult::Continue; + } + const auto d = date->current().addDays(direction); + *date = std::clamp(d, minDate, maxDate); + return base::EventFilterResult::Cancel; + } + return base::EventFilterResult::Continue; + }); + + content->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto paddings = width + - at->width() + - 2 * st::scheduleAtSkip + - st::scheduleDateWidth + - st::scheduleTimeWidth; + const auto left = paddings / 2; + dayInput->resizeToWidth(st::scheduleDateWidth); + dayInput->moveToLeft(left, st::scheduleDateTop, width); + at->moveToLeft( + left + st::scheduleDateWidth + st::scheduleAtSkip, + st::scheduleAtTop, + width); + timeInput->resizeToWidth(st::scheduleTimeWidth); + timeInput->moveToLeft( + width - left - st::scheduleTimeWidth, + st::scheduleDateTop, + width); + }, content->lifetime()); + + const auto calendar = + content->lifetime().make_state<QPointer<CalendarBox>>(); + QObject::connect(dayInput, &InputField::focused, [=] { + if (*calendar) { + return; + } + const auto chosen = [=](QDate chosen) { + *date = chosen; + (*calendar)->closeBox(); + }; + const auto finalize = [=](not_null<CalendarBox*> box) { + box->setMinDate(minDate); + box->setMaxDate(maxDate); + }; + *calendar = box->getDelegate()->show(Box<CalendarBox>( + date->current(), + date->current(), + crl::guard(box, chosen), + finalize)); + (*calendar)->boxClosing( + ) | rpl::start_with_next(crl::guard(timeInput, [=] { + timeInput->setFocusFast(); + }), (*calendar)->lifetime()); + }); + + const auto collect = [=] { + const auto timeValue = timeInput->valueCurrent().split(':'); + if (timeValue.size() != 2) { + timeInput->showError(); + return 0; + } + const auto time = QTime(timeValue[0].toInt(), timeValue[1].toInt()); + if (!time.isValid()) { + timeInput->showError(); + return 0; + } + const auto result = base::unixtime::serialize( + QDateTime(date->current(), time)); + if (result <= base::unixtime::now() + kMinimalSchedule) { + timeInput->showError(); + return 0; + } + return result; + }; + const auto save = [=] { + if (const auto result = collect()) { + done(result); + } + }; + timeInput->submitRequests( + ) | rpl::start_with_next( + save, + timeInput->lifetime()); + + auto result = ChooseDateTimeBoxDescriptor(); + box->setFocusCallback([=] { timeInput->setFocusFast(); }); + result.submit = box->addButton(std::move(submit), save); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + + return result; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.h b/Telegram/SourceFiles/ui/boxes/choose_date_time.h new file mode 100644 index 000000000..87b42c28e --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.h @@ -0,0 +1,28 @@ +/* +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 "ui/layers/generic_box.h" + +namespace Ui { + +class RoundButton; + +struct ChooseDateTimeBoxDescriptor { + QPointer<RoundButton> submit; + Fn<TimeId()> collect; +}; + +ChooseDateTimeBoxDescriptor ChooseDateTimeBox( + not_null<GenericBox*> box, + rpl::producer<QString> title, + rpl::producer<QString> submit, + Fn<void(TimeId)> done, + TimeId time); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp b/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp new file mode 100644 index 000000000..3ffa6417a --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp @@ -0,0 +1,290 @@ +/* +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 "ui/boxes/edit_invite_link.h" + +#include "lang/lang_keys.h" +#include "ui/boxes/choose_date_time.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/input_fields.h" +#include "ui/widgets/checkbox.h" +#include "base/unixtime.h" +#include "styles/style_settings.h" +#include "styles/style_layers.h" +#include "styles/style_info.h" + +namespace Ui { +namespace { + +constexpr auto kMaxLimit = std::numeric_limits<int>::max(); +constexpr auto kHour = 3600; +constexpr auto kDay = 86400; + +[[nodiscard]] QString FormatExpireDate(TimeId date) { + if (date > 0) { + return langDateTime(base::unixtime::parse(date)); + } else if (-date < kDay) { + return tr::lng_group_call_duration_hours( + tr::now, + lt_count, + (-date / kHour)); + } else if (-date < 7 * kDay) { + return tr::lng_group_call_duration_days( + tr::now, + lt_count, + (-date / kDay)); + } else { + return tr::lng_local_storage_limit_weeks( + tr::now, + lt_count, + (-date / (7 * kDay))); + } +} + +} // namespace + +void EditInviteLinkBox( + not_null<GenericBox*> box, + const InviteLinkFields &data, + Fn<void(InviteLinkFields)> done) { + const auto link = data.link; + box->setTitle(link.isEmpty() + ? tr::lng_group_invite_new_title() + : tr::lng_group_invite_edit_title()); + + const auto container = box->verticalLayout(); + const auto addTitle = [&](rpl::producer<QString> text) { + container->add( + object_ptr<FlatLabel>( + container, + std::move(text), + st::settingsSubsectionTitle), + st::settingsSubsectionTitlePadding); + }; + const auto addDivider = [&]( + rpl::producer<QString> text, + style::margins margins = style::margins()) { + container->add( + object_ptr<DividerLabel>( + container, + object_ptr<FlatLabel>( + container, + std::move(text), + st::boxDividerLabel), + st::settingsDividerLabelPadding), + margins); + }; + + addTitle(tr::lng_group_invite_expire_title()); + const auto expiresWrap = container->add( + object_ptr<VerticalLayout>(container), + style::margins(0, 0, 0, st::settingsSectionSkip)); + addDivider( + tr::lng_group_invite_expire_about(), + style::margins(0, 0, 0, st::settingsSectionSkip)); + + addTitle(tr::lng_group_invite_usage_title()); + const auto usagesWrap = container->add( + object_ptr<VerticalLayout>(container), + style::margins(0, 0, 0, st::settingsSectionSkip)); + addDivider(tr::lng_group_invite_usage_about()); + + static const auto addButton = []( + not_null<VerticalLayout*> container, + const std::shared_ptr<RadiobuttonGroup> &group, + int value, + const QString &text) { + return container->add( + object_ptr<Radiobutton>( + container, + group, + value, + text), + st::inviteLinkLimitMargin); + }; + + const auto now = base::unixtime::now(); + const auto expire = data.expireDate ? data.expireDate : kMaxLimit; + const auto expireGroup = std::make_shared<RadiobuttonGroup>(expire); + const auto usage = data.usageLimit ? data.usageLimit : kMaxLimit; + const auto usageGroup = std::make_shared<RadiobuttonGroup>(usage); + + using Buttons = base::flat_map<int, base::unique_qptr<Radiobutton>>; + struct State { + Buttons expireButtons; + Buttons usageButtons; + int expireValue = 0; + int usageValue = 0; + }; + const auto state = container->lifetime().make_state<State>(State{ + .expireValue = expire, + .usageValue = usage + }); + const auto regenerate = [=] { + expireGroup->setValue(state->expireValue); + usageGroup->setValue(state->usageValue); + + auto expires = std::vector{ kMaxLimit, -kHour, -kDay, -kDay * 7, 0 }; + auto usages = std::vector{ kMaxLimit, 1, 10, 100, 0 }; + auto defaults = State(); + for (auto i = begin(expires); i != end(expires); ++i) { + if (*i == state->expireValue) { + break; + } else if (*i == kMaxLimit) { + continue; + } else if (!*i || (now - *i >= state->expireValue)) { + expires.insert(i, state->expireValue); + break; + } + } + for (auto i = begin(usages); i != end(usages); ++i) { + if (*i == state->usageValue) { + break; + } else if (*i == kMaxLimit) { + continue; + } else if (!*i || *i > state->usageValue) { + usages.insert(i, state->usageValue); + break; + } + } + state->expireButtons.clear(); + state->usageButtons.clear(); + for (const auto limit : expires) { + const auto text = (limit == kMaxLimit) + ? tr::lng_group_invite_expire_never(tr::now) + : !limit + ? tr::lng_group_invite_expire_custom(tr::now) + : FormatExpireDate(limit); + state->expireButtons.emplace( + limit, + addButton(expiresWrap, expireGroup, limit, text)); + } + for (const auto limit : usages) { + const auto text = (limit == kMaxLimit) + ? tr::lng_group_invite_usage_any(tr::now) + : !limit + ? tr::lng_group_invite_usage_custom(tr::now) + : QString("%L1").arg(limit); + state->usageButtons.emplace( + limit, + addButton(usagesWrap, usageGroup, limit, text)); + } + }; + + const auto guard = MakeWeak(box); + expireGroup->setChangedCallback([=](int value) { + if (value) { + state->expireValue = value; + return; + } + expireGroup->setValue(state->expireValue); + box->getDelegate()->show(Box([=](not_null<GenericBox*> box) { + const auto save = [=](TimeId result) { + if (!result) { + return; + } + if (guard) { + state->expireValue = result; + regenerate(); + } + box->closeBox(); + }; + const auto now = base::unixtime::now(); + const auto time = (state->expireValue == kMaxLimit) + ? (now + kDay) + : (state->expireValue > now) + ? state->expireValue + : (state->expireValue < 0) + ? (now - state->expireValue) + : (now + kDay); + ChooseDateTimeBox( + box, + tr::lng_group_invite_expire_after(), + tr::lng_settings_save(), + save, + time); + })); + }); + usageGroup->setChangedCallback([=](int value) { + if (value) { + state->usageValue = value; + return; + } + usageGroup->setValue(state->usageValue); + box->getDelegate()->show(Box([=](not_null<GenericBox*> box) { + const auto height = st::boxPadding.bottom() + + st::defaultInputField.heightMin + + st::boxPadding.bottom(); + box->setTitle(tr::lng_group_invite_expire_after()); + const auto wrap = box->addRow(object_ptr<FixedHeightWidget>( + box, + height)); + const auto input = CreateChild<NumberInput>( + wrap, + st::defaultInputField, + tr::lng_group_invite_custom_limit(), + (state->usageValue == kMaxLimit + ? QString() + : QString::number(state->usageValue)), + 200'000); + wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + input->resize(width, input->height()); + input->moveToLeft(0, st::boxPadding.bottom()); + }, input->lifetime()); + box->setFocusCallback([=] { + input->setFocusFast(); + }); + + const auto save = [=] { + const auto value = input->getLastText().toInt(); + if (value <= 0) { + input->showError(); + return; + } + if (guard) { + state->usageValue = value; + regenerate(); + } + box->closeBox(); + }; + QObject::connect(input, &NumberInput::submitted, save); + box->addButton(tr::lng_settings_save(), save); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + }); + + regenerate(); + + const auto &saveLabel = link.isEmpty() + ? tr::lng_formatting_link_create + : tr::lng_settings_save; + box->addButton(saveLabel(), [=] { + const auto expireDate = (state->expireValue == kMaxLimit) + ? 0 + : (state->expireValue < 0) + ? (base::unixtime::now() - state->expireValue) + : state->expireValue; + const auto usageLimit = (state->usageValue == kMaxLimit) + ? 0 + : state->usageValue; + done(InviteLinkFields{ + .link = link, + .expireDate = expireDate, + .usageLimit = usageLimit + }); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +void CreateInviteLinkBox( + not_null<GenericBox*> box, + Fn<void(InviteLinkFields)> done) { + EditInviteLinkBox(box, InviteLinkFields(), std::move(done)); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/edit_invite_link.h b/Telegram/SourceFiles/ui/boxes/edit_invite_link.h new file mode 100644 index 000000000..82b3f1754 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/edit_invite_link.h @@ -0,0 +1,29 @@ +/* +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 "ui/layers/generic_box.h" + +namespace Ui { + +struct InviteLinkFields { + QString link; + TimeId expireDate = 0; + int usageLimit = 0; +}; + +void EditInviteLinkBox( + not_null<Ui::GenericBox*> box, + const InviteLinkFields &data, + Fn<void(InviteLinkFields)> done); + +void CreateInviteLinkBox( + not_null<Ui::GenericBox*> box, + Fn<void(InviteLinkFields)> done); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index dab0e9ba8..e112415b2 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -833,35 +833,6 @@ largeEmojiOutline: 1px; largeEmojiPadding: margins(0px, 0px, 0px, 0px); largeEmojiSkip: 4px; -scheduleHeight: 95px; -scheduleDateTop: 38px; -scheduleDateField: InputField(defaultInputField) { - textMargins: margins(2px, 0px, 2px, 0px); - placeholderScale: 0.; - heightMin: 30px; - textAlign: align(top); - font: font(14px); -} -scheduleTimeField: InputField(scheduleDateField) { - border: 0px; - borderActive: 0px; - heightMin: 28px; - placeholderFont: font(14px); - placeholderFgActive: placeholderFgActive; -} -scheduleDateWidth: 136px; -scheduleTimeWidth: 72px; -scheduleAtSkip: 24px; -scheduleAtTop: 42px; -scheduleAtLabel: FlatLabel(defaultFlatLabel) { -} -scheduleTimeSeparator: FlatLabel(defaultFlatLabel) { - style: TextStyle(defaultTextStyle) { - font: font(14px); - } -} -scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px); - youtubeIcon: icon { { "media_youtube_play_bg", youtubePlayIconBg }, { "media_youtube_play", youtubePlayIconFg, point(24px, 12px) }, diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 148db3026..c0aef5a3f 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -40,7 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/toasts/common_toasts.h" #include "calls/calls_instance.h" // Core::App().calls().inCall(). -#include "boxes/calendar_box.h" +#include "ui/boxes/calendar_box.h" #include "boxes/confirm_box.h" #include "mainwidget.h" #include "mainwindow.h" @@ -1051,7 +1051,7 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { auto callback = [=](const QDate &date) { session().api().jumpToDate(chat, date); }; - auto box = Box<CalendarBox>( + auto box = Box<Ui::CalendarBox>( month, highlighted, std::move(callback)); diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index fd12d7f3a..0c9020a3a 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -64,6 +64,12 @@ PRIVATE platform/mac/file_bookmark_mac.mm platform/platform_file_bookmark.h + ui/boxes/calendar_box.cpp + ui/boxes/calendar_box.h + ui/boxes/choose_date_time.cpp + ui/boxes/choose_date_time.h + ui/boxes/edit_invite_link.cpp + ui/boxes/edit_invite_link.h ui/chat/attach/attach_album_thumbnail.cpp ui/chat/attach/attach_album_thumbnail.h ui/chat/attach/attach_album_preview.cpp From 144bad6c74b61b2f6154f1b4f0bf9b4a1333047c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 19 Jan 2021 12:41:22 +0400 Subject: [PATCH 152/396] Update link rows in Manage Invite Links. --- Telegram/SourceFiles/api/api_invite_links.cpp | 20 ++- Telegram/SourceFiles/api/api_invite_links.h | 12 +- .../boxes/peers/edit_peer_invite_links.cpp | 139 ++++++++++++++++-- 3 files changed, 159 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 34ad40cf9..a288d10a4 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -123,10 +123,16 @@ auto InviteLinks::prepend( auto &links = i->second; const auto permanent = lookupPermanent(links); if (link.permanent) { + auto update = Update{ .peer = peer }; if (permanent) { + update.was = permanent->link; permanent->revoked = true; } editPermanentLink(peer, link.link); + if (permanent) { + update.now = *permanent; + _updates.fire(std::move(update)); + } } ++links.count; if (permanent && !link.permanent) { @@ -135,6 +141,7 @@ auto InviteLinks::prepend( links.links.insert(begin(links.links), link); } notify(peer); + _updates.fire(Update{ .peer = peer, .now = link }); return link; } @@ -205,6 +212,11 @@ void InviteLinks::performEdit( for (const auto &callback : *callbacks) { callback(link); } + _updates.fire(Update{ + .peer = peer, + .was = key.link, + .now = link + }); }); }).fail([=](const RPCError &error) { _editCallbacks.erase(key); @@ -298,6 +310,13 @@ rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue( })); } +auto InviteLinks::updates( + not_null<PeerData*> peer) const -> rpl::producer<Update> { + return _updates.events() | rpl::filter([=](const Update &update) { + return update.peer == peer; + }); +} + void InviteLinks::requestJoinedFirstSlice(LinkKey key) { if (_firstJoinedRequests.contains(key)) { return; @@ -422,7 +441,6 @@ auto InviteLinks::parse( .usageLimit = data.vusage_limit().value_or_empty(), .usage = data.vusage().value_or_empty(), .permanent = data.is_permanent(), - .expired = data.is_expired(), .revoked = data.is_revoked(), }; }); diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index cec3a9c03..c3bc5a97c 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -20,7 +20,6 @@ struct InviteLink { int usageLimit = 0; int usage = 0; bool permanent = false; - bool expired = false; bool revoked = false; }; @@ -39,12 +38,19 @@ struct JoinedByLinkSlice { int count = 0; }; +struct InviteLinkUpdate { + not_null<PeerData*> peer; + QString was; + std::optional<InviteLink> now; +}; + class InviteLinks final { public: explicit InviteLinks(not_null<ApiWrap*> api); using Link = InviteLink; using Links = PeerInviteLinks; + using Update = InviteLinkUpdate; void create( not_null<PeerData*> peer, @@ -77,6 +83,8 @@ public: not_null<PeerData*> peer, const QString &link, int fullCount); + [[nodiscard]] rpl::producer<Update> updates( + not_null<PeerData*> peer) const; void requestMoreLinks( not_null<PeerData*> peer, @@ -152,6 +160,8 @@ private: std::vector<Fn<void(Link)>>> _createCallbacks; base::flat_map<LinkKey, std::vector<Fn<void(Link)>>> _editCallbacks; + rpl::event_stream<Update> _updates; + }; } // namespace Api diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 10b344318..fede1dd8d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -53,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kPreloadPages = 2; +constexpr auto kFullArcLength = 360 * 16; enum class Color { Permanent, @@ -100,9 +101,11 @@ public: const InviteLinkData &data, TimeId now); - void update(const InviteLinkData &data); + void update(const InviteLinkData &data, TimeId now); + void updateExpireProgress(TimeId now); [[nodiscard]] InviteLinkData data() const; + [[nodiscard]] crl::time updateExpireIn() const; QString generateName() override; QString generateShortName() override; @@ -159,7 +162,6 @@ private: [[nodiscard]] Color ComputeColor( const InviteLinkData &link, float64 progress) { - const auto startDate = link.startDate ? link.startDate : link.date; return link.revoked ? Color::Revoked : (progress >= 1.) @@ -171,8 +173,20 @@ private: : Color::Permanent; } -[[nodiscard]] QString ComputeStatus(const InviteLinkData &link) { - return "nothing"; +[[nodiscard]] QString ComputeStatus(const InviteLinkData &link, TimeId now) { + auto result = link.usage + ? tr::lng_group_invite_joined(tr::now, lt_count_decimal, link.usage) + : tr::lng_group_invite_no_joined(tr::now); + const auto add = [&](const QString &text) { + result += QString::fromUtf8(" \xE2\xB8\xB1 ") + text; + }; + if (link.revoked) { + add(tr::lng_group_invite_link_revoked(tr::now)); + } else if ((link.usageLimit > 0 && link.usage >= link.usageLimit) + || (link.expireDate > 0 && now >= link.expireDate)) { + add(tr::lng_group_invite_link_expired(tr::now)); + } + return result; } void CopyLink(const QString &link) { @@ -338,21 +352,45 @@ Row::Row( , _data(data) , _progressTillExpire(ComputeProgress(data, now)) , _color(ComputeColor(data, _progressTillExpire)) { - setCustomStatus(ComputeStatus(data)); + setCustomStatus(ComputeStatus(data, now)); } -void Row::update(const InviteLinkData &data) { +void Row::update(const InviteLinkData &data, TimeId now) { _data = data; - _progressTillExpire = ComputeProgress(data, base::unixtime::now()); + _progressTillExpire = ComputeProgress(data, now); _color = ComputeColor(data, _progressTillExpire); - setCustomStatus(ComputeStatus(data)); + setCustomStatus(ComputeStatus(data, now)); _delegate->rowUpdateRow(this); } +void Row::updateExpireProgress(TimeId now) { + const auto updated = ComputeProgress(_data, now); + if (std::round(_progressTillExpire * 360) != std::round(updated * 360)) { + _progressTillExpire = updated; + const auto color = ComputeColor(_data, _progressTillExpire); + if (_color != color) { + _color = color; + setCustomStatus(ComputeStatus(_data, now)); + } + _delegate->rowUpdateRow(this); + } +} + InviteLinkData Row::data() const { return _data; } +crl::time Row::updateExpireIn() const { + if (_color != Color::Expiring && _color != Color::ExpireSoon) { + return 0; + } + const auto start = _data.startDate ? _data.startDate : _data.date; + if (_data.expireDate <= start) { + return 0; + } + return std::round((_data.expireDate - start) * crl::time(1000) / 720.); +} + QString Row::generateName() { auto result = _data.link; return result.replace(qstr("https://"), QString()); @@ -425,6 +463,13 @@ public: private: void appendRow(const InviteLinkData &data, TimeId now); + void prependRow(const InviteLinkData &data, TimeId now); + void updateRow(const InviteLinkData &data, TimeId now); + bool removeRow(const QString &link); + + void checkExpiringTimer(not_null<Row*> row); + void expiringProgressTimer(); + [[nodiscard]] base::unique_qptr<Ui::PopupMenu> createRowContextMenu( QWidget *parent, not_null<PeerListRow*> row); @@ -433,6 +478,9 @@ private: bool _revoked = false; base::unique_qptr<Ui::PopupMenu> _menu; + base::flat_set<not_null<Row*>> _expiringRows; + base::Timer _updateExpiringTimer; + std::array<QImage, int(Color::Count)> _icons; rpl::lifetime _lifetime; @@ -440,13 +488,31 @@ private: Controller::Controller(not_null<PeerData*> peer, bool revoked) : _peer(peer) -, _revoked(revoked) { +, _revoked(revoked) +, _updateExpiringTimer([=] { expiringProgressTimer(); }) { style::PaletteChanged( ) | rpl::start_with_next([=] { for (auto &image : _icons) { image = QImage(); } }, _lifetime); + + peer->session().api().inviteLinks().updates( + peer + ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { + const auto now = base::unixtime::now(); + if (!update.now + || (!update.was.isEmpty() && update.now->revoked != _revoked)) { + if (removeRow(update.was)) { + delegate()->peerListRefreshRows(); + } + } else if (update.was.isEmpty()) { + prependRow(*update.now, now); + delegate()->peerListRefreshRows(); + } else { + updateRow(*update.now, now); + } + }, _lifetime); } void Controller::prepare() { @@ -538,6 +604,60 @@ void Controller::appendRow(const InviteLinkData &data, TimeId now) { delegate()->peerListAppendRow(std::make_unique<Row>(this, data, now)); } +void Controller::prependRow(const InviteLinkData &data, TimeId now) { + delegate()->peerListPrependRow(std::make_unique<Row>(this, data, now)); +} + +void Controller::updateRow(const InviteLinkData &data, TimeId now) { + if (const auto row = delegate()->peerListFindRow(ComputeRowId(data))) { + const auto real = static_cast<Row*>(row); + real->update(data, now); + checkExpiringTimer(real); + delegate()->peerListUpdateRow(row); + } +} + +bool Controller::removeRow(const QString &link) { + if (const auto row = delegate()->peerListFindRow(ComputeRowId(link))) { + delegate()->peerListRemoveRow(row); + return true; + } + return false; +} + +void Controller::checkExpiringTimer(not_null<Row*> row) { + const auto updateIn = row->updateExpireIn(); + if (updateIn > 0) { + _expiringRows.emplace(row); + if (!_updateExpiringTimer.isActive() + || updateIn < _updateExpiringTimer.remainingTime()) { + _updateExpiringTimer.callOnce(updateIn); + } + } else { + _expiringRows.remove(row); + } +} + +void Controller::expiringProgressTimer() { + const auto now = base::unixtime::now(); + auto minimalIn = 0; + for (auto i = begin(_expiringRows); i != end(_expiringRows);) { + (*i)->updateExpireProgress(now); + const auto updateIn = (*i)->updateExpireIn(); + if (!updateIn) { + i = _expiringRows.erase(i); + } else { + ++i; + if (!minimalIn || minimalIn > updateIn) { + minimalIn = updateIn; + } + } + } + if (minimalIn) { + _updateExpiringTimer.callOnce(minimalIn); + } +} + void Controller::rowUpdateRow(not_null<Row*> row) { delegate()->peerListUpdateRow(row); } @@ -578,7 +698,6 @@ void Controller::rowPaintIcon( } p.drawImage(x + skip, y + skip, icon); if (progress >= 0. && progress < 1.) { - const auto kFullArcLength = 360 * 16; const auto stroke = st::inviteLinkIconStroke; auto hq = PainterHighQualityEnabler(p); auto pen = QPen((*bg)->c); From 819cd4a09912081f5121ca85fa89ab105ea957b2 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 19 Jan 2021 14:26:00 +0400 Subject: [PATCH 153/396] Allow deleting revoked invite links. --- Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/api/api_invite_links.cpp | 174 +++++++++++++++--- Telegram/SourceFiles/api/api_invite_links.h | 14 ++ .../boxes/peers/edit_peer_invite_links.cpp | 122 +++++++++++- Telegram/SourceFiles/info/info.style | 2 +- 5 files changed, 281 insertions(+), 33 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index fbe9396a3..1ea162bb2 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1192,6 +1192,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_context_revoke" = "Revoke"; "lng_group_invite_context_delete" = "Delete"; "lng_group_invite_context_delete_all" = "Delete all"; +"lng_group_invite_delete_sure" = "Are you sure you want to delete that revoked link?"; +"lng_group_invite_delete_all_sure" = "Are you sure you want to delete all revoked links? This action cannot be undone."; "lng_group_invite_revoked_title" = "Revoked links"; "lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?"; "lng_group_invite_link_expired" = "Expired"; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index a288d10a4..62b493fcc 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -122,25 +122,34 @@ auto InviteLinks::prepend( } auto &links = i->second; const auto permanent = lookupPermanent(links); - if (link.permanent) { - auto update = Update{ .peer = peer }; - if (permanent) { - update.was = permanent->link; - permanent->revoked = true; - } - editPermanentLink(peer, link.link); - if (permanent) { - update.now = *permanent; - _updates.fire(std::move(update)); + const auto hadPermanent = (permanent != nullptr); + auto updateOldPermanent = Update{ .peer = peer }; + if (link.permanent && hadPermanent) { + updateOldPermanent.was = permanent->link; + updateOldPermanent.now = *permanent; + updateOldPermanent.now->revoked = true; + links.links.erase(begin(links.links)); + if (links.count > 0) { + --links.count; } } + // Must not dereference 'permanent' pointer after that. + ++links.count; - if (permanent && !link.permanent) { + if (hadPermanent && !link.permanent) { links.links.insert(begin(links.links) + 1, link); } else { links.links.insert(begin(links.links), link); } + + if (link.permanent) { + editPermanentLink(peer, link.link); + } notify(peer); + + if (updateOldPermanent.now) { + _updates.fire(std::move(updateOldPermanent)); + } _updates.fire(Update{ .peer = peer, .now = link }); return link; } @@ -162,7 +171,10 @@ void InviteLinks::performEdit( TimeId expireDate, int usageLimit) { const auto key = LinkKey{ peer, link }; - if (const auto i = _editCallbacks.find(key); i != end(_editCallbacks)) { + if (_deleteCallbacks.contains(key)) { + return; + } else if (const auto i = _editCallbacks.find(key) + ; i != end(_editCallbacks)) { if (done) { i->second.push_back(std::move(done)); } @@ -184,7 +196,7 @@ void InviteLinks::performEdit( } using Flag = MTPmessages_EditExportedChatInvite::Flag; - const auto requestId = _api->request(MTPmessages_EditExportedChatInvite( + _api->request(MTPmessages_EditExportedChatInvite( MTP_flags((revoke ? Flag::f_revoked : Flag(0)) | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), @@ -205,8 +217,14 @@ void InviteLinks::performEdit( key.link, &Link::link); if (j != end(i->second.links)) { - *j = link; - notify(peer); + if (link.revoked && !j->revoked) { + i->second.links.erase(j); + if (i->second.count > 0) { + --i->second.count; + } + } else { + *j = link; + } } } for (const auto &callback : *callbacks) { @@ -215,7 +233,7 @@ void InviteLinks::performEdit( _updates.fire(Update{ .peer = peer, .was = key.link, - .now = link + .now = link, }); }); }).fail([=](const RPCError &error) { @@ -236,6 +254,72 @@ void InviteLinks::revokePermanent( performCreate(peer, std::move(done), true); } +void InviteLinks::destroy( + not_null<PeerData*> peer, + const QString &link, + Fn<void()> done) { + const auto key = LinkKey{ peer, link }; + + if (const auto i = _deleteCallbacks.find(key) + ; i != end(_deleteCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } + return; + } + + auto &callbacks = _deleteCallbacks[key]; + if (done) { + callbacks.push_back(std::move(done)); + } + + _api->request(MTPmessages_DeleteExportedChatInvite( + peer->input, + MTP_string(link) + )).done([=](const MTPBool &result) { + const auto callbacks = _deleteCallbacks.take(key); + if (callbacks) { + for (const auto &callback : *callbacks) { + callback(); + } + } + _updates.fire(Update{ + .peer = peer, + .was = key.link, + }); + }).fail([=](const RPCError &error) { + _deleteCallbacks.erase(key); + }).send(); +} + +void InviteLinks::destroyAllRevoked( + not_null<PeerData*> peer, + Fn<void()> done) { + if (const auto i = _deleteRevokedCallbacks.find(peer) + ; i != end(_deleteRevokedCallbacks)) { + if (done) { + i->second.push_back(std::move(done)); + } + return; + } + auto &callbacks = _deleteRevokedCallbacks[peer]; + if (done) { + callbacks.push_back(std::move(done)); + } + + _api->request(MTPmessages_DeleteRevokedExportedChatInvites( + peer->input + )).done([=](const MTPBool &result) { + if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { + for (const auto &callback : *callbacks) { + callback(); + } + } + _allRevokedDestroyed.fire_copy(peer); + }).fail([=](const RPCError &error) { + }).send(); +} + void InviteLinks::requestLinks(not_null<PeerData*> peer) { if (_firstSliceRequests.contains(peer)) { return; @@ -317,6 +401,15 @@ auto InviteLinks::updates( }); } +rpl::producer<> InviteLinks::allRevokedDestroyed( + not_null<PeerData*> peer) const { + using namespace rpl::mappers; + return _allRevokedDestroyed.events( + ) | rpl::filter( + _1 == peer + ) | rpl::to_empty; +} + void InviteLinks::requestJoinedFirstSlice(LinkKey key) { if (_firstJoinedRequests.contains(key)) { return; @@ -351,26 +444,63 @@ void InviteLinks::setPermanent( i = _firstSlices.emplace(peer).first; } auto &links = i->second; + auto updateOldPermanent = Update{ .peer = peer }; if (const auto permanent = lookupPermanent(links)) { if (permanent->link == link.link) { if (permanent->usage != link.usage) { permanent->usage = link.usage; - notify(peer); + _updates.fire(Update{ + .peer = peer, + .was = link.link, + .now = *permanent + }); } return; } - permanent->revoked = true; + updateOldPermanent.was = permanent->link; + updateOldPermanent.now = *permanent; + updateOldPermanent.now->revoked = true; + links.links.erase(begin(links.links)); + if (links.count > 0) { + --links.count; + } } links.links.insert(begin(links.links), link); + editPermanentLink(peer, link.link); notify(peer); + + if (updateOldPermanent.now) { + _updates.fire(std::move(updateOldPermanent)); + } + _updates.fire(Update{ .peer = peer, .now = link }); } void InviteLinks::clearPermanent(not_null<PeerData*> peer) { - if (const auto permanent = lookupPermanent(peer)) { - permanent->revoked = true; - editPermanentLink(peer, QString()); - notify(peer); + auto i = _firstSlices.find(peer); + if (i == end(_firstSlices)) { + return; + } + auto &links = i->second; + const auto permanent = lookupPermanent(links); + if (!permanent) { + return; + } + + auto updateOldPermanent = Update{ .peer = peer }; + updateOldPermanent.was = permanent->link; + updateOldPermanent.now = *permanent; + updateOldPermanent.now->revoked = true; + links.links.erase(begin(links.links)); + if (links.count > 0) { + --links.count; + } + + editPermanentLink(peer, QString()); + notify(peer); + + if (updateOldPermanent.now) { + _updates.fire(std::move(updateOldPermanent)); } } diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index c3bc5a97c..72deca709 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -70,6 +70,13 @@ public: void revokePermanent( not_null<PeerData*> peer, Fn<void(Link)> done = nullptr); + void destroy( + not_null<PeerData*> peer, + const QString &link, + Fn<void()> done = nullptr); + void destroyAllRevoked( + not_null<PeerData*> peer, + Fn<void()> done = nullptr); void setPermanent( not_null<PeerData*> peer, @@ -85,6 +92,8 @@ public: int fullCount); [[nodiscard]] rpl::producer<Update> updates( not_null<PeerData*> peer) const; + [[nodiscard]] rpl::producer<> allRevokedDestroyed( + not_null<PeerData*> peer) const; void requestMoreLinks( not_null<PeerData*> peer, @@ -159,8 +168,13 @@ private: not_null<PeerData*>, std::vector<Fn<void(Link)>>> _createCallbacks; base::flat_map<LinkKey, std::vector<Fn<void(Link)>>> _editCallbacks; + base::flat_map<LinkKey, std::vector<Fn<void()>>> _deleteCallbacks; + base::flat_map< + not_null<PeerData*>, + std::vector<Fn<void()>>> _deleteRevokedCallbacks; rpl::event_stream<Update> _updates; + rpl::event_stream<not_null<PeerData*>> _allRevokedDestroyed; }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index fede1dd8d..081538627 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -311,6 +311,36 @@ void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { Ui::LayerOption::KeepOther); } +void DeleteLink(not_null<PeerData*> peer, const QString &link) { + const auto box = std::make_shared<QPointer<ConfirmBox>>(); + const auto sure = [=] { + const auto finish = [=] { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().destroy(peer, link, finish); + }; + *box = Ui::show( + Box<ConfirmBox>(tr::lng_group_invite_delete_sure(tr::now), sure), + Ui::LayerOption::KeepOther); +} + +void DeleteAllRevoked(not_null<PeerData*> peer) { + const auto box = std::make_shared<QPointer<ConfirmBox>>(); + const auto sure = [=] { + const auto finish = [=] { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().destroyAllRevoked(peer, finish); + }; + *box = Ui::show( + Box<ConfirmBox>(tr::lng_group_invite_delete_all_sure(tr::now), sure), + Ui::LayerOption::KeepOther); +} + not_null<Ui::SettingsButton*> AddCreateLinkButton( not_null<Ui::VerticalLayout*> container) { const auto result = container->add( @@ -445,6 +475,7 @@ public: Controller(not_null<PeerData*> peer, bool revoked); void prepare() override; + void loadMoreRows() override; void rowClicked(not_null<PeerListRow*> row) override; void rowActionClicked(not_null<PeerListRow*> row) override; base::unique_qptr<Ui::PopupMenu> rowContextMenu( @@ -467,6 +498,7 @@ private: void updateRow(const InviteLinkData &data, TimeId now); bool removeRow(const QString &link); + void appendSlice(const InviteLinksSlice &slice); void checkExpiringTimer(not_null<Row*> row); void expiringProgressTimer(); @@ -474,10 +506,14 @@ private: QWidget *parent, not_null<PeerListRow*> row); - not_null<PeerData*> _peer; - bool _revoked = false; + const not_null<PeerData*> _peer; + const bool _revoked = false; base::unique_qptr<Ui::PopupMenu> _menu; + QString _offsetLink; + bool _requesting = false; + bool _allLoaded = false; + base::flat_set<not_null<Row*>> _expiringRows; base::Timer _updateExpiringTimer; @@ -501,8 +537,7 @@ Controller::Controller(not_null<PeerData*> peer, bool revoked) peer ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { const auto now = base::unixtime::now(); - if (!update.now - || (!update.was.isEmpty() && update.now->revoked != _revoked)) { + if (!update.now || update.now->revoked != _revoked) { if (removeRow(update.was)) { delegate()->peerListRefreshRows(); } @@ -513,15 +548,63 @@ Controller::Controller(not_null<PeerData*> peer, bool revoked) updateRow(*update.now, now); } }, _lifetime); + + if (_revoked) { + peer->session().api().inviteLinks().allRevokedDestroyed( + peer + ) | rpl::start_with_next([=] { + _requesting = false; + _allLoaded = true; + while (delegate()->peerListFullRowsCount()) { + delegate()->peerListRemoveRow(delegate()->peerListRowAt(0)); + } + delegate()->peerListRefreshRows(); + }, _lifetime); + } } void Controller::prepare() { + if (!_revoked) { + appendSlice(_peer->session().api().inviteLinks().links(_peer)); + } + if (!delegate()->peerListFullRowsCount()) { + loadMoreRows(); + } +} + +void Controller::loadMoreRows() { + if (_requesting || _allLoaded) { + return; + } + _requesting = true; + const auto done = [=](const InviteLinksSlice &slice) { + if (!_requesting) { + return; + } + _requesting = false; + if (slice.links.empty()) { + _allLoaded = true; + return; + } + appendSlice(slice); + }; + _peer->session().api().inviteLinks().requestMoreLinks( + _peer, + _offsetLink, + _revoked, + crl::guard(this, done)); +} + +void Controller::appendSlice(const InviteLinksSlice &slice) { const auto now = base::unixtime::now(); - const auto &links = _peer->session().api().inviteLinks().links(_peer); - for (const auto &link : links.links) { + for (const auto &link : slice.links) { if (!link.permanent || link.revoked) { appendRow(link, now); } + _offsetLink = link.link; + } + if (slice.links.size() >= slice.count) { + _allLoaded = true; } delegate()->peerListRefreshRows(); } @@ -559,9 +642,9 @@ base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( const auto link = data.link; auto result = base::make_unique_q<Ui::PopupMenu>(parent); if (data.revoked) { - //result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] { - // // #TODO links delete - //}); + result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] { + DeleteLink(_peer, link); + }); } else { result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] { CopyLink(link); @@ -576,7 +659,6 @@ base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto revoke = crl::guard(this, [=] { const auto done = crl::guard(this, [=](InviteLinkData data) { - // #TODO links add to revoked, remove from list if (*box) { (*box)->closeBox(); } @@ -946,6 +1028,23 @@ void ManageInviteLinksBox( st::inviteLinkRevokedTitlePadding)); const auto revoked = AddLinksList(container, peer, true); + const auto deleteAll = Ui::CreateChild<Ui::LinkButton>( + container.get(), + tr::lng_group_invite_context_delete_all(tr::now), + st::boxLinkButton); + rpl::combine( + header->topValue(), + container->widthValue() + ) | rpl::start_with_next([=](int top, int outerWidth) { + deleteAll->moveToRight( + st::inviteLinkRevokedTitlePadding.left(), + top + st::inviteLinkRevokedTitlePadding.top(), + outerWidth); + }, deleteAll->lifetime()); + deleteAll->setClickedCallback([=] { + DeleteAllRevoked(peer); + }); + rpl::combine( list->heightValue(), revoked->heightValue() @@ -953,5 +1052,8 @@ void ManageInviteLinksBox( dividerAbout->toggle(!list, anim::type::instant); divider->toggle(list > 0 && revoked > 0, anim::type::instant); header->toggle(revoked > 0, anim::type::instant); + deleteAll->setVisible(revoked > 0); }, header->lifetime()); + + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index f02207cda..f919fd15f 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -916,6 +916,6 @@ inviteLinkList: PeerList(defaultPeerList) { inviteLinkIconSkip: 7px; inviteLinkIconStroke: 2px; inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }}; -inviteLinkThreeDotsSkip: 8px; +inviteLinkThreeDotsSkip: 12px; inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 9px); inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px); From 50c07bfc98a109c3b9d3978a72a30720c6e1780c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 19 Jan 2021 14:56:01 +0400 Subject: [PATCH 154/396] Update API scheme, add view link box. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 1 + Telegram/Resources/tl/api.tl | 5 +- Telegram/SourceFiles/api/api_invite_links.cpp | 68 +- Telegram/SourceFiles/api/api_invite_links.h | 15 +- .../boxes/peers/edit_peer_invite_link.cpp | 590 ++++++++++++++++++ .../boxes/peers/edit_peer_invite_link.h | 32 + .../boxes/peers/edit_peer_invite_links.cpp | 319 +--------- .../boxes/peers/edit_peer_invite_links.h | 8 - .../boxes/peers/edit_peer_type_box.cpp | 8 +- .../history/view/history_view_send_action.cpp | 1 + 11 files changed, 699 insertions(+), 350 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp create mode 100644 Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2a14bab17..48d25273a 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -181,6 +181,8 @@ PRIVATE boxes/peers/edit_participants_box.h boxes/peers/edit_peer_info_box.cpp boxes/peers/edit_peer_info_box.h + boxes/peers/edit_peer_invite_link.cpp + boxes/peers/edit_peer_invite_link.h boxes/peers/edit_peer_invite_links.cpp boxes/peers/edit_peer_invite_links.h boxes/peers/edit_peer_type_box.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1ea162bb2..1f581be7c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1185,6 +1185,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_add" = "Create a New Link"; "lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used."; "lng_group_invite_expires_at" = "This link expires {when}."; +"lng_group_invite_expired_already" = "This link has expired."; "lng_group_invite_created_by" = "Link created by"; "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 2f3bb923e..68307dce9 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -455,6 +455,7 @@ sendMessageGamePlayAction#dd6a8f48 = SendMessageAction; sendMessageRecordRoundAction#88f27fbc = SendMessageAction; sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction; speakingInGroupCallAction#d92c2285 = SendMessageAction; +sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction; contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found; @@ -535,7 +536,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#6e24fc9d flags:# revoked:flags.0?true permanent:flags.5?true expired:flags.6?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#6e24fc9d flags:# revoked:flags.0?true permanent:flags.5?true link:string admin_id:int date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector<User> = ChatInvite; @@ -1463,7 +1464,7 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6d9cae03 flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvites#6a72ac6c flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 62b493fcc..bdc2c8edb 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -41,6 +41,27 @@ void RemovePermanent(PeerInviteLinks &links) { } // namespace +JoinedByLinkSlice ParseJoinedByLinkSlice( + not_null<PeerData*> peer, + const MTPmessages_ChatInviteImporters &slice) { + auto result = JoinedByLinkSlice(); + slice.match([&](const MTPDmessages_chatInviteImporters &data) { + auto &owner = peer->session().data(); + owner.processUsers(data.vusers()); + result.count = data.vcount().v; + result.users.reserve(data.vimporters().v.size()); + for (const auto importer : data.vimporters().v) { + importer.match([&](const MTPDchatInviteImporter &data) { + result.users.push_back({ + .user = owner.user(data.vuser_id().v), + .date = data.vdate().v, + }); + }); + } + }); + return result; +} + InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) { } @@ -328,6 +349,7 @@ void InviteLinks::requestLinks(not_null<PeerData*> peer) { MTP_flags(0), peer->input, MTPInputUser(), // admin_id + MTPint(), // offset_date MTPstring(), // offset_link MTP_int(kFirstPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { @@ -362,9 +384,18 @@ void InviteLinks::requestLinks(not_null<PeerData*> peer) { _firstSliceRequests.emplace(peer, requestId); } -JoinedByLinkSlice InviteLinks::lookupJoinedFirstSlice(LinkKey key) const { +std::optional<JoinedByLinkSlice> InviteLinks::lookupJoinedFirstSlice( + LinkKey key) const { const auto i = _firstJoined.find(key); - return (i != end(_firstJoined)) ? i->second : JoinedByLinkSlice(); + return (i != end(_firstJoined)) + ? std::make_optional(i->second) + : std::nullopt; +} + +std::optional<JoinedByLinkSlice> InviteLinks::joinedFirstSliceLoaded( + not_null<PeerData*> peer, + const QString &link) const { + return lookupJoinedFirstSlice({ peer, link }); } rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue( @@ -372,7 +403,7 @@ rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue( const QString &link, int fullCount) { const auto key = LinkKey{ peer, link }; - auto current = lookupJoinedFirstSlice(key); + auto current = lookupJoinedFirstSlice(key).value_or(JoinedByLinkSlice()); if (current.count == fullCount && (!fullCount || !current.users.empty())) { return rpl::single(current); @@ -390,7 +421,7 @@ rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue( ) | rpl::filter( _1 == key ) | rpl::map([=] { - return lookupJoinedFirstSlice(key); + return lookupJoinedFirstSlice(key).value_or(JoinedByLinkSlice()); })); } @@ -422,7 +453,7 @@ void InviteLinks::requestJoinedFirstSlice(LinkKey key) { MTP_int(kJoinedFirstPage) )).done([=](const MTPmessages_ChatInviteImporters &result) { _firstJoinedRequests.remove(key); - _firstJoined[key] = parseSlice(key.peer, result); + _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); _joinedFirstSliceLoaded.fire_copy(key); }).fail([=](const RPCError &error) { _firstJoinedRequests.remove(key); @@ -537,27 +568,6 @@ auto InviteLinks::parseSlice( return result; } -JoinedByLinkSlice InviteLinks::parseSlice( - not_null<PeerData*> peer, - const MTPmessages_ChatInviteImporters &slice) const { - auto result = JoinedByLinkSlice(); - slice.match([&](const MTPDmessages_chatInviteImporters &data) { - auto &owner = peer->session().data(); - owner.processUsers(data.vusers()); - result.count = data.vcount().v; - result.users.reserve(data.vimporters().v.size()); - for (const auto importer : data.vimporters().v) { - importer.match([&](const MTPDchatInviteImporter &data) { - result.users.push_back({ - .user = owner.user(data.vuser_id().v), - .date = data.vdate().v, - }); - }); - } - }); - return result; -} - auto InviteLinks::parse( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) const -> Link { @@ -578,7 +588,8 @@ auto InviteLinks::parse( void InviteLinks::requestMoreLinks( not_null<PeerData*> peer, - const QString &last, + TimeId lastDate, + const QString &lastLink, bool revoked, Fn<void(Links)> done) { using Flag = MTPmessages_GetExportedChatInvites::Flag; @@ -587,7 +598,8 @@ void InviteLinks::requestMoreLinks( | (revoked ? Flag::f_revoked : Flag(0))), peer->input, MTPInputUser(), // admin_id, - MTP_string(last), + MTP_int(lastDate), + MTP_string(lastLink), MTP_int(kPerPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { auto slice = parseSlice(peer, result); diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 72deca709..83ad34106 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -44,6 +44,10 @@ struct InviteLinkUpdate { std::optional<InviteLink> now; }; +[[nodiscard]] JoinedByLinkSlice ParseJoinedByLinkSlice( + not_null<PeerData*> peer, + const MTPmessages_ChatInviteImporters &slice); + class InviteLinks final { public: explicit InviteLinks(not_null<ApiWrap*> api); @@ -90,6 +94,9 @@ public: not_null<PeerData*> peer, const QString &link, int fullCount); + [[nodiscard]] std::optional<JoinedByLinkSlice> joinedFirstSliceLoaded( + not_null<PeerData*> peer, + const QString &link) const; [[nodiscard]] rpl::producer<Update> updates( not_null<PeerData*> peer) const; [[nodiscard]] rpl::producer<> allRevokedDestroyed( @@ -97,7 +104,8 @@ public: void requestMoreLinks( not_null<PeerData*> peer, - const QString &last, + TimeId lastDate, + const QString &lastLink, bool revoked, Fn<void(Links)> done); @@ -122,9 +130,6 @@ private: [[nodiscard]] Link parse( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) const; - [[nodiscard]] JoinedByLinkSlice parseSlice( - not_null<PeerData*> peer, - const MTPmessages_ChatInviteImporters &slice) const; [[nodiscard]] Link *lookupPermanent(not_null<PeerData*> peer); [[nodiscard]] Link *lookupPermanent(Links &links); [[nodiscard]] const Link *lookupPermanent(const Links &links) const; @@ -152,7 +157,7 @@ private: int usageLimit = 0); void requestJoinedFirstSlice(LinkKey key); - [[nodiscard]] JoinedByLinkSlice lookupJoinedFirstSlice( + [[nodiscard]] std::optional<JoinedByLinkSlice> lookupJoinedFirstSlice( LinkKey key) const; const not_null<ApiWrap*> _api; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp new file mode 100644 index 000000000..72b89e643 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -0,0 +1,590 @@ +/* +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 "boxes/peers/edit_peer_invite_link.h" + +#include "data/data_peer.h" +#include "data/data_user.h" +#include "data/data_changes.h" +#include "data/data_session.h" +#include "data/data_histories.h" +#include "main/main_session.h" +#include "api/api_invite_links.h" +#include "base/unixtime.h" +#include "apiwrap.h" +#include "ui/controls/invite_link_buttons.h" +#include "ui/controls/invite_link_label.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/widgets/popup_menu.h" +#include "ui/abstract_button.h" +#include "ui/toast/toast.h" +#include "ui/text/text_utilities.h" +#include "boxes/share_box.h" +#include "history/view/history_view_group_call_tracker.h" // GenerateUs... +#include "history/history_message.h" // GetErrorTextForSending. +#include "history/history.h" +#include "boxes/confirm_box.h" +#include "boxes/peer_list_box.h" +#include "mainwindow.h" +#include "facades.h" // Ui::showPerProfile. +#include "lang/lang_keys.h" +#include "window/window_session_controller.h" +#include "settings/settings_common.h" +#include "mtproto/sender.h" +#include "styles/style_info.h" + +#include <QtGui/QGuiApplication> + +namespace { + +constexpr auto kFirstPage = 20; +constexpr auto kPerPage = 100; + +using LinkData = Api::InviteLink; + +class Controller final : public PeerListController { +public: + Controller(not_null<PeerData*> peer, const LinkData &data); + + void prepare() override; + void loadMoreRows() override; + void rowClicked(not_null<PeerListRow*> row) override; + Main::Session &session() const override; + +private: + void appendSlice(const Api::JoinedByLinkSlice &slice); + [[nodiscard]] object_ptr<Ui::RpWidget> prepareHeader(); + + const not_null<PeerData*> _peer; + LinkData _data; + + mtpRequestId _requestId = 0; + std::optional<Api::JoinedByLinkUser> _lastUser; + bool _allLoaded = false; + + MTP::Sender _api; + rpl::lifetime _lifetime; + +}; + +class SingleRowController final : public PeerListController { +public: + SingleRowController(not_null<PeerData*> peer, TimeId date); + + void prepare() override; + void loadMoreRows() override; + void rowClicked(not_null<PeerListRow*> row) override; + Main::Session &session() const override; + +private: + const not_null<PeerData*> _peer; + TimeId _date = 0; + +}; + +void AddHeaderBlock( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer, + const LinkData &data, + TimeId now) { + const auto link = data.link; + const auto weak = Ui::MakeWeak(container); + const auto copyLink = crl::guard(weak, [=] { + CopyInviteLink(link); + }); + const auto shareLink = crl::guard(weak, [=] { + ShareInviteLinkBox(peer, link); + }); + const auto revokeLink = crl::guard(weak, [=] { + RevokeLink(peer, link); + }); + + const auto createMenu = [=] { + auto result = base::make_unique_q<Ui::PopupMenu>(container); + result->addAction( + tr::lng_group_invite_context_copy(tr::now), + copyLink); + result->addAction( + tr::lng_group_invite_context_share(tr::now), + shareLink); + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + return result; + }; + + const auto prefix = qstr("https://"); + const auto label = container->lifetime().make_state<Ui::InviteLinkLabel>( + container, + rpl::single(link.startsWith(prefix) + ? link.mid(prefix.size()) + : link), + createMenu); + container->add( + label->take(), + st::inviteLinkFieldPadding); + + label->clicks( + ) | rpl::start_with_next(copyLink, label->lifetime()); + + if ((data.expireDate <= 0 || now < data.expireDate) + && (data.usageLimit <= 0 || data.usage < data.usageLimit)) { + AddCopyShareLinkButtons( + container, + copyLink, + shareLink); + } +} + +void AddHeader( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer, + const LinkData &data) { + using namespace Settings; + + if (!data.revoked && !data.permanent) { + const auto now = base::unixtime::now(); + AddHeaderBlock(container, peer, data, now); + AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); + if (data.expireDate > 0) { + AddDividerText( + container, + (data.expireDate > now + ? tr::lng_group_invite_expires_at( + lt_when, + rpl::single(langDateTime( + base::unixtime::parse(data.expireDate)))) + : tr::lng_group_invite_expired_already())); + } else { + AddDivider(container); + } + } + AddSkip(container); + AddSubsectionTitle( + container, + tr::lng_group_invite_created_by()); + + const auto delegate = container->lifetime().make_state< + PeerListContentDelegateSimple + >(); + const auto controller = container->lifetime().make_state< + SingleRowController + >(data.admin, data.date); + const auto content = container->add(object_ptr<PeerListContent>( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); +} + +Controller::Controller(not_null<PeerData*> peer, const LinkData &data) +: _peer(peer) +, _data(data) +, _api(&_peer->session().api().instance()) { +} + +object_ptr<Ui::RpWidget> Controller::prepareHeader() { + using namespace Settings; + + auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr); + const auto container = result.data(); + AddHeader(container, _peer, _data); + AddDivider(container); + AddSkip(container); + AddSubsectionTitle( + container, + (_data.usage + ? tr::lng_group_invite_joined( + lt_count, + rpl::single(float64(_data.usage))) + : tr::lng_group_invite_no_joined())); + return result; +} + +void Controller::prepare() { + delegate()->peerListSetAboveWidget(prepareHeader()); + _allLoaded = (_data.usage == 0); + const auto &inviteLinks = _peer->session().api().inviteLinks(); + const auto slice = inviteLinks.joinedFirstSliceLoaded(_peer, _data.link); + if (slice) { + appendSlice(*slice); + } + loadMoreRows(); +} + +void Controller::loadMoreRows() { + if (_requestId || _allLoaded) { + return; + } + _requestId = _api.request(MTPmessages_GetChatInviteImporters( + _peer->input, + MTP_string(_data.link), + MTP_int(_lastUser ? _lastUser->date : 0), + _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), + MTP_int(_lastUser ? kPerPage : kFirstPage) + )).done([=](const MTPmessages_ChatInviteImporters &result) { + _requestId = 0; + auto slice = Api::ParseJoinedByLinkSlice(_peer, result); + _allLoaded = slice.users.empty(); + appendSlice(slice); + }).fail([=](const RPCError &error) { + _requestId = 0; + _allLoaded = true; + }).send(); +} + +void Controller::appendSlice(const Api::JoinedByLinkSlice &slice) { + for (const auto &user : slice.users) { + _lastUser = user; + delegate()->peerListAppendRow( + std::make_unique<PeerListRow>(user.user)); + } + delegate()->peerListRefreshRows(); +} + +void Controller::rowClicked(not_null<PeerListRow*> row) { + Ui::showPeerProfile(row->peer()); +} + +Main::Session &Controller::session() const { + return _peer->session(); +} + +SingleRowController::SingleRowController( + not_null<PeerData*> peer, + TimeId date) +: _peer(peer) +, _date(date) { +} + +void SingleRowController::prepare() { + auto row = std::make_unique<PeerListRow>(_peer); + row->setCustomStatus(langDateTime(base::unixtime::parse(_date))); + delegate()->peerListAppendRow(std::move(row)); + delegate()->peerListRefreshRows(); +} + +void SingleRowController::loadMoreRows() { +} + +void SingleRowController::rowClicked(not_null<PeerListRow*> row) { + Ui::showPeerProfile(row->peer()); +} + +Main::Session &SingleRowController::session() const { + return _peer->session(); +} + +} // namespace + +void AddPermanentLinkBlock( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer) { + const auto computePermanentLink = [=] { + const auto &links = peer->session().api().inviteLinks().links( + peer).links; + const auto link = links.empty() ? nullptr : &links.front(); + return (link && link->permanent && !link->revoked) ? link : nullptr; + }; + auto value = peer->session().changes().peerFlagsValue( + peer, + Data::PeerUpdate::Flag::InviteLinks + ) | rpl::map([=] { + const auto link = computePermanentLink(); + return link + ? std::make_tuple(link->link, link->usage) + : std::make_tuple(QString(), 0); + }) | rpl::distinct_until_changed( + ) | rpl::start_spawning(container->lifetime()); + + const auto weak = Ui::MakeWeak(container); + const auto copyLink = crl::guard(weak, [=] { + if (const auto link = computePermanentLink()) { + CopyInviteLink(link->link); + } + }); + const auto shareLink = crl::guard(weak, [=] { + if (const auto link = computePermanentLink()) { + ShareInviteLinkBox(peer, link->link); + } + }); + const auto revokeLink = crl::guard(weak, [=] { + const auto box = std::make_shared<QPointer<ConfirmBox>>(); + const auto done = crl::guard(weak, [=] { + const auto close = [=](auto&&) { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().revokePermanent(peer, close); + }); + *box = Ui::show( + Box<ConfirmBox>(tr::lng_group_invite_about_new(tr::now), done), + Ui::LayerOption::KeepOther); + }); + + auto link = rpl::duplicate( + value + ) | rpl::map([=](QString link, int usage) { + const auto prefix = qstr("https://"); + return link.startsWith(prefix) ? link.mid(prefix.size()) : link; + }); + const auto createMenu = [=] { + auto result = base::make_unique_q<Ui::PopupMenu>(container); + result->addAction( + tr::lng_group_invite_context_copy(tr::now), + copyLink); + result->addAction( + tr::lng_group_invite_context_share(tr::now), + shareLink); + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + return result; + }; + const auto label = container->lifetime().make_state<Ui::InviteLinkLabel>( + container, + std::move(link), + createMenu); + container->add( + label->take(), + st::inviteLinkFieldPadding); + + label->clicks( + ) | rpl::start_with_next(copyLink, label->lifetime()); + + AddCopyShareLinkButtons( + container, + copyLink, + shareLink); + + struct JoinedState { + QImage cachedUserpics; + std::vector<HistoryView::UserpicInRow> list; + int count = 0; + bool allUserpicsLoaded = false; + rpl::variable<Ui::JoinedCountContent> content; + rpl::lifetime lifetime; + }; + const auto state = container->lifetime().make_state<JoinedState>(); + const auto push = [=] { + HistoryView::GenerateUserpicsInRow( + state->cachedUserpics, + state->list, + st::inviteLinkUserpics, + 0); + state->allUserpicsLoaded = ranges::all_of( + state->list, + [](const HistoryView::UserpicInRow &element) { + return !element.peer->hasUserpic() || element.view->image(); + }); + state->content = Ui::JoinedCountContent{ + .count = state->count, + .userpics = state->cachedUserpics + }; + }; + std::move( + value + ) | rpl::map([=](QString link, int usage) { + return peer->session().api().inviteLinks().joinedFirstSliceValue( + peer, + link, + usage); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](const Api::JoinedByLinkSlice &slice) { + auto list = std::vector<HistoryView::UserpicInRow>(); + list.reserve(slice.users.size()); + for (const auto &item : slice.users) { + const auto i = ranges::find( + state->list, + item.user, + &HistoryView::UserpicInRow::peer); + if (i != end(state->list)) { + list.push_back(std::move(*i)); + } else { + list.push_back({ item.user }); + } + } + state->count = slice.count; + state->list = std::move(list); + push(); + }, state->lifetime); + + peer->session().downloaderTaskFinished( + ) | rpl::filter([=] { + return !state->allUserpicsLoaded; + }) | rpl::start_with_next([=] { + auto pushing = false; + state->allUserpicsLoaded = true; + for (const auto &element : state->list) { + if (!element.peer->hasUserpic()) { + continue; + } else if (element.peer->userpicUniqueKey(element.view) + != element.uniqueKey) { + pushing = true; + } else if (!element.view->image()) { + state->allUserpicsLoaded = false; + } + } + if (pushing) { + push(); + } + }, state->lifetime); + + Ui::AddJoinedCountButton( + container, + state->content.value(), + st::inviteLinkJoinedRowPadding + )->setClickedCallback([=] { + }); + + container->add(object_ptr<Ui::SlideWrap<Ui::FixedHeightWidget>>( + container, + object_ptr<Ui::FixedHeightWidget>( + container, + st::inviteLinkJoinedRowPadding.bottom())) + )->setDuration(0)->toggleOn(state->content.value( + ) | rpl::map([=](const Ui::JoinedCountContent &content) { + return (content.count <= 0); + })); +} + +void CopyInviteLink(const QString &link) { + QGuiApplication::clipboard()->setText(link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); +} + +void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) { + const auto session = &peer->session(); + const auto sending = std::make_shared<bool>(); + const auto box = std::make_shared<QPointer<ShareBox>>(); + + auto copyCallback = [=] { + QGuiApplication::clipboard()->setText(link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + }; + auto submitCallback = [=]( + std::vector<not_null<PeerData*>> &&result, + TextWithTags &&comment, + Api::SendOptions options) { + if (*sending || result.empty()) { + return; + } + + const auto error = [&] { + for (const auto peer : result) { + const auto error = GetErrorTextForSending( + peer, + {}, + comment); + if (!error.isEmpty()) { + return std::make_pair(error, peer); + } + } + return std::make_pair(QString(), result.front()); + }(); + if (!error.first.isEmpty()) { + auto text = TextWithEntities(); + if (result.size() > 1) { + text.append( + Ui::Text::Bold(error.second->name) + ).append("\n\n"); + } + text.append(error.first); + Ui::show( + Box<InformBox>(text), + Ui::LayerOption::KeepOther); + return; + } + + *sending = true; + if (!comment.text.isEmpty()) { + comment.text = link + "\n" + comment.text; + const auto add = link.size() + 1; + for (auto &tag : comment.tags) { + tag.offset += add; + } + } + const auto owner = &peer->owner(); + auto &api = peer->session().api(); + auto &histories = owner->histories(); + const auto requestType = Data::Histories::RequestType::Send; + for (const auto peer : result) { + const auto history = owner->history(peer); + auto message = ApiWrap::MessageToSend(history); + message.textWithTags = comment; + message.action.options = options; + message.action.clearDraft = false; + api.sendMessage(std::move(message)); + } + Ui::Toast::Show(tr::lng_share_done(tr::now)); + if (*box) { + (*box)->closeBox(); + } + }; + auto filterCallback = [](PeerData *peer) { + return peer->canWrite(); + }; + *box = Ui::show( + Box<ShareBox>( + App::wnd()->sessionController(), + std::move(copyCallback), + std::move(submitCallback), + std::move(filterCallback)), + Ui::LayerOption::KeepOther); +} + +void RevokeLink(not_null<PeerData*> peer, const QString &link) { + const auto box = std::make_shared<QPointer<ConfirmBox>>(); + const auto revoke = [=] { + const auto done = [=](const LinkData &data) { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().revoke(peer, link, done); + }; + *box = Ui::show( + Box<ConfirmBox>( + tr::lng_group_invite_revoke_about(tr::now), + revoke), + Ui::LayerOption::KeepOther); +} + +void ShowInviteLinkBox( + not_null<PeerData*> peer, + const Api::InviteLink &link) { + auto initBox = [=](not_null<Ui::BoxContent*> box) { + box->setTitle((link.permanent && !link.revoked) + ? tr::lng_manage_peer_link_permanent() + : tr::lng_manage_peer_link_invite()); + peer->session().api().inviteLinks().updates( + peer + ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { + if (update.was == link.link + && (!update.now || (!link.revoked && update.now->revoked))) { + box->closeBox(); + } + }, box->lifetime()); + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); + }; + if (link.usage > 0) { + Ui::show( + Box<PeerListBox>( + std::make_unique<Controller>(peer, link), + std::move(initBox)), + Ui::LayerOption::KeepOther); + } else { + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + initBox(box); + const auto container = box->verticalLayout(); + AddHeader(container, peer, link); + }), Ui::LayerOption::KeepOther); + } +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h new file mode 100644 index 000000000..abd353043 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -0,0 +1,32 @@ +/* +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 "ui/layers/generic_box.h" + +class PeerData; + +namespace Api { +struct InviteLink; +} // namespace Api + +namespace Ui { +class VerticalLayout; +} // namespace Ui + +void AddPermanentLinkBlock( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer); + +void CopyInviteLink(const QString &link); +void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link); +void RevokeLink(not_null<PeerData*> peer, const QString &link); + +void ShowInviteLinkBox( + not_null<PeerData*> peer, + const Api::InviteLink &link); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 081538627..91d0751ec 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -7,49 +7,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/edit_peer_invite_links.h" -#include "data/data_changes.h" -#include "data/data_user.h" -#include "data/data_drafts.h" -#include "data/data_session.h" -#include "data/data_histories.h" +#include "data/data_peer.h" #include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/boxes/edit_invite_link.h" -#include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/abstract_button.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" -#include "ui/widgets/checkbox.h" -#include "ui/widgets/input_fields.h" -#include "ui/controls/invite_link_label.h" -#include "ui/controls/invite_link_buttons.h" -#include "ui/text/text_utilities.h" -#include "ui/toast/toast.h" -#include "history/view/history_view_group_call_tracker.h" // GenerateUs... -#include "history/history_message.h" // GetErrorTextForSending. -#include "history/history.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" -#include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" +#include "boxes/peers/edit_peer_invite_link.h" #include "settings/settings_common.h" // AddDivider. #include "apiwrap.h" -#include "mainwindow.h" -#include "boxes/share_box.h" #include "base/weak_ptr.h" #include "base/unixtime.h" -#include "window/window_session_controller.h" -#include "api/api_common.h" #include "styles/style_info.h" #include "styles/style_layers.h" // st::boxDividerLabel #include "styles/style_settings.h" // st::settingsDividerLabelPadding #include <xxhash.h> -#include <QtGui/QGuiApplication> - namespace { constexpr auto kPreloadPages = 2; @@ -189,95 +167,15 @@ private: return result; } -void CopyLink(const QString &link) { - QGuiApplication::clipboard()->setText(link); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); -} - -void ShareLinkBox(not_null<PeerData*> peer, const QString &link) { - const auto session = &peer->session(); - const auto sending = std::make_shared<bool>(); - const auto box = std::make_shared<QPointer<ShareBox>>(); - - auto copyCallback = [=] { - QGuiApplication::clipboard()->setText(link); - Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); - }; - auto submitCallback = [=]( - std::vector<not_null<PeerData*>> &&result, - TextWithTags &&comment, - Api::SendOptions options) { - if (*sending || result.empty()) { - return; - } - - const auto error = [&] { - for (const auto peer : result) { - const auto error = GetErrorTextForSending( - peer, - {}, - comment); - if (!error.isEmpty()) { - return std::make_pair(error, peer); - } - } - return std::make_pair(QString(), result.front()); - }(); - if (!error.first.isEmpty()) { - auto text = TextWithEntities(); - if (result.size() > 1) { - text.append( - Ui::Text::Bold(error.second->name) - ).append("\n\n"); - } - text.append(error.first); - Ui::show( - Box<InformBox>(text), - Ui::LayerOption::KeepOther); - return; - } - - *sending = true; - if (!comment.text.isEmpty()) { - comment.text = link + "\n" + comment.text; - const auto add = link.size() + 1; - for (auto &tag : comment.tags) { - tag.offset += add; - } - } - const auto owner = &peer->owner(); - auto &api = peer->session().api(); - auto &histories = owner->histories(); - const auto requestType = Data::Histories::RequestType::Send; - for (const auto peer : result) { - const auto history = owner->history(peer); - auto message = ApiWrap::MessageToSend(history); - message.textWithTags = comment; - message.action.options = options; - message.action.clearDraft = false; - api.sendMessage(std::move(message)); - } - Ui::Toast::Show(tr::lng_share_done(tr::now)); - if (*box) { - (*box)->closeBox(); - } - }; - auto filterCallback = [](PeerData *peer) { - return peer->canWrite(); - }; - *box = Ui::show(Box<ShareBox>( - App::wnd()->sessionController(), - std::move(copyCallback), - std::move(submitCallback), - std::move(filterCallback))); -} - void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { const auto creating = data.link.isEmpty(); const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); using Fields = Ui::InviteLinkFields; const auto done = [=](Fields result) { const auto finish = [=](Api::InviteLink finished) { + if (creating) { + ShowInviteLinkBox(peer, finished); + } if (*box) { (*box)->closeBox(); } @@ -511,6 +409,7 @@ private: base::unique_qptr<Ui::PopupMenu> _menu; QString _offsetLink; + TimeId _offsetDate = 0; bool _requesting = false; bool _allLoaded = false; @@ -590,6 +489,7 @@ void Controller::loadMoreRows() { }; _peer->session().api().inviteLinks().requestMoreLinks( _peer, + _offsetDate, _offsetLink, _revoked, crl::guard(this, done)); @@ -602,6 +502,7 @@ void Controller::appendSlice(const InviteLinksSlice &slice) { appendRow(link, now); } _offsetLink = link.link; + _offsetDate = link.date; } if (slice.links.size() >= slice.count) { _allLoaded = true; @@ -610,7 +511,7 @@ void Controller::appendSlice(const InviteLinksSlice &slice) { } void Controller::rowClicked(not_null<PeerListRow*> row) { - // #TODO links show + ShowInviteLinkBox(_peer, static_cast<Row*>(row.get())->data()); } void Controller::rowActionClicked(not_null<PeerListRow*> row) { @@ -647,32 +548,16 @@ base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( }); } else { result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] { - CopyLink(link); + CopyInviteLink(link); }); result->addAction(tr::lng_group_invite_context_share(tr::now), [=] { - ShareLinkBox(_peer, link); + ShareInviteLinkBox(_peer, link); }); result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] { EditLink(_peer, data); }); result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] { - const auto box = std::make_shared<QPointer<ConfirmBox>>(); - const auto revoke = crl::guard(this, [=] { - const auto done = crl::guard(this, [=](InviteLinkData data) { - if (*box) { - (*box)->closeBox(); - } - }); - _peer->session().api().inviteLinks().revoke( - _peer, - link, - done); - }); - *box = Ui::show( - Box<ConfirmBox>( - tr::lng_group_invite_revoke_about(tr::now), - revoke), - Ui::LayerOption::KeepOther); + RevokeLink(_peer, link); }); } return result; @@ -800,178 +685,6 @@ void Controller::rowPaintIcon( } // namespace -void AddPermanentLinkBlock( - not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer) { - const auto computePermanentLink = [=] { - const auto &links = peer->session().api().inviteLinks().links( - peer).links; - const auto link = links.empty() ? nullptr : &links.front(); - return (link && link->permanent && !link->revoked) ? link : nullptr; - }; - auto value = peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::InviteLinks - ) | rpl::map([=] { - const auto link = computePermanentLink(); - return link - ? std::make_tuple(link->link, link->usage) - : std::make_tuple(QString(), 0); - }) | rpl::distinct_until_changed( - ) | rpl::start_spawning(container->lifetime()); - - const auto weak = Ui::MakeWeak(container); - const auto copyLink = crl::guard(weak, [=] { - if (const auto link = computePermanentLink()) { - CopyLink(link->link); - } - }); - const auto shareLink = crl::guard(weak, [=] { - if (const auto link = computePermanentLink()) { - ShareLinkBox(peer, link->link); - } - }); - const auto revokeLink = crl::guard(weak, [=] { - const auto box = std::make_shared<QPointer<ConfirmBox>>(); - const auto done = crl::guard(weak, [=] { - const auto close = [=](auto&&) { - if (*box) { - (*box)->closeBox(); - } - }; - peer->session().api().inviteLinks().revokePermanent(peer, close); - }); - *box = Ui::show( - Box<ConfirmBox>(tr::lng_group_invite_about_new(tr::now), done), - Ui::LayerOption::KeepOther); - }); - - auto link = rpl::duplicate( - value - ) | rpl::map([=](QString link, int usage) { - const auto prefix = qstr("https://"); - return link.startsWith(prefix) ? link.mid(prefix.size()) : link; - }); - const auto createMenu = [=] { - auto result = base::make_unique_q<Ui::PopupMenu>(container); - result->addAction( - tr::lng_group_invite_context_copy(tr::now), - copyLink); - result->addAction( - tr::lng_group_invite_context_share(tr::now), - shareLink); - result->addAction( - tr::lng_group_invite_context_revoke(tr::now), - revokeLink); - return result; - }; - const auto label = container->lifetime().make_state<Ui::InviteLinkLabel>( - container, - std::move(link), - createMenu); - container->add( - label->take(), - st::inviteLinkFieldPadding); - - label->clicks( - ) | rpl::start_with_next(copyLink, label->lifetime()); - - AddCopyShareLinkButtons( - container, - copyLink, - shareLink); - - struct JoinedState { - QImage cachedUserpics; - std::vector<HistoryView::UserpicInRow> list; - int count = 0; - bool allUserpicsLoaded = false; - rpl::variable<Ui::JoinedCountContent> content; - rpl::lifetime lifetime; - }; - const auto state = container->lifetime().make_state<JoinedState>(); - const auto push = [=] { - HistoryView::GenerateUserpicsInRow( - state->cachedUserpics, - state->list, - st::inviteLinkUserpics, - 0); - state->allUserpicsLoaded = ranges::all_of( - state->list, - [](const HistoryView::UserpicInRow &element) { - return !element.peer->hasUserpic() || element.view->image(); - }); - state->content = Ui::JoinedCountContent{ - .count = state->count, - .userpics = state->cachedUserpics - }; - }; - std::move( - value - ) | rpl::map([=](QString link, int usage) { - return peer->session().api().inviteLinks().joinedFirstSliceValue( - peer, - link, - usage); - }) | rpl::flatten_latest( - ) | rpl::start_with_next([=](const Api::JoinedByLinkSlice &slice) { - auto list = std::vector<HistoryView::UserpicInRow>(); - list.reserve(slice.users.size()); - for (const auto &item : slice.users) { - const auto i = ranges::find( - state->list, - item.user, - &HistoryView::UserpicInRow::peer); - if (i != end(state->list)) { - list.push_back(std::move(*i)); - } else { - list.push_back({ item.user }); - } - } - state->count = slice.count; - state->list = std::move(list); - push(); - }, state->lifetime); - - peer->session().downloaderTaskFinished( - ) | rpl::filter([=] { - return !state->allUserpicsLoaded; - }) | rpl::start_with_next([=] { - auto pushing = false; - state->allUserpicsLoaded = true; - for (const auto &element : state->list) { - if (!element.peer->hasUserpic()) { - continue; - } else if (element.peer->userpicUniqueKey(element.view) - != element.uniqueKey) { - pushing = true; - } else if (!element.view->image()) { - state->allUserpicsLoaded = false; - } - } - if (pushing) { - push(); - } - }, state->lifetime); - - Ui::AddJoinedCountButton( - container, - state->content.value(), - st::inviteLinkJoinedRowPadding - )->setClickedCallback([=] { - }); - - container->add(object_ptr<Ui::SlideWrap<Ui::FixedHeightWidget>>( - container, - object_ptr<Ui::FixedHeightWidget>( - container, - st::inviteLinkJoinedRowPadding.bottom())) - )->setDuration(0)->toggleOn(state->content.value( - ) | rpl::map([=](const Ui::JoinedCountContent &content) { - return (content.count <= 0); - })); -} - not_null<Ui::RpWidget*> AddLinksList( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, @@ -995,11 +708,14 @@ not_null<Ui::RpWidget*> AddLinksList( void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) { + using namespace Settings; + box->setTitle(tr::lng_group_invite_title()); const auto container = box->verticalLayout(); + AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); AddPermanentLinkBlock(container, peer); - Settings::AddDivider(container); + AddDivider(container); const auto add = AddCreateLinkButton(container); add->setClickedCallback([=] { @@ -1015,7 +731,8 @@ void ManageInviteLinksBox( container, tr::lng_group_invite_add_about(), st::boxDividerLabel), - st::settingsDividerLabelPadding))); + st::settingsDividerLabelPadding)), + style::margins(0, st::inviteLinkCreateSkip, 0, 0)); const auto divider = container->add(object_ptr<Ui::SlideWrap<>>( container, object_ptr<Ui::BoxContentDivider>(container))); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h index 365071e3a..09b2880c3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h @@ -11,14 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class PeerData; -namespace Ui { -class VerticalLayout; -} // namespace Ui - -void AddPermanentLinkBlock( - not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer); - void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, not_null<PeerData*> peer); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 6cdda9579..027953ac3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_info_box.h" // CreateButton. +#include "boxes/peers/edit_peer_invite_link.h" #include "boxes/peers/edit_peer_invite_links.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "core/application.h" @@ -541,13 +542,8 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { using namespace Settings; AddSkip(container); - container->add( - object_ptr<Ui::FlatLabel>( - container, - tr::lng_create_permanent_link_title(), - st::settingsSubsectionTitle), - st::settingsSubsectionTitlePadding); + AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); AddPermanentLinkBlock(container, _peer); AddSkip(container); diff --git a/Telegram/SourceFiles/history/view/history_view_send_action.cpp b/Telegram/SourceFiles/history/view/history_view_send_action.cpp index c7bd84f83..37869dd3c 100644 --- a/Telegram/SourceFiles/history/view/history_view_send_action.cpp +++ b/Telegram/SourceFiles/history/view/history_view_send_action.cpp @@ -105,6 +105,7 @@ bool SendActionPainter::updateNeedsAnimating( _speaking.emplace_or_assign( user, now + kStatusShowClientsideSpeaking); + }, [&](const MTPDsendMessageHistoryImportAction &) { }, [&](const MTPDsendMessageCancelAction &) { Unexpected("CancelAction here."); }); From 274779c1c8821041e2d083413fbdde2be803eb54 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 19 Jan 2021 23:25:18 +0400 Subject: [PATCH 155/396] Fix build on macOS. --- .../boxes/filters/edit_filter_chats_list.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index e0db55baa..30ea6e993 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -62,22 +62,6 @@ public: }; -class TypeDelegate final : public PeerListContentDelegate { -public: - void peerListSetTitle(rpl::producer<QString> title) override; - void peerListSetAdditionalTitle(rpl::producer<QString> title) override; - bool peerListIsRowChecked(not_null<PeerListRow*> row) override; - int peerListSelectedRowsCount() override; - void peerListScrollToTop() override; - void peerListAddSelectedPeerInBunch( - not_null<PeerData*> peer) override; - void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) override; - void peerListFinishSelectedRowsBunch() override; - void peerListSetDescription( - object_ptr<Ui::FlatLabel> description) override; - -}; - class TypeController final : public PeerListController { public: TypeController( From 34f7391ec9b2be9d6f372252fd7abaee758745fd Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 20 Jan 2021 15:40:53 +0400 Subject: [PATCH 156/396] Update API scheme. --- Telegram/Resources/tl/api.tl | 11 ++++++++--- Telegram/SourceFiles/apiwrap.cpp | 2 ++ .../SourceFiles/boxes/peers/edit_participants_box.cpp | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 68307dce9..4135cc33d 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -661,7 +661,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink; -messageFwdHeader#5f777dce flags:# from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader; +messageFwdHeader#5f777dce flags:# imported:flags.7?true from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader; auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType; @@ -1215,6 +1215,8 @@ messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector<User messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImporter> users:Vector<User> = messages.ChatInviteImporters; +messages.historyImport#1662af0b id:long = messages.HistoryImport; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1357,7 +1359,7 @@ messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; messages.editChatTitle#dc452855 chat_id:int title:string = Updates; messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates; messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates; -messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates; +messages.deleteChatUser#c534459a flags:# revoke_history:flags.0?true chat_id:int user_id:InputUser = Updates; messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; @@ -1471,6 +1473,9 @@ messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; +messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; +messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; +messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1522,7 +1527,7 @@ channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipant channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant; channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; -channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; +channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true for_import:flags.3?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 3a311b7a6..ebda54f7b 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1714,6 +1714,7 @@ void ApiWrap::kickParticipant( not_null<ChatData*> chat, not_null<UserData*> user) { request(MTPmessages_DeleteChatUser( + MTP_flags(0), chat->inputChat, user->inputUser )).done([=](const MTPUpdates &result) { @@ -2304,6 +2305,7 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer, bool revoke) { void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) { if (const auto chat = peer->asChat()) { request(MTPmessages_DeleteChatUser( + MTP_flags(0), chat->inputChat, _session->user()->inputUser )).done([=](const MTPUpdates &result) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index dd4e3587a..4adf4993f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -174,6 +174,7 @@ void SaveChatParticipantKick( Fn<void()> onDone, Fn<void()> onFail) { chat->session().api().request(MTPmessages_DeleteChatUser( + MTP_flags(0), chat->inputChat, user->inputUser )).done([=](const MTPUpdates &result) { From 19455d44db3396898e1a86e98cd59c18f20985e4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 20 Jan 2021 18:09:45 +0400 Subject: [PATCH 157/396] Add support for imported messages. --- Telegram/Resources/langs/lang.strings | 2 + .../SourceFiles/dialogs/dialogs_layout.cpp | 31 +++---- .../history/history_inner_widget.cpp | 12 +-- .../history/history_inner_widget.h | 2 - Telegram/SourceFiles/history/history_item.cpp | 7 +- .../history/history_item_components.cpp | 8 +- .../history/history_item_components.h | 3 +- .../SourceFiles/history/history_message.cpp | 6 +- .../history/view/history_view_element.cpp | 73 +++++++++++++--- .../history/view/history_view_element.h | 6 ++ .../history/view/history_view_list_widget.cpp | 11 +-- .../history/view/history_view_list_widget.h | 2 - .../history/view/history_view_message.cpp | 55 ++++++++---- .../history/view/history_view_message.h | 3 +- Telegram/SourceFiles/ui/empty_userpic.cpp | 87 +++++++++++++++---- Telegram/SourceFiles/ui/empty_userpic.h | 2 + 16 files changed, 211 insertions(+), 99 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1f581be7c..b99e97913 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1223,11 +1223,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}"; "lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_hidden" = "The account was hidden by the user."; +"lng_forwarded_imported" = "This message was imported from another app. It may not be real."; "lng_signed_author" = "Author: {user}"; "lng_in_reply_to" = "In reply to"; "lng_edited" = "edited"; "lng_edited_date" = "Edited: {date}"; "lng_sent_date" = "Sent: {date}"; +"lng_imported" = "imported"; "lng_admin_badge" = "admin"; "lng_owner_badge" = "owner"; "lng_channel_badge" = "channel"; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 8d52c7e4c..5b45e735f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -451,6 +451,12 @@ void paintRow( sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth); } + p.setFont(st::msgNameFont); + p.setPen(active + ? st::dialogsNameFgActive + : selected + ? st::dialogsNameFgOver + : st::dialogsNameFg); if (flags & (Flag::SavedMessages | Flag::RepliesMessages)) { auto text = (flags & Flag::SavedMessages) ? tr::lng_saved_messages(tr::now) @@ -459,12 +465,6 @@ void paintRow( if (textWidth > rectForName.width()) { text = st::msgNameFont->elided(text, rectForName.width()); } - p.setFont(st::msgNameFont); - p.setPen(active - ? st::dialogsNameFgActive - : selected - ? st::dialogsNameFgOver - : st::dialogsNameFg); p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text); } else if (from) { if (!(flags & Flag::SearchResult)) { @@ -488,22 +488,15 @@ void paintRow( badgeStyle); rectForName.setWidth(rectForName.width() - badgeWidth); } - p.setPen(active - ? st::dialogsNameFgActive - : selected - ? st::dialogsNameFgOver - : st::dialogsNameFg); from->nameText().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else if (hiddenSenderInfo) { hiddenSenderInfo->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else { - const auto nameFg = active - ? st::dialogsNameFgActive - : (selected + if (!active) { + p.setPen(selected ? st::dialogsArchiveFgOver : st::dialogsArchiveFg); - p.setPen(nameFg); - p.setFont(st::msgNameFont); + } auto text = entry->chatListName(); // TODO feed name with emoji auto textWidth = st::msgNameFont->width(text); if (textWidth > rectForName.width()) { @@ -825,8 +818,10 @@ void RowPainter::paint( const auto hiddenSenderInfo = [&]() -> const HiddenSenderInfo* { if (const auto searchChat = row->searchInChat()) { if (const auto peer = searchChat.peer()) { - if (peer->isSelf()) { - return item->hiddenForwardedInfo(); + if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (peer->isSelf() || forwarded->imported) { + return forwarded->hiddenSenderInfo.get(); + } } } } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 38ab5c227..93ef22236 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2779,10 +2779,7 @@ void HistoryInner::mouseActionUpdate() { const auto message = view->data()->toHistoryMessage(); Assert(message != nullptr); - const auto from = message->displayFrom(); - dragState = TextState(nullptr, from - ? from->openLink() - : hiddenUserpicLink(message->fullId())); + dragState = TextState(nullptr, view->fromPhotoLink()); _dragStateItem = session().data().message(dragState.itemId); lnkhost = view; return false; @@ -2923,13 +2920,6 @@ void HistoryInner::mouseActionUpdate() { } } -ClickHandlerPtr HistoryInner::hiddenUserpicLink(FullMsgId id) { - static const auto result = std::make_shared<LambdaClickHandler>([] { - Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now)); - }); - return result; -} - void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting) { if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) { return; diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 09b22bf1e..ab9ba5034 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -214,8 +214,6 @@ private: template <typename Method> void enumerateDates(Method method); - ClickHandlerPtr hiddenUserpicLink(FullMsgId id); - void scrollDateCheck(); void scrollDateHideByTimer(); bool canHaveFromUserpics() const; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 12b4b2c3b..c23a6e2df 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -273,9 +273,10 @@ HistoryItem *HistoryItem::lookupDiscussionPostOriginal() const { PeerData *HistoryItem::displayFrom() const { if (const auto sender = discussionPostOriginalSender()) { return sender; - } else if (history()->peer->isSelf() - || history()->peer->isRepliesChat()) { - return senderOriginal(); + } else if (const auto forwarded = Get<HistoryMessageForwarded>()) { + if (history()->peer->isSelf() || history()->peer->isRepliesChat() || forwarded->imported) { + return forwarded->originalSender; + } } return author().get(); } diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 59ccf1ca5..41281d1c7 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -112,10 +112,14 @@ int HistoryMessageEdited::maxWidth() const { return text.maxWidth(); } -HiddenSenderInfo::HiddenSenderInfo(const QString &name) +HiddenSenderInfo::HiddenSenderInfo(const QString &name, bool external) : name(name) , colorPeerId(Data::FakePeerIdForJustName(name)) -, userpic(Data::PeerUserpicColor(colorPeerId), name) { +, userpic( + Data::PeerUserpicColor(colorPeerId), + (external + ? Ui::EmptyUserpic::ExternalName() + : name)) { nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions()); const auto parts = name.trimmed().split(' ', base::QStringSkipEmptyParts); firstName = parts[0]; diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 36a7fc4cf..2061c4230 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -71,7 +71,7 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, Hist }; struct HiddenSenderInfo { - explicit HiddenSenderInfo(const QString &name); + HiddenSenderInfo(const QString &name, bool external); QString name; QString firstName; @@ -101,6 +101,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded PeerData *savedFromPeer = nullptr; MsgId savedFromMsgId = 0; + bool imported = false; }; struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 8c1a11eaf..605483c67 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -440,6 +440,7 @@ struct HistoryMessage::CreateConfig { QString authorOriginal; TimeId originalDate = 0; TimeId editDate = 0; + bool imported = false; // For messages created from MTP structs. const MTPMessageReplies *mtpReplies = nullptr; @@ -466,6 +467,7 @@ void HistoryMessage::FillForwardedInfo( config.savedFromPeer = peerFromMTP(*savedFromPeer); config.savedFromMsgId = savedFromMsgId->v; } + config.imported = data.is_imported(); } HistoryMessage::HistoryMessage( @@ -1118,7 +1120,8 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) { : nullptr; if (!forwarded->originalSender) { forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>( - config.senderNameOriginal); + config.senderNameOriginal, + config.imported); } forwarded->originalId = config.originalId; forwarded->originalAuthor = config.authorOriginal; @@ -1126,6 +1129,7 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) { forwarded->savedFromPeer = history()->owner().peerLoaded( config.savedFromPeer); forwarded->savedFromMsgId = config.savedFromMsgId; + forwarded->imported = config.imported; } void HistoryMessage::refreshMedia(const MTPMessageMedia *media) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 52a488d99..3d4d0303f 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -22,6 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "chat_helpers/stickers_emoji_pack.h" #include "window/window_session_controller.h" +#include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "data/data_session.h" #include "data/data_groups.h" #include "data/data_media_types.h" @@ -38,18 +40,19 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900; bool IsAttachedToPreviousInSavedMessages( not_null<HistoryItem*> previous, - not_null<HistoryItem*> item) { - const auto forwarded = previous->Has<HistoryMessageForwarded>(); + HistoryMessageForwarded *prevForwarded, + not_null<HistoryItem*> item, + HistoryMessageForwarded *forwarded) { const auto sender = previous->senderOriginal(); - if (forwarded != item->Has<HistoryMessageForwarded>()) { + if ((prevForwarded != nullptr) != (forwarded != nullptr)) { return false; } else if (sender != item->senderOriginal()) { return false; - } else if (!forwarded || sender) { + } else if (!prevForwarded || sender) { return true; } - const auto previousInfo = previous->hiddenForwardedInfo(); - const auto itemInfo = item->hiddenForwardedInfo(); + const auto previousInfo = prevForwarded->hiddenSenderInfo.get(); + const auto itemInfo = forwarded->hiddenSenderInfo.get(); Assert(previousInfo != nullptr); Assert(itemInfo != nullptr); return (*previousInfo == *itemInfo); @@ -174,14 +177,26 @@ QString DateTooltipText(not_null<Element*> view) { base::unixtime::parse(forwarded->originalDate).toString(format)); if (const auto media = view->media()) { if (media->hidesForwardedInfo()) { - dateText += '\n' + tr::lng_forwarded( - tr::now, - lt_user, - (forwarded->originalSender - ? forwarded->originalSender->shortName() - : forwarded->hiddenSenderInfo->firstName)); + const auto from = forwarded->originalSender + ? forwarded->originalSender->shortName() + : forwarded->hiddenSenderInfo->firstName; + if (forwarded->imported) { + dateText += '\n' + tr::lng_signed_author( + tr::now, + lt_user, + from); + } else { + dateText += '\n' + tr::lng_forwarded( + tr::now, + lt_user, + from); + } } } + if (forwarded->imported) { + dateText = tr::lng_forwarded_imported(tr::now) + + "\n\n" + dateText; + } } if (const auto msgsigned = view->data()->Get<HistoryMessageSigned>()) { if (msgsigned->isElided && !msgsigned->isAnonymousRank) { @@ -492,9 +507,17 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) { && mayBeAttached(item) && mayBeAttached(prev); if (possible) { + const auto forwarded = item->Get<HistoryMessageForwarded>(); + const auto prevForwarded = prev->Get<HistoryMessageForwarded>(); if (item->history()->peer->isSelf() - || item->history()->peer->isRepliesChat()) { - return IsAttachedToPreviousInSavedMessages(prev, item); + || item->history()->peer->isRepliesChat() + || (forwarded && forwarded->imported) + || (prevForwarded && prevForwarded->imported)) { + return IsAttachedToPreviousInSavedMessages( + prev, + prevForwarded, + item, + forwarded); } else { return prev->from() == item->from(); } @@ -503,6 +526,28 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) { return false; } +ClickHandlerPtr Element::fromLink() const { + const auto item = data(); + const auto from = item->displayFrom(); + if (from) { + return from->openLink(); + } + if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (forwarded->imported) { + static const auto imported = std::make_shared<LambdaClickHandler>([] { + Ui::ShowMultilineToast({ + .text = { tr::lng_forwarded_imported(tr::now) }, + }); + }); + return imported; + } + } + static const auto hidden = std::make_shared<LambdaClickHandler>([] { + Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now)); + }); + return hidden; +} + void Element::createUnreadBar(rpl::producer<QString> text) { if (!AddComponents(UnreadBar::Bit())) { return; diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 2515e9e91..54b4b4ad4 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -325,6 +325,10 @@ public: void previousInBlocksChanged(); void nextInBlocksRemoved(); + [[nodiscard]] ClickHandlerPtr fromPhotoLink() const { + return fromLink(); + } + virtual ~Element(); protected: @@ -332,6 +336,8 @@ protected: Painter &p, int geometryHeight) const; + [[nodiscard]] ClickHandlerPtr fromLink() const; + virtual void refreshDataIdHook(); private: diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 49089a05a..c639b9bcf 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2387,9 +2387,7 @@ void ListWidget::mouseActionUpdate() { Assert(message != nullptr); const auto from = message->displayFrom(); - dragState = TextState(nullptr, from - ? from->openLink() - : hiddenUserpicLink(message->fullId())); + dragState = TextState(nullptr, view->fromPhotoLink()); _overItemExact = session().data().message(dragState.itemId); lnkhost = view; return false; @@ -2461,13 +2459,6 @@ void ListWidget::mouseActionUpdate() { //} // #TODO select scroll } -ClickHandlerPtr ListWidget::hiddenUserpicLink(FullMsgId id) { - static const auto result = std::make_shared<LambdaClickHandler>([] { - Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now)); - }); - return result; -} - style::cursor ListWidget::computeMouseCursor() const { if (ClickHandler::getPressed() || ClickHandler::getActive()) { return style::cur_pointer; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 90b65f9af..d01e5dd06 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -472,8 +472,6 @@ private: template <typename Method> void enumerateDates(Method method); - ClickHandlerPtr hiddenUserpicLink(FullMsgId id); - static constexpr auto kMinimalIdsLimit = 24; const not_null<ListDelegate*> _delegate; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 3c2465a08..0511958f4 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_group_call_tracker.h" // UserpicInRow. #include "history/history.h" #include "ui/effects/ripple_animation.h" +#include "base/unixtime.h" #include "core/application.h" #include "core/core_settings.h" #include "ui/toast/toast.h" @@ -1181,9 +1182,10 @@ void Message::unloadHeavyPart() { _comments = nullptr; } -bool Message::showForwardsFromSender() const { +bool Message::showForwardsFromSender( + not_null<HistoryMessageForwarded*> forwarded) const { const auto peer = message()->history()->peer; - return peer->isSelf() || peer->isRepliesChat(); + return peer->isSelf() || peer->isRepliesChat() || forwarded->imported; } bool Message::hasFromPhoto() const { @@ -1204,8 +1206,10 @@ bool Message::hasFromPhoto() const { return false; } else if (Core::App().settings().chatWide()) { return true; - } else if (showForwardsFromSender()) { - return item->Has<HistoryMessageForwarded>(); + } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (showForwardsFromSender(forwarded)) { + return true; + } } return !item->out() && !item->history()->peer->isUser(); } break; @@ -1436,10 +1440,7 @@ bool Message::getStateFromName( if (point.x() >= availableLeft && point.x() < availableLeft + availableWidth && point.x() < availableLeft + nameText->maxWidth()) { - static const auto hidden = std::make_shared<LambdaClickHandler>([] { - Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now)); - }); - outResult->link = from ? from->openLink() : hidden; + outResult->link = fromLink(); return true; } auto via = item->Get<HistoryMessageVia>(); @@ -2068,9 +2069,17 @@ bool Message::hasFromName() const { case Context::Pinned: case Context::Replies: { const auto item = message(); - return (!hasOutLayout() || item->from()->isMegagroup()) - && (!item->history()->peer->isUser() - || showForwardsFromSender()); + if (hasOutLayout() && !item->from()->isMegagroup()) { + return false; + } else if (!item->history()->peer->isUser()) { + return true; + } + if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (showForwardsFromSender(forwarded)) { + return true; + } + } + return false; } break; case Context::ContactPreview: return false; @@ -2087,10 +2096,10 @@ bool Message::displayFromName() const { bool Message::displayForwardedFrom() const { const auto item = message(); - if (showForwardsFromSender()) { - return false; - } if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (showForwardsFromSender(forwarded)) { + return false; + } if (const auto sender = item->discussionPostOriginalSender()) { if (sender == forwarded->originalSender) { return false; @@ -2111,8 +2120,10 @@ bool Message::hasOutLayout() const { const auto item = message(); if (item->history()->peer->isSelf()) { return !item->Has<HistoryMessageForwarded>(); - } else if (showForwardsFromSender()) { - return false; + } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (showForwardsFromSender(forwarded)) { + return false; + } } return item->out() && !item->isPost(); } @@ -2217,7 +2228,7 @@ bool Message::displayFastShare() const { return !peer->isMegagroup(); } else if (const auto user = peer->asUser()) { if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { - return !showForwardsFromSender() + return !showForwardsFromSender(forwarded) && !item->out() && forwarded->originalSender && forwarded->originalSender->isChannel() @@ -2701,7 +2712,15 @@ void Message::initTime() { } else if (const auto edited = displayedEditBadge()) { item->_timeWidth = edited->maxWidth(); } else { - item->_timeText = dateTime().toString(cTimeFormat()); + const auto forwarded = item->Get<HistoryMessageForwarded>(); + if (forwarded && forwarded->imported) { + const auto date = base::unixtime::parse(forwarded->originalDate); + item->_timeText = date.toString( + u"d.MM.yy "_q + cTimeFormat() + ' ' + ) + tr::lng_imported(tr::now); + } else { + item->_timeText = dateTime().toString(cTimeFormat()); + } item->_timeWidth = st::msgDateFont->width(item->_timeText); } if (item->_text.hasSkipBlock()) { diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index d07f8a076..132c3420f 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -125,7 +125,8 @@ private: void refreshEditedBadge(); void fromNameUpdated(int width) const; - [[nodiscard]] bool showForwardsFromSender() const; + [[nodiscard]] bool showForwardsFromSender( + not_null<HistoryMessageForwarded*> forwarded) const; [[nodiscard]] TextSelection skipTextSelection( TextSelection selection) const; [[nodiscard]] TextSelection unskipTextSelection( diff --git a/Telegram/SourceFiles/ui/empty_userpic.cpp b/Telegram/SourceFiles/ui/empty_userpic.cpp index 9b4497f11..bdf4551d4 100644 --- a/Telegram/SourceFiles/ui/empty_userpic.cpp +++ b/Telegram/SourceFiles/ui/empty_userpic.cpp @@ -13,16 +13,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "app.h" #include "styles/style_chat.h" #include "styles/style_dialogs.h" +#include "styles/style_widgets.h" // style::IconButton +#include "styles/style_info.h" // st::topBarCall namespace Ui { namespace { +[[nodiscard]] bool IsExternal(const QString &name) { + return !name.isEmpty() + && (name.front() == QChar(0)) + && QStringView(name).mid(1) == qstr("external"); +} + void PaintSavedMessagesInner( Painter &p, int x, int y, int size, - const style::color &bg, const style::color &fg) { // |<----width----->| // @@ -92,27 +99,28 @@ void PaintSavedMessagesInner( } } -void PaintRepliesMessagesInner( +void PaintIconInner( Painter &p, int x, int y, int size, - const style::color &bg, + int defaultSize, + const style::icon &icon, const style::color &fg) { - if (size == st::dialogsPhotoSize) { + if (size == defaultSize) { const auto rect = QRect{ x, y, size, size }; - st::dialogsRepliesUserpic.paintInCenter( + icon.paintInCenter( p, rect, fg->c); } else { p.save(); - const auto ratio = size / float64(st::dialogsPhotoSize); + const auto ratio = size / float64(defaultSize); p.translate(x + size / 2., y + size / 2.); p.scale(ratio, ratio); - const auto skip = st::dialogsPhotoSize; + const auto skip = defaultSize; const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip }; - st::dialogsRepliesUserpic.paintInCenter( + icon.paintInCenter( p, rect, fg->c); @@ -120,6 +128,38 @@ void PaintRepliesMessagesInner( } } +void PaintRepliesMessagesInner( + Painter &p, + int x, + int y, + int size, + const style::color &fg) { + PaintIconInner( + p, + x, + y, + size, + st::dialogsPhotoSize, + st::dialogsRepliesUserpic, + fg); +} + +void PaintExternalMessagesInner( + Painter &p, + int x, + int y, + int size, + const style::color &fg) { + PaintIconInner( + p, + x, + y, + size, + st::msgPhotoSize, + st::topBarCall.icon, + fg); +} + template <typename Callback> [[nodiscard]] QPixmap Generate(int size, Callback callback) { auto result = QImage( @@ -141,6 +181,10 @@ EmptyUserpic::EmptyUserpic(const style::color &color, const QString &name) fillString(name); } +QString EmptyUserpic::ExternalName() { + return QChar(0) + u"external"_q; +} + template <typename Callback> void EmptyUserpic::paint( Painter &p, @@ -160,10 +204,17 @@ void EmptyUserpic::paint( p.setPen(Qt::NoPen); paintBackground(); - p.setFont(font); - p.setBrush(Qt::NoBrush); - p.setPen(st::historyPeerUserpicFg); - p.drawText(QRect(x, y, size, size), _string, QTextOption(style::al_center)); + if (IsExternal(_string)) { + PaintExternalMessagesInner(p, x, y, size, st::historyPeerUserpicFg); + } else { + p.setFont(font); + p.setBrush(Qt::NoBrush); + p.setPen(st::historyPeerUserpicFg); + p.drawText( + QRect(x, y, size, size), + _string, + QTextOption(style::al_center)); + } } void EmptyUserpic::paint( @@ -226,7 +277,7 @@ void EmptyUserpic::PaintSavedMessages( p.setPen(Qt::NoPen); p.drawEllipse(x, y, size, size); - PaintSavedMessagesInner(p, x, y, size, bg, fg); + PaintSavedMessagesInner(p, x, y, size, fg); } void EmptyUserpic::PaintSavedMessagesRounded( @@ -244,7 +295,7 @@ void EmptyUserpic::PaintSavedMessagesRounded( p.setPen(Qt::NoPen); p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall); - PaintSavedMessagesInner(p, x, y, size, bg, fg); + PaintSavedMessagesInner(p, x, y, size, fg); } QPixmap EmptyUserpic::GenerateSavedMessages(int size) { @@ -296,7 +347,7 @@ void EmptyUserpic::PaintRepliesMessages( p.setPen(Qt::NoPen); p.drawEllipse(x, y, size, size); - PaintRepliesMessagesInner(p, x, y, size, bg, fg); + PaintRepliesMessagesInner(p, x, y, size, fg); } void EmptyUserpic::PaintRepliesMessagesRounded( @@ -314,7 +365,7 @@ void EmptyUserpic::PaintRepliesMessagesRounded( p.setPen(Qt::NoPen); p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall); - PaintRepliesMessagesInner(p, x, y, size, bg, fg); + PaintRepliesMessagesInner(p, x, y, size, fg); } QPixmap EmptyUserpic::GenerateRepliesMessages(int size) { @@ -349,6 +400,10 @@ QPixmap EmptyUserpic::generate(int size) { } void EmptyUserpic::fillString(const QString &name) { + if (IsExternal(name)) { + _string = name; + return; + } QList<QString> letters; QList<int> levels; diff --git a/Telegram/SourceFiles/ui/empty_userpic.h b/Telegram/SourceFiles/ui/empty_userpic.h index c49ceb2dc..0480e8611 100644 --- a/Telegram/SourceFiles/ui/empty_userpic.h +++ b/Telegram/SourceFiles/ui/empty_userpic.h @@ -11,6 +11,8 @@ namespace Ui { class EmptyUserpic { public: + [[nodiscard]] static QString ExternalName(); + EmptyUserpic(const style::color &color, const QString &name); void paint( From 1774b21e88a8f9f35cc0223a74e52fac672aa49f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 21 Jan 2021 08:20:29 +0400 Subject: [PATCH 158/396] Add ability to completely delete legacy group. --- Telegram/SourceFiles/boxes/confirm_box.cpp | 6 +++--- .../SourceFiles/boxes/peers/edit_peer_invite_links.cpp | 2 +- Telegram/SourceFiles/data/data_histories.cpp | 9 ++++++++- Telegram/SourceFiles/data/data_peer.cpp | 2 ++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index e5b4f766d..224f7ec2c 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -601,9 +601,7 @@ void DeleteMessagesBox::prepare() { deleteText = _wipeHistoryPeer->isUser() ? tr::lng_box_delete() : tr::lng_box_leave(); - deleteStyle = &(peer->isChannel() - ? st::defaultBoxButton - : st::attentionBoxButton); + deleteStyle = &st::attentionBoxButton; } if (auto revoke = revokeText(peer)) { _revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox); @@ -702,6 +700,8 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const tr::now, lt_user, user->firstName); + } else if (_wipeHistoryJustClear) { + return std::nullopt; } else { result.checkbox = tr::lng_delete_for_everyone_check(tr::now); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 91d0751ec..4f8f08f5a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -748,7 +748,7 @@ void ManageInviteLinksBox( const auto deleteAll = Ui::CreateChild<Ui::LinkButton>( container.get(), tr::lng_group_invite_context_delete_all(tr::now), - st::boxLinkButton); + st::defaultLinkButton); rpl::combine( header->topValue(), container->widthValue() diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 69ba4ddbc..da2efa02c 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_channel.h" +#include "data/data_chat.h" #include "data/data_folder.h" #include "data/data_scheduled_messages.h" #include "main/main_session.h" @@ -599,11 +600,17 @@ void Histories::deleteAllMessages( )).done([=](const MTPBool &result) { finish(); }).fail(fail).send(); + } else if (revoke && peer->isChat() && peer->asChat()->amCreator()) { + return session().api().request(MTPmessages_DeleteChat( + peer->asChat()->inputChat + )).done([=](const MTPBool &result) { + finish(); + }).fail(fail).send(); } else { using Flag = MTPmessages_DeleteHistory::Flag; const auto flags = Flag(0) | (justClear ? Flag::f_just_clear : Flag(0)) - | ((peer->isUser() && revoke) ? Flag::f_revoke : Flag(0)); + | (revoke ? Flag::f_revoke : Flag(0)); return session().api().request(MTPmessages_DeleteHistory( MTP_flags(flags), peer->input, diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 75edd778e..ac3f2074c 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -825,6 +825,8 @@ bool PeerData::canRevokeFullHistory() const { && (!user->isBot() || user->isSupport()) && session().serverConfig().revokePrivateInbox && (session().serverConfig().revokePrivateTimeLimit == 0x7FFFFFFF); + } else if (const auto chat = asChat()) { + return chat->amCreator(); } return false; } From 58733ba6ea5c439f758da065775b909c7429bc87 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 21 Jan 2021 16:39:40 +0400 Subject: [PATCH 159/396] Add support for FAKE badge. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/Resources/tl/api.tl | 7 +- Telegram/SourceFiles/data/data_channel.h | 4 ++ Telegram/SourceFiles/data/data_peer.cpp | 9 +++ Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/data/data_user.h | 30 ++++---- .../SourceFiles/dialogs/dialogs_layout.cpp | 30 +++++--- .../info/profile/info_profile_cover.cpp | 70 ++++++++----------- .../info/profile/info_profile_cover.h | 8 ++- .../info/profile/info_profile_values.cpp | 34 +++++---- .../info/profile/info_profile_values.h | 10 ++- Telegram/SourceFiles/ui/unread_badge.cpp | 23 +++--- Telegram/SourceFiles/ui/unread_badge.h | 3 +- 13 files changed, 136 insertions(+), 94 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b99e97913..ba3f25c76 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -126,6 +126,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_channel_status" = "channel"; "lng_group_status" = "group"; "lng_scam_badge" = "SCAM"; +"lng_fake_badge" = "FAKE"; "lng_flood_error" = "Too many tries. Please try again later."; "lng_gif_error" = "An error has occurred while reading GIF animation :("; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 4135cc33d..75208c727 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -109,7 +109,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; userEmpty#200250ba id:int = User; -user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; +user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; @@ -124,7 +124,7 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; @@ -1217,6 +1217,8 @@ messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImpor messages.historyImport#1662af0b id:long = messages.HistoryImport; +messages.historyImportParsed#8d94ab42 flags:# pm:flags.0?true group:flags.1?true title:flags.1?string = messages.HistoryImportParsed; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1473,6 +1475,7 @@ messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; +messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index c478fab2a..5c248072b 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -94,6 +94,7 @@ public: | MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_scam + | MTPDchannel::Flag::f_fake | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_restricted | MTPDchannel::Flag::f_signatures @@ -203,6 +204,9 @@ public: [[nodiscard]] bool isScam() const { return flags() & MTPDchannel::Flag::f_scam; } + [[nodiscard]] bool isFake() const { + return flags() & MTPDchannel::Flag::f_fake; + } static MTPChatBannedRights KickedRestrictedRights(); static constexpr auto kRestrictUntilForever = TimeId(INT_MAX); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index ac3f2074c..2ca4a756d 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -747,6 +747,15 @@ bool PeerData::isScam() const { return false; } +bool PeerData::isFake() const { + if (const auto user = asUser()) { + return user->isFake(); + } else if (const auto channel = asChannel()) { + return channel->isFake(); + } + return false; +} + bool PeerData::isMegagroup() const { return isChannel() ? asChannel()->isMegagroup() : false; } diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index cb38944d4..72b4593ee 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -158,6 +158,7 @@ public: } [[nodiscard]] bool isVerified() const; [[nodiscard]] bool isScam() const; + [[nodiscard]] bool isFake() const; [[nodiscard]] bool isMegagroup() const; [[nodiscard]] bool isBroadcast() const; [[nodiscard]] bool isRepliesChat() const; diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 39665a968..a925afcac 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -50,6 +50,7 @@ public: | MTPDuser::Flag::f_bot_nochats | MTPDuser::Flag::f_verified | MTPDuser::Flag::f_scam + | MTPDuser::Flag::f_fake | MTPDuser::Flag::f_restricted | MTPDuser::Flag::f_bot_inline_geo; using Flags = Data::Flags< @@ -111,48 +112,51 @@ public: void removeFullFlags(MTPDuserFull::Flags which) { _fullFlags.remove(which); } - auto fullFlags() const { + [[nodiscard]] auto fullFlags() const { return _fullFlags.current(); } - auto fullFlagsValue() const { + [[nodiscard]] auto fullFlagsValue() const { return _fullFlags.value(); } - bool isVerified() const { + [[nodiscard]] bool isVerified() const { return flags() & MTPDuser::Flag::f_verified; } - bool isScam() const { + [[nodiscard]] bool isScam() const { return flags() & MTPDuser::Flag::f_scam; } - bool isBotInlineGeo() const { + [[nodiscard]] bool isFake() const { + return flags() & MTPDuser::Flag::f_fake; + } + [[nodiscard]] bool isBotInlineGeo() const { return flags() & MTPDuser::Flag::f_bot_inline_geo; } - bool isBot() const { + [[nodiscard]] bool isBot() const { return botInfo != nullptr; } - bool isSupport() const { + [[nodiscard]] bool isSupport() const { return flags() & MTPDuser::Flag::f_support; } - bool isInaccessible() const { + [[nodiscard]] bool isInaccessible() const { constexpr auto inaccessible = 0 | MTPDuser::Flag::f_deleted; // | MTPDuser_ClientFlag::f_inaccessible; return flags() & inaccessible; } - bool canWrite() const { + [[nodiscard]] bool canWrite() const { // Duplicated in Data::CanWriteValue(). return !isInaccessible() && !isRepliesChat(); } - bool canShareThisContact() const; - bool canAddContact() const { + [[nodiscard]] bool canShareThisContact() const; + [[nodiscard]] bool canAddContact() const { return canShareThisContact() && !isContact(); } // In Data::Session::processUsers() we check only that. // When actually trying to share contact we perform // a full check by canShareThisContact() call. - bool canShareThisContactFast() const { + [[nodiscard]] bool canShareThisContactFast() const { return !_phone.isEmpty(); } @@ -161,7 +165,7 @@ public: QString firstName; QString lastName; QString username; - const QString &phone() const { + [[nodiscard]] const QString &phone() const { return _phone; } QString nameOrPhone; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 5b45e735f..7aee35922 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -452,11 +452,6 @@ void paintRow( } p.setFont(st::msgNameFont); - p.setPen(active - ? st::dialogsNameFgActive - : selected - ? st::dialogsNameFgOver - : st::dialogsNameFg); if (flags & (Flag::SavedMessages | Flag::RepliesMessages)) { auto text = (flags & Flag::SavedMessages) ? tr::lng_saved_messages(tr::now) @@ -465,6 +460,11 @@ void paintRow( if (textWidth > rectForName.width()) { text = st::msgNameFont->elided(text, rectForName.width()); } + p.setPen(active + ? st::dialogsNameFgActive + : selected + ? st::dialogsNameFgOver + : st::dialogsNameFg); p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text); } else if (from) { if (!(flags & Flag::SearchResult)) { @@ -488,15 +488,25 @@ void paintRow( badgeStyle); rectForName.setWidth(rectForName.width() - badgeWidth); } + p.setPen(active + ? st::dialogsNameFgActive + : selected + ? st::dialogsNameFgOver + : st::dialogsNameFg); from->nameText().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else if (hiddenSenderInfo) { + p.setPen(active + ? st::dialogsNameFgActive + : selected + ? st::dialogsNameFgOver + : st::dialogsNameFg); hiddenSenderInfo->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else { - if (!active) { - p.setPen(selected - ? st::dialogsArchiveFgOver - : st::dialogsArchiveFg); - } + p.setPen(active + ? st::dialogsNameFgActive + : selected + ? st::dialogsArchiveFgOver + : st::dialogsArchiveFg); auto text = entry->chatListName(); // TODO feed name with emoji auto textWidth = st::msgNameFont->width(text); if (textWidth > rectForName.width()) { diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index ad7e4cd0a..6fb26e7b3 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -322,15 +322,10 @@ void Cover::initViewers(rpl::producer<QString> title) { } else if (_peer->isSelf()) { refreshUploadPhotoOverlay(); } - VerifiedValue( + BadgeValue( _peer - ) | rpl::start_with_next([=](bool verified) { - setVerified(verified); - }, lifetime()); - ScamValue( - _peer - ) | rpl::start_with_next([=](bool scam) { - setScam(scam); + ) | rpl::start_with_next([=](Badge badge) { + setBadge(badge); }, lifetime()); } @@ -345,50 +340,45 @@ void Cover::refreshUploadPhotoOverlay() { }()); } -void Cover::setVerified(bool verified) { - if ((_verifiedCheck != nullptr) == verified) { +void Cover::setBadge(Badge badge) { + if (_badge == badge) { return; } - if (verified) { - _scamBadge.destroy(); + _badge = badge; + _verifiedCheck.destroy(); + _scamFakeBadge.destroy(); + switch (_badge) { + case Badge::Verified: _verifiedCheck.create(this); _verifiedCheck->show(); _verifiedCheck->resize(st::infoVerifiedCheck.size()); _verifiedCheck->paintRequest( - ) | rpl::start_with_next([check = _verifiedCheck.data()] { + ) | rpl::start_with_next([check = _verifiedCheck.data()]{ Painter p(check); st::infoVerifiedCheck.paint(p, 0, 0, check->width()); - }, _verifiedCheck->lifetime()); - } else { - _verifiedCheck.destroy(); - } - refreshNameGeometry(width()); -} - -void Cover::setScam(bool scam) { - if ((_scamBadge != nullptr) == scam) { - return; - } - if (scam) { - _verifiedCheck.destroy(); - const auto size = Ui::ScamBadgeSize(); + }, _verifiedCheck->lifetime()); + break; + case Badge::Scam: + case Badge::Fake: { + const auto fake = (_badge == Badge::Fake); + const auto size = Ui::ScamBadgeSize(fake); const auto skip = st::infoVerifiedCheckPosition.x(); - _scamBadge.create(this); - _scamBadge->show(); - _scamBadge->resize( + _scamFakeBadge.create(this); + _scamFakeBadge->show(); + _scamFakeBadge->resize( size.width() + 2 * skip, size.height() + 2 * skip); - _scamBadge->paintRequest( - ) | rpl::start_with_next([=, badge = _scamBadge.data()] { + _scamFakeBadge->paintRequest( + ) | rpl::start_with_next([=, badge = _scamFakeBadge.data()]{ Painter p(badge); Ui::DrawScamBadge( + fake, p, badge->rect().marginsRemoved({ skip, skip, skip, skip }), badge->width(), st::attentionButtonFg); - }, _scamBadge->lifetime()); - } else { - _scamBadge.destroy(); + }, _scamFakeBadge->lifetime()); + } break; } refreshNameGeometry(width()); } @@ -452,9 +442,9 @@ void Cover::refreshNameGeometry(int newWidth) { if (_verifiedCheck) { nameWidth -= st::infoVerifiedCheckPosition.x() + _verifiedCheck->width(); - } else if (_scamBadge) { + } else if (_scamFakeBadge) { nameWidth -= st::infoVerifiedCheckPosition.x() - + _scamBadge->width(); + + _scamFakeBadge->width(); } _name->resizeToNaturalWidth(nameWidth); _name->moveToLeft(nameLeft, nameTop, newWidth); @@ -465,15 +455,15 @@ void Cover::refreshNameGeometry(int newWidth) { const auto checkTop = nameTop + st::infoVerifiedCheckPosition.y(); _verifiedCheck->moveToLeft(checkLeft, checkTop, newWidth); - } else if (_scamBadge) { + } else if (_scamFakeBadge) { const auto skip = st::infoVerifiedCheckPosition.x(); const auto badgeLeft = nameLeft + _name->width() + st::infoVerifiedCheckPosition.x() - skip; const auto badgeTop = nameTop - + (_name->height() - _scamBadge->height()) / 2; - _scamBadge->moveToLeft(badgeLeft, badgeTop, newWidth); + + (_name->height() - _scamFakeBadge->height()) / 2; + _scamFakeBadge->moveToLeft(badgeLeft, badgeTop, newWidth); } } diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h index 35018c6ef..6e0b8a845 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.h +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h @@ -34,6 +34,8 @@ class Section; namespace Info { namespace Profile { +enum class Badge; + class SectionWithToggle : public Ui::FixedHeightWidget { public: using FixedHeightWidget::FixedHeightWidget; @@ -85,16 +87,16 @@ private: void refreshNameGeometry(int newWidth); void refreshStatusGeometry(int newWidth); void refreshUploadPhotoOverlay(); - void setVerified(bool verified); - void setScam(bool scam); + void setBadge(Badge badge); not_null<PeerData*> _peer; int _onlineCount = 0; + Badge _badge = Badge(); object_ptr<Ui::UserpicButton> _userpic; object_ptr<Ui::FlatLabel> _name = { nullptr }; object_ptr<Ui::RpWidget> _verifiedCheck = { nullptr }; - object_ptr<Ui::RpWidget> _scamBadge = { nullptr }; + object_ptr<Ui::RpWidget> _scamFakeBadge = { nullptr }; object_ptr<Ui::FlatLabel> _status = { nullptr }; //object_ptr<CoverDropArea> _dropArea = { nullptr }; base::Timer _refreshStatusTimer; diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index dc31b11d9..cf3fb4b51 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -376,27 +376,31 @@ rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer) { return rpl::single(false); } -rpl::producer<bool> VerifiedValue(not_null<PeerData*> peer) { - if (const auto user = peer->asUser()) { - return Data::PeerFlagValue(user, MTPDuser::Flag::f_verified); - } else if (const auto channel = peer->asChannel()) { - return Data::PeerFlagValue( - channel, - MTPDchannel::Flag::f_verified); - } - return rpl::single(false); +template <typename Flag, typename Peer> +rpl::producer<Badge> BadgeValueFromFlags(Peer peer) { + return Data::PeerFlagsValue( + peer, + Flag::f_verified | Flag::f_scam | Flag::f_fake + ) | rpl::map([=](base::flags<Flag> value) { + return (value & Flag::f_verified) + ? Badge::Verified + : (value & Flag::f_scam) + ? Badge::Scam + : (value & Flag::f_fake) + ? Badge::Fake + : Badge::None; + }); } -rpl::producer<bool> ScamValue(not_null<PeerData*> peer) { +rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer) { if (const auto user = peer->asUser()) { - return Data::PeerFlagValue(user, MTPDuser::Flag::f_scam); + return BadgeValueFromFlags<MTPDuser::Flag>(user); } else if (const auto channel = peer->asChannel()) { - return Data::PeerFlagValue( - channel, - MTPDchannel::Flag::f_scam); + return BadgeValueFromFlags<MTPDchannel::Flag>(channel); } - return rpl::single(false); + return rpl::single(Badge::None); } + // // #feed //rpl::producer<int> FeedChannelsCountValue(not_null<Data::Feed*> feed) { // using Flag = Data::FeedUpdateFlag; diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index da87867f6..d60111088 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -59,8 +59,14 @@ rpl::producer<int> SharedMediaCountValue( Storage::SharedMediaType type); rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user); rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer); -rpl::producer<bool> VerifiedValue(not_null<PeerData*> peer); -rpl::producer<bool> ScamValue(not_null<PeerData*> peer); + +enum class Badge { + None, + Verified, + Scam, + Fake, +}; +rpl::producer<Badge> BadgeValue(not_null<PeerData*> peer); //rpl::producer<int> FeedChannelsCountValue(not_null<Data::Feed*> feed); // #feed diff --git a/Telegram/SourceFiles/ui/unread_badge.cpp b/Telegram/SourceFiles/ui/unread_badge.cpp index 23762262a..732c14247 100644 --- a/Telegram/SourceFiles/ui/unread_badge.cpp +++ b/Telegram/SourceFiles/ui/unread_badge.cpp @@ -48,8 +48,10 @@ void UnreadBadge::paintEvent(QPaintEvent *e) { unreadSt); } -QSize ScamBadgeSize() { - const auto phrase = tr::lng_scam_badge(tr::now); +QSize ScamBadgeSize(bool fake) { + const auto phrase = fake + ? tr::lng_fake_badge(tr::now) + : tr::lng_scam_badge(tr::now); const auto phraseWidth = st::dialogsScamFont->width(phrase); const auto width = st::dialogsScamPadding.left() + phraseWidth @@ -60,7 +62,7 @@ QSize ScamBadgeSize() { return { width, height }; } -void DrawScamBadge( +void DrawScamFakeBadge( Painter &p, QRect rect, int outerWidth, @@ -83,12 +85,15 @@ void DrawScamBadge( } void DrawScamBadge( + bool fake, Painter &p, QRect rect, int outerWidth, const style::color &color) { - const auto phrase = tr::lng_scam_badge(tr::now); - DrawScamBadge( + const auto phrase = fake + ? tr::lng_fake_badge(tr::now) + : tr::lng_scam_badge(tr::now); + DrawScamFakeBadge( p, rect, outerWidth, @@ -112,8 +117,10 @@ int DrawPeerBadgeGetWidth( rectForName.y(), outerWidth); return iconw; - } else if (peer->isScam() && st.scam) { - const auto phrase = tr::lng_scam_badge(tr::now); + } else if ((peer->isScam() || peer->isFake()) && st.scam) { + const auto phrase = peer->isScam() + ? tr::lng_scam_badge(tr::now) + : tr::lng_fake_badge(tr::now); const auto phraseWidth = st::dialogsScamFont->width(phrase); const auto width = st::dialogsScamPadding.left() + phraseWidth @@ -129,7 +136,7 @@ int DrawPeerBadgeGetWidth( rectForName.y() + (rectForName.height() - height) / 2, width, height); - DrawScamBadge(p, rect, outerWidth, *st.scam, phrase, phraseWidth); + DrawScamFakeBadge(p, rect, outerWidth, *st.scam, phrase, phraseWidth); return st::dialogsScamSkip + width; } return 0; diff --git a/Telegram/SourceFiles/ui/unread_badge.h b/Telegram/SourceFiles/ui/unread_badge.h index 426c53dec..7cd1a790c 100644 --- a/Telegram/SourceFiles/ui/unread_badge.h +++ b/Telegram/SourceFiles/ui/unread_badge.h @@ -38,8 +38,9 @@ int DrawPeerBadgeGetWidth( int nameWidth, int outerWidth, const PeerBadgeStyle &st); -QSize ScamBadgeSize(); +QSize ScamBadgeSize(bool fake); void DrawScamBadge( + bool fake, Painter &p, QRect rect, int outerWidth, From 417428b21d9b6016ba3271a0b9e9ad0d8a95a89d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 21 Jan 2021 17:31:27 +0400 Subject: [PATCH 160/396] Allow deleting small groups for everyone. --- Telegram/SourceFiles/apiwrap.cpp | 4 +-- Telegram/SourceFiles/boxes/confirm_box.cpp | 19 +++++++--- Telegram/SourceFiles/data/data_histories.cpp | 38 +++++++++++++++++--- Telegram/SourceFiles/data/data_peer.cpp | 4 +++ 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index ebda54f7b..23c5e8994 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2350,14 +2350,14 @@ void ApiWrap::deleteHistory( deleteTillId = history->lastMessage()->id; } if (const auto channel = peer->asChannel()) { - if (!justClear) { + if (!justClear && !revoke) { channel->ptsWaitingForShortPoll(-1); leaveChannel(channel); } else { if (const auto migrated = peer->migrateFrom()) { deleteHistory(migrated, justClear, revoke); } - if (IsServerMsgId(deleteTillId)) { + if (IsServerMsgId(deleteTillId) || (!justClear && revoke)) { history->owner().histories().deleteAllMessages( history, deleteTillId, diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 224f7ec2c..0e66771b8 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -578,7 +578,8 @@ void DeleteMessagesBox::prepare() { const auto appendDetails = [&](TextWithEntities &&text) { details.append(qstr("\n\n")).append(std::move(text)); }; - auto deleteText = tr::lng_box_delete(); + auto deleteText = lifetime().make_state<rpl::variable<QString>>(); + *deleteText = tr::lng_box_delete(); auto deleteStyle = &st::defaultBoxButton; if (const auto peer = _wipeHistoryPeer) { if (_wipeHistoryJustClear) { @@ -598,14 +599,22 @@ void DeleteMessagesBox::prepare() { : peer->isMegagroup() ? tr::lng_sure_leave_group(tr::now) : tr::lng_sure_leave_channel(tr::now); - deleteText = _wipeHistoryPeer->isUser() - ? tr::lng_box_delete() - : tr::lng_box_leave(); + if (!peer->isUser()) { + *deleteText = tr::lng_box_leave(); + } deleteStyle = &st::attentionBoxButton; } if (auto revoke = revokeText(peer)) { _revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox); appendDetails(std::move(revoke->description)); + if (!peer->isUser() && !_wipeHistoryJustClear) { + _revoke->checkedValue( + ) | rpl::start_with_next([=](bool revokeForAll) { + *deleteText = revokeForAll + ? tr::lng_box_delete() + : tr::lng_box_leave(); + }, _revoke->lifetime()); + } } } else if (_moderateFrom) { Assert(_moderateInChannel != nullptr); @@ -642,7 +651,7 @@ void DeleteMessagesBox::prepare() { _text.create(this, rpl::single(std::move(details)), st::boxLabel); addButton( - std::move(deleteText), + deleteText->value(), [=] { deleteAndClear(); }, *deleteStyle); addButton(tr::lng_cancel(), [=] { closeBox(); }); diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index da2efa02c..268d7a8d8 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -593,19 +593,49 @@ void Histories::deleteAllMessages( const auto fail = [=](const RPCError &error) { finish(); }; - if (const auto channel = peer->asChannel()) { + const auto chat = peer->asChat(); + const auto channel = peer->asChannel(); + if (revoke && channel && channel->canDelete()) { + return session().api().request(MTPchannels_DeleteChannel( + channel->inputChannel + )).done([=](const MTPUpdates &result) { + session().api().applyUpdates(result); + //}).fail([=](const RPCError &error) { + // if (error.type() == qstr("CHANNEL_TOO_LARGE")) { + // Ui::show(Box<InformBox>(tr::lng_cant_delete_channel(tr::now))); + // } + }).send(); + } else if (channel) { return session().api().request(MTPchannels_DeleteHistory( channel->inputChannel, MTP_int(deleteTillId) )).done([=](const MTPBool &result) { finish(); }).fail(fail).send(); - } else if (revoke && peer->isChat() && peer->asChat()->amCreator()) { + } else if (revoke && chat && chat->amCreator()) { return session().api().request(MTPmessages_DeleteChat( - peer->asChat()->inputChat + chat->inputChat )).done([=](const MTPBool &result) { finish(); - }).fail(fail).send(); + }).fail([=](const RPCError &error) { + if (error.type() == "PEER_ID_INVALID") { + // Try to join and delete, + // while delete fails for non-joined. + session().api().request(MTPmessages_AddChatUser( + chat->inputChat, + MTP_inputUserSelf(), + MTP_int(0) + )).done([=](const MTPUpdates &updates) { + session().api().applyUpdates(updates); + deleteAllMessages( + history, + deleteTillId, + justClear, + revoke); + }).send(); + } + finish(); + }).send(); } else { using Flag = MTPmessages_DeleteHistory::Flag; const auto flags = Flag(0) diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 2ca4a756d..5d36a346c 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -836,6 +836,10 @@ bool PeerData::canRevokeFullHistory() const { && (session().serverConfig().revokePrivateTimeLimit == 0x7FFFFFFF); } else if (const auto chat = asChat()) { return chat->amCreator(); + } else if (const auto megagroup = asMegagroup()) { + return megagroup->amCreator() + && megagroup->membersCountKnown() + && megagroup->canDelete(); } return false; } From ff9bf23461def0bfd3882cf4f53ddab20181f041 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 21 Jan 2021 17:46:46 +0400 Subject: [PATCH 161/396] Don't show checks for incoming service messages. --- Telegram/SourceFiles/history/history_service.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 55403cdd9..4d6dd048d 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -792,8 +792,9 @@ bool HistoryService::updateDependencyItem() { } bool HistoryService::needCheck() const { - return (GetDependentData() != nullptr) - || Has<HistoryServiceSelfDestruct>(); + return out() + && ((GetDependentData() != nullptr) + || Has<HistoryServiceSelfDestruct>()); } QString HistoryService::inDialogsText(DrawInDialog way) const { From b2c84d675c89fddda793d7e3513b9a381db20d87 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 21 Jan 2021 19:57:12 +0400 Subject: [PATCH 162/396] Allow clearing calls log. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/apiwrap.h | 2 +- .../calls/calls_box_controller.cpp | 83 ++++++++++++++++-- .../SourceFiles/calls/calls_box_controller.h | 9 ++ Telegram/SourceFiles/core/core_settings.cpp | 4 + Telegram/SourceFiles/core/core_settings.h | 4 +- .../SourceFiles/data/data_media_types.cpp | 5 ++ Telegram/SourceFiles/data/data_media_types.h | 1 + Telegram/SourceFiles/data/data_session.cpp | 14 +++ Telegram/SourceFiles/data/data_session.h | 5 ++ .../SourceFiles/settings/settings_calls.cpp | 86 +++++++++---------- .../SourceFiles/window/window_main_menu.cpp | 44 +++++++--- 13 files changed, 200 insertions(+), 64 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ba3f25c76..f84486dca 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1830,6 +1830,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_call_box_status_yesterday" = "Yesterday at {time}"; "lng_call_box_status_date" = "{date} at {time}"; "lng_call_box_status_group" = "({amount}) {status}"; +"lng_call_box_clear_all" = "Clear All"; +"lng_call_box_clear_sure" = "Are you sure you want to completely clear your calls log?"; +"lng_call_box_clear_button" = "Clear"; "lng_call_outgoing" = "Outgoing call"; "lng_call_video_outgoing" = "Outgoing video call"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 23c5e8994..c459ffcf5 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2386,10 +2386,10 @@ void ApiWrap::applyUpdates( } int ApiWrap::applyAffectedHistory( - not_null<PeerData*> peer, + PeerData *peer, const MTPmessages_AffectedHistory &result) { const auto &data = result.c_messages_affectedHistory(); - if (const auto channel = peer->asChannel()) { + if (const auto channel = peer ? peer->asChannel() : nullptr) { channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v); } else { updates().updateAndApply(data.vpts().v, data.vpts_count().v); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 47697939f..69ac68f7b 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -154,7 +154,7 @@ public: const MTPUpdates &updates, uint64 sentMessageRandomId = 0); int applyAffectedHistory( - not_null<PeerData*> peer, + PeerData *peer, // May be nullptr, like for deletePhoneCallHistory. const MTPmessages_AffectedHistory &result); void registerModifyRequest(const QString &key, mtpRequestId requestId); diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index f671469aa..1f52f2438 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -7,10 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_box_controller.h" -#include "styles/style_calls.h" -#include "styles/style_boxes.h" #include "lang/lang_keys.h" #include "ui/effects/ripple_animation.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/popup_menu.h" #include "core/application.h" #include "calls/calls_instance.h" #include "history/history.h" @@ -22,7 +23,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_media_types.h" #include "data/data_user.h" +#include "boxes/confirm_box.h" #include "app.h" +#include "apiwrap.h" +#include "styles/style_layers.h" // st::boxLabel. +#include "styles/style_calls.h" +#include "styles/style_boxes.h" namespace Calls { namespace { @@ -49,6 +55,7 @@ public: bool canAddItem(not_null<const HistoryItem*> item) const { return (ComputeType(item) == _type) + && (!hasItems() || _items.front()->history() == item->history()) && (ItemDateTime(item).date() == _date); } void addItem(not_null<HistoryItem*> item) { @@ -66,20 +73,26 @@ public: refreshStatus(); } } - bool hasItems() const { + [[nodiscard]] bool hasItems() const { return !_items.empty(); } - MsgId minItemId() const { + [[nodiscard]] MsgId minItemId() const { Expects(hasItems()); + return _items.back()->id; } - MsgId maxItemId() const { + [[nodiscard]] MsgId maxItemId() const { Expects(hasItems()); + return _items.front()->id; } + [[nodiscard]] const std::vector<not_null<HistoryItem*>> &items() const { + return _items; + } + void paintStatusText( Painter &p, const style::PeerListItem &st, @@ -333,6 +346,20 @@ void BoxController::loadMoreRows() { }).send(); } +base::unique_qptr<Ui::PopupMenu> BoxController::rowContextMenu( + QWidget *parent, + not_null<PeerListRow*> row) { + const auto &items = static_cast<Row*>(row.get())->items(); + const auto session = &this->session(); + const auto ids = session->data().itemsToIds(items); + + auto result = base::make_unique_q<Ui::PopupMenu>(parent); + result->addAction(tr::lng_context_delete_selected(tr::now), [=] { + Ui::show(Box<DeleteMessagesBox>(session, base::duplicate(ids))); + }); + return result; +} + void BoxController::refreshAbout() { setDescriptionText(delegate()->peerListFullRowsCount() ? QString() : tr::lng_call_box_about(tr::now)); } @@ -448,4 +475,50 @@ std::unique_ptr<PeerListRow> BoxController::createRow( return std::make_unique<Row>(item); } +void ClearCallsBox( + not_null<Ui::GenericBox*> box, + not_null<Window::SessionController*> window) { + const auto weak = Ui::MakeWeak(box); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_call_box_clear_sure(), + st::boxLabel), + st::boxPadding); + const auto revokeCheckbox = box->addRow( + object_ptr<Ui::Checkbox>( + box, + tr::lng_delete_for_everyone_check(tr::now), + false, + st::defaultBoxCheckbox), + style::margins( + st::boxPadding.left(), + st::boxPadding.bottom(), + st::boxPadding.right(), + st::boxPadding.bottom())); + + const auto api = &window->session().api(); + const auto sendRequest = [=](bool revoke, auto self) -> void { + using Flag = MTPmessages_DeletePhoneCallHistory::Flag; + api->request(MTPmessages_DeletePhoneCallHistory( + MTP_flags(revoke ? Flag::f_revoke : Flag(0)) + )).done([=](const MTPmessages_AffectedHistory &result) { + const auto offset = api->applyAffectedHistory(nullptr, result); + if (offset > 0) { + self(revoke, self); + } else { + api->session().data().destroyAllCallItems(); + if (const auto strong = weak.data()) { + strong->closeBox(); + } + } + }).send(); + }; + + box->addButton(tr::lng_call_box_clear_button(), [=] { + sendRequest(revokeCheckbox->checked(), sendRequest); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_box_controller.h b/Telegram/SourceFiles/calls/calls_box_controller.h index ae5894df4..a4bd41804 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.h +++ b/Telegram/SourceFiles/calls/calls_box_controller.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "boxes/peer_list_box.h" +#include "ui/layers/generic_box.h" namespace Window { class SessionController; @@ -25,6 +26,10 @@ public: void rowActionClicked(not_null<PeerListRow*> row) override; void loadMoreRows() override; + base::unique_qptr<Ui::PopupMenu> rowContextMenu( + QWidget *parent, + not_null<PeerListRow*> row) override; + private: void receivedCalls(const QVector<MTPMessage> &result); void refreshAbout(); @@ -49,4 +54,8 @@ private: }; +void ClearCallsBox( + not_null<Ui::GenericBox*> box, + not_null<Window::SessionController*> window); + } // namespace Calls diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index ca70b71c6..8940fdf6e 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -436,6 +436,10 @@ void Settings::setTabbedReplacedWithInfo(bool enabled) { } } +Webrtc::Backend Settings::callAudioBackend() const { + return Webrtc::Backend::OpenAL; +} + void Settings::setDialogsWidthRatio(float64 ratio) { _dialogsWidthRatio = ratio; } diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index a476dde4b..33a092770 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -221,9 +221,7 @@ public: void setCallAudioDuckingEnabled(bool value) { _callAudioDuckingEnabled = value; } - [[nodiscard]] Webrtc::Backend callAudioBackend() const { - return _callAudioBackend; - } + [[nodiscard]] Webrtc::Backend callAudioBackend() const; void setCallAudioBackend(Webrtc::Backend backend) { _callAudioBackend = backend; } diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index d32e043cb..e1df3afb9 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -892,6 +892,11 @@ MediaCall::MediaCall( const MTPDmessageActionPhoneCall &call) : Media(parent) , _call(ComputeCallData(call)) { + parent->history()->owner().registerCallItem(parent); +} + +MediaCall::~MediaCall() { + parent()->history()->owner().unregisterCallItem(parent()); } std::unique_ptr<Media> MediaCall::clone(not_null<HistoryItem*> parent) { diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index ed139bbcf..b99cdd065 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -274,6 +274,7 @@ public: MediaCall( not_null<HistoryItem*> parent, const MTPDmessageActionPhoneCall &call); + ~MediaCall(); std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index beee6604c..3c3be4212 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -3412,6 +3412,20 @@ void Session::unregisterContactItem( } } +void Session::registerCallItem(not_null<HistoryItem*> item) { + _callItems.emplace(item); +} + +void Session::unregisterCallItem(not_null<HistoryItem*> item) { + _callItems.erase(item); +} + +void Session::destroyAllCallItems() { + while (!_callItems.empty()) { + (*_callItems.begin())->destroy(); + } +} + void Session::documentMessageRemoved(not_null<DocumentData*> document) { if (_documentItems.find(document) != _documentItems.end()) { return; diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index ef3308820..4192aff98 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -358,6 +358,8 @@ public: not_null<HistoryItem*> dependent, not_null<HistoryItem*> dependency); + void destroyAllCallItems(); + void registerMessageRandomId(uint64 randomId, FullMsgId itemId); void unregisterMessageRandomId(uint64 randomId); [[nodiscard]] FullMsgId messageIdByRandomId(uint64 randomId) const; @@ -570,6 +572,8 @@ public: void unregisterContactItem( UserId contactId, not_null<HistoryItem*> item); + void registerCallItem(not_null<HistoryItem*> item); + void unregisterCallItem(not_null<HistoryItem*> item); void documentMessageRemoved(not_null<DocumentData*> document); @@ -908,6 +912,7 @@ private: std::unordered_map< UserId, base::flat_set<not_null<ViewElement*>>> _contactViews; + std::unordered_set<not_null<HistoryItem*>> _callItems; base::flat_set<not_null<WebPageData*>> _webpagesUpdated; base::flat_set<not_null<GameData*>> _gamesUpdated; diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index bbc0676b8..abcb8b49b 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -259,22 +259,22 @@ void Calls::setupContent() { // }, content->lifetime()); //#endif // Q_OS_MAC && !OS_MAC_STORE - const auto backend = [&]() -> QString { - using namespace Webrtc; - switch (settings.callAudioBackend()) { - case Backend::OpenAL: return "OpenAL"; - case Backend::ADM: return "WebRTC ADM"; - case Backend::ADM2: return "WebRTC ADM2"; - } - Unexpected("Value in backend."); - }(); - AddButton( - content, - rpl::single("Call audio backend: " + backend), - st::settingsButton - )->addClickHandler([] { - Ui::show(ChooseAudioBackendBox()); - }); + //const auto backend = [&]() -> QString { + // using namespace Webrtc; + // switch (settings.callAudioBackend()) { + // case Backend::OpenAL: return "OpenAL"; + // case Backend::ADM: return "WebRTC ADM"; + // case Backend::ADM2: return "WebRTC ADM2"; + // } + // Unexpected("Value in backend."); + //}(); + //AddButton( + // content, + // rpl::single("Call audio backend: " + backend), + // st::settingsButton + //)->addClickHandler([] { + // Ui::show(ChooseAudioBackendBox()); + //}); AddButton( content, tr::lng_settings_call_open_system_prefs(), @@ -418,33 +418,33 @@ object_ptr<SingleChoiceBox> ChooseAudioInputBox( radioSt); } -object_ptr<SingleChoiceBox> ChooseAudioBackendBox( - const style::Checkbox *st, - const style::Radio *radioSt) { - const auto &settings = Core::App().settings(); - const auto list = GetAudioInputList(settings.callAudioBackend()); - const auto options = std::vector<QString>{ - "OpenAL", - "Webrtc ADM", -#ifdef Q_OS_WIN - "Webrtc ADM2", -#endif // Q_OS_WIN - }; - const auto currentOption = static_cast<int>(settings.callAudioBackend()); - const auto save = [=](int option) { - Core::App().settings().setCallAudioBackend( - static_cast<Webrtc::Backend>(option)); - Core::App().saveSettings(); - App::restart(); - }; - return Box<SingleChoiceBox>( - rpl::single<QString>("Calls audio backend"), - options, - currentOption, - save, - st, - radioSt); -} +//object_ptr<SingleChoiceBox> ChooseAudioBackendBox( +// const style::Checkbox *st, +// const style::Radio *radioSt) { +// const auto &settings = Core::App().settings(); +// const auto list = GetAudioInputList(settings.callAudioBackend()); +// const auto options = std::vector<QString>{ +// "OpenAL", +// "Webrtc ADM", +//#ifdef Q_OS_WIN +// "Webrtc ADM2", +//#endif // Q_OS_WIN +// }; +// const auto currentOption = static_cast<int>(settings.callAudioBackend()); +// const auto save = [=](int option) { +// Core::App().settings().setCallAudioBackend( +// static_cast<Webrtc::Backend>(option)); +// Core::App().saveSettings(); +// App::restart(); +// }; +// return Box<SingleChoiceBox>( +// rpl::single<QString>("Calls audio backend"), +// options, +// currentOption, +// save, +// st, +// radioSt); +//} } // namespace Settings diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 14faab386..655ba1706 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_dialogs.h" #include "styles/style_settings.h" #include "styles/style_boxes.h" +#include "styles/style_info.h" // infoTopBarMenu #include "styles/style_layers.h" #include <QtGui/QWindow> @@ -96,6 +97,38 @@ constexpr auto kMinDiffIntensity = 0.25; return (modifiers & Qt::ShiftModifier) && (modifiers & Qt::AltModifier); } +void ShowCallsBox(not_null<Window::SessionController*> window) { + auto controller = std::make_unique<Calls::BoxController>(window); + const auto initBox = [=](not_null<PeerListBox*> box) { + box->addButton(tr::lng_close(), [=] { + box->closeBox(); + }); + using MenuPointer = base::unique_qptr<Ui::PopupMenu>; + const auto menu = std::make_shared<MenuPointer>(); + const auto menuButton = box->addTopButton(st::infoTopBarMenu); + menuButton->setClickedCallback([=] { + *menu = base::make_unique_q<Ui::PopupMenu>(menuButton); + const auto showSettings = [=] { + window->showSettings( + Settings::Type::Calls, + Window::SectionShow(anim::type::instant)); + }; + const auto clearAll = crl::guard(box, [=] { + box->getDelegate()->show(Box(Calls::ClearCallsBox, window)); + }); + (*menu)->addAction( + tr::lng_settings_section_call_settings(tr::now), + showSettings); + (*menu)->addAction( + tr::lng_call_box_clear_all(tr::now), + clearAll); + (*menu)->popup(QCursor::pos()); + return true; + }); + }; + Ui::show(Box<PeerListBox>(std::move(controller), initBox)); +} + } // namespace namespace Window { @@ -869,16 +902,7 @@ void MainMenu::refreshMenu() { }, &st::mainMenuContacts, &st::mainMenuContactsOver); if (_controller->session().serverConfig().phoneCallsEnabled.current()) { _menu->addAction(tr::lng_menu_calls(tr::now), [=] { - Ui::show(Box<PeerListBox>(std::make_unique<Calls::BoxController>(controller), [=](not_null<PeerListBox*> box) { - box->addButton(tr::lng_close(), [=] { - box->closeBox(); - }); - box->addTopButton(st::callSettingsButton, [=] { - controller->showSettings( - Settings::Type::Calls, - Window::SectionShow(anim::type::instant)); - }); - })); + ShowCallsBox(controller); }, &st::mainMenuCalls, &st::mainMenuCallsOver); } } else { From 7410c1fc7329f0e593e53c73ef5e26de5dad3fd3 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 22 Jan 2021 21:39:09 +0400 Subject: [PATCH 163/396] Fix display of imported messages in private chats. --- .../history/view/history_view_message.cpp | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 0511958f4..e2392905a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1185,7 +1185,9 @@ void Message::unloadHeavyPart() { bool Message::showForwardsFromSender( not_null<HistoryMessageForwarded*> forwarded) const { const auto peer = message()->history()->peer; - return peer->isSelf() || peer->isRepliesChat() || forwarded->imported; + return peer->isSelf() + || peer->isRepliesChat() + || forwarded->imported; } bool Message::hasFromPhoto() const { @@ -1202,12 +1204,13 @@ bool Message::hasFromPhoto() const { const auto item = message(); if (item->isPost() || item->isEmpty() - || (context() == Context::Replies && data()->isDiscussionPost())) { + || (context() == Context::Replies && item->isDiscussionPost())) { return false; } else if (Core::App().settings().chatWide()) { return true; } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { - if (showForwardsFromSender(forwarded)) { + const auto peer = item->history()->peer; + if (peer->isSelf() || peer->isRepliesChat()) { return true; } } @@ -2069,13 +2072,17 @@ bool Message::hasFromName() const { case Context::Pinned: case Context::Replies: { const auto item = message(); + const auto peer = item->history()->peer; if (hasOutLayout() && !item->from()->isMegagroup()) { return false; - } else if (!item->history()->peer->isUser()) { + } else if (!peer->isUser()) { return true; } if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { - if (showForwardsFromSender(forwarded)) { + if (forwarded->imported + && peer.get() == forwarded->originalSender) { + return false; + } else if (showForwardsFromSender(forwarded)) { return true; } } @@ -2121,8 +2128,12 @@ bool Message::hasOutLayout() const { if (item->history()->peer->isSelf()) { return !item->Has<HistoryMessageForwarded>(); } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { - if (showForwardsFromSender(forwarded)) { - return false; + if (!forwarded->imported + || !forwarded->originalSender + || !forwarded->originalSender->isSelf()) { + if (showForwardsFromSender(forwarded)) { + return false; + } } } return item->out() && !item->isPost(); @@ -2716,7 +2727,7 @@ void Message::initTime() { if (forwarded && forwarded->imported) { const auto date = base::unixtime::parse(forwarded->originalDate); item->_timeText = date.toString( - u"d.MM.yy "_q + cTimeFormat() + ' ' + u"d.MM.yy, "_q + cTimeFormat() + ' ' ) + tr::lng_imported(tr::now); } else { item->_timeText = dateTime().toString(cTimeFormat()); From 36ad24bfcdeff237e273c4c0396b508049860288 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 25 Jan 2021 17:42:02 +0400 Subject: [PATCH 164/396] Update API scheme. --- Telegram/Resources/tl/api.tl | 22 +- Telegram/SourceFiles/api/api_invite_links.cpp | 396 +++++++++--------- Telegram/SourceFiles/api/api_invite_links.h | 15 +- .../SourceFiles/boxes/add_contact_box.cpp | 1 - .../boxes/peers/edit_peer_info_box.cpp | 46 +- .../boxes/peers/edit_peer_invite_link.cpp | 31 +- .../boxes/peers/edit_peer_type_box.cpp | 28 +- .../SourceFiles/calls/calls_group_call.cpp | 13 +- 8 files changed, 271 insertions(+), 281 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 75208c727..d188e3293 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -217,7 +217,7 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; -peerSettings#733f2961 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true geo_distance:flags.6?int = PeerSettings; +peerSettings#733f2961 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true geo_distance:flags.6?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#8af40b25 flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -229,6 +229,7 @@ inputReportReasonChildAbuse#adf44ee3 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; +inputReportReasonFake#f5ddd6e7 = ReportReason; userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; @@ -1195,7 +1196,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#b881f32b flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int muted_cnt:flags.8?int = GroupCallParticipant; +groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall; @@ -1207,17 +1208,9 @@ inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType; inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType; inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType; -chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; - -messages.exportedChatInvites#bdc62dcc count:int invites:Vector<ExportedChatInvite> users:Vector<User> = messages.ExportedChatInvites; - -messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector<User> = messages.ExportedChatInvite; - -messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImporter> users:Vector<User> = messages.ChatInviteImporters; - messages.historyImport#1662af0b id:long = messages.HistoryImport; -messages.historyImportParsed#8d94ab42 flags:# pm:flags.0?true group:flags.1?true title:flags.1?string = messages.HistoryImportParsed; +messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true title:flags.2?string = messages.HistoryImportParsed; ---functions--- @@ -1378,7 +1371,7 @@ messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; -messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; +messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1468,11 +1461,6 @@ messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; -messages.getExportedChatInvites#6a72ac6c flags:# revoked:flags.3?true peer:InputPeer admin_id:flags.0?InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; -messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; -messages.deleteRevokedExportedChatInvites#52041463 peer:InputPeer = Bool; -messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; -messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.deleteChat#83247d11 chat_id:int = Bool; messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index bdc2c8edb..038581b02 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -41,26 +41,27 @@ void RemovePermanent(PeerInviteLinks &links) { } // namespace -JoinedByLinkSlice ParseJoinedByLinkSlice( - not_null<PeerData*> peer, - const MTPmessages_ChatInviteImporters &slice) { - auto result = JoinedByLinkSlice(); - slice.match([&](const MTPDmessages_chatInviteImporters &data) { - auto &owner = peer->session().data(); - owner.processUsers(data.vusers()); - result.count = data.vcount().v; - result.users.reserve(data.vimporters().v.size()); - for (const auto importer : data.vimporters().v) { - importer.match([&](const MTPDchatInviteImporter &data) { - result.users.push_back({ - .user = owner.user(data.vuser_id().v), - .date = data.vdate().v, - }); - }); - } - }); - return result; -} +// #TODO links +//JoinedByLinkSlice ParseJoinedByLinkSlice( +// not_null<PeerData*> peer, +// const MTPmessages_ChatInviteImporters &slice) { +// auto result = JoinedByLinkSlice(); +// slice.match([&](const MTPDmessages_chatInviteImporters &data) { +// auto &owner = peer->session().data(); +// owner.processUsers(data.vusers()); +// result.count = data.vcount().v; +// result.users.reserve(data.vimporters().v.size()); +// for (const auto importer : data.vimporters().v) { +// importer.match([&](const MTPDchatInviteImporter &data) { +// result.users.push_back({ +// .user = owner.user(data.vuser_id().v), +// .date = data.vdate().v, +// }); +// }); +// } +// }); +// return result; +//} InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) { } @@ -91,16 +92,19 @@ void InviteLinks::performCreate( callbacks.push_back(std::move(done)); } - using Flag = MTPmessages_ExportChatInvite::Flag; + // #TODO links + //using Flag = MTPmessages_ExportChatInvite::Flag; + //_api->request(MTPmessages_ExportChatInvite( + // MTP_flags((revokeLegacyPermanent + // ? Flag::f_legacy_revoke_permanent + // : Flag(0)) + // | (expireDate ? Flag::f_expire_date : Flag(0)) + // | (usageLimit ? Flag::f_usage_limit : Flag(0))), + // peer->input, + // MTP_int(expireDate), + // MTP_int(usageLimit) _api->request(MTPmessages_ExportChatInvite( - MTP_flags((revokeLegacyPermanent - ? Flag::f_legacy_revoke_permanent - : Flag(0)) - | (expireDate ? Flag::f_expire_date : Flag(0)) - | (usageLimit ? Flag::f_usage_limit : Flag(0))), - peer->input, - MTP_int(expireDate), - MTP_int(usageLimit) + peer->input )).done([=](const MTPExportedChatInvite &result) { const auto callbacks = _createCallbacks.take(peer); const auto link = prepend(peer, result); @@ -215,51 +219,51 @@ void InviteLinks::performEdit( if (done) { callbacks.push_back(std::move(done)); } - - using Flag = MTPmessages_EditExportedChatInvite::Flag; - _api->request(MTPmessages_EditExportedChatInvite( - MTP_flags((revoke ? Flag::f_revoked : Flag(0)) - | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) - | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), - peer->input, - MTP_string(link), - MTP_int(expireDate), - MTP_int(usageLimit) - )).done([=](const MTPmessages_ExportedChatInvite &result) { - const auto callbacks = _editCallbacks.take(key); - const auto peer = key.peer; - result.match([&](const MTPDmessages_exportedChatInvite &data) { - _api->session().data().processUsers(data.vusers()); - const auto link = parse(peer, data.vinvite()); - auto i = _firstSlices.find(peer); - if (i != end(_firstSlices)) { - const auto j = ranges::find( - i->second.links, - key.link, - &Link::link); - if (j != end(i->second.links)) { - if (link.revoked && !j->revoked) { - i->second.links.erase(j); - if (i->second.count > 0) { - --i->second.count; - } - } else { - *j = link; - } - } - } - for (const auto &callback : *callbacks) { - callback(link); - } - _updates.fire(Update{ - .peer = peer, - .was = key.link, - .now = link, - }); - }); - }).fail([=](const RPCError &error) { - _editCallbacks.erase(key); - }).send(); + // #TODO links + //using Flag = MTPmessages_EditExportedChatInvite::Flag; + //_api->request(MTPmessages_EditExportedChatInvite( + // MTP_flags((revoke ? Flag::f_revoked : Flag(0)) + // | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) + // | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), + // peer->input, + // MTP_string(link), + // MTP_int(expireDate), + // MTP_int(usageLimit) + //)).done([=](const MTPmessages_ExportedChatInvite &result) { + // const auto callbacks = _editCallbacks.take(key); + // const auto peer = key.peer; + // result.match([&](const MTPDmessages_exportedChatInvite &data) { + // _api->session().data().processUsers(data.vusers()); + // const auto link = parse(peer, data.vinvite()); + // auto i = _firstSlices.find(peer); + // if (i != end(_firstSlices)) { + // const auto j = ranges::find( + // i->second.links, + // key.link, + // &Link::link); + // if (j != end(i->second.links)) { + // if (link.revoked && !j->revoked) { + // i->second.links.erase(j); + // if (i->second.count > 0) { + // --i->second.count; + // } + // } else { + // *j = link; + // } + // } + // } + // for (const auto &callback : *callbacks) { + // callback(link); + // } + // _updates.fire(Update{ + // .peer = peer, + // .was = key.link, + // .now = link, + // }); + // }); + //}).fail([=](const RPCError &error) { + // _editCallbacks.erase(key); + //}).send(); } void InviteLinks::revoke( @@ -293,24 +297,24 @@ void InviteLinks::destroy( if (done) { callbacks.push_back(std::move(done)); } - - _api->request(MTPmessages_DeleteExportedChatInvite( - peer->input, - MTP_string(link) - )).done([=](const MTPBool &result) { - const auto callbacks = _deleteCallbacks.take(key); - if (callbacks) { - for (const auto &callback : *callbacks) { - callback(); - } - } - _updates.fire(Update{ - .peer = peer, - .was = key.link, - }); - }).fail([=](const RPCError &error) { - _deleteCallbacks.erase(key); - }).send(); + // #TODO links + //_api->request(MTPmessages_DeleteExportedChatInvite( + // peer->input, + // MTP_string(link) + //)).done([=](const MTPBool &result) { + // const auto callbacks = _deleteCallbacks.take(key); + // if (callbacks) { + // for (const auto &callback : *callbacks) { + // callback(); + // } + // } + // _updates.fire(Update{ + // .peer = peer, + // .was = key.link, + // }); + //}).fail([=](const RPCError &error) { + // _deleteCallbacks.erase(key); + //}).send(); } void InviteLinks::destroyAllRevoked( @@ -327,61 +331,62 @@ void InviteLinks::destroyAllRevoked( if (done) { callbacks.push_back(std::move(done)); } - - _api->request(MTPmessages_DeleteRevokedExportedChatInvites( - peer->input - )).done([=](const MTPBool &result) { - if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { - for (const auto &callback : *callbacks) { - callback(); - } - } - _allRevokedDestroyed.fire_copy(peer); - }).fail([=](const RPCError &error) { - }).send(); + // #TODO links + //_api->request(MTPmessages_DeleteRevokedExportedChatInvites( + // peer->input + //)).done([=](const MTPBool &result) { + // if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { + // for (const auto &callback : *callbacks) { + // callback(); + // } + // } + // _allRevokedDestroyed.fire_copy(peer); + //}).fail([=](const RPCError &error) { + //}).send(); } void InviteLinks::requestLinks(not_null<PeerData*> peer) { if (_firstSliceRequests.contains(peer)) { return; } - const auto requestId = _api->request(MTPmessages_GetExportedChatInvites( - MTP_flags(0), - peer->input, - MTPInputUser(), // admin_id - MTPint(), // offset_date - MTPstring(), // offset_link - MTP_int(kFirstPage) - )).done([=](const MTPmessages_ExportedChatInvites &result) { - _firstSliceRequests.remove(peer); - auto slice = parseSlice(peer, result); - auto i = _firstSlices.find(peer); - const auto permanent = (i != end(_firstSlices)) - ? lookupPermanent(i->second) - : nullptr; - if (!permanent) { - BringPermanentToFront(slice); - const auto j = _firstSlices.emplace_or_assign( - peer, - std::move(slice)).first; - if (const auto permanent = lookupPermanent(j->second)) { - editPermanentLink(peer, permanent->link); - } - } else { - RemovePermanent(slice); - auto &existing = i->second.links; - existing.erase(begin(existing) + 1, end(existing)); - existing.insert( - end(existing), - begin(slice.links), - end(slice.links)); - i->second.count = std::max(slice.count, int(existing.size())); - } - notify(peer); - }).fail([=](const RPCError &error) { - _firstSliceRequests.remove(peer); - }).send(); - _firstSliceRequests.emplace(peer, requestId); + // #TODO links + //const auto requestId = _api->request(MTPmessages_GetExportedChatInvites( + // MTP_flags(0), + // peer->input, + // MTPInputUser(), // admin_id + // MTPint(), // offset_date + // MTPstring(), // offset_link + // MTP_int(kFirstPage) + //)).done([=](const MTPmessages_ExportedChatInvites &result) { + // _firstSliceRequests.remove(peer); + // auto slice = parseSlice(peer, result); + // auto i = _firstSlices.find(peer); + // const auto permanent = (i != end(_firstSlices)) + // ? lookupPermanent(i->second) + // : nullptr; + // if (!permanent) { + // BringPermanentToFront(slice); + // const auto j = _firstSlices.emplace_or_assign( + // peer, + // std::move(slice)).first; + // if (const auto permanent = lookupPermanent(j->second)) { + // editPermanentLink(peer, permanent->link); + // } + // } else { + // RemovePermanent(slice); + // auto &existing = i->second.links; + // existing.erase(begin(existing) + 1, end(existing)); + // existing.insert( + // end(existing), + // begin(slice.links), + // end(slice.links)); + // i->second.count = std::max(slice.count, int(existing.size())); + // } + // notify(peer); + //}).fail([=](const RPCError &error) { + // _firstSliceRequests.remove(peer); + //}).send(); + //_firstSliceRequests.emplace(peer, requestId); } std::optional<JoinedByLinkSlice> InviteLinks::lookupJoinedFirstSlice( @@ -442,23 +447,23 @@ rpl::producer<> InviteLinks::allRevokedDestroyed( } void InviteLinks::requestJoinedFirstSlice(LinkKey key) { - if (_firstJoinedRequests.contains(key)) { - return; - } - const auto requestId = _api->request(MTPmessages_GetChatInviteImporters( - key.peer->input, - MTP_string(key.link), - MTP_int(0), // offset_date - MTP_inputUserEmpty(), // offset_user - MTP_int(kJoinedFirstPage) - )).done([=](const MTPmessages_ChatInviteImporters &result) { - _firstJoinedRequests.remove(key); - _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); - _joinedFirstSliceLoaded.fire_copy(key); - }).fail([=](const RPCError &error) { - _firstJoinedRequests.remove(key); - }).send(); - _firstJoinedRequests.emplace(key, requestId); + //if (_firstJoinedRequests.contains(key)) { // #TODO links + // return; + //} + //const auto requestId = _api->request(MTPmessages_GetChatInviteImporters( + // key.peer->input, + // MTP_string(key.link), + // MTP_int(0), // offset_date + // MTP_inputUserEmpty(), // offset_user + // MTP_int(kJoinedFirstPage) + //)).done([=](const MTPmessages_ChatInviteImporters &result) { + // _firstJoinedRequests.remove(key); + // _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); + // _joinedFirstSliceLoaded.fire_copy(key); + //}).fail([=](const RPCError &error) { + // _firstJoinedRequests.remove(key); + //}).send(); + //_firstJoinedRequests.emplace(key, requestId); } void InviteLinks::setPermanent( @@ -546,27 +551,27 @@ auto InviteLinks::links(not_null<PeerData*> peer) const -> const Links & { const auto i = _firstSlices.find(peer); return (i != end(_firstSlices)) ? i->second : kEmpty; } - -auto InviteLinks::parseSlice( - not_null<PeerData*> peer, - const MTPmessages_ExportedChatInvites &slice) const -> Links { - auto i = _firstSlices.find(peer); - const auto permanent = (i != end(_firstSlices)) - ? lookupPermanent(i->second) - : nullptr; - auto result = Links(); - slice.match([&](const MTPDmessages_exportedChatInvites &data) { - peer->session().data().processUsers(data.vusers()); - result.count = data.vcount().v; - for (const auto &invite : data.vinvites().v) { - const auto link = parse(peer, invite); - if (!permanent || link.link != permanent->link) { - result.links.push_back(link); - } - } - }); - return result; -} +// #TODO links +//auto InviteLinks::parseSlice( +// not_null<PeerData*> peer, +// const MTPmessages_ExportedChatInvites &slice) const -> Links { +// auto i = _firstSlices.find(peer); +// const auto permanent = (i != end(_firstSlices)) +// ? lookupPermanent(i->second) +// : nullptr; +// auto result = Links(); +// slice.match([&](const MTPDmessages_exportedChatInvites &data) { +// peer->session().data().processUsers(data.vusers()); +// result.count = data.vcount().v; +// for (const auto &invite : data.vinvites().v) { +// const auto link = parse(peer, invite); +// if (!permanent || link.link != permanent->link) { +// result.links.push_back(link); +// } +// } +// }); +// return result; +//} auto InviteLinks::parse( not_null<PeerData*> peer, @@ -592,22 +597,23 @@ void InviteLinks::requestMoreLinks( const QString &lastLink, bool revoked, Fn<void(Links)> done) { - using Flag = MTPmessages_GetExportedChatInvites::Flag; - _api->request(MTPmessages_GetExportedChatInvites( - MTP_flags(Flag::f_offset_link - | (revoked ? Flag::f_revoked : Flag(0))), - peer->input, - MTPInputUser(), // admin_id, - MTP_int(lastDate), - MTP_string(lastLink), - MTP_int(kPerPage) - )).done([=](const MTPmessages_ExportedChatInvites &result) { - auto slice = parseSlice(peer, result); - RemovePermanent(slice); - done(std::move(slice)); - }).fail([=](const RPCError &error) { - done(Links()); - }).send(); + // #TODO links + //using Flag = MTPmessages_GetExportedChatInvites::Flag; + //_api->request(MTPmessages_GetExportedChatInvites( + // MTP_flags(Flag::f_offset_link + // | (revoked ? Flag::f_revoked : Flag(0))), + // peer->input, + // MTPInputUser(), // admin_id, + // MTP_int(lastDate), + // MTP_string(lastLink), + // MTP_int(kPerPage) + //)).done([=](const MTPmessages_ExportedChatInvites &result) { + // auto slice = parseSlice(peer, result); + // RemovePermanent(slice); + // done(std::move(slice)); + //}).fail([=](const RPCError &error) { + // done(Links()); + //}).send(); } void InviteLinks::editPermanentLink( diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 83ad34106..e7089dbcf 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -43,10 +43,10 @@ struct InviteLinkUpdate { QString was; std::optional<InviteLink> now; }; - -[[nodiscard]] JoinedByLinkSlice ParseJoinedByLinkSlice( - not_null<PeerData*> peer, - const MTPmessages_ChatInviteImporters &slice); +// #TODO links +//[[nodiscard]] JoinedByLinkSlice ParseJoinedByLinkSlice( +// not_null<PeerData*> peer, +// const MTPmessages_ChatInviteImporters &slice); class InviteLinks final { public: @@ -124,9 +124,10 @@ private: } }; - [[nodiscard]] Links parseSlice( - not_null<PeerData*> peer, - const MTPmessages_ExportedChatInvites &slice) const; + // #TODO links + //[[nodiscard]] Links parseSlice( + // not_null<PeerData*> peer, + // const MTPmessages_ExportedChatInvites &slice) const; [[nodiscard]] Link parse( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) const; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index ee74e1c52..09324ddba 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -696,7 +696,6 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio channel, std::move(image)); } - using Flag = MTPmessages_ExportChatInvite::Flag; _createdChannel = channel; checkInviteLink(); }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 5671917e0..5869bb67c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -987,29 +987,29 @@ void Controller::fillManageSection() { [=] { ShowEditPermissions(_navigation, _peer); }, st::infoIconPermissions); } - if (canEditInviteLinks) { - AddButtonWithCount( - _controls.buttonsLayout, - tr::lng_manage_peer_invite_links(), - Info::Profile::MigratedOrMeValue( - _peer - ) | rpl::map([=](not_null<PeerData*> peer) { - peer->session().api().inviteLinks().requestLinks(peer); - return peer->session().changes().peerUpdates( - peer, - Data::PeerUpdate::Flag::InviteLinks - ) | rpl::map([=] { - return peer->session().api().inviteLinks().links( - peer).count; - }); - }) | rpl::flatten_latest( - ) | ToPositiveNumberString(), - [=] { Ui::show( - Box(ManageInviteLinksBox, _peer), - Ui::LayerOption::KeepOther); - }, - st::infoIconInviteLinks); - } + //if (canEditInviteLinks) { // #TODO links + // AddButtonWithCount( + // _controls.buttonsLayout, + // tr::lng_manage_peer_invite_links(), + // Info::Profile::MigratedOrMeValue( + // _peer + // ) | rpl::map([=](not_null<PeerData*> peer) { + // peer->session().api().inviteLinks().requestLinks(peer); + // return peer->session().changes().peerUpdates( + // peer, + // Data::PeerUpdate::Flag::InviteLinks + // ) | rpl::map([=] { + // return peer->session().api().inviteLinks().links( + // peer).count; + // }); + // }) | rpl::flatten_latest( + // ) | ToPositiveNumberString(), + // [=] { Ui::show( + // Box(ManageInviteLinksBox, _peer), + // Ui::LayerOption::KeepOther); + // }, + // st::infoIconInviteLinks); + //} if (canViewAdmins) { AddButtonWithCount( _controls.buttonsLayout, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 72b89e643..df593d0d7 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -222,21 +222,22 @@ void Controller::loadMoreRows() { if (_requestId || _allLoaded) { return; } - _requestId = _api.request(MTPmessages_GetChatInviteImporters( - _peer->input, - MTP_string(_data.link), - MTP_int(_lastUser ? _lastUser->date : 0), - _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), - MTP_int(_lastUser ? kPerPage : kFirstPage) - )).done([=](const MTPmessages_ChatInviteImporters &result) { - _requestId = 0; - auto slice = Api::ParseJoinedByLinkSlice(_peer, result); - _allLoaded = slice.users.empty(); - appendSlice(slice); - }).fail([=](const RPCError &error) { - _requestId = 0; - _allLoaded = true; - }).send(); + _allLoaded = true; // #TODO links + //_requestId = _api.request(MTPmessages_GetChatInviteImporters( + // _peer->input, + // MTP_string(_data.link), + // MTP_int(_lastUser ? _lastUser->date : 0), + // _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), + // MTP_int(_lastUser ? kPerPage : kFirstPage) + //)).done([=](const MTPmessages_ChatInviteImporters &result) { + // _requestId = 0; + // auto slice = Api::ParseJoinedByLinkSlice(_peer, result); + // _allLoaded = slice.users.empty(); + // appendSlice(slice); + //}).fail([=](const RPCError &error) { + // _requestId = 0; + // _allLoaded = true; + //}).send(); } void Controller::appendSlice(const Api::JoinedByLinkSlice &slice) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 027953ac3..8a9967c9a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -177,20 +177,20 @@ void Controller::createContent() { _wrap->add(createInviteLinkBlock()); _wrap->add(createUsernameEdit()); - using namespace Settings; - AddSkip(_wrap.get()); - _wrap->add(EditPeerInfoBox::CreateButton( - _wrap.get(), - tr::lng_group_invite_manage(), - rpl::single(QString()), - [=] { Ui::show( - Box(ManageInviteLinksBox, _peer), - Ui::LayerOption::KeepOther); - }, - st::manageGroupButton, - &st::infoIconInviteLinks)); - AddSkip(_wrap.get()); - AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); + //using namespace Settings; // #TODO links + //AddSkip(_wrap.get()); + //_wrap->add(EditPeerInfoBox::CreateButton( + // _wrap.get(), + // tr::lng_group_invite_manage(), + // rpl::single(QString()), + // [=] { Ui::show( + // Box(ManageInviteLinksBox, _peer), + // Ui::LayerOption::KeepOther); + // }, + // st::manageGroupButton, + // &st::infoIconInviteLinks)); + //AddSkip(_wrap.get()); + //AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); if (_controls.privacy->value() == Privacy::NoUsername) { checkUsernameAvailability(); diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 45dc658b9..c81a37162 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -377,12 +377,10 @@ void GroupCall::applySelfInCallLocally() { ? i->lastActive : TimeId(0); const auto canSelfUnmute = (muted() != MuteState::ForceMuted); - const auto mutedCount = (i != end(participants)) ? /*i->mutedCount*/0 : 0; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) | (_mySsrc ? Flag(0) : Flag::f_left) - | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)) - | (mutedCount ? Flag::f_muted_cnt : Flag(0)); + | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)); call->applyUpdateChecked( MTP_updateGroupCallParticipants( inputCall(), @@ -394,8 +392,7 @@ void GroupCall::applySelfInCallLocally() { MTP_int(date), MTP_int(lastActive), MTP_int(_mySsrc), - MTP_int(10000), // volume - MTP_int(mutedCount))), + MTP_int(10000))), // volume MTP_int(0)).c_updateGroupCallParticipants()); } @@ -413,8 +410,7 @@ void GroupCall::applyParticipantLocally( const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (participant->lastActive ? Flag::f_active_date : Flag(0)) | (participant->muted ? Flag::f_muted : Flag(0)) - | (participant->mutedByMe ? Flag::f_muted_by_you : Flag(0)) - | (mutedCount ? Flag::f_muted_cnt : Flag(0)); + | (participant->mutedByMe ? Flag::f_muted_by_you : Flag(0)); _peer->groupCall()->applyUpdateChecked( MTP_updateGroupCallParticipants( inputCall(), @@ -426,8 +422,7 @@ void GroupCall::applyParticipantLocally( MTP_int(participant->date), MTP_int(participant->lastActive), MTP_int(participant->ssrc), - MTP_int(volume.value_or(participant->volume)), // volume - MTP_int(mutedCount))), + MTP_int(volume.value_or(participant->volume)))), MTP_int(0)).c_updateGroupCallParticipants()); } From 827c950468f0a5803a8323c3e7494ae8a11168df Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 14 Jan 2021 03:25:59 +0300 Subject: [PATCH 165/396] Moved common GroupCall structs to separate file. --- Telegram/CMakeLists.txt | 1 + .../SourceFiles/calls/calls_group_call.cpp | 14 +++++----- .../SourceFiles/calls/calls_group_common.h | 27 +++++++++++++++++++ .../SourceFiles/calls/calls_group_members.cpp | 21 +++++++-------- .../SourceFiles/calls/calls_group_members.h | 23 +++++++--------- .../SourceFiles/calls/calls_group_panel.cpp | 5 ++-- Telegram/SourceFiles/data/data_group_call.cpp | 4 ++- Telegram/SourceFiles/data/data_group_call.h | 1 - 8 files changed, 60 insertions(+), 36 deletions(-) create mode 100644 Telegram/SourceFiles/calls/calls_group_common.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 48d25273a..72b1799cf 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -269,6 +269,7 @@ PRIVATE calls/calls_call.h calls/calls_group_call.cpp calls/calls_group_call.h + calls/calls_group_common.h calls/calls_group_members.cpp calls/calls_group_members.h calls/calls_group_panel.cpp diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index c81a37162..548dc587d 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_group_call.h" +#include "calls/calls_group_common.h" #include "main/main_session.h" #include "api/api_send_progress.h" #include "apiwrap.h" @@ -263,14 +264,14 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) { const auto &was = update.was; const auto volumeChanged = was ? (was->volume != now.volume || was->mutedByMe != now.mutedByMe) - : (now.volume != Data::GroupCall::kDefaultVolume || now.mutedByMe); + : (now.volume != Group::kDefaultVolume || now.mutedByMe); if (volumeChanged) { _instance->setVolume( now.ssrc, (now.mutedByMe ? 0. : (now.volume - / float64(Data::GroupCall::kDefaultVolume)))); + / float64(Group::kDefaultVolume)))); } } }, _lifetime); @@ -392,7 +393,7 @@ void GroupCall::applySelfInCallLocally() { MTP_int(date), MTP_int(lastActive), MTP_int(_mySsrc), - MTP_int(10000))), // volume + MTP_int(Group::kDefaultVolume))), // volume MTP_int(0)).c_updateGroupCallParticipants()); } @@ -689,14 +690,13 @@ void GroupCall::updateInstanceVolumes() { const auto &participants = real->participants(); for (const auto &participant : participants) { const auto setVolume = participant.mutedByMe - || (participant.volume != Data::GroupCall::kDefaultVolume); + || (participant.volume != Group::kDefaultVolume); if (setVolume && participant.ssrc) { _instance->setVolume( participant.ssrc, (participant.mutedByMe ? 0. - : (participant.volume - / float64(Data::GroupCall::kDefaultVolume)))); + : (participant.volume / float64(Group::kDefaultVolume)))); } } } @@ -896,7 +896,7 @@ void GroupCall::editParticipant( MTP_flags(flags), inputCall(), user->inputUser, - MTP_int(std::clamp(volume.value_or(0), 1, 20000)) + MTP_int(std::clamp(volume.value_or(0), 1, Group::kMaxVolume)) )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); }).fail([=](const RPCError &error) { diff --git a/Telegram/SourceFiles/calls/calls_group_common.h b/Telegram/SourceFiles/calls/calls_group_common.h new file mode 100644 index 000000000..4346d4c2f --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_group_common.h @@ -0,0 +1,27 @@ +/* +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 + +class UserData; + +namespace Calls::Group { + +constexpr auto kDefaultVolume = 10000; +constexpr auto kMaxVolume = 20000; + +struct MuteRequest { + not_null<UserData*> user; + bool mute = false; +}; +struct VolumeRequest { + not_null<UserData*> user; + int volume = kDefaultVolume; + bool finalized = true; +}; + +} // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 98b31e790..c72918487 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_group_members.h" #include "calls/calls_group_call.h" +#include "calls/calls_group_common.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" @@ -196,7 +197,7 @@ private: Ui::Animations::Simple _mutedAnimation; // For gray/red icon. Ui::Animations::Simple _activeAnimation; // For icon cross animation. uint32 _ssrc = 0; - int _volume = Data::GroupCall::kDefaultVolume; + int _volume = Group::kDefaultVolume; bool _sounding = false; bool _speaking = false; bool _skipLevelUpdate = false; @@ -213,8 +214,8 @@ public: not_null<QWidget*> menuParent); ~MembersController(); - using MuteRequest = GroupMembers::MuteRequest; - using VolumeRequest = GroupMembers::VolumeRequest; + using MuteRequest = Group::MuteRequest; + using VolumeRequest = Group::VolumeRequest; Main::Session &session() const override; void prepare() override; @@ -318,7 +319,7 @@ void Row::updateState(const Data::GroupCall::Participant *participant) { setSsrc(participant ? participant->ssrc : 0); setVolume(participant ? participant->volume - : Data::GroupCall::kDefaultVolume); + : Group::kDefaultVolume); if (!participant) { setState(State::Invited); setSounding(false); @@ -1160,15 +1161,15 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( ? (muteState == Row::State::Active) : (muteState != Row::State::Muted); const auto toggleMute = crl::guard(this, [=] { - _toggleMuteRequests.fire(MuteRequest{ + _toggleMuteRequests.fire(Group::MuteRequest{ .user = user, .mute = mute, }); }); const auto changeVolume = crl::guard(this, [=](int volume) { - _changeVolumeRequests.fire(VolumeRequest{ + _changeVolumeRequests.fire(Group::VolumeRequest{ .user = user, - .volume = std::clamp(volume, 1, 20000), + .volume = std::clamp(volume, 1, Group::kMaxVolume), }); }); @@ -1303,8 +1304,6 @@ std::unique_ptr<Row> MembersController::createInvitedRow( } // namespace -const int GroupMembers::kDefaultVolume = Data::GroupCall::kDefaultVolume; - GroupMembers::GroupMembers( not_null<QWidget*> parent, not_null<GroupCall*> call) @@ -1320,13 +1319,13 @@ GroupMembers::GroupMembers( } auto GroupMembers::toggleMuteRequests() const --> rpl::producer<GroupMembers::MuteRequest> { +-> rpl::producer<Group::MuteRequest> { return static_cast<MembersController*>( _listController.get())->toggleMuteRequests(); } auto GroupMembers::changeVolumeRequests() const --> rpl::producer<GroupMembers::VolumeRequest> { +-> rpl::producer<Group::VolumeRequest> { return static_cast<MembersController*>( _listController.get())->changeVolumeRequests(); } diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index 76ed90e93..5d5014af7 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -20,6 +20,11 @@ class GroupCall; namespace Calls { +namespace Group { +struct VolumeRequest; +struct MuteRequest; +} // namespace Group + class GroupCall; class GroupMembers final @@ -30,23 +35,13 @@ public: not_null<QWidget*> parent, not_null<GroupCall*> call); - static const int kDefaultVolume;/* = Data::GroupCall::kDefaultVolume*/ - - struct MuteRequest { - not_null<UserData*> user; - bool mute = false; - }; - struct VolumeRequest { - not_null<UserData*> user; - int volume = kDefaultVolume; - bool finalized = true; - }; - [[nodiscard]] int desiredHeight() const; [[nodiscard]] rpl::producer<int> desiredHeightValue() const override; [[nodiscard]] rpl::producer<int> fullCountValue() const; - [[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const; - [[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const; + [[nodiscard]] auto toggleMuteRequests() const + -> rpl::producer<Group::MuteRequest>; + [[nodiscard]] auto changeVolumeRequests() const + -> rpl::producer<Group::VolumeRequest>; [[nodiscard]] auto kickMemberRequests() const -> rpl::producer<not_null<UserData*>>; [[nodiscard]] rpl::producer<> addMembersRequests() const { diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 32826be62..2aaed107e 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_group_panel.h" +#include "calls/calls_group_common.h" #include "calls/calls_group_members.h" #include "calls/calls_group_settings.h" #include "ui/widgets/buttons.h" @@ -505,14 +506,14 @@ void GroupPanel::initWithCall(GroupCall *call) { }, _callLifetime); _members->toggleMuteRequests( - ) | rpl::start_with_next([=](GroupMembers::MuteRequest request) { + ) | rpl::start_with_next([=](Group::MuteRequest request) { if (_call) { _call->toggleMute(request.user, request.mute); } }, _callLifetime); _members->changeVolumeRequests( - ) | rpl::start_with_next([=](GroupMembers::VolumeRequest request) { + ) | rpl::start_with_next([=](Group::VolumeRequest request) { if (_call) { _call->changeVolume(request.user, request.volume); } diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 9ca685979..75bf646f1 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "calls/calls_instance.h" #include "calls/calls_group_call.h" +#include "calls/calls_group_common.h" #include "core/application.h" #include "apiwrap.h" @@ -275,12 +276,13 @@ void GroupCall::applyParticipantsSlice( && ((was ? was->speaking : false) || (!amInCall && (lastActive + speakingAfterActive > now))); + const auto defaultVolume = Calls::Group::kDefaultVolume; const auto value = Participant{ .user = user, .date = data.vdate().v, .lastActive = lastActive, .ssrc = uint32(data.vsource().v), - .volume = data.vvolume().value_or(kDefaultVolume), + .volume = data.vvolume().value_or(defaultVolume), .speaking = canSelfUnmute && (was ? was->speaking : false), .muted = data.is_muted(), .mutedByMe = data.is_muted_by_you(), diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 5bf7e7c40..da039bae2 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -32,7 +32,6 @@ public: void setPeer(not_null<PeerData*> peer); - static constexpr auto kDefaultVolume = 10000; struct Participant { not_null<UserData*> user; TimeId date = 0; From 250add3a9683b755665c65acc98f0a3919fb0e37 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 15 Jan 2021 14:22:12 +0300 Subject: [PATCH 166/396] Added ability to apply volume and mute user in group calls locally only. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 16 ++++++++++++---- Telegram/SourceFiles/calls/calls_group_call.h | 9 +++++++-- Telegram/SourceFiles/calls/calls_group_common.h | 2 ++ .../SourceFiles/calls/calls_group_members.cpp | 7 ++++--- Telegram/SourceFiles/calls/calls_group_panel.cpp | 4 ++-- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 548dc587d..f9693a751 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -871,12 +871,20 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) { } } -void GroupCall::toggleMute(not_null<UserData*> user, bool mute) { - editParticipant(user, mute, std::nullopt); +void GroupCall::toggleMute(const Group::MuteRequest &data) { + if (data.locallyOnly) { + applyParticipantLocally(data.user, data.mute, std::nullopt); + } else { + editParticipant(data.user, data.mute, std::nullopt); + } } -void GroupCall::changeVolume(not_null<UserData*> user, int volume) { - editParticipant(user, false, volume); +void GroupCall::changeVolume(const Group::VolumeRequest &data) { + if (data.locallyOnly) { + applyParticipantLocally(data.user, false, data.volume); + } else { + editParticipant(data.user, false, data.volume); + } } void GroupCall::editParticipant( diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index f4f07dae4..3e5d8202a 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -35,6 +35,11 @@ struct LastSpokeTimes; namespace Calls { +namespace Group { +struct MuteRequest; +struct VolumeRequest; +} // namespace Group + enum class MuteState { Active, PushToTalk, @@ -131,8 +136,8 @@ public: //void setAudioVolume(bool input, float level); void setAudioDuckingEnabled(bool enabled); - void toggleMute(not_null<UserData*> user, bool mute); - void changeVolume(not_null<UserData*> user, int volume); + void toggleMute(const Group::MuteRequest &data); + void changeVolume(const Group::VolumeRequest &data); std::variant<int, not_null<UserData*>> inviteUsers( const std::vector<not_null<UserData*>> &users); diff --git a/Telegram/SourceFiles/calls/calls_group_common.h b/Telegram/SourceFiles/calls/calls_group_common.h index 4346d4c2f..1dbe7f08d 100644 --- a/Telegram/SourceFiles/calls/calls_group_common.h +++ b/Telegram/SourceFiles/calls/calls_group_common.h @@ -17,11 +17,13 @@ constexpr auto kMaxVolume = 20000; struct MuteRequest { not_null<UserData*> user; bool mute = false; + bool locallyOnly = false; }; struct VolumeRequest { not_null<UserData*> user; int volume = kDefaultVolume; bool finalized = true; + bool locallyOnly = false; }; } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index c72918487..b8364aaeb 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1166,10 +1166,11 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( .mute = mute, }); }); - const auto changeVolume = crl::guard(this, [=](int volume) { + const auto changeVolume = crl::guard(this, [=](int volume, bool local) { _changeVolumeRequests.fire(Group::VolumeRequest{ .user = user, .volume = std::clamp(volume, 1, Group::kMaxVolume), + .locallyOnly = local, }); }); @@ -1245,10 +1246,10 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( && muteState != Row::State::MutedByMe) { const auto volume = real->volume(); result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume + 2000); + changeVolume(volume + 2000, false); }); result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume - 2000); + changeVolume(volume - 2000, false); }); } } diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 2aaed107e..9a752a35d 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -508,14 +508,14 @@ void GroupPanel::initWithCall(GroupCall *call) { _members->toggleMuteRequests( ) | rpl::start_with_next([=](Group::MuteRequest request) { if (_call) { - _call->toggleMute(request.user, request.mute); + _call->toggleMute(request); } }, _callLifetime); _members->changeVolumeRequests( ) | rpl::start_with_next([=](Group::VolumeRequest request) { if (_call) { - _call->changeVolume(request.user, request.volume); + _call->changeVolume(request); } }, _callLifetime); From e1f5e107641870594a137a531e4751200d505abe Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 15 Jan 2021 14:38:56 +0300 Subject: [PATCH 167/396] Added missed implementation of ContinuousSlider::value. --- Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index 09f88c890..7813d2aed 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -50,6 +50,10 @@ QRect ContinuousSlider::getSeekRect() const { : QRect(0, decrease.height() / 2, width(), height() - decrease.width()); } +float64 ContinuousSlider::value() const { + return getCurrentValue(); +} + void ContinuousSlider::setValue(float64 value) { setValue(value, -1); } From e12689c8c1fcefc345883c167d45c0a4fafef5c0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 15 Jan 2021 19:05:34 +0300 Subject: [PATCH 168/396] Added handler of state changes of other participants in group calls. --- .../SourceFiles/calls/calls_group_call.cpp | 20 +++++++++++++++++++ Telegram/SourceFiles/calls/calls_group_call.h | 6 ++++++ .../SourceFiles/calls/calls_group_common.h | 7 +++++++ 3 files changed, 33 insertions(+) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index f9693a751..8aa4e4e3d 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -589,10 +589,25 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { return; } + const auto handleOtherParticipants = [=]( + const MTPDgroupCallParticipant &data) { + const auto user = _peer->owner().user(data.vuser_id().v); + const auto participant = LookupParticipant(_peer, _id, user); + if (!participant) { + return; + } + _otherParticipantStateValue.fire(Group::ParticipantState{ + .user = user, + .mutedByMe = data.is_muted_by_you(), + .volume = data.vvolume().value_or_empty(), + }); + }; + const auto self = _peer->session().userId(); for (const auto &participant : data.vparticipants().v) { participant.match([&](const MTPDgroupCallParticipant &data) { if (data.vuser_id().v != self) { + handleOtherParticipants(data); return; } if (data.is_left() && data.vsource().v == _mySsrc) { @@ -1030,6 +1045,11 @@ void GroupCall::pushToTalkCancel() { } } +auto GroupCall::otherParticipantStateValue() const +-> rpl::producer<Group::ParticipantState> { + return _otherParticipantStateValue.events(); +} + //void GroupCall::setAudioVolume(bool input, float level) { // if (_instance) { // if (input) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 3e5d8202a..c038fd6c2 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -38,6 +38,7 @@ namespace Calls { namespace Group { struct MuteRequest; struct VolumeRequest; +struct ParticipantState; } // namespace Group enum class MuteState { @@ -109,6 +110,9 @@ public: return _muted.value(); } + [[nodiscard]] auto otherParticipantStateValue() const + -> rpl::producer<Group::ParticipantState>; + enum State { Creating, Joining, @@ -206,6 +210,8 @@ private: rpl::variable<MuteState> _muted = MuteState::Muted; bool _acceptFields = false; + rpl::event_stream<Group::ParticipantState> _otherParticipantStateValue; + uint64 _id = 0; uint64 _accessHash = 0; uint32 _mySsrc = 0; diff --git a/Telegram/SourceFiles/calls/calls_group_common.h b/Telegram/SourceFiles/calls/calls_group_common.h index 1dbe7f08d..22ac7d856 100644 --- a/Telegram/SourceFiles/calls/calls_group_common.h +++ b/Telegram/SourceFiles/calls/calls_group_common.h @@ -26,4 +26,11 @@ struct VolumeRequest { bool locallyOnly = false; }; +struct ParticipantState { + not_null<UserData*> user; + std::optional<int> volume; + bool mutedByMe = false; + bool locallyOnly = false; +}; + } // namespace Calls::Group From 173564bcd58671f058e8bdc08c3bff02e0fbdbd9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 15 Jan 2021 19:18:35 +0300 Subject: [PATCH 169/396] Added initial implementation of volume menu item in group calls. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/calls/calls.style | 2 + .../SourceFiles/calls/calls_group_members.cpp | 49 +++++ .../SourceFiles/calls/calls_volume_item.cpp | 170 ++++++++++++++++++ .../SourceFiles/calls/calls_volume_item.h | 67 +++++++ 5 files changed, 290 insertions(+) create mode 100644 Telegram/SourceFiles/calls/calls_volume_item.cpp create mode 100644 Telegram/SourceFiles/calls/calls_volume_item.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 72b1799cf..a71a9ac1f 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -290,6 +290,8 @@ PRIVATE calls/calls_userpic.h calls/calls_video_bubble.cpp calls/calls_video_bubble.h + calls/calls_volume_item.cpp + calls/calls_volume_item.h chat_helpers/bot_keyboard.cpp chat_helpers/bot_keyboard.h chat_helpers/emoji_keywords.cpp diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index adbf8543d..612a52b6b 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -778,6 +778,8 @@ groupCallMajorBlobMaxRadius: 4px; groupCallMinorBlobIdleRadius: 3px; groupCallMinorBlobMaxRadius: 12px; +groupCallMenuVolumeItemHeight: 100px; + callTopBarMuteCrossLine: CrossLineAnimation { fg: callBarFg; icon: icon {{ "calls/call_record_active", callBarFg }}; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index b8364aaeb..985821650 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_group_call.h" #include "calls/calls_group_common.h" +#include "calls/calls_volume_item.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" @@ -1166,6 +1167,13 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( .mute = mute, }); }); + const auto toggleMute_ = crl::guard(this, [=](bool mute, bool local) { + _toggleMuteRequests.fire(Group::MuteRequest{ + .user = user, + .mute = mute, + .locallyOnly = local, + }); + }); const auto changeVolume = crl::guard(this, [=](int volume, bool local) { _changeVolumeRequests.fire(Group::VolumeRequest{ .user = user, @@ -1252,6 +1260,47 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( changeVolume(volume - 2000, false); }); } + + { + const auto call = _call.get(); + auto otherParticipantStateValue = call + ? call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.user == user; + }) + : rpl::never<Group::ParticipantState>(); + + auto volumeItem = base::make_unique_q<MenuVolumeItem>( + result, + st::groupCallPopupMenu.menu, + otherParticipantStateValue, + real->volume(), + Group::kMaxVolume, + (muteState == Row::State::Muted + || muteState == Row::State::MutedByMe)); + + volumeItem->toggleMuteRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute_(muted, false); + }, volumeItem->lifetime()); + + volumeItem->toggleMuteLocallyRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute_(muted, true); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, false); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeLocallyRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, true); + }, volumeItem->lifetime()); + + result->addAction(std::move(volumeItem)); + } } result->addAction( tr::lng_context_view_profile(tr::now), diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp new file mode 100644 index 000000000..84550a48c --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -0,0 +1,170 @@ +/* +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 "calls/calls_volume_item.h" + +#include "calls/calls_group_common.h" +#include "ui/widgets/continuous_sliders.h" +#include "styles/style_calls.h" +#include "styles/style_media_player.h" + +namespace Calls { +namespace { + +constexpr auto kMaxVolumePercent = 200; + +} // namespace + +MenuVolumeItem::MenuVolumeItem( + not_null<RpWidget*> parent, + const style::Menu &st, + rpl::producer<Group::ParticipantState> participantState, + int startVolume, + int maxVolume, + bool muted) +: Ui::Menu::ItemBase(parent, st) +, _maxVolume(maxVolume) +, _cloudMuted(muted) +, _localMuted(muted) +, _slider(base::make_unique_q<Ui::MediaSlider>( + this, + st::mediaPlayerPanelPlayback)) +, _dummyAction(new QAction(parent)) +, _font(st.itemStyle.font) { + + initResizeHook(parent->sizeValue()); + enableMouseSelecting(); + + paintRequest( + ) | rpl::start_with_next([=](const QRect &clip) { + Painter p(this); + + const auto enabled = isEnabled(); + const auto selected = isSelected(); + p.fillRect(clip, selected ? st.itemBgOver : st.itemBg); + p.setPen(_localMuted + ? (selected ? st::attentionButtonFgOver : st::attentionButtonFg) + : selected + ? st.itemFgOver + : (enabled ? st.itemFg : st.itemFgDisabled)); + p.setFont(_font); + const auto volume = std::round(_slider->value() * kMaxVolumePercent); + p.drawText(QPoint(0, _font->ascent), u"%1%"_q.arg(volume)); + }, lifetime()); + + setCloudVolume(startVolume); + + _slider->setChangeProgressCallback([=](float64 value) { + const auto newMuted = (value == 0); + if (_localMuted != newMuted) { + _localMuted = newMuted; + _toggleMuteLocallyRequests.fire_copy(newMuted); + } + if (value > 0) { + _changeVolumeLocallyRequests.fire(value * _maxVolume); + } + }); + + const auto returnVolume = [=] { + _changeVolumeLocallyRequests.fire_copy(_cloudVolume); + crl::on_main(_slider.get(), [=] { + setSliderVolume(_cloudVolume); + }); + }; + + _slider->setChangeFinishedCallback([=](float64 value) { + const auto newVolume = std::round(value * _maxVolume); + const auto muted = (value == 0); + + if (!_cloudMuted && muted) { + returnVolume(); + _localMuted = true; + _toggleMuteRequests.fire(true); + } + if (_cloudMuted && muted) { + returnVolume(); + } + if (_cloudMuted && !muted) { + _waitingForUpdateVolume = true; + _localMuted = false; + _toggleMuteRequests.fire(false); + } + if (!_cloudMuted && !muted) { + _changeVolumeRequests.fire_copy(newVolume); + } + }); + + std::move( + participantState + ) | rpl::start_with_next([=](const Group::ParticipantState &state) { + const auto newMuted = state.mutedByMe; + const auto newVolume = state.volume.value_or(0); + + _cloudMuted = _localMuted = newMuted; + + if (!newVolume) { + return; + } + if (_waitingForUpdateVolume) { + const auto localVolume = + std::round(_slider->value() * _maxVolume); + if ((localVolume != newVolume) + && (_cloudVolume == newVolume)) { + _changeVolumeRequests.fire(localVolume); + } + } else { + setCloudVolume(newVolume); + } + _waitingForUpdateVolume = false; + }, lifetime()); + + _slider->setGeometry(rect()); +} + +void MenuVolumeItem::setCloudVolume(int volume) { + if (_cloudVolume == volume) { + return; + } + _cloudVolume = volume; + if (!_slider->isChanging()) { + setSliderVolume(volume); + } +} + +void MenuVolumeItem::setSliderVolume(int volume) { + _slider->setValue(float64(volume) / _maxVolume); +} + +not_null<QAction*> MenuVolumeItem::action() const { + return _dummyAction; +} + +bool MenuVolumeItem::isEnabled() const { + return true; +} + +int MenuVolumeItem::contentHeight() const { + return st::groupCallMenuVolumeItemHeight; +} + +rpl::producer<bool> MenuVolumeItem::toggleMuteRequests() const { + return _toggleMuteRequests.events(); +} + +rpl::producer<bool> MenuVolumeItem::toggleMuteLocallyRequests() const { + return _toggleMuteLocallyRequests.events(); +} + +rpl::producer<int> MenuVolumeItem::changeVolumeRequests() const { + return _changeVolumeRequests.events(); +} + +rpl::producer<int> MenuVolumeItem::changeVolumeLocallyRequests() const { + return _changeVolumeLocallyRequests.events(); +} + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h new file mode 100644 index 000000000..e047a9f6f --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -0,0 +1,67 @@ +/* +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 "ui/rp_widget.h" +#include "ui/widgets/menu/menu_item_base.h" + +namespace Ui { +class MediaSlider; +} // namespace Ui + +namespace Calls { + +namespace Group { +struct MuteRequest; +struct VolumeRequest; +struct ParticipantState; +} // namespace Group + +class MenuVolumeItem final : public Ui::Menu::ItemBase { +public: + MenuVolumeItem( + not_null<RpWidget*> parent, + const style::Menu &st, + rpl::producer<Group::ParticipantState> participantState, + int startVolume, + int maxVolume, + bool muted); + + not_null<QAction*> action() const override; + bool isEnabled() const override; + + [[nodiscard]] rpl::producer<bool> toggleMuteRequests() const; + [[nodiscard]] rpl::producer<bool> toggleMuteLocallyRequests() const; + [[nodiscard]] rpl::producer<int> changeVolumeRequests() const; + [[nodiscard]] rpl::producer<int> changeVolumeLocallyRequests() const; + +protected: + int contentHeight() const override; + +private: + void setCloudVolume(int volume); + void setSliderVolume(int volume); + + const int _maxVolume; + int _cloudVolume = 0; + bool _waitingForUpdateVolume = false; + bool _cloudMuted = false; + bool _localMuted = false; + + const base::unique_qptr<Ui::MediaSlider> _slider; + const not_null<QAction*> _dummyAction; + const style::font &_font; + + rpl::event_stream<bool> _toggleMuteRequests; + rpl::event_stream<bool> _toggleMuteLocallyRequests; + rpl::event_stream<int> _changeVolumeRequests; + rpl::event_stream<int> _changeVolumeLocallyRequests; + +}; + +} // namespace Calls From 70cdc055441cb396a56847e25990ac803ba5b90c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 16 Jan 2021 00:58:47 +0300 Subject: [PATCH 170/396] Fixed applying participant locally in group calls. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 8aa4e4e3d..2a7a4e92a 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -409,8 +409,9 @@ void GroupCall::applyParticipantLocally( const auto mutedCount = 0/*participant->mutedCount*/; using Flag = MTPDgroupCallParticipant::Flag; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) + | (volume.has_value() ? Flag::f_volume : Flag(0)) | (participant->lastActive ? Flag::f_active_date : Flag(0)) - | (participant->muted ? Flag::f_muted : Flag(0)) + | (mute ? Flag::f_muted : Flag(0)) | (participant->mutedByMe ? Flag::f_muted_by_you : Flag(0)); _peer->groupCall()->applyUpdateChecked( MTP_updateGroupCallParticipants( From 1cbb21721087f527581a71a64dd551c7c4e217c8 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 16 Jan 2021 18:11:43 +0300 Subject: [PATCH 171/396] Added speaker icon to volume menu item in group calls. --- .../Resources/icons/calls/volume/speaker.png | Bin 0 -> 556 bytes .../icons/calls/volume/speaker@2x.png | Bin 0 -> 998 bytes .../icons/calls/volume/speaker@3x.png | Bin 0 -> 1464 bytes Telegram/SourceFiles/calls/calls.style | 8 ++++- .../SourceFiles/calls/calls_volume_item.cpp | 31 +++++++++++++++--- .../SourceFiles/calls/calls_volume_item.h | 6 ++++ 6 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 Telegram/Resources/icons/calls/volume/speaker.png create mode 100644 Telegram/Resources/icons/calls/volume/speaker@2x.png create mode 100644 Telegram/Resources/icons/calls/volume/speaker@3x.png diff --git a/Telegram/Resources/icons/calls/volume/speaker.png b/Telegram/Resources/icons/calls/volume/speaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d7859a773505ed65e74098c8c718e88cbaec9c31 GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^azHG>!3HGXX8P_2QjEnx?oJHr&dIz4a$Hg)JkxxA z8MJ_G4hF{dOa>N^5+IfWVg?501&j>LK$;OGwtxvPE4P3d&XxcvbY(oW6sT;Pr;B5V z2k+add%F%f2pm_J3fSPYRAHJJqqMx^BD<EU9Y2`%HT4Uya9dX&jBDEZAbe?S$Bp)k zr4Rla9iIEdpshK&_^fV@L|XcDbK^HYCQ42FljA#Ybk09eUy$)sx|fZ&-8pOf?I|8B zw1w=~U6|{mmU!E&Y4!d0?YH)Q5?sjNo@ONJqb8ha)z|E_u;Fc4_usnpVryPooqB4d zbJ}Td-0>Pa@fR5;PFhoiBwqI&Uu?A@Q6fTb`oXTQJFmYk$h^h8L}<<D>{n&GL$s!5 z7)VTzXIP!FHR@uHneb0pc}DSW*N=B!mb|FjzrXqOli;eu`padGu%^z~wXEipX4FUB z#~Hy}mox2)(R*Y%{q)MOau0nEJpLHrQ?^_7C$p2;>rFRvj$FMj?>XOWwy)3f;Fr(O zrYYCfSjZ&IdKUd_#^E{(zEf+%xVPWtUH5uX(8`Y9Q;eU4&NyDlGOd``{`xD|Kg&7i z({;qUFE}(i{tw_g|Ks&0`->am)-V0C{4(d&EK@lH_5+`-=AT!d>$ke8$z9C4yY}zf fCtbF&@;?}T?yNfe-cxBeD5gAJ{an^LB{Ts5N;Khh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/volume/speaker@2x.png b/Telegram/Resources/icons/calls/volume/speaker@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..76dffc75655d68495247c4809aa2d534d816db53 GIT binary patch literal 998 zcmV<C0~!2@P)<h;3K|Lk000e1NJLTq002Ay001xu1^@s6O+Fb300001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91JfH&r1ONa40RR91FaQ7m08TFiT>t<BP)S5VRA>e5na@iyVGzg1&_kqQ zAqsX84|RwNJVcic9TZPR5D0n-tbd?OQC*`$f%k-No-FWW)Gh4SA*e1&p{Gu21Qk&f z&HFsQ?XVB7pt<F`?gPutjJway=lPD$?z3wG0AyRow62aJ{RTCF^{%*~u%WQdMxf(L z+N`F47K;UZJ|EcacDTE{gF>O8X7{S~#UDJH-j<h_@#*Oa<t?2~qs!%Lx^Fe|ll9RY ztl4bFot+&yr(oyvdF<)w(Gv8}fo(P$9vmD9fJ)BV+FJVq4-5?8_4T#D$4fdC3gO`3 zAYNWx(r>)9WhK>WyK#j_M@RAg{+@ui!TqtZF@lko`RVB?#aj*D@NFp&2tX#20j4m^ z{eQc;xq*p^31Es#A<ga;Z%ciBeK0vW2~06y|5HldYPG_`!UC{~ieiU{hcGuc2hY#X z6w|sPlv^Yc!BVLtlxr;~9*-lRr5Rw=rqz<>Vo<U(Gc&c=s^Y7wt8|{c56RXlFx4i1 zN@9xf0iebvB6xXuA)g!Nav8R^wjh;C3GsGu2zX><1RM?r<-NGL2uvmN^<vO0Jv=-t zFzxQ{3cg+(0+!Fb9Pab;Q!k7H2LTIF9XkfFj>WB@844Q;>udz5q`$tt1hlR$`bmhl zyF*mDTuxw_o}Lzby*Tt}d3AMFS>j2{!M(jbU~es|o~0ii9v~Kr!O6)<+l~RPdgkZn zt5!uyfsc<5j7Fn$9wV=^wYpm3DhbIhEiK{e>#GuHRcvEp15GBAaK6-^R)MLcIXQ3J z+uM*#CLtIMR^E8<w;WRA_U!De<ig6z3V1voV3h&y@9z}Xx}m5LOIk4=4u_G!32Gl7 zA7g)izi_-(6{cCbUR4r_1o-`aczk@MQ~3L@v$Hesdc73WvLPtL60OT08X7|WRYBg0 z#Uf5kO`+TEMy|Tlj*gD%>a*T!H+furfJzr}I-PiaelBs<+;47f>I0Y$LU(sJ?(gr{ zgk|r*kPnQ~fVC+0bA_dZ*<&cKudn0V+nWG>adA=k>>?efg}v!J3ASs#_4f9{`1m-m zuZ3`XdkfiYw)sv~?m<;xm2>-*?i#)={Yn;p*r@^hhlAacOBo8cWZ3`kq8+F3Kie%s UncvQ6mH+?%07*qoM6N<$f&-GkiU0rr literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/volume/speaker@3x.png b/Telegram/Resources/icons/calls/volume/speaker@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3397ea286b6f29eadb8e2283bf18e3d2135c737a GIT binary patch literal 1464 zcmb7EYd8}M7~WX!jpeqQBu0uh_xp8LIBv@=XEv9!VNBLRh$k(H<LF33iN%CfNbZ_T zq?E8_WH{>NaGZ#lA&%V6{ORv`&X4zd-|ze0=lk<L&zptubUr9^L<Rr=9CUR-dy5z= zf{T=ds3x>b(?kS}^L9o7YB_52q6Qrn;2Q4X0XQYnQUG9PBmlH;5#fjk0Dw3H2oM)B zaDT=S`@Y38#J_8oeWMS~%MSoJpy`Ts^i2Zti`{gu;FY@XZfIT3lq)pwrfCYGP$_ZO zw+_0OmAxNS<gY-bwVlxFH{e-Dx$t~=X!^Ad9nD)d8?77scZptG^qGVuOV~y&-u!b- z^B6W_ucm#2wN*0%0HmkZ9d_^LhV0Gl9s`0->nbalq^Y1CFwj8~==3jChCv}$3abEg z3^gc+G--&X^@N<&A~}LUHy|Zkp-?!)<pRlM^2L-CSyfflIu=XX)YNo}&p%BXw^z*I z{~^8E)Z7edPDn^NJ3T#}j>~>zSlnx<-jJOwdstoFpRiq-lANrWm6g>ZH5o8uXJ<zY z3!9a#(3Z^1%zVXWvw0yfNh^K*n~MTLV9%X}ywXyN5945<9DzXa$j15l<}daeo0xzM z4GnXPiew}uC2_~B&c`)$A%<+@8X6nl?(Ep!t#n@&3LUXnEZnfVx|;P|QBjd*r?gUP zm&mhrtE#GEbafr0)9DstNF);D?=K;`eduVVzGcJ-Fc?f<M9RjCv%BLH6P*Zza{XJy z#jw7Cf$O8Aem{0Tk-Y@uFD-$!w6ri7jK;vi+L}^BL&MbEoQpq2zK7!2*n1O5%=W=x z6fCS)b#`}m9Rvo%%NHmVr<fQ74v&BPg@2~cXUjY`I5-#zg;INZQ0&#&4<FQ&l$Br1 z%#?R7`>H+EpPQNr83{rYi4_n2Oq6NS;Ej!mcMkkhTaBM%ZIN0Yu?GjK{(W-MPEO+Q zc|1*Gqtdid(Us9W8f~yGAT=p96<knIU|?@gVcovJ_Lsxm{QQef4`H@LBO~Gq3k%EJ z+k^A>s2`tmODDUR3hIR*i=o&FTu=~ar*8^hhW?F8%n64HN=iy_SIr^_jX6|M(+69l zxOYr&G!K$XZQ&?mt04|UQStHbkCTpuR<Th3Q`4xaNlCeU*r7w3dd_9DAUI<pXmxg0 zJA&OGU0_Ck#?j5y?&doq5eQUd<l_osYcDr9<AS8e7q&$odpSHzd!^#FdqU%6^RGEM zIs$<JmvyHyO(^^swO|9ci_{&B!+w0lDy7qXn>RV1DI;!7@at3xMc&HVdZLW^x?hoW z4*`;ph-zr{(*m2oNMy3!+WLBwx^aqHOMQLWrfb{Z-Cg4t6%#!xqUm8vOUt_U_Uic+ zElo}Ly1cwR(}~F<NGr5R{$gk-a6EG~=}|%Yjf@Nw9)Hxr>W4iF5C~*ORy=zKWipwA z_{rINL^k+|l6KQtcHsL$>L}XW)b!-*`hi{yxr)u<ppuiVaKXWGh=8v?=g+Ikfx-Pe zk2nc4-Q3i%Cu&nXo(dce*S+)E-`BUWs<5M@qvqz?#^9v`?2N0iu|J(U>2t~=;?nS9 zn5Ws(t7i80UGH8#?P>`SR#+qspCj{FZEZ9z_uPbKv2^5p^)jT~?fLo0m2cnrzmj8O zcxsbH3uZ(9*6wbIs?N^)*GX9#+S&&k1I5NW<gYlon2`&pjr!)Tt+xpT<8O-I;o&-E z$O*mr9hzWqQ2{G;i(&sFaucSm?l}kkr0TnRy}jLfcx=qs#>OTZViby8t-mIHLCJzh zBmz<x%j@eBZ{ECN^8G3$<xxluBg9_pcSikoRBPKO=e;l7-KpN=B*$gQf27g2<je9| U2iGHkefz2Ddd3r7iwa5m7nrS)7XSbN literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 612a52b6b..bb5e042fd 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -778,7 +778,13 @@ groupCallMajorBlobMaxRadius: 4px; groupCallMinorBlobIdleRadius: 3px; groupCallMinorBlobMaxRadius: 12px; -groupCallMenuVolumeItemHeight: 100px; +groupCallMuteCrossLine: CrossLineAnimation { + fg: groupCallIconFg; + icon: icon {{ "calls/volume/speaker", groupCallIconFg }}; + startPosition: point(2px, 5px); + endPosition: point(16px, 19px); + stroke: 2px; +} callTopBarMuteCrossLine: CrossLineAnimation { fg: callBarFg; diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 84550a48c..ab8701f57 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_volume_item.h" #include "calls/calls_group_common.h" +#include "ui/effects/cross_line.h" #include "ui/widgets/continuous_sliders.h" #include "styles/style_calls.h" #include "styles/style_media_player.h" @@ -34,11 +35,18 @@ MenuVolumeItem::MenuVolumeItem( this, st::mediaPlayerPanelPlayback)) , _dummyAction(new QAction(parent)) -, _font(st.itemStyle.font) { +, _st(st) +, _font(st.itemStyle.font) +, _stCross(st::groupCallMuteCrossLine) +, _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true)) { initResizeHook(parent->sizeValue()); enableMouseSelecting(); + const auto itemRect = rect() - st.itemPadding; + const auto speakerRect = QRect(itemRect.topLeft(), _stCross.icon.size()); + const auto volumeRect = speakerRect.translated(_stCross.icon.width(), 0); + paintRequest( ) | rpl::start_with_next([=](const QRect &clip) { Painter p(this); @@ -53,7 +61,12 @@ MenuVolumeItem::MenuVolumeItem( : (enabled ? st.itemFg : st.itemFgDisabled)); p.setFont(_font); const auto volume = std::round(_slider->value() * kMaxVolumePercent); - p.drawText(QPoint(0, _font->ascent), u"%1%"_q.arg(volume)); + p.drawText(volumeRect, u"%1%"_q.arg(volume), style::al_center); + + _crossLineMute->paint( + p, + speakerRect.topLeft(), + _crossLineAnimation.value(_localMuted ? 1. : 0.)); }, lifetime()); setCloudVolume(startVolume); @@ -63,10 +76,17 @@ MenuVolumeItem::MenuVolumeItem( if (_localMuted != newMuted) { _localMuted = newMuted; _toggleMuteLocallyRequests.fire_copy(newMuted); + + _crossLineAnimation.start( + [=] { update(speakerRect); }, + _localMuted ? 0. : 1., + _localMuted ? 1. : 0., + st::callPanelDuration); } if (value > 0) { _changeVolumeLocallyRequests.fire(value * _maxVolume); } + update(volumeRect); }); const auto returnVolume = [=] { @@ -122,7 +142,8 @@ MenuVolumeItem::MenuVolumeItem( _waitingForUpdateVolume = false; }, lifetime()); - _slider->setGeometry(rect()); + _slider->setGeometry(itemRect + - style::margins(0, contentHeight() / 2, 0, 0)); } void MenuVolumeItem::setCloudVolume(int volume) { @@ -148,7 +169,9 @@ bool MenuVolumeItem::isEnabled() const { } int MenuVolumeItem::contentHeight() const { - return st::groupCallMenuVolumeItemHeight; + return _st.itemPadding.top() + + _st.itemPadding.bottom() + + _stCross.icon.height() * 2; } rpl::producer<bool> MenuVolumeItem::toggleMuteRequests() const { diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index e047a9f6f..d76a17f6d 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/menu/menu_item_base.h" namespace Ui { +class CrossLineAnimation; class MediaSlider; } // namespace Ui @@ -55,7 +56,12 @@ private: const base::unique_qptr<Ui::MediaSlider> _slider; const not_null<QAction*> _dummyAction; + const style::Menu &_st; const style::font &_font; + const style::CrossLineAnimation &_stCross; + + const std::unique_ptr<Ui::CrossLineAnimation> _crossLineMute; + Ui::Animations::Simple _crossLineAnimation; rpl::event_stream<bool> _toggleMuteRequests; rpl::event_stream<bool> _toggleMuteLocallyRequests; From 1d3e76e1fe4f08c27f3459983b583aafa2e190fd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 16 Jan 2021 18:18:03 +0300 Subject: [PATCH 172/396] Added resize handler of volume menu item in group calls. --- .../SourceFiles/calls/calls_volume_item.cpp | 27 ++++++++++--------- .../SourceFiles/calls/calls_volume_item.h | 5 +++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index ab8701f57..df2b3034c 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -36,16 +36,22 @@ MenuVolumeItem::MenuVolumeItem( st::mediaPlayerPanelPlayback)) , _dummyAction(new QAction(parent)) , _st(st) -, _font(st.itemStyle.font) , _stCross(st::groupCallMuteCrossLine) , _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true)) { initResizeHook(parent->sizeValue()); enableMouseSelecting(); - const auto itemRect = rect() - st.itemPadding; - const auto speakerRect = QRect(itemRect.topLeft(), _stCross.icon.size()); - const auto volumeRect = speakerRect.translated(_stCross.icon.width(), 0); + sizeValue( + ) | rpl::start_with_next([=](const QSize &size) { + const auto geometry = QRect(QPoint(), size); + _itemRect = geometry - _st.itemPadding; + _speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size()); + _volumeRect = _speakerRect.translated(_stCross.icon.width(), 0); + + _slider->setGeometry(_itemRect + - style::margins(0, contentHeight() / 2, 0, 0)); + }, lifetime()); paintRequest( ) | rpl::start_with_next([=](const QRect &clip) { @@ -59,13 +65,13 @@ MenuVolumeItem::MenuVolumeItem( : selected ? st.itemFgOver : (enabled ? st.itemFg : st.itemFgDisabled)); - p.setFont(_font); + p.setFont(_st.itemStyle.font); const auto volume = std::round(_slider->value() * kMaxVolumePercent); - p.drawText(volumeRect, u"%1%"_q.arg(volume), style::al_center); + p.drawText(_volumeRect, u"%1%"_q.arg(volume), style::al_center); _crossLineMute->paint( p, - speakerRect.topLeft(), + _speakerRect.topLeft(), _crossLineAnimation.value(_localMuted ? 1. : 0.)); }, lifetime()); @@ -78,7 +84,7 @@ MenuVolumeItem::MenuVolumeItem( _toggleMuteLocallyRequests.fire_copy(newMuted); _crossLineAnimation.start( - [=] { update(speakerRect); }, + [=] { update(_speakerRect); }, _localMuted ? 0. : 1., _localMuted ? 1. : 0., st::callPanelDuration); @@ -86,7 +92,7 @@ MenuVolumeItem::MenuVolumeItem( if (value > 0) { _changeVolumeLocallyRequests.fire(value * _maxVolume); } - update(volumeRect); + update(_volumeRect); }); const auto returnVolume = [=] { @@ -141,9 +147,6 @@ MenuVolumeItem::MenuVolumeItem( } _waitingForUpdateVolume = false; }, lifetime()); - - _slider->setGeometry(itemRect - - style::margins(0, contentHeight() / 2, 0, 0)); } void MenuVolumeItem::setCloudVolume(int volume) { diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index d76a17f6d..c9b042077 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -54,10 +54,13 @@ private: bool _cloudMuted = false; bool _localMuted = false; + QRect _itemRect; + QRect _speakerRect; + QRect _volumeRect; + const base::unique_qptr<Ui::MediaSlider> _slider; const not_null<QAction*> _dummyAction; const style::Menu &_st; - const style::font &_font; const style::CrossLineAnimation &_stCross; const std::unique_ptr<Ui::CrossLineAnimation> _crossLineMute; From 833ffe1784749d546a7202dc871a17da07574be1 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 16 Jan 2021 23:10:15 +0300 Subject: [PATCH 173/396] Added color animation to volume menu item in group calls. --- .../SourceFiles/calls/calls_volume_item.cpp | 36 ++++++++++++++----- .../SourceFiles/calls/calls_volume_item.h | 3 ++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index df2b3034c..15836eb06 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_volume_item.h" #include "calls/calls_group_common.h" +#include "ui/effects/animation_value.h" #include "ui/effects/cross_line.h" #include "ui/widgets/continuous_sliders.h" #include "styles/style_calls.h" @@ -57,14 +58,17 @@ MenuVolumeItem::MenuVolumeItem( ) | rpl::start_with_next([=](const QRect &clip) { Painter p(this); - const auto enabled = isEnabled(); + const auto muteProgress = + _crossLineAnimation.value(_localMuted ? 1. : 0.); + const auto selected = isSelected(); p.fillRect(clip, selected ? st.itemBgOver : st.itemBg); - p.setPen(_localMuted - ? (selected ? st::attentionButtonFgOver : st::attentionButtonFg) - : selected - ? st.itemFgOver - : (enabled ? st.itemFg : st.itemFgDisabled)); + + const auto mutePen = anim::color( + unmuteColor(), + muteColor(), + muteProgress); + p.setPen(mutePen); p.setFont(_st.itemStyle.font); const auto volume = std::round(_slider->value() * kMaxVolumePercent); p.drawText(_volumeRect, u"%1%"_q.arg(volume), style::al_center); @@ -72,7 +76,8 @@ MenuVolumeItem::MenuVolumeItem( _crossLineMute->paint( p, _speakerRect.topLeft(), - _crossLineAnimation.value(_localMuted ? 1. : 0.)); + muteProgress, + (!muteProgress) ? std::nullopt : std::optional<QColor>(mutePen)); }, lifetime()); setCloudVolume(startVolume); @@ -84,7 +89,7 @@ MenuVolumeItem::MenuVolumeItem( _toggleMuteLocallyRequests.fire_copy(newMuted); _crossLineAnimation.start( - [=] { update(_speakerRect); }, + [=] { update(_speakerRect.united(_volumeRect)); }, _localMuted ? 0. : 1., _localMuted ? 1. : 0., st::callPanelDuration); @@ -149,6 +154,20 @@ MenuVolumeItem::MenuVolumeItem( }, lifetime()); } +QColor MenuVolumeItem::unmuteColor() const { + return (isSelected() + ? _st.itemFgOver + : isEnabled() + ? _st.itemFg + : _st.itemFgDisabled)->c; +} + +QColor MenuVolumeItem::muteColor() const { + return (isSelected() + ? st::attentionButtonFgOver + : st::attentionButtonFg)->c; +} + void MenuVolumeItem::setCloudVolume(int volume) { if (_cloudVolume == volume) { return; @@ -161,6 +180,7 @@ void MenuVolumeItem::setCloudVolume(int volume) { void MenuVolumeItem::setSliderVolume(int volume) { _slider->setValue(float64(volume) / _maxVolume); + update(_volumeRect); } not_null<QAction*> MenuVolumeItem::action() const { diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index c9b042077..a51888868 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -48,6 +48,9 @@ private: void setCloudVolume(int volume); void setSliderVolume(int volume); + QColor unmuteColor() const; + QColor muteColor() const; + const int _maxVolume; int _cloudVolume = 0; bool _waitingForUpdateVolume = false; From 50f87cce84f594e57f359432a8741e41d45c4375 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 18 Jan 2021 00:44:03 +0300 Subject: [PATCH 174/396] Added arcs animation to volume menu item in group calls. --- Telegram/SourceFiles/calls/calls.style | 15 ++++++ .../SourceFiles/calls/calls_volume_item.cpp | 51 +++++++++++++++++-- .../SourceFiles/calls/calls_volume_item.h | 8 +++ 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index bb5e042fd..8f6017179 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -786,6 +786,21 @@ groupCallMuteCrossLine: CrossLineAnimation { stroke: 2px; } +groupCallMenuSpeakerArcsSkip: 1px; +groupCallMenuVolumeSkip: 5px; + +groupCallSpeakerArcsAnimation: ArcsAnimation { + fg: groupCallIconFg; + stroke: 2px; + space: 4px; + duration: 200; + deltaAngle: 60; + deltaHeight: 6px; + deltaWidth: 7px; + startHeight: 3px; + startWidth: 0px; +} + callTopBarMuteCrossLine: CrossLineAnimation { fg: callBarFg; icon: icon {{ "calls/call_record_active", callBarFg }}; diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 15836eb06..95ed0afda 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -14,11 +14,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_calls.h" #include "styles/style_media_player.h" +#include "ui/paint/arcs.h" + namespace Calls { namespace { constexpr auto kMaxVolumePercent = 200; +constexpr auto kSpeakerThreshold = { + 10.0f / kMaxVolumePercent, + 50.0f / kMaxVolumePercent, + 150.0f / kMaxVolumePercent }; + } // namespace MenuVolumeItem::MenuVolumeItem( @@ -38,7 +45,12 @@ MenuVolumeItem::MenuVolumeItem( , _dummyAction(new QAction(parent)) , _st(st) , _stCross(st::groupCallMuteCrossLine) -, _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true)) { +, _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true)) +, _arcs(std::make_unique<Ui::Paint::ArcsAnimation>( + st::groupCallSpeakerArcsAnimation, + kSpeakerThreshold, + _localMuted ? 0. : (startVolume / float(maxVolume)), + Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) { initResizeHook(parent->sizeValue()); enableMouseSelecting(); @@ -48,12 +60,18 @@ MenuVolumeItem::MenuVolumeItem( const auto geometry = QRect(QPoint(), size); _itemRect = geometry - _st.itemPadding; _speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size()); - _volumeRect = _speakerRect.translated(_stCross.icon.width(), 0); + _volumeRect = _speakerRect.translated( + _stCross.icon.width() + st::groupCallMenuVolumeSkip, + 0); + _arcPosition = _speakerRect.center() + + QPoint(0, st::groupCallMenuSpeakerArcsSkip); _slider->setGeometry(_itemRect - style::margins(0, contentHeight() / 2, 0, 0)); }, lifetime()); + setCloudVolume(startVolume); + paintRequest( ) | rpl::start_with_next([=](const QRect &clip) { Painter p(this); @@ -78,9 +96,12 @@ MenuVolumeItem::MenuVolumeItem( _speakerRect.topLeft(), muteProgress, (!muteProgress) ? std::nullopt : std::optional<QColor>(mutePen)); - }, lifetime()); - setCloudVolume(startVolume); + { + p.translate(_arcPosition); + _arcs->paint(p); + } + }, lifetime()); _slider->setChangeProgressCallback([=](float64 value) { const auto newMuted = (value == 0); @@ -98,6 +119,7 @@ MenuVolumeItem::MenuVolumeItem( _changeVolumeLocallyRequests.fire(value * _maxVolume); } update(_volumeRect); + _arcs->setValue(value); }); const auto returnVolume = [=] { @@ -152,6 +174,27 @@ MenuVolumeItem::MenuVolumeItem( } _waitingForUpdateVolume = false; }, lifetime()); + + initArcsAnimation(); +} + +void MenuVolumeItem::initArcsAnimation() { + _arcsAnimation.init([=](crl::time now) { + _arcs->update(now); + update(_speakerRect); + }); + + _arcs->startUpdateRequests( + ) | rpl::start_with_next([=] { + if (!_arcsAnimation.animating()) { + _arcsAnimation.start(); + } + }, lifetime()); + + _arcs->stopUpdateRequests( + ) | rpl::start_with_next([=] { + _arcsAnimation.stop(); + }, lifetime()); } QColor MenuVolumeItem::unmuteColor() const { diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index a51888868..5ba925835 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class CrossLineAnimation; class MediaSlider; +namespace Paint { +class ArcsAnimation; +} // namespace Paint } // namespace Ui namespace Calls { @@ -45,6 +48,8 @@ protected: int contentHeight() const override; private: + void initArcsAnimation(); + void setCloudVolume(int volume); void setSliderVolume(int volume); @@ -60,6 +65,7 @@ private: QRect _itemRect; QRect _speakerRect; QRect _volumeRect; + QPoint _arcPosition; const base::unique_qptr<Ui::MediaSlider> _slider; const not_null<QAction*> _dummyAction; @@ -68,6 +74,8 @@ private: const std::unique_ptr<Ui::CrossLineAnimation> _crossLineMute; Ui::Animations::Simple _crossLineAnimation; + const std::unique_ptr<Ui::Paint::ArcsAnimation> _arcs; + Ui::Animations::Basic _arcsAnimation; rpl::event_stream<bool> _toggleMuteRequests; rpl::event_stream<bool> _toggleMuteLocallyRequests; From 9fed46fb6e30f432b561ecee0e2ccf6e6b89ac43 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 18 Jan 2021 02:53:09 +0300 Subject: [PATCH 175/396] Removed draft volume items from menu in group calls. --- Telegram/Resources/langs/lang.strings | 2 + .../SourceFiles/calls/calls_group_members.cpp | 170 +++++++++--------- 2 files changed, 88 insertions(+), 84 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f84486dca..f8e200f16 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1910,6 +1910,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_too_many" = "Sorry, there are too many members in this voice chat. Please try again later."; "lng_group_call_context_mute" = "Mute"; "lng_group_call_context_unmute" = "Allow to speak"; +"lng_group_call_context_mute_for_me" = "Mute for me"; +"lng_group_call_context_unmute_for_me" = "Unmute for me"; "lng_group_call_duration_days#one" = "{count} day"; "lng_group_call_duration_days#other" = "{count} days"; "lng_group_call_duration_hours#one" = "{count} hour"; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 985821650..40660df1e 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1157,30 +1157,6 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( } return false; }(); - const auto amCallAdmin = _peer->canManageGroupCall(); - const auto mute = (admin || !amCallAdmin) - ? (muteState == Row::State::Active) - : (muteState != Row::State::Muted); - const auto toggleMute = crl::guard(this, [=] { - _toggleMuteRequests.fire(Group::MuteRequest{ - .user = user, - .mute = mute, - }); - }); - const auto toggleMute_ = crl::guard(this, [=](bool mute, bool local) { - _toggleMuteRequests.fire(Group::MuteRequest{ - .user = user, - .mute = mute, - .locallyOnly = local, - }); - }); - const auto changeVolume = crl::guard(this, [=](int volume, bool local) { - _changeVolumeRequests.fire(Group::VolumeRequest{ - .user = user, - .volume = std::clamp(volume, 1, Group::kMaxVolume), - .locallyOnly = local, - }); - }); const auto session = &user->session(); const auto getCurrentWindow = [=]() -> Window::SessionController* { @@ -1231,77 +1207,103 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( _kickMemberRequests.fire_copy(user); }); - if ((muteState != Row::State::Invited) - && amCallAdmin - && (!admin || mute)) { - result->addAction( - (mute - ? tr::lng_group_call_context_mute(tr::now) - : tr::lng_group_call_context_unmute(tr::now)), - toggleMute); - } if (real->ssrc() != 0) { - if (!amCallAdmin - && ((muteState == Row::State::Active) - || (real->state() == Row::State::MutedByMe))) { - result->addAction( - ((muteState == Row::State::Active) - ? "Mute for me" - : "Unmute for me"), - toggleMute); - } - if (muteState != Row::State::Muted - && muteState != Row::State::MutedByMe) { - const auto volume = real->volume(); - result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume + 2000, false); + const auto muteString = [=] { + return (_peer->canManageGroupCall() + ? tr::lng_group_call_context_mute + : tr::lng_group_call_context_mute_for_me)(tr::now); + }; + + const auto unmuteString = [=] { + return (_peer->canManageGroupCall() + ? tr::lng_group_call_context_unmute + : tr::lng_group_call_context_unmute_for_me)(tr::now); + }; + + const auto toggleMute = crl::guard(this, [=](bool mute, bool local) { + _toggleMuteRequests.fire(Group::MuteRequest{ + .user = user, + .mute = mute, + .locallyOnly = local, }); - result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] { - changeVolume(volume - 2000, false); + }); + const auto changeVolume = crl::guard(this, [=]( + int volume, + bool local) { + _changeVolumeRequests.fire(Group::VolumeRequest{ + .user = user, + .volume = std::clamp(volume, 1, Group::kMaxVolume), + .locallyOnly = local, }); - } + }); - { - const auto call = _call.get(); - auto otherParticipantStateValue = call - ? call->otherParticipantStateValue( - ) | rpl::filter([=](const Group::ParticipantState &data) { - return data.user == user; - }) - : rpl::never<Group::ParticipantState>(); + const auto call = _call.get(); + auto otherParticipantStateValue = call + ? call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.user == user; + }) + : rpl::never<Group::ParticipantState>(); - auto volumeItem = base::make_unique_q<MenuVolumeItem>( - result, - st::groupCallPopupMenu.menu, - otherParticipantStateValue, - real->volume(), - Group::kMaxVolume, - (muteState == Row::State::Muted - || muteState == Row::State::MutedByMe)); + const auto isMuted = (muteState == Row::State::Muted) + || (muteState == Row::State::MutedByMe); - volumeItem->toggleMuteRequests( - ) | rpl::start_with_next([=](bool muted) { - toggleMute_(muted, false); - }, volumeItem->lifetime()); + auto volumeItem = base::make_unique_q<MenuVolumeItem>( + result, + st::groupCallPopupMenu.menu, + otherParticipantStateValue, + real->volume(), + Group::kMaxVolume, + isMuted); - volumeItem->toggleMuteLocallyRequests( - ) | rpl::start_with_next([=](bool muted) { - toggleMute_(muted, true); - }, volumeItem->lifetime()); + auto mutesFromVolume = volumeItem->toggleMuteRequests(); - volumeItem->changeVolumeRequests( - ) | rpl::start_with_next([=](int volume) { - changeVolume(volume, false); - }, volumeItem->lifetime()); + volumeItem->toggleMuteRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute(muted, false); + }, volumeItem->lifetime()); - volumeItem->changeVolumeLocallyRequests( - ) | rpl::start_with_next([=](int volume) { - changeVolume(volume, true); - }, volumeItem->lifetime()); + volumeItem->toggleMuteLocallyRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute(muted, true); + }, volumeItem->lifetime()); - result->addAction(std::move(volumeItem)); - } + volumeItem->changeVolumeRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, false); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeLocallyRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, true); + }, volumeItem->lifetime()); + + result->addAction(std::move(volumeItem)); + + const auto muteAction = [&]() -> QAction* { + if (muteState == Row::State::Invited) { + return nullptr; + } + auto callback = [=] { + const auto state = real->state(); + const auto muted = (state == Row::State::Muted) + || (state == Row::State::MutedByMe); + toggleMute(!muted, false); + }; + return result->addAction( + isMuted ? unmuteString() : muteString(), + std::move(callback)); + }(); + + std::move( + mutesFromVolume + ) | rpl::filter([=] { + return muteAction != nullptr; + }) | rpl::start_with_next([=](bool muted) { + muteAction->setText(muted ? unmuteString() : muteString()); + }, result->lifetime()); } + result->addAction( tr::lng_context_view_profile(tr::now), showProfile); From ef1a4e4ce361be127edb00650f9e8598d344dd5c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 19 Jan 2021 17:29:57 +0300 Subject: [PATCH 176/396] Moved adding volume and mute items in group calls to separate place. --- Telegram/SourceFiles/calls/calls.style | 1 + .../SourceFiles/calls/calls_group_members.cpp | 204 ++++++++++-------- .../SourceFiles/calls/calls_volume_item.cpp | 7 +- 3 files changed, 115 insertions(+), 97 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 8f6017179..5487702e0 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -788,6 +788,7 @@ groupCallMuteCrossLine: CrossLineAnimation { groupCallMenuSpeakerArcsSkip: 1px; groupCallMenuVolumeSkip: 5px; +groupCallMenuVolumeSkipSlider: defaultContinuousSlider; groupCallSpeakerArcsAnimation: ArcsAnimation { fg: groupCallIconFg; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 40660df1e..b8e396ad4 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -258,6 +258,10 @@ private: [[nodiscard]] base::unique_qptr<Ui::PopupMenu> createRowContextMenu( QWidget *parent, not_null<PeerListRow*> row); + void addMuteActionsToContextMenu( + not_null<Ui::PopupMenu*> menu, + not_null<UserData*> user, + not_null<Row*> row); void setupListChangeViewers(not_null<GroupCall*> call); void subscribeToChanges(not_null<Data::GroupCall*> real); void updateRow( @@ -1208,100 +1212,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( }); if (real->ssrc() != 0) { - const auto muteString = [=] { - return (_peer->canManageGroupCall() - ? tr::lng_group_call_context_mute - : tr::lng_group_call_context_mute_for_me)(tr::now); - }; - - const auto unmuteString = [=] { - return (_peer->canManageGroupCall() - ? tr::lng_group_call_context_unmute - : tr::lng_group_call_context_unmute_for_me)(tr::now); - }; - - const auto toggleMute = crl::guard(this, [=](bool mute, bool local) { - _toggleMuteRequests.fire(Group::MuteRequest{ - .user = user, - .mute = mute, - .locallyOnly = local, - }); - }); - const auto changeVolume = crl::guard(this, [=]( - int volume, - bool local) { - _changeVolumeRequests.fire(Group::VolumeRequest{ - .user = user, - .volume = std::clamp(volume, 1, Group::kMaxVolume), - .locallyOnly = local, - }); - }); - - const auto call = _call.get(); - auto otherParticipantStateValue = call - ? call->otherParticipantStateValue( - ) | rpl::filter([=](const Group::ParticipantState &data) { - return data.user == user; - }) - : rpl::never<Group::ParticipantState>(); - - const auto isMuted = (muteState == Row::State::Muted) - || (muteState == Row::State::MutedByMe); - - auto volumeItem = base::make_unique_q<MenuVolumeItem>( - result, - st::groupCallPopupMenu.menu, - otherParticipantStateValue, - real->volume(), - Group::kMaxVolume, - isMuted); - - auto mutesFromVolume = volumeItem->toggleMuteRequests(); - - volumeItem->toggleMuteRequests( - ) | rpl::start_with_next([=](bool muted) { - toggleMute(muted, false); - }, volumeItem->lifetime()); - - volumeItem->toggleMuteLocallyRequests( - ) | rpl::start_with_next([=](bool muted) { - toggleMute(muted, true); - }, volumeItem->lifetime()); - - volumeItem->changeVolumeRequests( - ) | rpl::start_with_next([=](int volume) { - changeVolume(volume, false); - }, volumeItem->lifetime()); - - volumeItem->changeVolumeLocallyRequests( - ) | rpl::start_with_next([=](int volume) { - changeVolume(volume, true); - }, volumeItem->lifetime()); - - result->addAction(std::move(volumeItem)); - - const auto muteAction = [&]() -> QAction* { - if (muteState == Row::State::Invited) { - return nullptr; - } - auto callback = [=] { - const auto state = real->state(); - const auto muted = (state == Row::State::Muted) - || (state == Row::State::MutedByMe); - toggleMute(!muted, false); - }; - return result->addAction( - isMuted ? unmuteString() : muteString(), - std::move(callback)); - }(); - - std::move( - mutesFromVolume - ) | rpl::filter([=] { - return muteAction != nullptr; - }) | rpl::start_with_next([=](bool muted) { - muteAction->setText(muted ? unmuteString() : muteString()); - }, result->lifetime()); + addMuteActionsToContextMenu(result, user, real); } result->addAction( @@ -1329,6 +1240,111 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( return result; } +void MembersController::addMuteActionsToContextMenu( + not_null<Ui::PopupMenu*> menu, + not_null<UserData*> user, + not_null<Row*> row) { + const auto muteString = [=] { + return (_peer->canManageGroupCall() + ? tr::lng_group_call_context_mute + : tr::lng_group_call_context_mute_for_me)(tr::now); + }; + + const auto unmuteString = [=] { + return (_peer->canManageGroupCall() + ? tr::lng_group_call_context_unmute + : tr::lng_group_call_context_unmute_for_me)(tr::now); + }; + + const auto toggleMute = crl::guard(this, [=](bool mute, bool local) { + _toggleMuteRequests.fire(Group::MuteRequest{ + .user = user, + .mute = mute, + .locallyOnly = local, + }); + }); + const auto changeVolume = crl::guard(this, [=]( + int volume, + bool local) { + _changeVolumeRequests.fire(Group::VolumeRequest{ + .user = user, + .volume = std::clamp(volume, 1, Group::kMaxVolume), + .locallyOnly = local, + }); + }); + + const auto muteState = row->state(); + const auto isMuted = (muteState == Row::State::Muted) + || (muteState == Row::State::MutedByMe); + + auto mutesFromVolume = rpl::never<bool>(); + + if (!isMuted) { + const auto call = _call.get(); + auto otherParticipantStateValue = call + ? call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.user == user; + }) + : rpl::never<Group::ParticipantState>(); + + auto volumeItem = base::make_unique_q<MenuVolumeItem>( + menu, + st::groupCallPopupMenu.menu, + otherParticipantStateValue, + row->volume(), + Group::kMaxVolume, + isMuted); + + mutesFromVolume = volumeItem->toggleMuteRequests(); + + volumeItem->toggleMuteRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute(muted, false); + }, volumeItem->lifetime()); + + volumeItem->toggleMuteLocallyRequests( + ) | rpl::start_with_next([=](bool muted) { + toggleMute(muted, true); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, false); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeLocallyRequests( + ) | rpl::start_with_next([=](int volume) { + changeVolume(volume, true); + }, volumeItem->lifetime()); + + menu->addAction(std::move(volumeItem)); + }; + + const auto muteAction = [&]() -> QAction* { + if (muteState == Row::State::Invited) { + return nullptr; + } + auto callback = [=] { + const auto state = row->state(); + const auto muted = (state == Row::State::Muted) + || (state == Row::State::MutedByMe); + toggleMute(!muted, false); + }; + return menu->addAction( + isMuted ? unmuteString() : muteString(), + std::move(callback)); + }(); + + if (muteAction) { + std::move( + mutesFromVolume + ) | rpl::start_with_next([=](bool muted) { + muteAction->setText(muted ? unmuteString() : muteString()); + }, menu->lifetime()); + } +} + std::unique_ptr<Row> MembersController::createSelfRow() { const auto self = _peer->session().user(); auto result = std::make_unique<Row>(this, self); diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 95ed0afda..5ccd89373 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/cross_line.h" #include "ui/widgets/continuous_sliders.h" #include "styles/style_calls.h" -#include "styles/style_media_player.h" #include "ui/paint/arcs.h" @@ -41,7 +40,7 @@ MenuVolumeItem::MenuVolumeItem( , _localMuted(muted) , _slider(base::make_unique_q<Ui::MediaSlider>( this, - st::mediaPlayerPanelPlayback)) + st::groupCallMenuVolumeSkipSlider)) , _dummyAction(new QAction(parent)) , _st(st) , _stCross(st::groupCallMuteCrossLine) @@ -88,7 +87,9 @@ MenuVolumeItem::MenuVolumeItem( muteProgress); p.setPen(mutePen); p.setFont(_st.itemStyle.font); - const auto volume = std::round(_slider->value() * kMaxVolumePercent); + const auto volume = _localMuted + ? 0 + : std::round(_slider->value() * kMaxVolumePercent); p.drawText(_volumeRect, u"%1%"_q.arg(volume), style::al_center); _crossLineMute->paint( From 130b8bc83c766697f73a08e37b848e86d4d3e398 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 25 Jan 2021 01:45:26 +0300 Subject: [PATCH 177/396] Changed slider colors from volume menu item in group calls. --- Telegram/SourceFiles/calls/calls.style | 9 ++++++++- Telegram/SourceFiles/calls/calls_volume_item.cpp | 12 +++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 5487702e0..b1616807b 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -788,7 +788,14 @@ groupCallMuteCrossLine: CrossLineAnimation { groupCallMenuSpeakerArcsSkip: 1px; groupCallMenuVolumeSkip: 5px; -groupCallMenuVolumeSkipSlider: defaultContinuousSlider; +groupCallMenuVolumeSlider: MediaSlider(defaultContinuousSlider) { + activeFg: groupCallMembersFg; + inactiveFg: groupCallMemberInactiveIcon; + activeFgOver: groupCallMembersFg; + inactiveFgOver: groupCallMemberInactiveIcon; + activeFgDisabled: groupCallMemberInactiveIcon; + receivedTillFg: groupCallMemberInactiveIcon; +} groupCallSpeakerArcsAnimation: ArcsAnimation { fg: groupCallIconFg; diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 5ccd89373..d35ad96cb 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -40,7 +40,7 @@ MenuVolumeItem::MenuVolumeItem( , _localMuted(muted) , _slider(base::make_unique_q<Ui::MediaSlider>( this, - st::groupCallMenuVolumeSkipSlider)) + st::groupCallMenuVolumeSlider)) , _dummyAction(new QAction(parent)) , _st(st) , _stCross(st::groupCallMuteCrossLine) @@ -54,6 +54,8 @@ MenuVolumeItem::MenuVolumeItem( initResizeHook(parent->sizeValue()); enableMouseSelecting(); + _slider->setAlwaysDisplayMarker(true); + sizeValue( ) | rpl::start_with_next([=](const QSize &size) { const auto geometry = QRect(QPoint(), size); @@ -75,8 +77,11 @@ MenuVolumeItem::MenuVolumeItem( ) | rpl::start_with_next([=](const QRect &clip) { Painter p(this); + const auto volume = _localMuted + ? 0 + : std::round(_slider->value() * kMaxVolumePercent); const auto muteProgress = - _crossLineAnimation.value(_localMuted ? 1. : 0.); + _crossLineAnimation.value((!volume) ? 1. : 0.); const auto selected = isSelected(); p.fillRect(clip, selected ? st.itemBgOver : st.itemBg); @@ -87,9 +92,6 @@ MenuVolumeItem::MenuVolumeItem( muteProgress); p.setPen(mutePen); p.setFont(_st.itemStyle.font); - const auto volume = _localMuted - ? 0 - : std::round(_slider->value() * kMaxVolumePercent); p.drawText(_volumeRect, u"%1%"_q.arg(volume), style::al_center); _crossLineMute->paint( From 5d1f55e29d04e801ded938662d526f5fa2ee9af0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 25 Jan 2021 02:44:27 +0300 Subject: [PATCH 178/396] Removed display of volume value in menu when participant is muted. --- Telegram/SourceFiles/calls/calls_volume_item.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index d35ad96cb..a156537f5 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -127,9 +127,6 @@ MenuVolumeItem::MenuVolumeItem( const auto returnVolume = [=] { _changeVolumeLocallyRequests.fire_copy(_cloudVolume); - crl::on_main(_slider.get(), [=] { - setSliderVolume(_cloudVolume); - }); }; _slider->setChangeFinishedCallback([=](float64 value) { @@ -220,7 +217,7 @@ void MenuVolumeItem::setCloudVolume(int volume) { } _cloudVolume = volume; if (!_slider->isChanging()) { - setSliderVolume(volume); + setSliderVolume(_cloudMuted ? 0. : volume); } } From 7424e6afcc65ae7e45bbaca1b6ea5c0095c3f784 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 25 Jan 2021 04:02:21 +0300 Subject: [PATCH 179/396] Fixed applying active participant state when changing local volume. --- Telegram/SourceFiles/calls/calls_group_members.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index b8e396ad4..b39264a63 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -331,7 +331,11 @@ void Row::updateState(const Data::GroupCall::Participant *participant) { setSpeaking(false); } else if (!participant->muted || (participant->sounding && participant->ssrc != 0)) { - setState(participant->mutedByMe ? State::MutedByMe : State::Active); + setState(participant->mutedByMe + ? State::MutedByMe + : (participant->sounding || participant->speaking) + ? State::Active + : State::Inactive); setSounding(participant->sounding && participant->ssrc != 0); setSpeaking(participant->speaking && participant->ssrc != 0); } else if (participant->canSelfUnmute) { From 416489a84f904cadea187727e0b50019f3cd2d30 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 25 Jan 2021 16:51:20 +0300 Subject: [PATCH 180/396] Added volume info to list of participants in group calls. --- Telegram/Resources/langs/lang.strings | 1 + .../SourceFiles/calls/calls_group_members.cpp | 122 +++++++++++++++++- 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f8e200f16..773ee980e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1881,6 +1881,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_settings_title" = "Settings"; "lng_group_call_invite" = "Invite Member"; "lng_group_call_invited_status" = "invited"; +"lng_group_call_muted_by_me_status" = "muted by me"; "lng_group_call_invite_title" = "Invite members"; "lng_group_call_invite_button" = "Invite"; "lng_group_call_add_to_group_one" = "{user} isn't a member of «{group}» yet. Add them to the group?"; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index b39264a63..61a668b2e 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer_values.h" // Data::CanWriteValue. #include "data/data_session.h" // Data::Session::invitedToCallUsers. #include "settings/settings_common.h" // Settings::CreateButton. +#include "ui/paint/arcs.h" #include "ui/paint/blobs.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" @@ -46,6 +47,11 @@ constexpr auto kUserpicMinScale = 0.8; constexpr auto kMaxLevel = 1.; constexpr auto kWideScale = 5; +constexpr auto kSpeakerThreshold = { + Group::kDefaultVolume * 0.1f / Group::kMaxVolume, + Group::kDefaultVolume * 0.5f / Group::kMaxVolume, + Group::kDefaultVolume * 1.5f / Group::kMaxVolume }; + auto RowBlobs() -> std::array<Ui::Paint::Blobs::BlobData, 2> { return { { { @@ -179,6 +185,30 @@ private: rpl::lifetime lifetime; }; + + struct StatusIcon { + StatusIcon(float volume) + : speaker(st::groupCallMuteCrossLine.icon) + , arcs(std::make_unique<Ui::Paint::ArcsAnimation>( + st::groupCallSpeakerArcsAnimation, + kSpeakerThreshold, + volume, + Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) { + } + const style::icon &speaker; + const std::unique_ptr<Ui::Paint::ArcsAnimation> arcs; + + rpl::lifetime lifetime; + }; + + int statusIconWidth() const; + int statusIconHeight() const; + void paintStatusIcon( + Painter &p, + const style::PeerListItem &st, + const style::font &font, + bool selected); + void refreshStatus() override; void setSounding(bool sounding); void setSpeaking(bool speaking); @@ -194,9 +224,11 @@ private: State _state = State::Inactive; std::unique_ptr<Ui::RippleAnimation> _actionRipple; std::unique_ptr<BlobsAnimation> _blobsAnimation; + std::unique_ptr<StatusIcon> _statusIcon; Ui::Animations::Simple _speakingAnimation; // For gray-red/green icon. Ui::Animations::Simple _mutedAnimation; // For gray/red icon. Ui::Animations::Simple _activeAnimation; // For icon cross animation. + Ui::Animations::Simple _arcsAnimation; // For volume arcs animation. uint32 _ssrc = 0; int _volume = Group::kDefaultVolume; bool _sounding = false; @@ -359,6 +391,28 @@ void Row::setSpeaking(bool speaking) { _speaking ? 0. : 1., _speaking ? 1. : 0., st::widgetFadeDuration); + + if (!_speaking || (_state == State::MutedByMe)) { + _statusIcon = nullptr; + } else if (!_statusIcon) { + _statusIcon = std::make_unique<StatusIcon>( + (float)_volume / Group::kMaxVolume); + + _statusIcon->arcs->startUpdateRequests( + ) | rpl::start_with_next([=] { + auto callback = [=] { + if (_statusIcon) { + _statusIcon->arcs->update(crl::now()); + } + _delegate->rowUpdateRow(this); + }; + _arcsAnimation.start( + std::move(callback), + 0., + 1., + st::groupCallSpeakerArcsAnimation.duration); + }, _statusIcon->lifetime); + } } void Row::setSounding(bool sounding) { @@ -410,6 +464,9 @@ void Row::setSsrc(uint32 ssrc) { void Row::setVolume(int volume) { _volume = volume; + if (_statusIcon) { + _statusIcon->arcs->setValue((float)volume / Group::kMaxVolume); + } } void Row::updateLevel(float level) { @@ -525,6 +582,62 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback { }; } +int Row::statusIconWidth() const { + if (!_statusIcon) { + return 0; + } + return _speaking + ? 2 * (_statusIcon->speaker.width() + st::groupCallMenuVolumeSkip) + : 0; +} + +int Row::statusIconHeight() const { + if (!_statusIcon) { + return 0; + } + return _speaking + ? _statusIcon->speaker.height() + : 0; +} + +void Row::paintStatusIcon( + Painter &p, + const style::PeerListItem &st, + const style::font &font, + bool selected) { + if (!_statusIcon) { + return; + } + p.setFont(font); + const auto color = (_speaking + ? st.statusFgActive + : (selected ? st.statusFgOver : st.statusFg))->c; + p.setPen(color); + + const auto speakerRect = QRect( + st.statusPosition + + QPoint(0, (font->height - statusIconHeight()) / 2), + _statusIcon->speaker.size()); + const auto volumeRect = speakerRect.translated( + _statusIcon->speaker.width() + st::groupCallMenuVolumeSkip, + 0); + const auto arcPosition = speakerRect.center() + + QPoint(0, st::groupCallMenuSpeakerArcsSkip); + + const auto volume = std::round(_volume / 100.); + _statusIcon->speaker.paint( + p, + speakerRect.topLeft(), + speakerRect.width(), + color); + p.drawText(volumeRect, QString("%1%").arg(volume), style::al_center); + + p.save(); + p.translate(arcPosition); + _statusIcon->arcs->paint(p, color); + p.restore(); +} + void Row::paintStatusText( Painter &p, const style::PeerListItem &st, @@ -533,6 +646,11 @@ void Row::paintStatusText( int availableWidth, int outerWidth, bool selected) { + p.save(); + const auto &font = st::normalFont; + paintStatusIcon(p, st, font, selected); + p.translate(statusIconWidth(), 0); + const auto guard = gsl::finally([&] { p.restore(); }); if (_state != State::Invited && _state != State::MutedByMe) { PeerListRow::paintStatusText( p, @@ -544,7 +662,7 @@ void Row::paintStatusText( selected); return; } - p.setFont(st::normalFont); + p.setFont(font); if (_state == State::MutedByMe) { p.setPen(st::groupCallMemberMutedIcon); } else { @@ -555,7 +673,7 @@ void Row::paintStatusText( y, outerWidth, (_state == State::MutedByMe - ? "muted by me" + ? tr::lng_group_call_muted_by_me_status(tr::now) : peer()->isSelf() ? tr::lng_status_connecting(tr::now) : tr::lng_group_call_invited_status(tr::now))); From b22c65a8db859c91c6859dcfd15b8f3589c57071 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 12:15:00 +0400 Subject: [PATCH 181/396] Fix build on Windows, add report Fake-s. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/report_box.cpp | 20 +++++------- Telegram/SourceFiles/boxes/report_box.h | 2 ++ .../SourceFiles/calls/calls_group_call.cpp | 2 +- .../SourceFiles/calls/calls_group_members.cpp | 32 +++++++++++-------- .../SourceFiles/calls/calls_volume_item.cpp | 2 +- Telegram/lib_ui | 2 +- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 773ee980e..ab31c4d07 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -977,6 +977,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_report_bot_title" = "Report bot"; "lng_report_message_title" = "Report message"; "lng_report_reason_spam" = "Spam"; +"lng_report_reason_fake" = "Fake"; "lng_report_reason_violence" = "Violence"; "lng_report_reason_child_abuse" = "Child Abuse"; "lng_report_reason_pornography" = "Pornography"; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 0a26a020a..41b3d24f3 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -73,10 +73,9 @@ void ReportBox::prepare() { st::defaultBoxCheckbox); }; createButton(_reasonSpam, Reason::Spam, tr::lng_report_reason_spam(tr::now)); + createButton(_reasonFake, Reason::Fake, tr::lng_report_reason_fake(tr::now)); createButton(_reasonViolence, Reason::Violence, tr::lng_report_reason_violence(tr::now)); - if (_ids) { - createButton(_reasonChildAbuse, Reason::ChildAbuse, tr::lng_report_reason_child_abuse(tr::now)); - } + createButton(_reasonChildAbuse, Reason::ChildAbuse, tr::lng_report_reason_child_abuse(tr::now)); createButton(_reasonPornography, Reason::Pornography, tr::lng_report_reason_pornography(tr::now)); createButton(_reasonOther, Reason::Other, tr::lng_report_reason_other(tr::now)); _reasonGroup->setChangedCallback([=](Reason value) { @@ -90,14 +89,10 @@ void ReportBox::resizeEvent(QResizeEvent *e) { BoxContent::resizeEvent(e); _reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _reasonSpam->getMargins().top()); - _reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->bottomNoMargins() + st::boxOptionListSkip); - if (_ids) { - _reasonChildAbuse->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip); - _reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonChildAbuse->bottomNoMargins() + st::boxOptionListSkip); - } - else{ - _reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip); - } + _reasonFake->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->bottomNoMargins() + st::boxOptionListSkip); + _reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonFake->bottomNoMargins() + st::boxOptionListSkip); + _reasonChildAbuse->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip); + _reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonChildAbuse->bottomNoMargins() + st::boxOptionListSkip); _reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->bottomNoMargins() + st::boxOptionListSkip); if (_reasonOtherText) { @@ -156,6 +151,7 @@ void ReportBox::report() { const auto reason = [&] { switch (_reasonGroup->value()) { case Reason::Spam: return MTP_inputReportReasonSpam(); + case Reason::Fake: return MTP_inputReportReasonFake(); case Reason::Violence: return MTP_inputReportReasonViolence(); case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse(); case Reason::Pornography: return MTP_inputReportReasonPornography(); @@ -203,7 +199,7 @@ void ReportBox::reportFail(const RPCError &error) { } void ReportBox::updateMaxHeight() { - const auto buttonsCount = _ids ? 5 : 4; + const auto buttonsCount = 6; auto newHeight = st::boxOptionListPadding.top() + _reasonSpam->getMargins().top() + buttonsCount * _reasonSpam->heightNoMargins() + (buttonsCount - 1) * st::boxOptionListSkip + _reasonSpam->getMargins().bottom() + st::boxOptionListPadding.bottom(); if (_reasonOtherText) { diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index 04b5beb5d..ad637aae3 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -37,6 +37,7 @@ protected: private: enum class Reason { Spam, + Fake, Violence, ChildAbuse, Pornography, @@ -56,6 +57,7 @@ private: std::shared_ptr<Ui::RadioenumGroup<Reason>> _reasonGroup; object_ptr<Ui::Radioenum<Reason>> _reasonSpam = { nullptr }; + object_ptr<Ui::Radioenum<Reason>> _reasonFake = { nullptr }; object_ptr<Ui::Radioenum<Reason>> _reasonViolence = { nullptr }; object_ptr<Ui::Radioenum<Reason>> _reasonChildAbuse = { nullptr }; object_ptr<Ui::Radioenum<Reason>> _reasonPornography = { nullptr }; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 2a7a4e92a..81b766353 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -599,8 +599,8 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { } _otherParticipantStateValue.fire(Group::ParticipantState{ .user = user, - .mutedByMe = data.is_muted_by_you(), .volume = data.vvolume().value_or_empty(), + .mutedByMe = data.is_muted_by_you(), }); }; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 61a668b2e..8bdd36921 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -902,12 +902,14 @@ void MembersController::appendInvitedUsers() { void MembersController::updateRow( const std::optional<Data::GroupCall::Participant> &was, const Data::GroupCall::Participant &now) { + auto reorderIfInvitedBeforeIndex = 0; auto countChange = 0; if (const auto row = findRow(now.user)) { if (now.speaking && (!was || !was->speaking)) { checkSpeakingRowPosition(row); } if (row->state() == Row::State::Invited) { + reorderIfInvitedBeforeIndex = row->absoluteIndex(); countChange = 1; } updateRow(row, &now); @@ -915,25 +917,27 @@ void MembersController::updateRow( if (row->speaking()) { delegate()->peerListPrependRow(std::move(row)); } else { - static constexpr auto kInvited = Row::State::Invited; - const auto reorder = [&] { - const auto count = delegate()->peerListFullRowsCount(); - if (!count) { - return false; - } - const auto row = delegate()->peerListRowAt(count - 1).get(); - return (static_cast<Row*>(row)->state() == kInvited); - }(); + reorderIfInvitedBeforeIndex = delegate()->peerListFullRowsCount(); delegate()->peerListAppendRow(std::move(row)); - if (reorder) { - delegate()->peerListPartitionRows([](const PeerListRow &row) { - return static_cast<const Row&>(row).state() != kInvited; - }); - } } delegate()->peerListRefreshRows(); countChange = 1; } + static constexpr auto kInvited = Row::State::Invited; + const auto reorder = [&] { + const auto count = reorderIfInvitedBeforeIndex; + if (count <= 0) { + return false; + } + const auto row = delegate()->peerListRowAt( + reorderIfInvitedBeforeIndex - 1).get(); + return (static_cast<Row*>(row)->state() == kInvited); + }(); + if (reorder) { + delegate()->peerListPartitionRows([](const PeerListRow &row) { + return static_cast<const Row&>(row).state() != kInvited; + }); + } if (countChange) { const auto fullCountMin = _fullCountMin.current() + countChange; if (_fullCountMax.current() < fullCountMin) { diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index a156537f5..f78e93e2f 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -167,7 +167,7 @@ MenuVolumeItem::MenuVolumeItem( std::round(_slider->value() * _maxVolume); if ((localVolume != newVolume) && (_cloudVolume == newVolume)) { - _changeVolumeRequests.fire(localVolume); + _changeVolumeRequests.fire(int(localVolume)); } } else { setCloudVolume(newVolume); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index a5fb99372..bda12f2be 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit a5fb99372184d5f3c00e5e851aaa16d8d28d2ce1 +Subproject commit bda12f2becd2497056d325a897b8285cc57662de From 0d0a79b0b57f00d6a2aeb1bde9daf63575447049 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 12:15:54 +0400 Subject: [PATCH 182/396] Revert "Fixed adding caption to grouped files." This reverts commit 5277080115211ce717f00952444cd362ecbebc89. When sending an album of files each one of them can have its own caption that will be shown below the file. A caption for the whole album (in case of media albums) is added to the first album item. In case of media albums this caption is displayed under the whole album mosaic. But in case of an album of files this caption will be displayed under the first file, between the first and the second file of the album. This is not what user can expect when he adds a caption for an album. So we will send it as a separate comment, like it was done before. --- .../ui/chat/attach/attach_prepare.cpp | 19 ------------------- .../ui/chat/attach/attach_prepare.h | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index a2e3f21ed..8ba397094 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -152,25 +152,6 @@ bool PreparedList::canAddCaption(bool sendingAlbum) const { } else if (!sendingAlbum) { return false; } - - // All music. - { - auto pred = [](const PreparedFile &file) { - return file.type == PreparedFile::Type::Music; - }; - if (ranges::all_of(files, std::move(pred))) { - return true; - } - } - // All files. - { - auto pred = [](const PreparedFile &file) { - return file.type == PreparedFile::Type::File; - }; - if (ranges::all_of(files, std::move(pred))) { - return true; - } - } const auto hasFiles = ranges::contains( files, PreparedFile::Type::File, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index 3f9ad0453..fe7d5251d 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -120,7 +120,7 @@ struct PreparedGroup { [[nodiscard]] bool sentWithCaption() const { return (list.files.size() == 1) - || (type != AlbumType::None); + || (type == AlbumType::PhotoVideo); } }; From fa8dd61b02d87e5da7b36f29bb2bcb1b8fe0f4d4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 12:24:54 +0400 Subject: [PATCH 183/396] All invite links are permanent right now. --- Telegram/SourceFiles/api/api_invite_links.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 038581b02..34a79cc44 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -585,7 +585,7 @@ auto InviteLinks::parse( .expireDate = data.vexpire_date().value_or_empty(), .usageLimit = data.vusage_limit().value_or_empty(), .usage = data.vusage().value_or_empty(), - .permanent = data.is_permanent(), + .permanent = true,//data.is_permanent(), // #TODO links .revoked = data.is_revoked(), }; }); From 52000566cf6d64e6ba75843f81447a352c82d365 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 12:41:52 +0400 Subject: [PATCH 184/396] Don't close calls log box after delete confirmation. --- Telegram/SourceFiles/boxes/confirm_box.cpp | 7 +++++-- Telegram/SourceFiles/calls/calls_box_controller.cpp | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 0e66771b8..d7f68a63c 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -873,10 +873,13 @@ void DeleteMessagesBox::deleteAndClear() { // deleteMessages can initiate closing of the current section, // which will cause this box to be destroyed. const auto session = _session; + const auto weak = Ui::MakeWeak(this); - _session->data().histories().deleteMessages(_ids, revoke); + session->data().histories().deleteMessages(_ids, revoke); - Ui::hideLayer(); + if (const auto strong = weak.data()) { + strong->closeBox(); + } session->data().sendHistoryChangeNotifications(); } diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 1f52f2438..68121430a 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -355,7 +355,9 @@ base::unique_qptr<Ui::PopupMenu> BoxController::rowContextMenu( auto result = base::make_unique_q<Ui::PopupMenu>(parent); result->addAction(tr::lng_context_delete_selected(tr::now), [=] { - Ui::show(Box<DeleteMessagesBox>(session, base::duplicate(ids))); + Ui::show( + Box<DeleteMessagesBox>(session, base::duplicate(ids)), + Ui::LayerOption::KeepOther); }); return result; } From ce5c19dfe9a929a80eed71b7cc31d77050c4a2c1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 14:28:25 +0400 Subject: [PATCH 185/396] Update API scheme for phone log clearing. --- Telegram/Resources/tl/api.tl | 4 ++- Telegram/SourceFiles/boxes/peer_list_box.h | 6 ++-- .../calls/calls_box_controller.cpp | 34 ++++++++++++++----- .../SourceFiles/window/window_main_menu.cpp | 13 ++++--- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index d188e3293..1d57ee601 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1212,6 +1212,8 @@ messages.historyImport#1662af0b id:long = messages.HistoryImport; messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true title:flags.2?string = messages.HistoryImportParsed; +messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector<int> = messages.AffectedFoundMessages; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1462,7 +1464,7 @@ messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.Disc messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory; messages.deleteChat#83247d11 chat_id:int = Bool; -messages.deletePhoneCallHistory#6cff1b45 flags:# revoke:flags.0?true = messages.AffectedHistory; +messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages; messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index b9ce4f203..6a2b87786 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -375,6 +375,9 @@ public: _delegate = delegate; prepare(); } + [[nodiscard]] not_null<PeerListDelegate*> delegate() const { + return _delegate; + } void setStyleOverrides( const style::PeerList *listSt, @@ -453,9 +456,6 @@ public: virtual ~PeerListController() = default; protected: - not_null<PeerListDelegate*> delegate() const { - return _delegate; - } PeerListSearchController *searchController() const { return _searchController.get(); } diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 68121430a..e32bf71c7 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_media_types.h" #include "data/data_user.h" #include "boxes/confirm_box.h" +#include "base/unixtime.h" +#include "api/api_updates.h" #include "app.h" #include "apiwrap.h" #include "styles/style_layers.h" // st::boxLabel. @@ -504,16 +506,30 @@ void ClearCallsBox( using Flag = MTPmessages_DeletePhoneCallHistory::Flag; api->request(MTPmessages_DeletePhoneCallHistory( MTP_flags(revoke ? Flag::f_revoke : Flag(0)) - )).done([=](const MTPmessages_AffectedHistory &result) { - const auto offset = api->applyAffectedHistory(nullptr, result); - if (offset > 0) { - self(revoke, self); - } else { - api->session().data().destroyAllCallItems(); - if (const auto strong = weak.data()) { - strong->closeBox(); + )).done([=](const MTPmessages_AffectedFoundMessages &result) { + result.match([&]( + const MTPDmessages_affectedFoundMessages &data) { + api->applyUpdates(MTP_updates( + MTP_vector<MTPUpdate>( + 1, + MTP_updateDeleteMessages( + data.vmessages(), + data.vpts(), + data.vpts_count())), + MTP_vector<MTPUser>(), + MTP_vector<MTPChat>(), + MTP_int(base::unixtime::now()), + MTP_int(0))); + const auto offset = data.voffset().v; + if (offset > 0) { + self(revoke, self); + } else { + api->session().data().destroyAllCallItems(); + if (const auto strong = weak.data()) { + strong->closeBox(); + } } - } + }); }).send(); }; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 655ba1706..5edad0b6f 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -99,7 +99,10 @@ constexpr auto kMinDiffIntensity = 0.25; void ShowCallsBox(not_null<Window::SessionController*> window) { auto controller = std::make_unique<Calls::BoxController>(window); - const auto initBox = [=](not_null<PeerListBox*> box) { + const auto initBox = [ + window, + controller = controller.get() + ](not_null<PeerListBox*> box) { box->addButton(tr::lng_close(), [=] { box->closeBox(); }); @@ -119,9 +122,11 @@ void ShowCallsBox(not_null<Window::SessionController*> window) { (*menu)->addAction( tr::lng_settings_section_call_settings(tr::now), showSettings); - (*menu)->addAction( - tr::lng_call_box_clear_all(tr::now), - clearAll); + if (controller->delegate()->peerListFullRowsCount() > 0) { + (*menu)->addAction( + tr::lng_call_box_clear_all(tr::now), + clearAll); + } (*menu)->popup(QCursor::pos()); return true; }); From 7da224d7259fc8a17a4c6b8288e29e512c81f0dd Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 15:58:30 +0400 Subject: [PATCH 186/396] Allow disabling calls on tdesktop device. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/calls/calls_instance.cpp | 2 ++ Telegram/SourceFiles/core/core_settings.cpp | 21 ++++++++++--------- Telegram/SourceFiles/core/core_settings.h | 9 +++++--- .../SourceFiles/settings/settings_calls.cpp | 14 +++++++++++++ .../settings/settings_notifications.cpp | 17 +++++++++++++++ 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ab31c4d07..cf6752028 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -328,6 +328,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_events_title" = "Events"; "lng_settings_events_joined" = "Contact joined Telegram"; "lng_settings_events_pinned" = "Pinned messages"; +"lng_settings_notifications_calls_title" = "Calls"; "lng_notification_preview" = "You have a new message"; "lng_notification_reply" = "Reply"; @@ -395,6 +396,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_call_stop_mic_test" = "Stop test"; "lng_settings_call_section_other" = "Other settings"; "lng_settings_call_open_system_prefs" = "Open system sound preferences"; +"lng_settings_call_accept_calls" = "Accept calls from this device"; "lng_settings_call_device_default" = "Same as the System"; "lng_settings_call_audio_ducking" = "Mute other sounds during calls"; diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 79ad8a91b..e6c78e82d 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -377,6 +377,8 @@ void Instance::handleCallUpdate( } else if (phoneCall.vdate().v + (config.callRingTimeoutMs / 1000) < base::unixtime::now()) { LOG(("Ignoring too old call.")); + } else if (Core::App().settings().disableCalls()) { + LOG(("Ignoring call because of 'accept calls' settings.")); } else { createCall(user, Call::Type::Incoming, phoneCall.is_video()); _currentCall->handleUpdate(call); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 8940fdf6e..aa6bded0d 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -19,8 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Core { Settings::Settings() -: _callAudioBackend(Webrtc::Backend::OpenAL) -, _sendSubmitWay(Ui::InputSubmitSettings::Enter) +: _sendSubmitWay(Ui::InputSubmitSettings::Enter) , _floatPlayerColumn(Window::Column::Second) , _floatPlayerCorner(RectPart::TopRight) , _dialogsWidthRatio(DefaultDialogsWidthRatio()) { @@ -115,7 +114,8 @@ QByteArray Settings::serialize() const { << qint32(_groupCallPushToTalk ? 1 : 0) << _groupCallPushToTalkShortcut << qint64(_groupCallPushToTalkDelay) - << qint32(_callAudioBackend); + << qint32(0) // Call audio backend + << qint32(_disableCalls ? 1 : 0); } return result; } @@ -186,7 +186,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 groupCallPushToTalk = _groupCallPushToTalk ? 1 : 0; QByteArray groupCallPushToTalkShortcut = _groupCallPushToTalkShortcut; qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay; - qint32 callAudioBackend = static_cast<qint32>(_callAudioBackend); + qint32 callAudioBackend = 0; + qint32 disableCalls = _disableCalls ? 1 : 0; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -285,6 +286,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> callAudioBackend; } + if (!stream.atEnd()) { + stream >> disableCalls; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -379,12 +383,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _groupCallPushToTalk = (groupCallPushToTalk == 1); _groupCallPushToTalkShortcut = groupCallPushToTalkShortcut; _groupCallPushToTalkDelay = groupCallPushToTalkDelay; - auto uncheckedBackend = static_cast<Webrtc::Backend>(callAudioBackend); - switch (uncheckedBackend) { - case Webrtc::Backend::OpenAL: - case Webrtc::Backend::ADM: - case Webrtc::Backend::ADM2: _callAudioBackend = uncheckedBackend; break; - } + _disableCalls = (disableCalls == 1); } bool Settings::chatWide() const { @@ -495,6 +494,8 @@ void Settings::resetOnLastLogout() { //_callInputVolume = 100; //_callAudioDuckingEnabled = true; + _disableCalls = false; + _groupCallPushToTalk = false; _groupCallPushToTalkShortcut = QByteArray(); _groupCallPushToTalkDelay = 20; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 33a092770..64b201ee8 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -222,8 +222,11 @@ public: _callAudioDuckingEnabled = value; } [[nodiscard]] Webrtc::Backend callAudioBackend() const; - void setCallAudioBackend(Webrtc::Backend backend) { - _callAudioBackend = backend; + void setDisableCalls(bool value) { + _disableCalls = value; + } + [[nodiscard]] bool disableCalls() const { + return _disableCalls; } [[nodiscard]] bool groupCallPushToTalk() const { return _groupCallPushToTalk; @@ -539,7 +542,7 @@ private: int _callOutputVolume = 100; int _callInputVolume = 100; bool _callAudioDuckingEnabled = true; - Webrtc::Backend _callAudioBackend = Webrtc::Backend(); + bool _disableCalls = false; bool _groupCallPushToTalk = false; QByteArray _groupCallPushToTalkShortcut; crl::time _groupCallPushToTalkDelay = 20; diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index abcb8b49b..94d981359 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -275,6 +275,19 @@ void Calls::setupContent() { //)->addClickHandler([] { // Ui::show(ChooseAudioBackendBox()); //}); + AddButton( + content, + tr::lng_settings_call_accept_calls(), + st::settingsButton + )->toggleOn(rpl::single( + !settings.disableCalls() + ))->toggledChanges( + ) | rpl::filter([&settings](bool value) { + return (settings.disableCalls() == value); + }) | rpl::start_with_next([=](bool value) { + Core::App().settings().setDisableCalls(!value); + Core::App().saveSettingsDelayed(); + }, content->lifetime()); AddButton( content, tr::lng_settings_call_open_system_prefs(), @@ -286,6 +299,7 @@ void Calls::setupContent() { Ui::show(Box<InformBox>(tr::lng_linux_no_audio_prefs(tr::now))); } }); + AddSkip(content); Ui::ResizeFitChild(this, content); diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index f6ce983b0..71dce89b9 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -676,6 +676,23 @@ void SetupNotificationsContent( Core::App().saveSettingsDelayed(); }, joined->lifetime()); + AddSkip(container, st::settingsCheckboxesSkip); + AddDivider(container); + AddSkip(container, st::settingsCheckboxesSkip); + AddSubsectionTitle( + container, + tr::lng_settings_notifications_calls_title()); + addCheckbox( + tr::lng_settings_call_accept_calls(tr::now), + !settings.disableCalls() + )->checkedChanges( + ) | rpl::filter([&settings](bool value) { + return (settings.disableCalls() == value); + }) | rpl::start_with_next([=](bool value) { + Core::App().settings().setDisableCalls(!value); + Core::App().saveSettingsDelayed(); + }, container->lifetime()); + const auto nativeText = [&] { if (!Platform::Notifications::Supported() || Platform::Notifications::Enforced()) { From 61f6851486a33fbfe1cce8dcb18807a5c5e8641c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 16:33:12 +0400 Subject: [PATCH 187/396] Update settings and report phrases. --- Telegram/Resources/langs/lang.strings | 2 +- .../settings/settings_notifications.cpp | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index cf6752028..eb1d8f846 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -979,7 +979,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_report_bot_title" = "Report bot"; "lng_report_message_title" = "Report message"; "lng_report_reason_spam" = "Spam"; -"lng_report_reason_fake" = "Fake"; +"lng_report_reason_fake" = "Fake Account"; "lng_report_reason_violence" = "Violence"; "lng_report_reason_child_abuse" = "Child Abuse"; "lng_report_reason_pornography" = "Pornography"; diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 71dce89b9..ac51e033c 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -629,18 +629,6 @@ void SetupNotificationsContent( : tr::lng_settings_alert_linux)(tr::now), settings.flashBounceNotify()); - AddSkip(container, st::settingsCheckboxesSkip); - AddDivider(container); - AddSkip(container, st::settingsCheckboxesSkip); - AddSubsectionTitle(container, tr::lng_settings_badge_title()); - - const auto muted = addCheckbox( - tr::lng_settings_include_muted(tr::now), - settings.includeMutedCounter()); - const auto count = addCheckbox( - tr::lng_settings_count_unread(tr::now), - settings.countUnreadMessages()); - AddSkip(container, st::settingsCheckboxesSkip); AddDivider(container); AddSkip(container, st::settingsCheckboxesSkip); @@ -693,6 +681,18 @@ void SetupNotificationsContent( Core::App().saveSettingsDelayed(); }, container->lifetime()); + AddSkip(container, st::settingsCheckboxesSkip); + AddDivider(container); + AddSkip(container, st::settingsCheckboxesSkip); + AddSubsectionTitle(container, tr::lng_settings_badge_title()); + + const auto muted = addCheckbox( + tr::lng_settings_include_muted(tr::now), + settings.includeMutedCounter()); + const auto count = addCheckbox( + tr::lng_settings_count_unread(tr::now), + settings.countUnreadMessages()); + const auto nativeText = [&] { if (!Platform::Notifications::Supported() || Platform::Notifications::Enforced()) { From dd401a063be8e09cd40947a56d432eba288c2efa Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 27 Jan 2021 20:25:45 +0400 Subject: [PATCH 188/396] Set preview as cancelled if no preview in editing message. --- .../SourceFiles/history/history_widget.cpp | 20 +++++++++++-------- .../history_view_compose_controls.cpp | 11 ++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index bdd9a0e6e..e46bc467e 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1327,7 +1327,7 @@ void HistoryWidget::fieldChanged() { } updateSendButtonType(); - if (showRecordButton()) { + if (!HasSendText(_field)) { _previewCancelled = false; } if (updateCmdStartShown()) { @@ -5682,19 +5682,23 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) { editData.text.size(), QFIXED_MAX }; + const auto previewPage = [&]() -> WebPageData* { + if (const auto media = item->media()) { + return media->webpage(); + } + return nullptr; + }(); + const auto previewCancelled = !previewPage; _history->setLocalEditDraft(std::make_unique<Data::Draft>( editData, item->id, cursor, - false)); + previewCancelled)); applyDraft(); - _previewData = nullptr; - if (const auto media = item->media()) { - if (const auto page = media->webpage()) { - _previewData = page; - updatePreview(); - } + _previewData = previewPage; + if (_previewData) { + updatePreview(); } updateBotKeyboard(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index da991c175..c3d962906 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1223,7 +1223,7 @@ void ComposeControls::fieldChanged() { _sendActionUpdates.fire({ Api::SendProgressType::Typing }); } updateSendButtonType(); - if (showRecordButton()) { + if (!HasSendText(_field)) { _previewCancelled = false; } if (updateBotCommandShown()) { @@ -1831,13 +1831,20 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) { editData.text.size(), QFIXED_MAX }; + const auto previewPage = [&]() -> WebPageData* { + if (const auto media = item->media()) { + return media->webpage(); + } + return nullptr; + }(); + const auto previewCancelled = !previewPage; _history->setDraft( draftKey(DraftType::Edit), std::make_unique<Data::Draft>( editData, item->id, cursor, - false)); + previewCancelled)); applyDraft(); if (_autocomplete) { From e9864bcf5b188f12b1f604010435b0590b8eb9a1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 26 Jan 2021 14:54:05 +0400 Subject: [PATCH 189/396] Update instructions for Windows x64 build. --- docs/building-msvc-x64.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/building-msvc-x64.md b/docs/building-msvc-x64.md index 586339642..5750c29fa 100644 --- a/docs/building-msvc-x64.md +++ b/docs/building-msvc-x64.md @@ -33,7 +33,7 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** cd ThirdParty git clone https://github.com/desktop-app/patches.git cd patches - git checkout 9fb66f2 + git checkout 41ead72 cd ../ git clone https://chromium.googlesource.com/external/gyp cd gyp @@ -63,7 +63,7 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** git clone https://github.com/desktop-app/patches.git cd patches - git checkout 9fb66f2 + git checkout 41ead72 cd .. git clone https://github.com/desktop-app/lzma.git @@ -108,9 +108,9 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** cmake --build . --config Release cd .. - git clone https://github.com/telegramdesktop/openal-soft.git + git clone https://github.com/kcat/openal-soft.git cd openal-soft - git checkout fix_mono + git checkout openal-soft-1.21.0 cd build cmake .. ^ -G "Visual Studio 16 2019" ^ @@ -161,17 +161,17 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** cd .. SET LibrariesPath=%cd% - git clone git://code.qt.io/qt/qt5.git qt_5_15_1 - cd qt_5_15_1 + git clone git://code.qt.io/qt/qt5.git qt_5_15_2 + cd qt_5_15_2 perl init-repository --module-subset=qtbase,qtimageformats - git checkout v5.15.1 + git checkout v5.15.2 git submodule update qtbase qtimageformats cd qtbase - for /r %i in (..\..\patches\qtbase_5_15_1\*) do git apply %i + for /r %i in (..\..\patches\qtbase_5_15_2\*) do git apply %i cd .. configure ^ - -prefix "%LibrariesPath%\Qt-5.15.1" ^ + -prefix "%LibrariesPath%\Qt-5.15.2" ^ -debug-and-release ^ -force-debug-info ^ -opensource ^ From 71ee98137178fc1fcdc1e923996262220ec38d42 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 26 Jan 2021 18:40:37 +0300 Subject: [PATCH 190/396] Fixed volume percents painting in volume menu item in group calls. --- .../SourceFiles/calls/calls_volume_item.cpp | 26 ++++++++++++++++--- .../SourceFiles/calls/calls_volume_item.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index f78e93e2f..60dc8219c 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -25,6 +25,10 @@ constexpr auto kSpeakerThreshold = { 50.0f / kMaxVolumePercent, 150.0f / kMaxVolumePercent }; +QString VolumeString(int volumePercent) { + return u"%1%"_q.arg(volumePercent); +} + } // namespace MenuVolumeItem::MenuVolumeItem( @@ -61,14 +65,13 @@ MenuVolumeItem::MenuVolumeItem( const auto geometry = QRect(QPoint(), size); _itemRect = geometry - _st.itemPadding; _speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size()); - _volumeRect = _speakerRect.translated( - _stCross.icon.width() + st::groupCallMenuVolumeSkip, - 0); _arcPosition = _speakerRect.center() + QPoint(0, st::groupCallMenuSpeakerArcsSkip); _slider->setGeometry(_itemRect - style::margins(0, contentHeight() / 2, 0, 0)); + + computeVolumeRect(); }, lifetime()); setCloudVolume(startVolume); @@ -92,7 +95,7 @@ MenuVolumeItem::MenuVolumeItem( muteProgress); p.setPen(mutePen); p.setFont(_st.itemStyle.font); - p.drawText(_volumeRect, u"%1%"_q.arg(volume), style::al_center); + p.drawText(_volumeRect, VolumeString(volume), style::al_left); _crossLineMute->paint( p, @@ -121,6 +124,7 @@ MenuVolumeItem::MenuVolumeItem( if (value > 0) { _changeVolumeLocallyRequests.fire(value * _maxVolume); } + computeVolumeRect(); update(_volumeRect); _arcs->setValue(value); }); @@ -182,6 +186,7 @@ void MenuVolumeItem::initArcsAnimation() { _arcsAnimation.init([=](crl::time now) { _arcs->update(now); update(_speakerRect); + computeVolumeRect(); }); _arcs->startUpdateRequests( @@ -197,6 +202,19 @@ void MenuVolumeItem::initArcsAnimation() { }, lifetime()); } +void MenuVolumeItem::computeVolumeRect() { + const auto was = _volumeRect; + _volumeRect = QRect( + _arcPosition.x() + st::groupCallMenuVolumeSkip + _arcs->width(), + _speakerRect.y(), + _st.itemStyle.font->width(VolumeString(kMaxVolumePercent)), + _speakerRect.height()); + if (was != _volumeRect) { + // Clear the previous text rendering. + update(); + } +} + QColor MenuVolumeItem::unmuteColor() const { return (isSelected() ? _st.itemFgOver diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index 5ba925835..1fec0d884 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -53,6 +53,8 @@ private: void setCloudVolume(int volume); void setSliderVolume(int volume); + void computeVolumeRect(); + QColor unmuteColor() const; QColor muteColor() const; From 5a88b4f0b9b76a0c30fc5ef6fe75ff2155daa904 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 27 Jan 2021 00:44:39 +0300 Subject: [PATCH 191/396] Added slider sticking to values for volume menu items in group calls. --- .../SourceFiles/calls/calls_volume_item.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 60dc8219c..07669d144 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -25,6 +25,17 @@ constexpr auto kSpeakerThreshold = { 50.0f / kMaxVolumePercent, 150.0f / kMaxVolumePercent }; +constexpr auto kVolumeStickedValues = + std::array<std::pair<float64, float64>, 7>{{ + { 25. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + { 50. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + { 75. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + { 100. / kMaxVolumePercent, 5. / kMaxVolumePercent }, + { 125. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + { 150. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + { 175. / kMaxVolumePercent, 2. / kMaxVolumePercent }, + }}; + QString VolumeString(int volumePercent) { return u"%1%"_q.arg(volumePercent); } @@ -179,6 +190,16 @@ MenuVolumeItem::MenuVolumeItem( _waitingForUpdateVolume = false; }, lifetime()); + _slider->setAdjustCallback([=](float64 value) { + for (const auto &snap : kVolumeStickedValues) { + if (value > (snap.first - snap.second) + && value < (snap.first + snap.second)) { + return snap.first; + } + } + return value; + }); + initArcsAnimation(); } From 3001ad4b89e600638da73e7cd94635658ac27585 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 27 Jan 2021 04:29:20 +0300 Subject: [PATCH 192/396] Improved design of volume info in list of participants in group calls. --- .../SourceFiles/calls/calls_group_members.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 8bdd36921..e53244ae1 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -587,7 +587,9 @@ int Row::statusIconWidth() const { return 0; } return _speaking - ? 2 * (_statusIcon->speaker.width() + st::groupCallMenuVolumeSkip) + ? (_statusIcon->speaker.width() / 2 + + _statusIcon->arcs->width() + + st::groupCallMenuVolumeSkip) : 0; } @@ -618,9 +620,6 @@ void Row::paintStatusIcon( st.statusPosition + QPoint(0, (font->height - statusIconHeight()) / 2), _statusIcon->speaker.size()); - const auto volumeRect = speakerRect.translated( - _statusIcon->speaker.width() + st::groupCallMenuVolumeSkip, - 0); const auto arcPosition = speakerRect.center() + QPoint(0, st::groupCallMenuSpeakerArcsSkip); @@ -630,7 +629,6 @@ void Row::paintStatusIcon( speakerRect.topLeft(), speakerRect.width(), color); - p.drawText(volumeRect, QString("%1%").arg(volume), style::al_center); p.save(); p.translate(arcPosition); @@ -649,7 +647,8 @@ void Row::paintStatusText( p.save(); const auto &font = st::normalFont; paintStatusIcon(p, st, font, selected); - p.translate(statusIconWidth(), 0); + const auto translatedWidth = statusIconWidth(); + p.translate(translatedWidth, 0); const auto guard = gsl::finally([&] { p.restore(); }); if (_state != State::Invited && _state != State::MutedByMe) { PeerListRow::paintStatusText( @@ -657,7 +656,7 @@ void Row::paintStatusText( st, x, y, - availableWidth, + availableWidth - translatedWidth, outerWidth, selected); return; @@ -723,7 +722,9 @@ void Row::paintAction( void Row::refreshStatus() { setCustomStatus( (_speaking - ? tr::lng_group_call_active(tr::now) + ? u"%1% %2"_q + .arg(std::round(_volume / 100.)) + .arg(tr::lng_group_call_active(tr::now)) : tr::lng_group_call_inactive(tr::now)), _speaking); } From 6d5bf53dd131bf49c5c5da13283395fdf3eeabdc Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 27 Jan 2021 20:04:30 +0300 Subject: [PATCH 193/396] Added animation of volume percents in menu of group calls. --- .../SourceFiles/calls/calls_volume_item.cpp | 41 +++++++++++-------- .../SourceFiles/calls/calls_volume_item.h | 2 - 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 07669d144..3e7f48efc 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -78,11 +78,16 @@ MenuVolumeItem::MenuVolumeItem( _speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size()); _arcPosition = _speakerRect.center() + QPoint(0, st::groupCallMenuSpeakerArcsSkip); + _volumeRect = QRect( + _arcPosition.x() + + st::groupCallMenuVolumeSkip + + _arcs->finishedWidth(), + _speakerRect.y(), + _st.itemStyle.font->width(VolumeString(kMaxVolumePercent)), + _speakerRect.height()); _slider->setGeometry(_itemRect - style::margins(0, contentHeight() / 2, 0, 0)); - - computeVolumeRect(); }, lifetime()); setCloudVolume(startVolume); @@ -135,7 +140,6 @@ MenuVolumeItem::MenuVolumeItem( if (value > 0) { _changeVolumeLocallyRequests.fire(value * _maxVolume); } - computeVolumeRect(); update(_volumeRect); _arcs->setValue(value); }); @@ -204,15 +208,31 @@ MenuVolumeItem::MenuVolumeItem( } void MenuVolumeItem::initArcsAnimation() { + const auto volumeLeftWas = lifetime().make_state<int>(0); + const auto lastTime = lifetime().make_state<int>(0); _arcsAnimation.init([=](crl::time now) { _arcs->update(now); update(_speakerRect); - computeVolumeRect(); + + const auto wasRect = _volumeRect; + _volumeRect.moveLeft(anim::interpolate( + *volumeLeftWas, + _arcPosition.x() + + st::groupCallMenuVolumeSkip + + _arcs->finishedWidth(), + std::clamp( + (now - (*lastTime)) + / float64(st::groupCallSpeakerArcsAnimation.duration), + 0., + 1.))); + update(_speakerRect.united(wasRect.united(_volumeRect))); }); _arcs->startUpdateRequests( ) | rpl::start_with_next([=] { if (!_arcsAnimation.animating()) { + *volumeLeftWas = _volumeRect.left(); + *lastTime = crl::now(); _arcsAnimation.start(); } }, lifetime()); @@ -223,19 +243,6 @@ void MenuVolumeItem::initArcsAnimation() { }, lifetime()); } -void MenuVolumeItem::computeVolumeRect() { - const auto was = _volumeRect; - _volumeRect = QRect( - _arcPosition.x() + st::groupCallMenuVolumeSkip + _arcs->width(), - _speakerRect.y(), - _st.itemStyle.font->width(VolumeString(kMaxVolumePercent)), - _speakerRect.height()); - if (was != _volumeRect) { - // Clear the previous text rendering. - update(); - } -} - QColor MenuVolumeItem::unmuteColor() const { return (isSelected() ? _st.itemFgOver diff --git a/Telegram/SourceFiles/calls/calls_volume_item.h b/Telegram/SourceFiles/calls/calls_volume_item.h index 1fec0d884..5ba925835 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.h +++ b/Telegram/SourceFiles/calls/calls_volume_item.h @@ -53,8 +53,6 @@ private: void setCloudVolume(int volume); void setSliderVolume(int volume); - void computeVolumeRect(); - QColor unmuteColor() const; QColor muteColor() const; From bad2d8afd989e82992830323bfa667a7f1c45f04 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 27 Jan 2021 21:19:04 +0400 Subject: [PATCH 194/396] Fix frame rotation in calls from iOS. --- Telegram/SourceFiles/calls/calls_panel.cpp | 27 ++++++++++++++++++---- Telegram/ThirdParty/tgcalls | 2 +- Telegram/lib_webrtc | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index add124453..d64c829d9 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "base/platform/base_platform_info.h" #include "window/main_window.h" +#include "media/view/media_view_pip.h" // Utilities for frame rotation. #include "app.h" #include "webrtc/webrtc_video_track.h" #include "styles/style_calls.h" @@ -98,12 +99,26 @@ Panel::Incoming::Incoming( void Panel::Incoming::paintEvent(QPaintEvent *e) { QPainter p(this); - const auto frame = _track->frame(Webrtc::FrameRequest()); - if (frame.isNull()) { + const auto [image, rotation] = _track->frameOriginalWithRotation(); + if (image.isNull()) { p.fillRect(e->rect(), Qt::black); } else { + using namespace Media::View; auto hq = PainterHighQualityEnabler(p); - p.drawImage(rect(), frame); + if (UsePainterRotation(rotation)) { + if (rotation) { + p.save(); + p.rotate(rotation); + } + p.drawImage(RotatedRect(rect(), rotation), image); + if (rotation) { + p.restore(); + } + } else if (rotation) { + p.drawImage(rect(), RotateFrameImage(image, rotation)); + } else { + p.drawImage(rect(), image); + } fillBottomShadow(p); fillTopShadow(p); } @@ -462,7 +477,11 @@ void Panel::reinitWithCall(Call *call) { _call->videoIncoming()->renderNextFrame( ) | rpl::start_with_next([=] { - setIncomingSize(_call->videoIncoming()->frame({}).size()); + const auto track = _call->videoIncoming(); + const auto [frame, rotation] = track->frameOriginalWithRotation(); + setIncomingSize((rotation == 90 || rotation == 270) + ? QSize(frame.height(), frame.width()) + : frame.size()); if (_incoming->isHidden()) { return; } diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index b85f5e9a2..bea2e2189 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit b85f5e9a25171c08a0e9486c27f07331e26ff1eb +Subproject commit bea2e218959244590fa57b62803bf2a7a5e15bc7 diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index c9691526c..de46d688f 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit c9691526c98fa76f08de295052ca8bd3560ec80b +Subproject commit de46d688f0fb6a04b0607dff892016d99e01e8b6 From 1baa833e8f081700ab6e77971fb73de8686fb09e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 27 Jan 2021 23:16:21 +0400 Subject: [PATCH 195/396] Improve selected state of volume changing item. --- Telegram/SourceFiles/calls/calls_volume_item.cpp | 1 + Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 3e7f48efc..7ba5b5c17 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -68,6 +68,7 @@ MenuVolumeItem::MenuVolumeItem( initResizeHook(parent->sizeValue()); enableMouseSelecting(); + enableMouseSelecting(_slider.get()); _slider->setAlwaysDisplayMarker(true); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index bda12f2be..315a0457c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit bda12f2becd2497056d325a897b8285cc57662de +Subproject commit 315a0457cf3340983e3217246e5cac27e1d2e091 From bf61f624c5b8e8be100a81e47150477ddf5fdf29 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 27 Jan 2021 23:48:16 +0300 Subject: [PATCH 196/396] Added speaking status animation in list of participants in group calls. --- .../SourceFiles/calls/calls_group_members.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index e53244ae1..540f8d08e 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -197,6 +197,7 @@ private: } const style::icon &speaker; const std::unique_ptr<Ui::Paint::ArcsAnimation> arcs; + int arcsWidth = 0; rpl::lifetime lifetime; }; @@ -397,12 +398,23 @@ void Row::setSpeaking(bool speaking) { } else if (!_statusIcon) { _statusIcon = std::make_unique<StatusIcon>( (float)_volume / Group::kMaxVolume); + _statusIcon->arcsWidth = _statusIcon->arcs->finishedWidth(); + + const auto wasArcsWidth = _statusIcon->lifetime.make_state<int>(0); _statusIcon->arcs->startUpdateRequests( ) | rpl::start_with_next([=] { - auto callback = [=] { + if (!_arcsAnimation.animating()) { + *wasArcsWidth = _statusIcon->arcsWidth; + } + auto callback = [=](float64 value) { if (_statusIcon) { _statusIcon->arcs->update(crl::now()); + + _statusIcon->arcsWidth = anim::interpolate( + *wasArcsWidth, + _statusIcon->arcs->finishedWidth(), + value); } _delegate->rowUpdateRow(this); }; @@ -588,7 +600,7 @@ int Row::statusIconWidth() const { } return _speaking ? (_statusIcon->speaker.width() / 2 - + _statusIcon->arcs->width() + + _statusIcon->arcsWidth + st::groupCallMenuVolumeSkip) : 0; } From 85b3672bc8d3bf9e78fe4c7fa185aeb5cee9575b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 28 Jan 2021 02:33:49 +0300 Subject: [PATCH 197/396] Reduced size of volume speaker in list of participants in group calls. --- .../icons/calls/volume/speaker_small.png | Bin 0 -> 310 bytes .../icons/calls/volume/speaker_small@2x.png | Bin 0 -> 521 bytes .../icons/calls/volume/speaker_small@3x.png | Bin 0 -> 734 bytes Telegram/SourceFiles/calls/calls.style | 10 +++++++++ .../SourceFiles/calls/calls_group_members.cpp | 20 ++++++++++-------- 5 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 Telegram/Resources/icons/calls/volume/speaker_small.png create mode 100644 Telegram/Resources/icons/calls/volume/speaker_small@2x.png create mode 100644 Telegram/Resources/icons/calls/volume/speaker_small@3x.png diff --git a/Telegram/Resources/icons/calls/volume/speaker_small.png b/Telegram/Resources/icons/calls/volume/speaker_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ae4490bf6556e9696750876313d363357a7be2 GIT binary patch literal 310 zcmV-60m=S}P)<h;3K|Lk000e1NJLTq000mG000sQ0ssI2-C4;!00009a7bBm001r{ z001r{0eGc9b^rhX=t)FDRCwCOlEDf@P#A@6kc9>16_h7n$JUEjC`;Lj#KKc}0ZX#5 zWhE=6lqW!nq|}{T(@ayt7@L`YdHTNFse2IQpC`+5p66MX34(A=D~hsh8>Gv!tn2!a zLs1mRao2SL06`F77-m^k-}gHx&-3sl@Hoib^FyI1imIwKO;c4>6vbz5+xEUNj-zRs z@A+lnu+n9r>pIW#E>sG`aGIthNunt7eV-tR6M>>A$i;CCopoK|moW_UNq9X;l7?Yu znucXr$8liW^J#D#*S76E&&#r;Y5LXpXqq+*L)UeZB!9U18?%>|F7w`Y4*&oF07*qo IM6N<$f@PY9nE(I) literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/volume/speaker_small@2x.png b/Telegram/Resources/icons/calls/volume/speaker_small@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..83ec9546b586cab72657467f8305aac6a3b56390 GIT binary patch literal 521 zcmV+k0`~ohP)<h;3K|Lk000e1NJLTq001BW001Ni0ssI2XNj-100009a7bBm001r{ z001r{0eGc9b^rhYyGcYrRCwBBU>F5M7f47*w6(PzK79Dtv13h5O#%V})HGdNTl?X| zhyRhl&6_ucg@vhOx|5UBhYuhAV}Qw%CsV=nxVX4qzkWeXeDL6bjg5`Hy*-3`@!~~F zO|P%7hr9R8nKQs>01C3QvO+}e+_^)ti5wgpJYc}j&p&_ue7NbGH*e<V=7y*t#{w=c zu8xk5yLa#Y#|EZOoyyG23|CCD1#E0=K<{HU`rp5QB_$<j>Pfbss;UZZ;{E&gufo95 zqep#xeX%--Gz<3c-w)B??d?sZk4duto`Z;tFp@2J^ym>u7LXhVqZW(+3oc!{1kpfb z`A%{&oi}eDL<7*K%F4=o7>JLL2bOwNus}mY<LlS2Sj(bYw{EGZs8DJF5V*OyJ%0Qc zs~xXjzqYcnq9_g^09f2RJ3Hs*<`#iLX=&-RXU`x8egFPFG&GbVA7BM?a&lL$T!EL{ zKr5+i0T2M|wLN?Gz=Lbrv}wSa2Ut77V~7fsJ}|`L$qWdtUAqQsut3<`wr!(YyQsFd z7Bl``U0tc!_6iOThUa-;=m!M_QNQ)b$jAuH?Sg`WK*nfWdhh}OOF~*@ek_l700000 LNkvXXu0mjffh_GE literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/volume/speaker_small@3x.png b/Telegram/Resources/icons/calls/volume/speaker_small@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6dc605d24ac7b952b0d6b88ad1971deadfe7c95b GIT binary patch literal 734 zcmV<40wMj0P)<h;3K|Lk000e1NJLTq001xm001@!0ssI24mpj@00009a7bBm001r{ z001r{0eGc9b^rhZkV!;ARCwC$n9Yl6VHm*2M;Re?vmj-mxRaaBM5b)UhGJzSe}RRC znvKmYe3Y1yWR#*P8_Gv<Vkae~*wNH1EM{e)scCBRm5=+BI=yqasCyju*7+^p_w~HL zocHT_uC8ckX#P(a494Bv-Q(k9rBW%E%i(ZXrBYqY=KA`&SS)UKh{a-THk)c2kx10( zbT)q=o6S;cqfjV@!{O#2Mx&7;8@*ot{{D{6y;`j>469bFXk#Lgpu)yxv(08RbmGZm zVzpYgy~$(}Z*RBTXSZ>?-Rt!lwHc4cI-L$55DJCxu5Pz`Mimx|1)qShN?u=I<#IVX z>|8<uf#3&%L?WrxY6P2Bs|8(zACfGjR;%G~`~3Xe93z*@aX6ga2&4(Yf$R7C7mLN_ z1cJdJjNcoFG$EJEb&ASzxpX)j2jY+;baQhvolcM1j7B4q$#ggrIYMwFq7$dnX|vgU z{~e7+12@;vP)|=!BnUN|O*~4yUjLhILC!za@AvU2KA-RMLW98okK*-uX(3vO7NUh{ zA@rpFCxqbbhev_L@A5+NcpSa$1A)Np?d{L+NTt$KLXmUZ!}Rd-I{F@Zd3n)lwNwZJ z9*^gg7bBX2o%aa~SxzdII>iZ+)Q5+MvkPr87z~|GhmZ>H<@x!U$z+~g2v5qCO64yE zSY*3}4#?zrJRTK7d*O1qyQQFBuO}9ZFC(<g-3aLb@b>nGmdq#>+TwIN31`H|#|JFF zZEq|V!`tC2Q&b<aSS(*(U#Qc3KF{a#rBVsaeKVO1brm#N=8!iY-ida*ozkkFOeRC0 z2?QFA#s#XdLZPrwDD2w6e&qA{7b^}!q8*J!;aI{jEE0)8(tSlkLqoI6Z`h?w@lmpx QWB>pF07*qoM6N<$f_4B~#Q*>R literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index b1616807b..446780c1b 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -809,6 +809,16 @@ groupCallSpeakerArcsAnimation: ArcsAnimation { startWidth: 0px; } +groupCallStatusSpeakerIcon: icon {{ "calls/volume/speaker_small", groupCallIconFg }}; +groupCallStatusSpeakerArcsSkip: 3px; +groupCallStatusSpeakerArcsAnimation: ArcsAnimation(groupCallSpeakerArcsAnimation) { + deltaAngle: 68; + space: 3px; + deltaHeight: 5px; + deltaWidth: 4px; + startHeight: 1px; +} + callTopBarMuteCrossLine: CrossLineAnimation { fg: callBarFg; icon: icon {{ "calls/call_record_active", callBarFg }}; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 540f8d08e..a1ffe1151 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -49,8 +49,9 @@ constexpr auto kWideScale = 5; constexpr auto kSpeakerThreshold = { Group::kDefaultVolume * 0.1f / Group::kMaxVolume, - Group::kDefaultVolume * 0.5f / Group::kMaxVolume, - Group::kDefaultVolume * 1.5f / Group::kMaxVolume }; + Group::kDefaultVolume * 0.9f / Group::kMaxVolume }; + +constexpr auto kArcsStrokeRatio = 0.8; auto RowBlobs() -> std::array<Ui::Paint::Blobs::BlobData, 2> { return { { @@ -188,9 +189,9 @@ private: struct StatusIcon { StatusIcon(float volume) - : speaker(st::groupCallMuteCrossLine.icon) + : speaker(st::groupCallStatusSpeakerIcon) , arcs(std::make_unique<Ui::Paint::ArcsAnimation>( - st::groupCallSpeakerArcsAnimation, + st::groupCallStatusSpeakerArcsAnimation, kSpeakerThreshold, volume, Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) { @@ -398,6 +399,7 @@ void Row::setSpeaking(bool speaking) { } else if (!_statusIcon) { _statusIcon = std::make_unique<StatusIcon>( (float)_volume / Group::kMaxVolume); + _statusIcon->arcs->setStrokeRatio(kArcsStrokeRatio); _statusIcon->arcsWidth = _statusIcon->arcs->finishedWidth(); const auto wasArcsWidth = _statusIcon->lifetime.make_state<int>(0); @@ -599,9 +601,7 @@ int Row::statusIconWidth() const { return 0; } return _speaking - ? (_statusIcon->speaker.width() / 2 - + _statusIcon->arcsWidth - + st::groupCallMenuVolumeSkip) + ? (_statusIcon->speaker.width() + _statusIcon->arcsWidth) : 0; } @@ -632,8 +632,10 @@ void Row::paintStatusIcon( st.statusPosition + QPoint(0, (font->height - statusIconHeight()) / 2), _statusIcon->speaker.size()); - const auto arcPosition = speakerRect.center() - + QPoint(0, st::groupCallMenuSpeakerArcsSkip); + const auto arcPosition = speakerRect.topLeft() + + QPoint( + speakerRect.width() - st::groupCallStatusSpeakerArcsSkip, + speakerRect.height() / 2); const auto volume = std::round(_volume / 100.); _statusIcon->speaker.paint( From 037506c0b7f9040e7f2ccd9b3dd769d69d974a84 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 28 Jan 2021 03:52:53 +0300 Subject: [PATCH 198/396] Fixed local applying of mute participant state in group calls. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 7 +++++-- Telegram/SourceFiles/calls/calls_group_members.cpp | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 81b766353..b0ec7c655 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -411,8 +411,11 @@ void GroupCall::applyParticipantLocally( const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (volume.has_value() ? Flag::f_volume : Flag(0)) | (participant->lastActive ? Flag::f_active_date : Flag(0)) - | (mute ? Flag::f_muted : Flag(0)) - | (participant->mutedByMe ? Flag::f_muted_by_you : Flag(0)); + | (!mute + ? Flag(0) + : user->canManageGroupCall() + ? Flag::f_muted + : Flag::f_muted_by_you); _peer->groupCall()->applyUpdateChecked( MTP_updateGroupCallParticipants( inputCall(), diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index a1ffe1151..af49f8ef4 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -394,7 +394,9 @@ void Row::setSpeaking(bool speaking) { _speaking ? 1. : 0., st::widgetFadeDuration); - if (!_speaking || (_state == State::MutedByMe)) { + if (!_speaking + || (_state == State::MutedByMe) + || (_state == State::Muted)) { _statusIcon = nullptr; } else if (!_statusIcon) { _statusIcon = std::make_unique<StatusIcon>( From ec8ddb047deff0eca656a4496f483a9b29f15b94 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 25 Jan 2021 11:46:29 +0400 Subject: [PATCH 199/396] Use style::CheckScale when setting gtk scale factor --- Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 964dff0f1..cef213366 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -221,7 +221,7 @@ void SetScaleFactor() { } LOG(("GTK scale factor: %1").arg(scaleFactor)); - cSetScreenScale(std::clamp(scaleFactor * 100, 100, 300)); + cSetScreenScale(style::CheckScale(scaleFactor * 100)); }); } From 9b59e74d66e949454c9333c74b6a0441de97ed00 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 25 Jan 2021 03:16:32 +0400 Subject: [PATCH 200/396] Make native notifications setting tri-state --- Telegram/SourceFiles/core/core_settings.cpp | 13 +++++++++---- Telegram/SourceFiles/core/core_settings.h | 9 ++++++--- .../platform/linux/notifications_manager_linux.cpp | 6 +++++- .../linux/notifications_manager_linux_dummy.cpp | 4 ++++ .../platform/mac/notifications_manager_mac.mm | 4 ++++ .../platform/platform_notifications_manager.h | 1 + .../platform/win/notifications_manager_win.cpp | 4 ++++ 7 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index aa6bded0d..ace2fcd29 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -60,7 +60,7 @@ QByteArray Settings::serialize() const { << qint32(_desktopNotify ? 1 : 0) << qint32(_flashBounceNotify ? 1 : 0) << static_cast<qint32>(_notifyView) - << qint32(_nativeNotifications ? 1 : 0) + << qint32(_nativeNotifications ? (*_nativeNotifications ? 1 : 2) : 0) << qint32(_notificationsCount) << static_cast<qint32>(_notificationsCorner) << qint32(_autoLock) @@ -141,7 +141,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 desktopNotify = _desktopNotify ? 1 : 0; qint32 flashBounceNotify = _flashBounceNotify ? 1 : 0; qint32 notifyView = static_cast<qint32>(_notifyView); - qint32 nativeNotifications = _nativeNotifications ? 1 : 0; + qint32 nativeNotifications = _nativeNotifications ? (*_nativeNotifications ? 1 : 2) : 0; qint32 notificationsCount = _notificationsCount; qint32 notificationsCorner = static_cast<qint32>(_notificationsCorner); qint32 autoLock = _autoLock; @@ -313,7 +313,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) { case dbinvShowName: case dbinvShowPreview: _notifyView = uncheckedNotifyView; break; } - _nativeNotifications = (nativeNotifications == 1); + switch (nativeNotifications) { + case 0: _nativeNotifications = std::nullopt; break; + case 1: _nativeNotifications = true; break; + case 2: _nativeNotifications = false; break; + default: break; + } _notificationsCount = (notificationsCount > 0) ? notificationsCount : 3; const auto uncheckedNotificationsCorner = static_cast<ScreenCorner>(notificationsCorner); switch (uncheckedNotificationsCorner) { @@ -479,7 +484,7 @@ void Settings::resetOnLastLogout() { _desktopNotify = true; _flashBounceNotify = true; _notifyView = dbinvShowPreview; - //_nativeNotifications = false; + //_nativeNotifications = std::nullopt; //_notificationsCount = 3; //_notificationsCorner = ScreenCorner::BottomRight; _includeMutedCounter = true; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 64b201ee8..67ffc1f25 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_themes_embedded.h" #include "window/window_controls_layout.h" #include "ui/chat/attach/attach_send_files_way.h" +#include "platform/platform_notifications_manager.h" enum class RectPart; @@ -135,10 +136,12 @@ public: _notifyView = value; } [[nodiscard]] bool nativeNotifications() const { - return _nativeNotifications; + return _nativeNotifications.value_or(Platform::Notifications::ByDefault()); } void setNativeNotifications(bool value) { - _nativeNotifications = value; + _nativeNotifications = (value == Platform::Notifications::ByDefault()) + ? std::nullopt + : std::make_optional(value); } [[nodiscard]] int notificationsCount() const { return _notificationsCount; @@ -529,7 +532,7 @@ private: bool _desktopNotify = true; bool _flashBounceNotify = true; DBINotifyView _notifyView = dbinvShowPreview; - bool _nativeNotifications = false; + std::optional<bool> _nativeNotifications; int _notificationsCount = 3; ScreenCorner _notificationsCorner = ScreenCorner::BottomRight; bool _includeMutedCounter = true; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 9effa1964..7df885b33 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -698,7 +698,11 @@ bool Supported() { bool Enforced() { // Wayland doesn't support positioning // and custom notifications don't work here - return IsQualifiedDaemon() || IsWayland(); + return IsWayland(); +} + +bool ByDefault() { + return IsQualifiedDaemon(); } void Create(Window::Notifications::System *system) { diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp index 3e11353d9..c51b92fed 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -35,6 +35,10 @@ bool Enforced() { return IsWayland(); } +bool ByDefault() { + return false; +} + void Create(Window::Notifications::System *system) { if (Enforced()) { using DummyManager = Window::Notifications::DummyManager; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 522f585bb..310b2829b 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -167,6 +167,10 @@ bool Enforced() { return Supported(); } +bool ByDefault() { + return Supported(); +} + void Create(Window::Notifications::System *system) { if (Supported()) { system->setManager(std::make_unique<Manager>(system)); diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 2a4a7849f..eb3efbddb 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -18,6 +18,7 @@ namespace Notifications { [[nodiscard]] bool Supported(); [[nodiscard]] bool Enforced(); +[[nodiscard]] bool ByDefault(); void Create(Window::Notifications::System *system); } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index c4ac40370..0fbdb331d 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -273,6 +273,10 @@ bool Enforced() { return false; } +bool ByDefault() { + return false; +} + void Create(Window::Notifications::System *system) { #ifndef __MINGW32__ if (Core::App().settings().nativeNotifications() && Supported()) { From 834ee4eae7fa8511049bfac575d26abf999a6d72 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 26 Jan 2021 20:38:15 +0400 Subject: [PATCH 201/396] Fix crash with fcitx and custom titlebar --- Telegram/SourceFiles/window/window_title_qt.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 2ee27b67f..53eae8e56 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -248,7 +248,8 @@ void TitleWidgetQt::mouseReleaseEvent(QMouseEvent *e) { bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { if (e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress) { - if (window()->isAncestorOf(static_cast<QWidget*>(obj))) { + if (obj->isWidgetType() + && window()->isAncestorOf(static_cast<QWidget*>(obj))) { const auto mouseEvent = static_cast<QMouseEvent*>(e); const auto currentPoint = mouseEvent->windowPos().toPoint(); const auto edges = edgesFromPos(currentPoint); @@ -277,12 +278,12 @@ bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { } } } else if (e->type() == QEvent::Leave) { - if (window() == static_cast<QWidget*>(obj)) { + if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { restoreCursor(); } } else if (e->type() == QEvent::Move || e->type() == QEvent::Resize) { - if (window() == static_cast<QWidget*>(obj)) { + if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { updateWindowExtents(); } } From 7c031a4fb69352d487b76d00cccbf8f0a8fca062 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Thu, 28 Jan 2021 05:36:20 +0400 Subject: [PATCH 202/396] Perform additional checks for gtk scaling factor --- .../platform/linux/linux_gtk_integration.cpp | 21 +++++++++++++++++-- .../platform/linux/linux_gtk_integration_p.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index cef213366..59fcd12d9 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -328,6 +328,7 @@ void GtkIntegration::load() { if (GtkLoaded) { LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_default", gdk_display_get_default); + LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_monitor", gdk_display_get_monitor); LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); LOAD_GTK_SYMBOL(lib_gtk, "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); @@ -440,13 +441,29 @@ std::optional<QString> GtkIntegration::getStringSetting( std::optional<int> GtkIntegration::scaleFactor() const { if (!loaded() || (gdk_display_get_default == nullptr) + || (gdk_display_get_monitor == nullptr) || (gdk_display_get_primary_monitor == nullptr) || (gdk_monitor_get_scale_factor == nullptr)) { return std::nullopt; } - return gdk_monitor_get_scale_factor( - gdk_display_get_primary_monitor(gdk_display_get_default())); + const auto display = gdk_display_get_default(); + if (!display) { + return std::nullopt; + } + + const auto monitor = [&] { + if (const auto primary = gdk_display_get_primary_monitor(display)) { + return primary; + } + return gdk_display_get_monitor(display, 0); + }(); + + if (!monitor) { + return std::nullopt; + } + + return gdk_monitor_get_scale_factor(monitor); } bool GtkIntegration::fileDialogSupported() const { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h index c12faa846..b0b60e6bc 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h @@ -149,6 +149,7 @@ inline bool g_type_cit_helper(Object *instance, GType iface_type) { inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr; inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr; inline GdkDisplay* (*gdk_display_get_default)(void) = nullptr; +inline GdkMonitor* (*gdk_display_get_monitor)(GdkDisplay *display, int monitor_num) = nullptr; inline GdkMonitor* (*gdk_display_get_primary_monitor)(GdkDisplay *display) = nullptr; inline int (*gdk_monitor_get_scale_factor)(GdkMonitor *monitor) = nullptr; inline GdkPixbuf* (*gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error) = nullptr; From 0f17a3b300d1bd0b3b3c7a958c88bf24f0ce65b6 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 12:17:06 +0400 Subject: [PATCH 203/396] Update lib_ui submodule. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 315a0457c..f03209c1f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 315a0457cf3340983e3217246e5cac27e1d2e091 +Subproject commit f03209c1f9c7f9d47eff5740e927d3d41df2e09e From d2662ba1fd2138b238f34adeb45ef63fd55c9cd9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 14:07:35 +0400 Subject: [PATCH 204/396] Version 2.5.7. - Delete not only messages, but also groups you created and call history for all sides, without a trace. - Adjust volume for individual participants of a voice chat. - Report fake groups or channels impersonating famous people or organizations by opening their Profile > ... > Report. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/_other/packer.cpp | 4 +- Telegram/SourceFiles/core/version.h | 6 +- Telegram/build/build.bat | 111 +++++++++++++------ Telegram/build/setup.iss | 9 +- Telegram/build/version | 10 +- changelog.txt | 6 + 9 files changed, 112 insertions(+), 52 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index eabfd46fd..8e62f6314 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="2.5.6.0" /> + Version="2.5.7.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 1ec6edc21..45b954c36 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,6,0 - PRODUCTVERSION 2,5,6,0 + FILEVERSION 2,5,7,0 + PRODUCTVERSION 2,5,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.6.0" + VALUE "FileVersion", "2.5.7.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.6.0" + VALUE "ProductVersion", "2.5.7.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index f92532279..79d75ec67 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,6,0 - PRODUCTVERSION 2,5,6,0 + FILEVERSION 2,5,7,0 + PRODUCTVERSION 2,5,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.6.0" + VALUE "FileVersion", "2.5.7.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.6.0" + VALUE "ProductVersion", "2.5.7.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp index 69e73fcbd..99fae21f1 100644 --- a/Telegram/SourceFiles/_other/packer.cpp +++ b/Telegram/SourceFiles/_other/packer.cpp @@ -149,6 +149,7 @@ int main(int argc, char *argv[]) QString remove; int version = 0; bool targetosx = false; + bool targetwin64 = false; QFileInfoList files; for (int i = 0; i < argc; ++i) { if (string("-path") == argv[i] && i + 1 < argc) { @@ -158,6 +159,7 @@ int main(int argc, char *argv[]) if (remove.isEmpty()) remove = info.canonicalPath() + "/"; } else if (string("-target") == argv[i] && i + 1 < argc) { targetosx = (string("osx") == argv[i + 1]); + targetwin64 = (string("win64") == argv[i + 1]); } else if (string("-version") == argv[i] && i + 1 < argc) { version = QString(argv[i + 1]).toInt(); } else if (string("-beta") == argv[i]) { @@ -464,7 +466,7 @@ int main(int argc, char *argv[]) cout << "Signature verified!\n"; RSA_free(pbKey); #ifdef Q_OS_WIN - QString outName(QString("tupdate%1").arg(AlphaVersion ? AlphaVersion : version)); + QString outName((targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version)); #elif defined Q_OS_MAC QString outName((targetosx ? QString("tosxupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version)); #elif defined Q_OS_UNIX diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index a94af807e..8c4ddc021 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005006; -constexpr auto AppVersionStr = "2.5.6"; -constexpr auto AppBetaVersion = true; +constexpr auto AppVersion = 2005007; +constexpr auto AppVersionStr = "2.5.7"; +constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/build.bat b/Telegram/build/build.bat index 1c0959e5e..3f6afd7e1 100644 --- a/Telegram/build/build.bat +++ b/Telegram/build/build.bat @@ -16,10 +16,44 @@ FOR /F "tokens=1* delims= " %%i in (%FullScriptPath%target) do set "BuildTarget= if "%BuildTarget%" equ "uwp" ( set "BuildUWP=1" +) else if "%BuildTarget%" equ "uwp64" ( + set "BuildUWP=1" ) else ( set "BuildUWP=0" ) +if "%BuildTarget%" equ "win64" ( + set "Build64=1" +) else if "%BuildTarget%" equ "uwp64" ( + set "Build64=1" +) else ( + set "Build64=0" +) + +if %Build64% neq 0 ( + if "%Platform%" neq "x64" ( + echo Bad environment. Make sure to run from 'x64 Native Tools Command Prompt for VS 2019'. + exit /b + ) else if "%VSCMD_ARG_HOST_ARCH%" neq "x64" ( + echo Bad environment. Make sure to run from 'x64 Native Tools Command Prompt for VS 2019'. + exit /b + ) else if "%VSCMD_ARG_TGT_ARCH%" neq "x64" ( + echo Bad environment. Make sure to run from 'x64 Native Tools Command Prompt for VS 2019'. + exit /b + ) +) else ( + if "%Platform%" neq "x86" ( + echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. + exit /b + ) else if "%VSCMD_ARG_HOST_ARCH%" neq "x86" ( + echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. + exit /b + ) else if "%VSCMD_ARG_TGT_ARCH%" neq "x86" ( + echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. + exit /b + ) +) + FOR /F "tokens=1,2* delims= " %%i in (%FullScriptPath%version) do set "%%i=%%j" set "VersionForPacker=%AppVersion%" @@ -40,18 +74,32 @@ if %AlphaVersion% neq 0 ( echo. if %BuildUWP% neq 0 ( - echo Building version %AppVersionStrFull% for UWP.. + if %Build64% neq 0 ( + echo Building version %AppVersionStrFull% for UWP 64 bit.. + ) else ( + echo Building version %AppVersionStrFull% for UWP.. + ) ) else ( - echo Building version %AppVersionStrFull% for Windows.. + if %Build64% neq 0 ( + echo Building version %AppVersionStrFull% for Windows 64 bit.. + ) else ( + echo Building version %AppVersionStrFull% for Windows.. + ) ) echo. set "HomePath=%FullScriptPath%.." set "ResourcesPath=%HomePath%\Resources" set "SolutionPath=%HomePath%\..\out" -set "UpdateFile=tupdate%AppVersion%" -set "SetupFile=tsetup.%AppVersionStrFull%.exe" -set "PortableFile=tportable.%AppVersionStrFull%.zip" +if %Build64% neq 0 ( + set "UpdateFile=tx64upd%AppVersion%" + set "SetupFile=tsetup-x64.%AppVersionStrFull%.exe" + set "PortableFile=tportable-x64.%AppVersionStrFull%.zip" +) else ( + set "UpdateFile=tupdate%AppVersion%" + set "SetupFile=tsetup.%AppVersionStrFull%.exe" + set "PortableFile=tportable.%AppVersionStrFull%.zip" +) set "ReleasePath=%SolutionPath%\Release" set "DeployPath=%ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStrFull%" set "SignPath=%HomePath%\..\..\DesktopPrivate\Sign.bat" @@ -116,13 +164,13 @@ echo. echo Version %AppVersionStrFull% build successfull. Preparing.. echo. -if not exist "%SolutionPath%\..\..\Libraries\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ( +if not exist "%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ( echo Utility dump_syms not found! exit /b 1 ) echo Dumping debug symbols.. -call "%SolutionPath%\..\..\Libraries\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" "%ReleasePath%\%BinaryName%.pdb" > "%ReleasePath%\%BinaryName%.sym" +call "%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" "%ReleasePath%\%BinaryName%.pdb" > "%ReleasePath%\%BinaryName%.sym" echo Done! set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5" @@ -145,7 +193,7 @@ if %BuildUWP% equ 0 ( ) if %AlphaVersion% equ 0 ( - iscc /dMyAppVersion=%AppVersionStrSmall% /dMyAppVersionZero=%AppVersionStr% /dMyAppVersionFull=%AppVersionStrFull% "/dReleasePath=%ReleasePath%" "%FullScriptPath%setup.iss" + iscc /dMyAppVersion=%AppVersionStrSmall% /dMyAppVersionZero=%AppVersionStr% /dMyAppVersionFull=%AppVersionStrFull% "/dReleasePath=%ReleasePath%" "/dMyBuildTarget=%BuildTarget%" "%FullScriptPath%setup.iss" if %errorlevel% neq 0 goto error if not exist "%SetupFile%" goto error :sign3 @@ -156,7 +204,7 @@ if %BuildUWP% equ 0 ( ) ) - call Packer.exe -version %VersionForPacker% -path %BinaryName%.exe -path Updater.exe %AlphaBetaParam% + call Packer.exe -version %VersionForPacker% -path %BinaryName%.exe -path Updater.exe -target %BuildTarget% %AlphaBetaParam% if %errorlevel% neq 0 goto error if %AlphaVersion% neq 0 ( @@ -193,30 +241,24 @@ echo Done! if %BuildUWP% neq 0 ( cd "%HomePath%" - mkdir "%ReleasePath%\AppX_x86" - xcopy "Resources\uwp\AppX\*" "%ReleasePath%\AppX_x86\" /E - set "ResourcePath=%ReleasePath%\AppX_x86\AppxManifest.xml" - call :repl "Argument= (ProcessorArchitecture=)"ARCHITECTURE"/ $1"x86"" "Filename=!ResourcePath!" || goto error - - makepri new /pr Resources\uwp\AppX\ /cf Resources\uwp\priconfig.xml /mn %ReleasePath%\AppX_x86\AppxManifest.xml /of %ReleasePath%\AppX_x86\resources.pri + mkdir "%ReleasePath%\AppX" + xcopy "Resources\uwp\AppX\*" "%ReleasePath%\AppX\" /E + set "ResourcePath=%ReleasePath%\AppX\AppxManifest.xml" + if %Build64% equ 0 ( + call :repl "Argument= (ProcessorArchitecture=)"ARCHITECTURE"/ $1"x86"" "Filename=!ResourcePath!" || goto error + ) else ( + call :repl "Argument= (ProcessorArchitecture=)"ARCHITECTURE"/ $1"x64"" "Filename=!ResourcePath!" || goto error + ) + makepri new /pr Resources\uwp\AppX\ /cf Resources\uwp\priconfig.xml /mn %ReleasePath%\AppX\AppxManifest.xml /of %ReleasePath%\AppX\resources.pri if %errorlevel% neq 0 goto error - xcopy "%ReleasePath%\%BinaryName%.exe" "%ReleasePath%\AppX_x86\" + xcopy "%ReleasePath%\%BinaryName%.exe" "%ReleasePath%\AppX\" - MakeAppx.exe pack /d "%ReleasePath%\AppX_x86" /l /p ..\out\Release\%BinaryName%.x86.appx - if %errorlevel% neq 0 goto error - - mkdir "%ReleasePath%\AppX_x64" - xcopy "Resources\uwp\AppX\*" "%ReleasePath%\AppX_x64\" /E - set "ResourcePath=%ReleasePath%\AppX_x64\AppxManifest.xml" - call :repl "Argument= (ProcessorArchitecture=)"ARCHITECTURE"/ $1"x64"" "Filename=!ResourcePath!" || goto error - - makepri new /pr Resources\uwp\AppX\ /cf Resources\uwp\priconfig.xml /mn %ReleasePath%\AppX_x64\AppxManifest.xml /of %ReleasePath%\AppX_x64\resources.pri - if %errorlevel% neq 0 goto error - - xcopy "%ReleasePath%\%BinaryName%.exe" "%ReleasePath%\AppX_x64\" - - MakeAppx.exe pack /d "%ReleasePath%\AppX_x64" /l /p ..\out\Release\%BinaryName%.x64.appx + if %Build64% equ 0 ( + MakeAppx.exe pack /d "%ReleasePath%\AppX" /l /p ..\out\Release\%BinaryName%.x86.appx + ) else ( + MakeAppx.exe pack /d "%ReleasePath%\AppX" /l /p ..\out\Release\%BinaryName%.x64.appx + ) if %errorlevel% neq 0 goto error if not exist "%ReleasePath%\deploy" mkdir "%ReleasePath%\deploy" @@ -229,8 +271,7 @@ if %BuildUWP% neq 0 ( move "%ReleasePath%\%BinaryName%.exe" "%DeployPath%\" if "%AlphaBetaParam%" equ "" ( - move "%ReleasePath%\AppX_x86" "%DeployPath%\AppX_x86" - move "%ReleasePath%\AppX_x64" "%DeployPath%\AppX_x64" + move "%ReleasePath%\AppX" "%DeployPath%\AppX" ) else ( echo Leaving result in out\Release\AppX_arch for now.. ) @@ -262,7 +303,11 @@ if %BuildUWP% neq 0 ( if %errorlevel% neq 0 goto error ) -set "FinalDeployPath=%FinalReleasePath%\%AppVersionStrMajor%\%AppVersionStrFull%\tsetup" +if %Build64% equ 0 ( + set "FinalDeployPath=%FinalReleasePath%\%AppVersionStrMajor%\%AppVersionStrFull%\tsetup" +) else ( + set "FinalDeployPath=%FinalReleasePath%\%AppVersionStrMajor%\%AppVersionStrFull%\tx64" +) if %BuildUWP% equ 0 ( echo. diff --git a/Telegram/build/setup.iss b/Telegram/build/setup.iss index 819ad5618..952af4ecc 100644 --- a/Telegram/build/setup.iss +++ b/Telegram/build/setup.iss @@ -20,7 +20,6 @@ DefaultDirName={userappdata}\{#MyAppName} DefaultGroupName={#MyAppName} AllowNoIcons=yes OutputDir={#ReleasePath} -OutputBaseFilename=tsetup.{#MyAppVersionFull} SetupIconFile={#SourcePath}..\Resources\art\icon256.ico UninstallDisplayIcon={app}\Telegram.exe Compression=lzma @@ -32,6 +31,14 @@ CloseApplications=force DisableDirPage=no DisableProgramGroupPage=no +#if MyBuildTarget == "win64" +ArchitecturesAllowed=x64 +ArchitecturesInstallIn64BitMode=x64 +OutputBaseFilename=tsetup-x64.{#MyAppVersionFull} +#else +OutputBaseFilename=tsetup.{#MyAppVersionFull} +#endif + [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "it"; MessagesFile: "compiler:Languages\Italian.isl" diff --git a/Telegram/build/version b/Telegram/build/version index 667a3ae8e..1da20bb68 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005006 +AppVersion 2005007 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.6 -AppVersionStr 2.5.6 -BetaChannel 1 +AppVersionStrSmall 2.5.7 +AppVersionStr 2.5.7 +BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 2.5.6.beta +AppVersionOriginal 2.5.7 diff --git a/changelog.txt b/changelog.txt index 002eaba88..708152a0c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +2.5.7 (28.01.21) + +- Delete not only messages, but also groups you created and call history for all sides, without a trace. +- Adjust volume for individual participants of a voice chat. +- Report fake groups or channels impersonating famous people or organizations by opening their Profile > ... > Report. + 2.5.6 beta (22.01.21) - Press Up arrow to edit your last sent comment. From 0aea9bc46fefc61c3fe052599f7a2adc15685e04 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 16:58:42 +0400 Subject: [PATCH 205/396] Version 2.5.7: Fix build on macOS and Linux. --- Telegram/SourceFiles/calls/calls_group_members.cpp | 6 +++--- Telegram/SourceFiles/calls/calls_volume_item.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index af49f8ef4..2d1535df5 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -47,7 +47,7 @@ constexpr auto kUserpicMinScale = 0.8; constexpr auto kMaxLevel = 1.; constexpr auto kWideScale = 5; -constexpr auto kSpeakerThreshold = { +const auto kSpeakerThreshold = std::vector<float>{ Group::kDefaultVolume * 0.1f / Group::kMaxVolume, Group::kDefaultVolume * 0.9f / Group::kMaxVolume }; @@ -1420,7 +1420,7 @@ void MembersController::addMuteActionsToContextMenu( const auto isMuted = (muteState == Row::State::Muted) || (muteState == Row::State::MutedByMe); - auto mutesFromVolume = rpl::never<bool>(); + auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased(); if (!isMuted) { const auto call = _call.get(); @@ -1429,7 +1429,7 @@ void MembersController::addMuteActionsToContextMenu( ) | rpl::filter([=](const Group::ParticipantState &data) { return data.user == user; }) - : rpl::never<Group::ParticipantState>(); + : rpl::never<Group::ParticipantState>() | rpl::type_erased(); auto volumeItem = base::make_unique_q<MenuVolumeItem>( menu, diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index 7ba5b5c17..a147b3d00 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -20,7 +20,7 @@ namespace { constexpr auto kMaxVolumePercent = 200; -constexpr auto kSpeakerThreshold = { +const auto kSpeakerThreshold = std::vector<float>{ 10.0f / kMaxVolumePercent, 50.0f / kMaxVolumePercent, 150.0f / kMaxVolumePercent }; From 9a0023cc99c0fc09045b9b5b453864387e2b9134 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 17:00:42 +0400 Subject: [PATCH 206/396] Version 2.5.7: Support win64 autoupdate. --- Telegram/SourceFiles/core/update_checker.cpp | 1 + Telegram/build/deploy.sh | 37 ++++++++++++++++++-- Telegram/build/release.py | 18 ++++++++-- Telegram/lib_base | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp index c4e94f8d9..850e1535e 100644 --- a/Telegram/SourceFiles/core/update_checker.cpp +++ b/Telegram/SourceFiles/core/update_checker.cpp @@ -224,6 +224,7 @@ QString FindUpdateFile() { if (QRegularExpression( "^(" "tupdate|" + "tx64upd|" "tmacupd|" "tosxupd|" "tlinuxupd|" diff --git a/Telegram/build/deploy.sh b/Telegram/build/deploy.sh index ea4bde9bb..e1b9c4498 100755 --- a/Telegram/build/deploy.sh +++ b/Telegram/build/deploy.sh @@ -49,6 +49,7 @@ HomePath="$FullScriptPath/.." DeployMac="0" DeployOsx="0" DeployWin="0" +DeployWin64="0" DeployLinux="0" DeployLinux32="0" if [ "$DeployTarget" == "mac" ]; then @@ -59,7 +60,10 @@ elif [ "$DeployTarget" == "osx" ]; then echo "Deploying version $AppVersionStrFull for OS X 10.10 and 10.11.." elif [ "$DeployTarget" == "win" ]; then DeployWin="1" - echo "Deploying version $AppVersionStrFull for Windows.." + echo "Deploying version $AppVersionStrFull for Windows 32 bit.." +elif [ "$DeployTarget" == "win64" ]; then + DeployWin64="1" + echo "Deploying version $AppVersionStrFull for Windows 64 bit.." elif [ "$DeployTarget" == "linux" ]; then DeployLinux="1" echo "Deploying version $AppVersionStrFull for Linux 64 bit.." @@ -69,8 +73,9 @@ elif [ "$DeployTarget" == "linux32" ]; then else DeployMac="1" DeployWin="1" + DeployWin64="1" DeployLinux="1" - echo "Deploying three versions of $AppVersionStrFull: for Windows, macOS and Linux 64 bit.." + echo "Deploying four versions of $AppVersionStrFull: for Windows 32 bit, Windows 64 bit, macOS and Linux 64 bit.." fi if [ "$BuildTarget" == "mac" ]; then BackupPath="$HOME/Projects/backup/tdesktop" @@ -92,6 +97,11 @@ WinUpdateFile="tupdate$AppVersion" WinSetupFile="tsetup.$AppVersionStrFull.exe" WinPortableFile="tportable.$AppVersionStrFull.zip" WinRemoteFolder="tsetup" +Win64DeployPath="$BackupPath/$AppVersionStrMajor/$AppVersionStrFull/tx64" +Win64UpdateFile="tx64upd$AppVersion" +Win64SetupFile="tsetup-x64.$AppVersionStrFull.exe" +Win64PortableFile="tportable-x64.$AppVersionStrFull.zip" +Win64RemoteFolder="tx64" LinuxDeployPath="$BackupPath/$AppVersionStrMajor/$AppVersionStrFull/tlinux" LinuxUpdateFile="tlinuxupd$AppVersion" LinuxSetupFile="tsetup.$AppVersionStrFull.tar.xz" @@ -105,6 +115,8 @@ DeployPath="$BackupPath/$AppVersionStrMajor/$AppVersionStrFull" if [ "$AlphaVersion" != "0" ]; then if [ "$DeployTarget" == "win" ]; then AlphaFilePath="$WinDeployPath/$AlphaKeyFile" + elif [ "$DeployTarget" == "win64" ]; then + AlphaFilePath="$Win64DeployPath/$AlphaKeyFile" elif [ "$DeployTarget" == "osx" ]; then AlphaFilePath="$OsxDeployPath/$AlphaKeyFile" elif [ "$DeployTarget" == "linux" ]; then @@ -128,6 +140,8 @@ if [ "$AlphaVersion" != "0" ]; then OsxSetupFile="talpha${AlphaVersion}_${AlphaSignature}.zip" WinUpdateFile="${WinUpdateFile}_${AlphaSignature}" WinPortableFile="talpha${AlphaVersion}_${AlphaSignature}.zip" + Win64UpdateFile="${Win64UpdateFile}_${AlphaSignature}" + Win64PortableFile="talpha${AlphaVersion}_${AlphaSignature}.zip" LinuxUpdateFile="${LinuxUpdateFile}_${AlphaSignature}" LinuxSetupFile="talpha${AlphaVersion}_${AlphaSignature}.tar.xz" Linux32UpdateFile="${Linux32UpdateFile}_${AlphaSignature}" @@ -163,6 +177,19 @@ if [ "$DeployWin" == "1" ]; then Error "$WinPortableFile not found!" fi fi +if [ "$DeployWin64" == "1" ]; then + if [ ! -f "$Win64DeployPath/$Win64UpdateFile" ]; then + Error "$Win64UpdateFile not found!" + fi + if [ "$AlphaVersion" == "0" ]; then + if [ ! -f "$Win64DeployPath/$Win64SetupFile" ]; then + Error "$Win64SetupFile not found!" + fi + fi + if [ ! -f "$Win64DeployPath/$Win64PortableFile" ]; then + Error "$Win64PortableFile not found!" + fi +fi if [ "$DeployLinux" == "1" ]; then if [ ! -f "$LinuxDeployPath/$LinuxUpdateFile" ]; then Error "$LinuxDeployPath/$LinuxUpdateFile not found!" @@ -195,6 +222,12 @@ if [ "$DeployWin" == "1" ]; then Files+=("tsetup/$WinSetupFile") fi fi +if [ "$DeployWin64" == "1" ]; then + Files+=("tx64/$Win64UpdateFile" "tx64/$Win64PortableFile") + if [ "$AlphaVersion" == "0" ]; then + Files+=("tx64/$Win64SetupFile") + fi +fi if [ "$DeployLinux" == "1" ]; then Files+=("tlinux/$LinuxUpdateFile" "tlinux/$LinuxSetupFile") fi diff --git a/Telegram/build/release.py b/Telegram/build/release.py index 5253a25ab..bb32ba7fe 100644 --- a/Telegram/build/release.py +++ b/Telegram/build/release.py @@ -168,14 +168,28 @@ files.append({ 'remote': 'tsetup.' + version_full + '.exe', 'backup_folder': 'tsetup', 'mime': 'application/octet-stream', - 'label': 'Windows: Installer', + 'label': 'Windows 32 bit: Installer', }) files.append({ 'local': 'tportable.' + version_full + '.zip', 'remote': 'tportable.' + version_full + '.zip', 'backup_folder': 'tsetup', 'mime': 'application/zip', - 'label': 'Windows: Portable', + 'label': 'Windows 32 bit: Portable', +}) +files.append({ + 'local': 'tsetup-x64.' + version_full + '.exe', + 'remote': 'tsetup-x64.' + version_full + '.exe', + 'backup_folder': 'tx64', + 'mime': 'application/octet-stream', + 'label': 'Windows 64 bit: Installer', +}) +files.append({ + 'local': 'tportable-x64.' + version_full + '.zip', + 'remote': 'tportable-x64.' + version_full + '.zip', + 'backup_folder': 'tx64', + 'mime': 'application/zip', + 'label': 'Windows 64 bit: Portable', }) files.append({ 'local': 'tsetup.' + version_full + '.dmg', diff --git a/Telegram/lib_base b/Telegram/lib_base index d7342985e..f770025cc 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit d7342985eb85b0ab791dee0514cffb0b05b3010d +Subproject commit f770025cc18e2fba295733923a527c8cb0f1d513 From fbf4f912c6a98bd8ffcb5d4c597b4bc836e7464e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 19:30:47 +0400 Subject: [PATCH 207/396] Fix OpenAL device closing in calls and voice chats. Also add 64 bit Windows info in crash platform string. --- Telegram/SourceFiles/boxes/about_box.cpp | 7 ++++- Telegram/SourceFiles/core/crash_reports.cpp | 30 ++++++++++++++++++--- Telegram/lib_base | 2 +- Telegram/lib_webrtc | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/boxes/about_box.cpp b/Telegram/SourceFiles/boxes/about_box.cpp index cb4895c89..31798dc59 100644 --- a/Telegram/SourceFiles/boxes/about_box.cpp +++ b/Telegram/SourceFiles/boxes/about_box.cpp @@ -88,8 +88,10 @@ void AboutBox::resizeEvent(QResizeEvent *e) { void AboutBox::showVersionHistory() { if (cRealAlphaVersion()) { auto url = qsl("https://tdesktop.com/"); - if (Platform::IsWindows()) { + if (Platform::IsWindows32Bit()) { url += qsl("win/%1.zip"); + } else if (Platform::IsWindows64Bit()) { + url += qsl("win64/%1.zip"); } else if (Platform::IsOSXBuild()) { url += qsl("osx/%1.zip"); } else if (Platform::IsMac()) { @@ -143,5 +145,8 @@ QString currentVersionText() { } else if (AppBetaVersion) { result += " beta"; } + if (Platform::IsWindows64Bit()) { + result += " x64"; + } return result; } diff --git a/Telegram/SourceFiles/core/crash_reports.cpp b/Telegram/SourceFiles/core/crash_reports.cpp index d0e403968..eef6bc32f 100644 --- a/Telegram/SourceFiles/core/crash_reports.cpp +++ b/Telegram/SourceFiles/core/crash_reports.cpp @@ -318,12 +318,36 @@ bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS } // namespace +if (Platform::IsWindowsStoreBuild()) { + return Platform::IsWindows64Bit() + ? "WinStore64Bit" + : "WinStore32Bit"; +} else if (Platform::IsWindows32Bit()) { + return "Windows32Bit"; +} else if (Platform::IsWindows64Bit()) { + return "Windows64Bit"; +} else if (Platform::IsMacStoreBuild()) { + return "MacAppStore"; +} else if (Platform::IsOSXBuild()) { + return "OSX"; +} else if (Platform::IsMac()) { + return "MacOS"; +} else if (Platform::IsLinux32Bit()) { + return "Linux32Bit"; +} else if (Platform::IsLinux64Bit()) { + return "Linux64bit"; +} +Unexpected("Platform in CrashReports::PlatformString."); QString PlatformString() { if (Platform::IsWindowsStoreBuild()) { - return qsl("WinStore"); - } else if (Platform::IsWindows()) { - return qsl("Windows"); + return Platform::IsWindows64Bit() + ? qsl("WinStore64Bit") + : qsl("WinStore32Bit"); + } else if (Platform::IsWindows32Bit()) { + return qsl("Windows32Bit"); + } else if (Platform::IsWindows64Bit()) { + return qsl("Windows64Bit"); } else if (Platform::IsMacStoreBuild()) { return qsl("MacAppStore"); } else if (Platform::IsOSXBuild()) { diff --git a/Telegram/lib_base b/Telegram/lib_base index f770025cc..2bf29ab1a 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit f770025cc18e2fba295733923a527c8cb0f1d513 +Subproject commit 2bf29ab1a5458003c8ed250886e08c61cce5ff72 diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index de46d688f..af7269454 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit de46d688f0fb6a04b0607dff892016d99e01e8b6 +Subproject commit af7269454c371b7e1045429d60cd728f7580ee4a From c13d0e96ef3f48cd84f9d631cb2f694b1ec3f121 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 19:58:35 +0400 Subject: [PATCH 208/396] Fix build. --- Telegram/SourceFiles/core/crash_reports.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/Telegram/SourceFiles/core/crash_reports.cpp b/Telegram/SourceFiles/core/crash_reports.cpp index eef6bc32f..232794298 100644 --- a/Telegram/SourceFiles/core/crash_reports.cpp +++ b/Telegram/SourceFiles/core/crash_reports.cpp @@ -318,26 +318,6 @@ bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS } // namespace -if (Platform::IsWindowsStoreBuild()) { - return Platform::IsWindows64Bit() - ? "WinStore64Bit" - : "WinStore32Bit"; -} else if (Platform::IsWindows32Bit()) { - return "Windows32Bit"; -} else if (Platform::IsWindows64Bit()) { - return "Windows64Bit"; -} else if (Platform::IsMacStoreBuild()) { - return "MacAppStore"; -} else if (Platform::IsOSXBuild()) { - return "OSX"; -} else if (Platform::IsMac()) { - return "MacOS"; -} else if (Platform::IsLinux32Bit()) { - return "Linux32Bit"; -} else if (Platform::IsLinux64Bit()) { - return "Linux64bit"; -} -Unexpected("Platform in CrashReports::PlatformString."); QString PlatformString() { if (Platform::IsWindowsStoreBuild()) { From 160cd975ce7cf3711b734ce6d9a691c81973a75b Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Thu, 28 Jan 2021 04:22:56 +0400 Subject: [PATCH 209/396] Another attempt to implement shadows on Wayland Works only with patched Qt --- .../platform/linux/specific_linux.cpp | 39 +++++++++++++++---- Telegram/build/docker/centos_env/Dockerfile | 2 + 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index f70ef0b94..6f2acd99e 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -65,6 +65,8 @@ using namespace Platform; using Platform::internal::WaylandIntegration; using Platform::internal::GtkIntegration; +Q_DECLARE_METATYPE(QMargins); + namespace Platform { namespace { @@ -796,24 +798,45 @@ bool ShowWindowMenu(QWindow *window) { } bool SetWindowExtents(QWindow *window, const QMargins &extents) { - if (!IsWayland()) { + if (IsWayland()) { +#ifdef DESKTOP_APP_QT_PATCHED + window->setProperty("WaylandCustomMargins", QVariant::fromValue<QMargins>(extents)); + return true; +#else // DESKTOP_APP_QT_PATCHED + return false; +#endif // !DESKTOP_APP_QT_PATCHED + } else { return SetXCBFrameExtents(window, extents); } - - return false; } bool UnsetWindowExtents(QWindow *window) { - if (!IsWayland()) { + if (IsWayland()) { +#ifdef DESKTOP_APP_QT_PATCHED + window->setProperty("WaylandCustomMargins", QVariant()); + return true; +#else // DESKTOP_APP_QT_PATCHED + return false; +#endif // !DESKTOP_APP_QT_PATCHED + } else { return UnsetXCBFrameExtents(window); } - - return false; } bool WindowsNeedShadow() { - return !IsWayland() - && base::Platform::XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16()); +#ifdef DESKTOP_APP_QT_PATCHED + if (IsWayland()) { + return true; + } +#endif // DESKTOP_APP_QT_PATCHED + + namespace XCB = base::Platform::XCB; + if (!IsWayland() + && XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16())) { + return true; + } + + return false; } Window::ControlsLayout WindowControlsLayout() { diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 346badae7..45e2f0313 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -479,6 +479,8 @@ RUN git submodule update qtbase qtwayland qtimageformats qtsvg WORKDIR qtbase RUN find ../../patches/qtbase_${QT} -type f -print0 | sort -z | xargs -r0 git apply +WORKDIR ../qtwayland +RUN find ../../patches/qtwayland_${QT} -type f -print0 | sort -z | xargs -r0 git apply WORKDIR .. # I couldn't make it work with direct ./configure call :( From 7947af665c7396fb3f267018c5333d16bcbce53f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 22:19:58 +0400 Subject: [PATCH 210/396] Skip notifications from imported messages. --- Telegram/SourceFiles/window/notifications_manager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 6b6b68034..324aad458 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -106,6 +106,12 @@ System::SkipState System::skipNotification( } const auto scheduled = item->out() && item->isFromScheduled(); + if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { + if (forwarded->imported) { + return { SkipState::Skip }; + } + } + history->owner().requestNotifySettings(history->peer); if (notifyBy) { history->owner().requestNotifySettings(notifyBy); From 30468746adb27e62061f9d8056949eb12e8b5410 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 29 Jan 2021 02:39:13 +0400 Subject: [PATCH 211/396] Build Qt in snap to get newer version than in Ubuntu --- snap/snapcraft.yaml | 218 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 32 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index dcc04b57f..bd3e96c40 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -68,17 +68,16 @@ parts: source: . source-type: git parse-info: [usr/share/metainfo/telegram-desktop_telegram-desktop.appdata.xml] + build-environment: + - LD_LIBRARY_PATH: $SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$LD_LIBRARY_PATH build-packages: - python - - qtbase5-private-dev - libasound2-dev - libglib2.0-dev - libgtk-3-dev - - libkf5wayland-dev - liblzma-dev - libopus-dev - libpulse-dev - - libqt5waylandclient5-dev - libssl-dev - libxcb1-dev - libxcb-keysyms1-dev @@ -86,16 +85,12 @@ parts: - libxcb-screensaver0-dev - zlib1g-dev stage-packages: - - qt5-image-formats-plugins - - qtwayland5 - libasound2 - libglib2.0-0 - libgtk-3-0 - - libkf5waylandclient5 - liblzma5 - libopus0 - libpulse0 - - libqt5waylandclient5 - libssl1.1 - libxcb1 - libxcb-keysyms1 @@ -108,7 +103,7 @@ parts: - -DTDESKTOP_API_ID=611335 - -DTDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c - -DDESKTOP_APP_USE_PACKAGED_LAZY=ON - - -DDESKTOP_APP_QTWAYLANDCLIENT_PRIVATE_HEADERS=$SNAPCRAFT_STAGE/usr/include/$SNAPCRAFT_ARCH_TRIPLET/qt5/QtWaylandClient/5.12.8 + - -DDESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES=OFF - -DTDESKTOP_LAUNCHER_BASENAME=telegram-desktop_telegram-desktop override-pull: | snapcraftctl pull @@ -134,11 +129,19 @@ parts: after: - desktop-qt5 - ffmpeg + - kwayland - mozjpeg - openal - - qtwayland - webrtc + patches: + source: https://github.com/desktop-app/patches.git + source-depth: 1 + plugin: dump + organize: + "*": patches/ + prime: [-./*] + desktop-qt5: source: https://github.com/ubuntu/snapcraft-desktop-helpers.git source-subdir: qt @@ -146,7 +149,6 @@ parts: make-parameters: ["FLAVOR=qt5"] build-packages: - build-essential - - qtbase5-dev - dpkg-dev stage-packages: - libxkbcommon0 @@ -156,26 +158,25 @@ parts: - adwaita-icon-theme - gnome-themes-standard - shared-mime-info - - libqt5gui5 - libgdk-pixbuf2.0-0 - - libqt5svg5 # for loading icon themes which are svg - - try: [appmenu-qt5] # not available on core18 - locales-all - xdg-user-dirs - - fcitx-frontend-qt5 stage: - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2 after: - mozjpeg + - qt5 - qt5-xdgdesktopportal-platform: - plugin: nil - stage-packages: - - qt5-xdgdesktopportal-platformtheme - stage: - - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2 - after: - - mozjpeg + extra-cmake-modules: + source: https://github.com/KDE/extra-cmake-modules.git + source-depth: 1 + source-tag: v5.77.0 + plugin: cmake + cmake-parameters: + - -DCMAKE_BUILD_TYPE=Release + - -DCMAKE_INSTALL_PREFIX=/usr + - -DBUILD_TESTING=OFF + prime: [-./*] ffmpeg: plugin: nil @@ -196,6 +197,32 @@ parts: after: - mozjpeg + kwayland: + source: https://github.com/KDE/kwayland.git + source-depth: 1 + source-tag: v5.77.0 + plugin: cmake + build-packages: + - libwayland-dev + - wayland-protocols + stage-packages: + - libwayland-client0 + - libwayland-server0 + cmake-parameters: + - -DCMAKE_BUILD_TYPE=Release + - -DCMAKE_INSTALL_PREFIX=/usr + - -DBUILD_TESTING=OFF + prime: + - -./usr/include + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libexec + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pkgconfig + - -./usr/mkspecs + - -./usr/share + after: + - extra-cmake-modules + - desktop-qt5 + - plasma-wayland-protocols + mozjpeg: source: https://github.com/mozilla/mozjpeg.git source-depth: 1 @@ -236,19 +263,146 @@ parts: - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/cmake - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pkgconfig - qtwayland: - source: https://github.com/qt/qtwayland.git + plasma-wayland-protocols: + source: https://github.com/KDE/plasma-wayland-protocols.git source-depth: 1 - source-tag: v5.12.8 - plugin: dump - override-build: | - qmake - make -j$(nproc) - make INSTALL_ROOT="$SNAPCRAFT_PART_INSTALL" install - stage: [-./usr/lib] + source-tag: v1.1.1 + plugin: cmake + cmake-parameters: + - -DCMAKE_BUILD_TYPE=Release + - -DCMAKE_INSTALL_PREFIX=/usr prime: [-./*] after: - - desktop-qt5 + - extra-cmake-modules + + qt5: + plugin: nil + build-packages: + - libdbus-1-dev + - libegl-dev + - libfontconfig1-dev + - libfreetype-dev + - libgl-dev + - libglib2.0-dev + - libharfbuzz-dev + - libicu-dev + - libpcre2-dev + - libpng-dev + - libssl-dev + - libwayland-dev + - libx11-dev + - libx11-xcb-dev + - libxcb1-dev + - libxcb-glx0-dev + - libxcb-icccm4-dev + - libxcb-image0-dev + - libxcb-keysyms1-dev + - libxcb-randr0-dev + - libxcb-render0-dev + - libxcb-render-util0-dev + - libxcb-shape0-dev + - libxcb-shm0-dev + - libxcb-sync-dev + - libxcb-util-dev + - libxcb-xfixes0-dev + - libxcb-xinerama0-dev + - libxcb-xinput-dev + - libxcb-xkb-dev + - libxcursor-dev + - libxkbcommon-dev + - libxkbcommon-x11-dev + - zlib1g-dev + stage-packages: + - libdbus-1-3 + - libegl1 + - libfontconfig1 + - libfreetype6 + - libgl1 + - libglib2.0-0 + - libharfbuzz0b + - libicu66 + - libpcre2-16-0 + - libpng16-16 + - libssl1.1 + - libwayland-client0 + - libwayland-egl1 + - libx11-6 + - libx11-xcb1 + - libxcb1 + - libxcb-glx0 + - libxcb-icccm4 + - libxcb-image0 + - libxcb-keysyms1 + - libxcb-randr0 + - libxcb-render0 + - libxcb-render-util0 + - libxcb-shape0 + - libxcb-shm0 + - libxcb-sync1 + - libxcb-util1 + - libxcb-xfixes0 + - libxcb-xinerama0 + - libxcb-xinput0 + - libxcb-xkb1 + - libxcursor1 + - libxkbcommon0 + - libxkbcommon-x11-0 + - zlib1g + override-pull: | + QT=5_15_2 + + git clone -b v5.15.2 --depth=1 git://code.qt.io/qt/qt5.git . + perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg + git submodule update qtbase qtwayland qtimageformats qtsvg + + cd qtbase + find $SNAPCRAFT_STAGE/patches/qtbase_${QT} -type f -print0 | sort -z | xargs -r0 git apply + cd ../qtwayland + find $SNAPCRAFT_STAGE/patches/qtwayland_${QT} -type f -print0 | sort -z | xargs -r0 git apply + cd .. + override-build: | + ./configure \ + -prefix /usr \ + -bindir /usr/lib/qt5/bin \ + -libdir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET \ + -docdir /usr/share/qt5/doc \ + -headerdir /usr/include/$SNAPCRAFT_ARCH_TRIPLET/qt5 \ + -datadir /usr/share/qt5 \ + -archdatadir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5 \ + -plugindir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins \ + -importdir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/imports \ + -translationdir /usr/share/qt5/translations \ + -hostdatadir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5 \ + -sysconfdir /etc/xdg \ + -examplesdir /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/examples \ + -release \ + -opensource \ + -confirm-license \ + -no-gtk \ + -no-feature-xcb-sm \ + -no-feature-wayland-server \ + -openssl-linked \ + -nomake examples \ + -nomake tests \ + -I $SNAPCRAFT_STAGE/usr/include \ + -L $SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET + + make -j$(nproc) + make INSTALL_ROOT="$SNAPCRAFT_PART_INSTALL" install + stage: + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2 + prime: + - -./usr/include + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/cmake + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pkgconfig + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/bin + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/mkspecs + - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/examples + - -./usr/lib/qt5 + - -./usr/share + after: + - mozjpeg + - patches webrtc: source: https://github.com/desktop-app/tg_owt.git From 9be65f88124d9f816990b7c5a2c6222a64197288 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 29 Jan 2021 13:28:20 +0400 Subject: [PATCH 212/396] Fix missing libraries warning in snap --- snap/snapcraft.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index bd3e96c40..8806abb4a 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -325,6 +325,7 @@ parts: - libpng16-16 - libssl1.1 - libwayland-client0 + - libwayland-cursor0 - libwayland-egl1 - libx11-6 - libx11-xcb1 @@ -380,6 +381,8 @@ parts: -confirm-license \ -no-gtk \ -no-feature-xcb-sm \ + -no-feature-xcomposite-egl \ + -no-feature-xcomposite-glx \ -no-feature-wayland-server \ -openssl-linked \ -nomake examples \ From 1ec6b4313d1822c55e8e926379c56838dea5d1ff Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 22:23:51 +0400 Subject: [PATCH 213/396] Remove redundant Cancel button in ScheduleBox. --- Telegram/SourceFiles/history/view/history_view_schedule_box.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index e22bffb1c..5ddfe67c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -90,7 +90,6 @@ void ScheduleBox( [=] { return SendMenu::Type::SilentOnly; }, [=] { save(true, descriptor.collect()); }, nullptr); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); if (type == SendMenu::Type::ScheduledToUser) { const auto sendUntilOnline = box->addTopButton(st::infoTopBarMenu); From fcdc39c5f90e387a01e39341ed279d941f12364d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 28 Jan 2021 22:25:50 +0400 Subject: [PATCH 214/396] Add external_xxhash dependency to Telegram project. --- Telegram/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a71a9ac1f..663453b10 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -67,6 +67,7 @@ PRIVATE desktop-app::external_auto_updates desktop-app::external_openssl desktop-app::external_openal + desktop-app::external_xxhash ) if (LINUX) From fc4ed2ff91ab0c5fe8dcc1e428eac85585f0d415 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 14:38:07 +0400 Subject: [PATCH 215/396] Fix repainting of non-first file album item. --- Telegram/SourceFiles/data/data_groups.cpp | 4 ++-- Telegram/SourceFiles/data/data_groups.h | 4 ++-- Telegram/SourceFiles/data/data_session.cpp | 20 +++++++++++++++++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/data/data_groups.cpp b/Telegram/SourceFiles/data/data_groups.cpp index 6e0998d4d..965566994 100644 --- a/Telegram/SourceFiles/data/data_groups.cpp +++ b/Telegram/SourceFiles/data/data_groups.cpp @@ -21,7 +21,7 @@ constexpr auto kMaxItemsInGroup = 10; Groups::Groups(not_null<Session*> data) : _data(data) { } -bool Groups::isGrouped(not_null<HistoryItem*> item) const { +bool Groups::isGrouped(not_null<const HistoryItem*> item) const { if (!item->groupId()) { return false; } @@ -124,7 +124,7 @@ HistoryItemsList::const_iterator Groups::findPositionForItem( return last; } -const Group *Groups::find(not_null<HistoryItem*> item) const { +const Group *Groups::find(not_null<const HistoryItem*> item) const { const auto groupId = item->groupId(); if (!groupId) { return nullptr; diff --git a/Telegram/SourceFiles/data/data_groups.h b/Telegram/SourceFiles/data/data_groups.h index be628cbaf..6364251ba 100644 --- a/Telegram/SourceFiles/data/data_groups.h +++ b/Telegram/SourceFiles/data/data_groups.h @@ -22,14 +22,14 @@ class Groups { public: Groups(not_null<Session*> data); - bool isGrouped(not_null<HistoryItem*> item) const; + [[nodiscard]] bool isGrouped(not_null<const HistoryItem*> item) const; void registerMessage(not_null<HistoryItem*> item); void unregisterMessage(not_null<const HistoryItem*> item); void refreshMessage( not_null<HistoryItem*> item, bool justRefreshViews = false); - const Group *find(not_null<HistoryItem*> item) const; + [[nodiscard]] const Group *find(not_null<const HistoryItem*> item) const; not_null<HistoryItem*> findItemToEdit(not_null<HistoryItem*> item) const; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 3c3be4212..bcc4392b1 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1417,9 +1417,23 @@ rpl::producer<Session::IdChange> Session::itemIdChanged() const { void Session::requestItemRepaint(not_null<const HistoryItem*> item) { _itemRepaintRequest.fire_copy(item); - enumerateItemViews(item, [&](not_null<const ViewElement*> view) { - requestViewRepaint(view); - }); + auto repaintGroupLeader = false; + auto repaintView = [&](not_null<const ViewElement*> view) { + if (view->isHiddenByGroup()) { + repaintGroupLeader = true; + } else { + requestViewRepaint(view); + } + }; + enumerateItemViews(item, repaintView); + if (repaintGroupLeader) { + if (const auto group = groups().find(item)) { + const auto leader = group->items.front(); + if (leader != item) { + enumerateItemViews(leader, repaintView); + } + } + } } rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const { From 8f0e23bb257ac5327aff265a823d880e3c4c6fb6 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 15:27:17 +0400 Subject: [PATCH 216/396] Improve editing messages with link previews. Now preview state can be one of (allowed, cancelled, empty-in-edit). In case of editing a message without preview we set the state to empty-in-edit and it changes to allowed if the links in the message are changed somehow. That way we don't need to cancel the preview when editing a message with a cancelled preview and at the same time adding a link to a message that had no preview in the first place will add a preview. --- Telegram/SourceFiles/apiwrap.cpp | 2 +- .../chat_helpers/message_field.cpp | 5 ++ .../SourceFiles/chat_helpers/message_field.h | 4 +- Telegram/SourceFiles/data/data_drafts.cpp | 15 ++-- Telegram/SourceFiles/data/data_drafts.h | 14 +++- Telegram/SourceFiles/history/history.cpp | 10 +-- .../SourceFiles/history/history_widget.cpp | 73 +++++++++++++------ Telegram/SourceFiles/history/history_widget.h | 6 +- .../history_view_compose_controls.cpp | 45 ++++++++---- .../controls/history_view_compose_controls.h | 4 +- .../view/history_view_replies_section.cpp | 6 +- .../view/history_view_scheduled_section.cpp | 6 +- Telegram/SourceFiles/mainwidget.cpp | 13 +++- .../SourceFiles/storage/storage_account.cpp | 34 ++++++--- .../SourceFiles/storage/storage_account.h | 2 +- .../SourceFiles/support/support_helper.cpp | 2 +- 16 files changed, 159 insertions(+), 82 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index c459ffcf5..6222b28bb 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2449,7 +2449,7 @@ void ApiWrap::saveDraftsToCloud() { auto flags = MTPmessages_SaveDraft::Flags(0); auto &textWithTags = cloudDraft->textWithTags; - if (cloudDraft->previewCancelled) { + if (cloudDraft->previewState != Data::PreviewState::Allowed) { flags |= MTPmessages_SaveDraft::Flag::f_no_webpage; } if (cloudDraft->msgId) { diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 2513d507e..12037e961 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -501,6 +501,11 @@ MessageLinksParser::MessageLinksParser(not_null<Ui::InputField*> field) _field->installEventFilter(this); } +void MessageLinksParser::parseNow() { + _timer.cancel(); + parse(); +} + bool MessageLinksParser::eventFilter(QObject *object, QEvent *event) { if (object == _field) { if (event->type() == QEvent::KeyPress) { diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index fd7e53aad..72c737dd4 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -71,7 +71,9 @@ class MessageLinksParser : private QObject { public: MessageLinksParser(not_null<Ui::InputField*> field); - const rpl::variable<QStringList> &list() const; + void parseNow(); + + [[nodiscard]] const rpl::variable<QStringList> &list() const; protected: bool eventFilter(QObject *object, QEvent *event) override; diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp index f24fda93f..ef649ad83 100644 --- a/Telegram/SourceFiles/data/data_drafts.cpp +++ b/Telegram/SourceFiles/data/data_drafts.cpp @@ -18,32 +18,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" namespace Data { -namespace { - -} // namespace Draft::Draft( const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, - bool previewCancelled, + PreviewState previewState, mtpRequestId saveRequestId) : textWithTags(textWithTags) , msgId(msgId) , cursor(cursor) -, previewCancelled(previewCancelled) +, previewState(previewState) , saveRequestId(saveRequestId) { } Draft::Draft( not_null<const Ui::InputField*> field, MsgId msgId, - bool previewCancelled, + PreviewState previewState, mtpRequestId saveRequestId) : textWithTags(field->getTextWithTags()) , msgId(msgId) , cursor(field) -, previewCancelled(previewCancelled) { +, previewState(previewState) { } void ApplyPeerCloudDraft( @@ -66,7 +63,9 @@ void ApplyPeerCloudDraft( textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), - draft.is_no_webpage()); + (draft.is_no_webpage() + ? Data::PreviewState::Cancelled + : Data::PreviewState::Allowed)); cloudDraft->date = draft.vdate().v; history->setCloudDraft(std::move(cloudDraft)); diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h index b797cb3d3..a8c391d5b 100644 --- a/Telegram/SourceFiles/data/data_drafts.h +++ b/Telegram/SourceFiles/data/data_drafts.h @@ -26,25 +26,31 @@ void ClearPeerCloudDraft( PeerId peerId, TimeId date); +enum class PreviewState : char { + Allowed, + Cancelled, + EmptyOnEdit, +}; + struct Draft { Draft() = default; Draft( const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, - bool previewCancelled, + PreviewState previewState, mtpRequestId saveRequestId = 0); Draft( not_null<const Ui::InputField*> field, MsgId msgId, - bool previewCancelled, + PreviewState previewState, mtpRequestId saveRequestId = 0); TimeId date = 0; TextWithTags textWithTags; MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft MessageCursor cursor; - bool previewCancelled = false; + PreviewState previewState = PreviewState::Allowed; mtpRequestId saveRequestId = 0; }; @@ -144,7 +150,7 @@ inline bool draftsAreEqual(const Draft *a, const Draft *b) { return (a->textWithTags == b->textWithTags) && (a->msgId == b->msgId) - && (a->previewCancelled == b->previewCancelled); + && (a->previewState == b->previewState); } } // namespace Data diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 8e51f250a..8728fe610 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -215,13 +215,13 @@ void History::createLocalDraftFromCloud() { draft->textWithTags, draft->msgId, draft->cursor, - draft->previewCancelled)); + draft->previewState)); existing = localDraft(); } else if (existing != draft) { existing->textWithTags = draft->textWithTags; existing->msgId = draft->msgId; existing->cursor = draft->cursor; - existing->previewCancelled = draft->previewCancelled; + existing->previewState = draft->previewState; } existing->date = draft->date; } @@ -280,7 +280,7 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) { TextWithTags(), 0, MessageCursor(), - false)); + Data::PreviewState::Allowed)); cloudDraft()->date = TimeId(0); } else { auto existing = cloudDraft(); @@ -289,13 +289,13 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) { fromDraft->textWithTags, fromDraft->msgId, fromDraft->cursor, - fromDraft->previewCancelled)); + fromDraft->previewState)); existing = cloudDraft(); } else if (existing != fromDraft) { existing->textWithTags = fromDraft->textWithTags; existing->msgId = fromDraft->msgId; existing->cursor = fromDraft->cursor; - existing->previewCancelled = fromDraft->previewCancelled; + existing->previewState = fromDraft->previewState; } existing->date = base::unixtime::now(); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e46bc467e..8cad61a24 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -174,6 +174,7 @@ HistoryWidget::HistoryWidget( , _updateEditTimeLeftDisplay([=] { updateField(); }) , _fieldBarCancel(this, st::historyReplyCancel) , _previewTimer([=] { requestPreview(); }) +, _previewState(Data::PreviewState::Allowed) , _topBar(this, controller) , _scroll(this, st::historyScroll, false) , _updateHistoryItems([=] { updateHistoryItemsByTimer(); }) @@ -335,6 +336,10 @@ HistoryWidget::HistoryWidget( _fieldLinksParser = std::make_unique<MessageLinksParser>(_field); _fieldLinksParser->list().changes( ) | rpl::start_with_next([=](QStringList &&parsed) { + if (_previewState == Data::PreviewState::EmptyOnEdit + && _parsedLinks != parsed) { + _previewState = Data::PreviewState::Allowed; + } _parsedLinks = std::move(parsed); checkPreview(); }, lifetime()); @@ -1328,7 +1333,7 @@ void HistoryWidget::fieldChanged() { updateSendButtonType(); if (!HasSendText(_field)) { - _previewCancelled = false; + _previewState = Data::PreviewState::Allowed; } if (updateCmdStartShown()) { updateControlsVisibility(); @@ -1377,10 +1382,17 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() { if (!_history) return; if (_editMsgId) { - _history->setLocalEditDraft(std::make_unique<Data::Draft>(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); + _history->setLocalEditDraft(std::make_unique<Data::Draft>( + _field, + _editMsgId, + _previewState, + _saveEditMsgRequestId)); } else { if (_replyToId || !_field->empty()) { - _history->setLocalDraft(std::make_unique<Data::Draft>(_field, _replyToId, _previewCancelled)); + _history->setLocalDraft(std::make_unique<Data::Draft>( + _field, + _replyToId, + _previewState)); } else { _history->clearLocalDraft(); } @@ -1401,7 +1413,7 @@ void HistoryWidget::writeDraftTexts() { Storage::MessageDraft{ _editMsgId ? _editMsgId : _replyToId, _field->getTextWithTags(), - _previewCancelled, + _previewState, }); if (_migrated) { _migrated->clearDrafts(); @@ -1476,7 +1488,11 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U if (_history) { TextWithTags textWithTags = { '@' + samePeerBot->username + ' ' + query, TextWithTags::Tags() }; MessageCursor cursor = { textWithTags.text.size(), textWithTags.text.size(), QFIXED_MAX }; - _history->setLocalDraft(std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); + _history->setLocalDraft(std::make_unique<Data::Draft>( + textWithTags, + 0, + cursor, + Data::PreviewState::Allowed)); applyDraft(); return true; } @@ -1497,7 +1513,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, U textWithTags, to.currentReplyToId, cursor, - false); + Data::PreviewState::Allowed); if (to.section == Section::Replies) { history->setDraft( @@ -1653,8 +1669,15 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { setFieldText(draft->textWithTags, 0, fieldHistoryAction); _field->setFocus(); draft->cursor.applyTo(_field); - _textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; - _previewCancelled = draft->previewCancelled; + _textUpdateEvents = TextUpdateEvent::SaveDraft + | TextUpdateEvent::SendTyping; + + // Save links from _field to _parsedLinks without generating preview. + _previewState = Data::PreviewState::Cancelled; + _fieldLinksParser->parseNow(); + _parsedLinks = _fieldLinksParser->list().current(); + _previewState = draft->previewState; + _replyEditMsg = nullptr; if (const auto editDraft = _history->localEditDraft()) { _editMsgId = editDraft->msgId; @@ -2986,7 +3009,7 @@ void HistoryWidget::saveEditMsg() { cancelEdit(); return; } - const auto webPageId = _previewCancelled + const auto webPageId = (_previewState != Data::PreviewState::Allowed) ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id @@ -3106,7 +3129,7 @@ void HistoryWidget::send(Api::SendOptions options) { return; } - const auto webPageId = _previewCancelled + const auto webPageId = (_previewState != Data::PreviewState::Allowed) ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id @@ -5580,7 +5603,7 @@ void HistoryWidget::setFieldText( | TextUpdateEvent::SendTyping; previewCancel(); - _previewCancelled = false; + _previewState = Data::PreviewState::Allowed; } void HistoryWidget::clearFieldText( @@ -5623,7 +5646,7 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) { TextWithTags(), item->id, MessageCursor(), - false)); + Data::PreviewState::Allowed)); } } else { _replyEditMsg = item; @@ -5670,7 +5693,7 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) { _history->setLocalDraft(std::make_unique<Data::Draft>( _field, _replyToId, - _previewCancelled)); + _previewState)); } else { _history->clearLocalDraft(); } @@ -5688,12 +5711,14 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) { } return nullptr; }(); - const auto previewCancelled = !previewPage; + const auto previewState = previewPage + ? Data::PreviewState::Allowed + : Data::PreviewState::EmptyOnEdit; _history->setLocalEditDraft(std::make_unique<Data::Draft>( editData, item->id, cursor, - previewCancelled)); + previewState)); applyDraft(); _previewData = previewPage; @@ -5853,7 +5878,7 @@ void HistoryWidget::cancelFieldAreaState() { Ui::hideLayer(); _replyForwardPressed = false; if (_previewData && _previewData->pendingTill >= 0) { - _previewCancelled = true; + _previewState = Data::PreviewState::Cancelled; previewCancel(); _saveDraftText = true; @@ -5881,7 +5906,7 @@ void HistoryWidget::checkPreview() { const auto previewRestricted = [&] { return _peer && _peer->amRestricted(ChatRestriction::f_embed_links); }(); - if (_previewCancelled || previewRestricted) { + if (_previewState != Data::PreviewState::Allowed || previewRestricted) { previewCancel(); return; } @@ -5929,7 +5954,10 @@ void HistoryWidget::requestPreview() { }).send(); } -void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtpRequestId req) { +void HistoryWidget::gotPreview( + QString links, + const MTPMessageMedia &result, + mtpRequestId req) { if (req == _previewRequest) { _previewRequest = 0; } @@ -5937,10 +5965,12 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp const auto &data = result.c_messageMediaWebPage().vwebpage(); const auto page = session().data().processWebpage(data); _previewCache.insert(links, page->id); - if (page->pendingTill > 0 && page->pendingTill <= base::unixtime::now()) { + if (page->pendingTill > 0 + && page->pendingTill <= base::unixtime::now()) { page->pendingTill = -1; } - if (links == _previewLinks && !_previewCancelled) { + if (links == _previewLinks + && _previewState == Data::PreviewState::Allowed) { _previewData = (page->id && page->pendingTill >= 0) ? page.get() : nullptr; @@ -5949,7 +5979,8 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp session().data().sendWebPageGamePollNotifications(); } else if (result.type() == mtpc_messageMediaEmpty) { _previewCache.insert(links, 0); - if (links == _previewLinks && !_previewCancelled) { + if (links == _previewLinks + && _previewState == Data::PreviewState::Allowed) { _previewData = nullptr; updatePreview(); } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 163b9ac01..3c52c09ac 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -25,6 +25,10 @@ struct SendingAlbum; enum class SendMediaType; class MessageLinksParser; +namespace Data { +enum class PreviewState : char; +} // namespace Data + namespace SendMenu { enum class Type; } // namespace SendMenu @@ -617,7 +621,7 @@ private: Ui::Text::String _previewTitle; Ui::Text::String _previewDescription; base::Timer _previewTimer; - bool _previewCancelled = false; + Data::PreviewState _previewState = Data::PreviewState(); bool _replyForwardPressed = false; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index c3d962906..c34332fbe 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -612,7 +612,8 @@ ComposeControls::ComposeControls( _send, st::historySendSize.height())) , _sendMenuType(sendMenuType) -, _saveDraftTimer([=] { saveDraft(); }) { +, _saveDraftTimer([=] { saveDraft(); }) +, _previewState(Data::PreviewState::Allowed) { init(); } @@ -865,7 +866,7 @@ void ComposeControls::setFieldText( | TextUpdateEvent::SendTyping; _previewCancel(); - _previewCancelled = false; + _previewState = Data::PreviewState::Allowed; } void ComposeControls::saveFieldToHistoryLocalDraft() { @@ -880,7 +881,7 @@ void ComposeControls::saveFieldToHistoryLocalDraft() { std::make_unique<Data::Draft>( _field, _header->getDraftMessageId(), - _previewCancelled)); + _previewState)); } else { _history->clearDraft(draftKeyCurrent()); } @@ -964,7 +965,7 @@ void ComposeControls::init() { _header->previewCancelled( ) | rpl::start_with_next([=] { - _previewCancelled = true; + _previewState = Data::PreviewState::Cancelled; _saveDraftText = true; _saveDraftStart = crl::now(); saveDraft(); @@ -1224,7 +1225,7 @@ void ComposeControls::fieldChanged() { } updateSendButtonType(); if (!HasSendText(_field)) { - _previewCancelled = false; + _previewState = Data::PreviewState::Allowed; } if (updateBotCommandShown()) { updateControlsVisibility(); @@ -1294,7 +1295,7 @@ void ComposeControls::writeDraftTexts() { Storage::MessageDraft{ _header->getDraftMessageId(), _field->getTextWithTags(), - _previewCancelled, + _previewState, }); } @@ -1353,7 +1354,8 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) { _field->setFocus(); draft->cursor.applyTo(_field); _textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; - _previewCancelled = draft->previewCancelled; + _previewSetState(draft->previewState); + if (draft == editDraft) { _header->editMessage({ _history->channelId(), draft->msgId }); _header->replyToMessage({}); @@ -1837,14 +1839,16 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) { } return nullptr; }(); - const auto previewCancelled = !previewPage; + const auto previewState = previewPage + ? Data::PreviewState::Allowed + : Data::PreviewState::EmptyOnEdit; _history->setDraft( draftKey(DraftType::Edit), std::make_unique<Data::Draft>( editData, item->id, cursor, - previewCancelled)); + previewState)); applyDraft(); if (_autocomplete) { @@ -1883,7 +1887,7 @@ void ComposeControls::replyToMessage(FullMsgId id) { TextWithTags(), id.msg, MessageCursor(), - false)); + Data::PreviewState::Allowed)); } } else { _header->replyToMessage(id); @@ -1995,7 +1999,8 @@ void ComposeControls::initWebpageProcess() { if (till > 0 && till <= base::unixtime::now()) { till = -1; } - if (links == *previewLinks && !_previewCancelled) { + if (links == *previewLinks + && _previewState == Data::PreviewState::Allowed) { *previewData = (page->id && page->pendingTill >= 0) ? page.get() : nullptr; @@ -2003,7 +2008,8 @@ void ComposeControls::initWebpageProcess() { } }, [=](const MTPDmessageMediaEmpty &d) { previewCache->insert({ links, 0 }); - if (links == *previewLinks && !_previewCancelled) { + if (links == *previewLinks + && _previewState == Data::PreviewState::Allowed) { *previewData = nullptr; updatePreview(); } @@ -2032,7 +2038,8 @@ void ComposeControls::initWebpageProcess() { const auto checkPreview = crl::guard(_wrap.get(), [=] { const auto previewRestricted = peer && peer->amRestricted(ChatRestriction::f_embed_links); - if (_previewCancelled || previewRestricted) { + if (_previewState != Data::PreviewState::Allowed + || previewRestricted) { _previewCancel(); return; } @@ -2105,8 +2112,20 @@ void ComposeControls::initWebpageProcess() { const auto fieldLinksParser = lifetime.make_state<MessageLinksParser>(_field); + _previewSetState = [=](Data::PreviewState state) { + // Save links from _field to _parsedLinks without generating preview. + _previewState = Data::PreviewState::Cancelled; + fieldLinksParser->parseNow(); + *parsedLinks = fieldLinksParser->list().current(); + _previewState = state; + }; + fieldLinksParser->list().changes( ) | rpl::start_with_next([=](QStringList &&parsed) { + if (_previewState == Data::PreviewState::EmptyOnEdit + && *parsedLinks != parsed) { + _previewState = Data::PreviewState::Allowed; + } *parsedLinks = std::move(parsed); checkPreview(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 30201f1ac..d6328a055 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -34,6 +34,7 @@ namespace Data { struct MessagePosition; struct Draft; class DraftKey; +enum class PreviewState : char; } // namespace Data namespace InlineBots { @@ -307,7 +308,8 @@ private: bool _botCommandShown = false; Fn<void()> _previewCancel; - bool _previewCancelled = false; + Fn<void(Data::PreviewState)> _previewSetState; + Data::PreviewState _previewState = Data::PreviewState(); rpl::lifetime _uploaderSubscriptions; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 1ec922262..5b8ee8c4a 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -902,11 +902,7 @@ void RepliesWidget::send(Api::SendOptions options) { return; } - const auto webPageId = _composeControls->webPageId();/* _previewCancelled - ? CancelledWebPageId - : ((_previewData && _previewData->pendingTill >= 0) - ? _previewData->id - : WebPageId(0));*/ + const auto webPageId = _composeControls->webPageId(); auto message = ApiWrap::MessageToSend(_history); message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 1755b6ed6..9faee54c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -523,11 +523,7 @@ void ScheduledWidget::send() { } void ScheduledWidget::send(Api::SendOptions options) { - const auto webPageId = _composeControls->webPageId();/* _previewCancelled - ? CancelledWebPageId - : ((_previewData && _previewData->pendingTill >= 0) - ? _previewData->id - : WebPageId(0));*/ + const auto webPageId = _composeControls->webPageId(); auto message = ApiWrap::MessageToSend(_history); message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index bbf183590..50c8614bf 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -548,8 +548,11 @@ bool MainWidget::shareUrl( QFIXED_MAX }; auto history = peer->owner().history(peer); - history->setLocalDraft( - std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); + history->setLocalDraft(std::make_unique<Data::Draft>( + textWithTags, + 0, + cursor, + Data::PreviewState::Allowed)); history->clearLocalEditDraft(); history->session().changes().historyUpdated( history, @@ -576,7 +579,11 @@ bool MainWidget::inlineSwitchChosen(PeerId peerId, const QString &botAndQuery) { const auto h = peer->owner().history(peer); TextWithTags textWithTags = { botAndQuery, TextWithTags::Tags() }; MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX }; - h->setLocalDraft(std::make_unique<Data::Draft>(textWithTags, 0, cursor, false)); + h->setLocalDraft(std::make_unique<Data::Draft>( + textWithTags, + 0, + cursor, + Data::PreviewState::Allowed)); h->clearLocalEditDraft(); h->session().changes().historyUpdated( h, diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index ee882fc6a..d274b60ff 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -958,7 +958,7 @@ void EnumerateDrafts( key, draft->msgId, draft->textWithTags, - draft->previewCancelled, + draft->previewState, draft->cursor); } if (replaceKey @@ -969,7 +969,7 @@ void EnumerateDrafts( replaceKey, replaceDraft.msgId, replaceDraft.textWithTags, - replaceDraft.previewCancelled, + replaceDraft.previewState, replaceCursor); } } @@ -1017,12 +1017,12 @@ void Account::writeDrafts( auto&&, // key MsgId, // msgId const TextWithTags &text, - bool, // previewCancelled + Data::PreviewState, auto&&) { // cursor size += sizeof(qint32) // key + Serialize::stringSize(text.text) + sizeof(quint32) + TextUtilities::SerializeTagsSize(text.tags) - + 2 * sizeof(qint32); // msgId, previewCancelled + + 2 * sizeof(qint32); // msgId, previewState }; EnumerateDrafts( map, @@ -1043,14 +1043,14 @@ void Account::writeDrafts( const Data::DraftKey &key, MsgId msgId, const TextWithTags &text, - bool previewCancelled, + Data::PreviewState previewState, auto&&) { // cursor data.stream << key.serialize() << text.text << TextUtilities::SerializeTags(text.tags) << qint32(msgId) - << qint32(previewCancelled ? 1 : 0); + << qint32(previewState); }; EnumerateDrafts( map, @@ -1109,7 +1109,7 @@ void Account::writeDraftCursors( auto&&, // key MsgId, // msgId auto&&, // text - bool, // previewCancelled + Data::PreviewState, const MessageCursor &cursor) { // cursor data.stream << qint32(cursor.position) @@ -1249,23 +1249,29 @@ void Account::readDraftsWithCursors(not_null<History*> history) { for (auto i = 0; i != count; ++i) { TextWithTags data; QByteArray tagsSerialized; - qint32 keyValue = 0, messageId = 0, previewCancelled = 0; + qint32 keyValue = 0, messageId = 0, uncheckedPreviewState = 0; draft.stream >> keyValue >> data.text >> tagsSerialized >> messageId - >> previewCancelled; + >> uncheckedPreviewState; data.tags = TextUtilities::DeserializeTags( tagsSerialized, data.text.size()); + auto previewState = Data::PreviewState::Allowed; + switch (static_cast<Data::PreviewState>(uncheckedPreviewState)) { + case Data::PreviewState::Cancelled: + case Data::PreviewState::EmptyOnEdit: + previewState = Data::PreviewState(uncheckedPreviewState); + } const auto key = Data::DraftKey::FromSerialized(keyValue); if (key && key != Data::DraftKey::Cloud()) { map.emplace(key, std::make_unique<Data::Draft>( data, messageId, MessageCursor(), - previewCancelled)); + previewState)); } } if (draft.stream.status() != QDataStream::Ok) { @@ -1326,14 +1332,18 @@ void Account::readDraftsWithCursorsLegacy( msgData, msgReplyTo, MessageCursor(), - msgPreviewCancelled)); + (msgPreviewCancelled + ? Data::PreviewState::Cancelled + : Data::PreviewState::Allowed))); } if (editMsgId) { map.emplace(Data::DraftKey::LocalEdit(), std::make_unique<Data::Draft>( editData, editMsgId, MessageCursor(), - editPreviewCancelled)); + (editPreviewCancelled + ? Data::PreviewState::Cancelled + : Data::PreviewState::Allowed))); } readDraftCursors(peerId, map); history->setDraftsMap(std::move(map)); diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index 1aeb64d0b..4cb6fbc5a 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -52,7 +52,7 @@ enum class StartResult : uchar; struct MessageDraft { MsgId msgId = 0; TextWithTags textWithTags; - bool previewCancelled = false; + Data::PreviewState previewState = Data::PreviewState::Allowed; }; class Account final { diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp index ffbd3c1cb..940b25388 100644 --- a/Telegram/SourceFiles/support/support_helper.cpp +++ b/Telegram/SourceFiles/support/support_helper.cpp @@ -155,7 +155,7 @@ Data::Draft OccupiedDraft(const QString &normalizedName) { + normalizedName }, MsgId(0), MessageCursor(), - false + Data::PreviewState::Allowed }; } From 8d85dd7c1906a1a6a4e49bd23e3dfaccb1c67118 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 18:05:13 +0400 Subject: [PATCH 217/396] Fix scheduling messages without sound. --- Telegram/SourceFiles/ui/boxes/choose_date_time.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp index a9d413e97..c753c7637 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp @@ -694,6 +694,7 @@ ChooseDateTimeBoxDescriptor ChooseDateTimeBox( auto result = ChooseDateTimeBoxDescriptor(); box->setFocusCallback([=] { timeInput->setFocusFast(); }); result.submit = box->addButton(std::move(submit), save); + result.collect = collect; box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); return result; From 2559c5d6f4c3bb7da4dda690f42beb057c90db8e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 19:19:07 +0400 Subject: [PATCH 218/396] Allow other admins to mute me in voice chats. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index b0ec7c655..77d119d47 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -631,6 +631,8 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { setMuted(MuteState::ForceMuted); } else if (muted() == MuteState::ForceMuted) { setMuted(MuteState::Muted); + } else if (data.is_muted() && muted() != MuteState::Muted) { + setMuted(MuteState::Muted); } }); } From 5d4e5ed527b123c91f2850d5c4bd9ec8f94a2d7d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 19:19:37 +0400 Subject: [PATCH 219/396] Allow changing my own volume in voice chat. --- .../SourceFiles/calls/calls_group_members.cpp | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 2d1535df5..be356e31b 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1275,10 +1275,11 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( not_null<PeerListRow*> row) { Expects(row->peer()->isUser()); - if (row->peer()->isSelf()) { + const auto real = static_cast<Row*>(row.get()); + if (row->peer()->isSelf() + && (!_peer->canManageGroupCall() || !real->ssrc())) { return nullptr; } - const auto real = static_cast<Row*>(row.get()); const auto user = row->peer()->asUser(); auto result = base::make_unique_q<Ui::PopupMenu>( parent, @@ -1358,27 +1359,29 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( addMuteActionsToContextMenu(result, user, real); } - result->addAction( - tr::lng_context_view_profile(tr::now), - showProfile); - result->addAction( - tr::lng_context_send_message(tr::now), - showHistory); - const auto canKick = [&] { - if (static_cast<Row*>(row.get())->state() == Row::State::Invited) { - return false; - } else if (const auto chat = _peer->asChat()) { - return chat->amCreator() - || (chat->canBanMembers() && !chat->admins.contains(user)); - } else if (const auto group = _peer->asMegagroup()) { - return group->canRestrictUser(user); - } - return false; - }(); - if (canKick) { + if (!user->isSelf()) { result->addAction( - tr::lng_context_remove_from_group(tr::now), - removeFromGroup); + tr::lng_context_view_profile(tr::now), + showProfile); + result->addAction( + tr::lng_context_send_message(tr::now), + showHistory); + const auto canKick = [&] { + if (static_cast<Row*>(row.get())->state() == Row::State::Invited) { + return false; + } else if (const auto chat = _peer->asChat()) { + return chat->amCreator() + || (chat->canBanMembers() && !chat->admins.contains(user)); + } else if (const auto group = _peer->asMegagroup()) { + return group->canRestrictUser(user); + } + return false; + }(); + if (canKick) { + result->addAction( + tr::lng_context_remove_from_group(tr::now), + removeFromGroup); + } } return result; } @@ -1422,7 +1425,7 @@ void MembersController::addMuteActionsToContextMenu( auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased(); - if (!isMuted) { + if (!isMuted || user->isSelf()) { const auto call = _call.get(); auto otherParticipantStateValue = call ? call->otherParticipantStateValue( @@ -1448,7 +1451,9 @@ void MembersController::addMuteActionsToContextMenu( volumeItem->toggleMuteLocallyRequests( ) | rpl::start_with_next([=](bool muted) { - toggleMute(muted, true); + if (!user->isSelf()) { + toggleMute(muted, true); + } }, volumeItem->lifetime()); volumeItem->changeVolumeRequests( @@ -1458,14 +1463,16 @@ void MembersController::addMuteActionsToContextMenu( volumeItem->changeVolumeLocallyRequests( ) | rpl::start_with_next([=](int volume) { - changeVolume(volume, true); + if (!user->isSelf()) { + changeVolume(volume, true); + } }, volumeItem->lifetime()); menu->addAction(std::move(volumeItem)); }; const auto muteAction = [&]() -> QAction* { - if (muteState == Row::State::Invited) { + if (muteState == Row::State::Invited || user->isSelf()) { return nullptr; } auto callback = [=] { From 6c0553f4d6bf7f29905875865452de772a1badb3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 29 Jan 2021 17:58:31 +0300 Subject: [PATCH 220/396] Returned default icon color for songs without cover art. Related commit: 9b9531d279. --- .../SourceFiles/boxes/edit_caption_box.cpp | 2 +- .../view/media/history_view_document.cpp | 10 +++++----- .../inline_bots/inline_bot_layout_internal.cpp | 2 +- .../SourceFiles/overview/overview_layout.cpp | 18 ++++++++++++++++++ .../chat/attach/attach_single_file_preview.cpp | 4 +++- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index ace98f86e..9facde242 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -963,7 +963,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { } const auto icon = &(_isAudio - ? st::historyFileSongPlay + ? (_thumbw ? st::historyFileSongPlay : st::historyFileInPlay) : _isImage ? st::historyFileInImage : st::historyFileInDocument); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index edbee6cd6..0654b8203 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -456,21 +456,21 @@ void Document::draw( const auto icon = [&] { if (_data->waitingForAlbum()) { - if (_data->isSong()) { + if (_data->isSongWithCover()) { return &(selected ? st::historyFileSongWaitingSelected : st::historyFileSongWaiting); } return &(outbg ? (selected ? st::historyFileOutWaitingSelected : st::historyFileOutWaiting) : (selected ? st::historyFileInWaitingSelected : st::historyFileInWaiting)); } else if (!cornerDownload && (_data->loading() || _data->uploading())) { - if (_data->isSong()) { + if (_data->isSongWithCover()) { return &(selected ? st::historyFileSongCancelSelected : st::historyFileSongCancel); } return &(outbg ? (selected ? st::historyFileOutCancelSelected : st::historyFileOutCancel) : (selected ? st::historyFileInCancelSelected : st::historyFileInCancel)); } else if (showPause) { - if (_data->isSong()) { + if (_data->isSongWithCover()) { return &(selected ? st::historyFileSongPauseSelected : st::historyFileSongPause); @@ -478,7 +478,7 @@ void Document::draw( return &(outbg ? (selected ? st::historyFileOutPauseSelected : st::historyFileOutPause) : (selected ? st::historyFileInPauseSelected : st::historyFileInPause)); } else if (loaded || _dataMedia->canBePlayed()) { if (_dataMedia->canBePlayed()) { - if (_data->isSong()) { + if (_data->isSongWithCover()) { return &(selected ? st::historyFileSongPlaySelected : st::historyFileSongPlay); @@ -489,7 +489,7 @@ void Document::draw( } return &(outbg ? (selected ? st::historyFileOutDocumentSelected : st::historyFileOutDocument) : (selected ? st::historyFileInDocumentSelected : st::historyFileInDocument)); } - if (_data->isSong()) { + if (_data->isSongWithCover()) { return &(selected ? st::historyFileSongDownloadSelected : st::historyFileSongDownload); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index c6beea47d..5b6086416 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -896,7 +896,7 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con return &st::historyFileInPause; } else if (_document->isImage()) { return &st::historyFileInImage; - } else if (_document->isSong()) { + } else if (_document->isSongWithCover()) { return &st::historyFileSongPlay; } else if (_document->isVoiceMessage() || _document->isAudioFile()) { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index ad2c727ac..ef0d0c5a0 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -1037,6 +1037,24 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } const auto icon = [&] { + if (!coverDrawn) { + if (isLoading) { + return &(selected + ? _st.voiceCancelSelected + : _st.voiceCancel); + } else if (showPause) { + return &(selected + ? _st.voicePauseSelected + : _st.voicePause); + } else if (loaded || _dataMedia->canBePlayed()) { + return &(selected + ? _st.voicePlaySelected + : _st.voicePlay); + } + return &(selected + ? _st.voiceDownloadSelected + : _st.voiceDownload); + } if (isLoading) { return &(selected ? _st.songCancelSelected : _st.songCancel); } else if (showPause) { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp index c479ba93a..1c93cdc89 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -168,7 +168,9 @@ void SingleFilePreview::paintEvent(QPaintEvent *e) { } auto &icon = _fileIsAudio - ? st::historyFileSongPlay + ? (_fileThumb.isNull() + ? st::historyFileInPlay + : st::historyFileSongPlay) : _fileIsImage ? st::historyFileInImage : st::historyFileInDocument; From fba7bd780703e6574890032595b35c538d98b763 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 29 Jan 2021 18:24:26 +0300 Subject: [PATCH 221/396] Fixed speaker display when participant is muted by me in group calls. --- Telegram/SourceFiles/calls/calls_group_members.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index be356e31b..e1cabf7c4 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -660,13 +660,13 @@ void Row::paintStatusText( int availableWidth, int outerWidth, bool selected) { - p.save(); const auto &font = st::normalFont; - paintStatusIcon(p, st, font, selected); - const auto translatedWidth = statusIconWidth(); - p.translate(translatedWidth, 0); - const auto guard = gsl::finally([&] { p.restore(); }); if (_state != State::Invited && _state != State::MutedByMe) { + p.save(); + paintStatusIcon(p, st, font, selected); + const auto translatedWidth = statusIconWidth(); + p.translate(translatedWidth, 0); + const auto guard = gsl::finally([&] { p.restore(); }); PeerListRow::paintStatusText( p, st, From e0680fc2a5e2e874d5f4083227d5f42b4313da6f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 20:56:56 +0400 Subject: [PATCH 222/396] Fix voice chat menu for admins editing admins. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 4 ++-- .../SourceFiles/calls/calls_group_members.cpp | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 77d119d47..4b190dde5 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -409,11 +409,11 @@ void GroupCall::applyParticipantLocally( const auto mutedCount = 0/*participant->mutedCount*/; using Flag = MTPDgroupCallParticipant::Flag; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) - | (volume.has_value() ? Flag::f_volume : Flag(0)) + | Flag::f_volume // Without flag the volume is reset to 100%. | (participant->lastActive ? Flag::f_active_date : Flag(0)) | (!mute ? Flag(0) - : user->canManageGroupCall() + : _peer->canManageGroupCall() ? Flag::f_muted : Flag::f_muted_by_you); _peer->groupCall()->applyUpdateChecked( diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index e1cabf7c4..a31061f14 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -295,6 +295,7 @@ private: void addMuteActionsToContextMenu( not_null<Ui::PopupMenu*> menu, not_null<UserData*> user, + bool userIsCallAdmin, not_null<Row*> row); void setupListChangeViewers(not_null<GroupCall*> call); void subscribeToChanges(not_null<Data::GroupCall*> real); @@ -1356,7 +1357,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( }); if (real->ssrc() != 0) { - addMuteActionsToContextMenu(result, user, real); + addMuteActionsToContextMenu(result, user, admin, real); } if (!user->isSelf()) { @@ -1389,6 +1390,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( void MembersController::addMuteActionsToContextMenu( not_null<Ui::PopupMenu*> menu, not_null<UserData*> user, + bool userIsCallAdmin, not_null<Row*> row) { const auto muteString = [=] { return (_peer->canManageGroupCall() @@ -1446,6 +1448,13 @@ void MembersController::addMuteActionsToContextMenu( volumeItem->toggleMuteRequests( ) | rpl::start_with_next([=](bool muted) { + if (muted) { + // Slider value is changed after the callback is called. + // To capture good state inside the slider frame we postpone. + crl::on_main(menu, [=] { + menu->hideMenu(); + }); + } toggleMute(muted, false); }, volumeItem->lifetime()); @@ -1472,7 +1481,9 @@ void MembersController::addMuteActionsToContextMenu( }; const auto muteAction = [&]() -> QAction* { - if (muteState == Row::State::Invited || user->isSelf()) { + if (muteState == Row::State::Invited + || user->isSelf() + || (userIsCallAdmin && row->state() != Row::State::Active)) { return nullptr; } auto callback = [=] { From 10f58c2ac750db26b7abb77a858a9966767c03ae Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 29 Jan 2021 20:21:40 +0400 Subject: [PATCH 223/396] Add debug logging about media viewer screen --- .../media/view/media_view_overlay_widget.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 9e403dd5b..1c18feba0 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -444,7 +444,13 @@ void OverlayWidget::moveToScreen() { const auto activeWindowScreen = widgetScreen(window); const auto myScreen = widgetScreen(this); if (activeWindowScreen && myScreen != activeWindowScreen) { + const auto screenList = QGuiApplication::screens(); + DEBUG_LOG(("Viewer Pos: Currently on screen %1, moving to screen %2") + .arg(screenList.indexOf(myScreen)) + .arg(screenList.indexOf(activeWindowScreen))); windowHandle()->setScreen(activeWindowScreen); + DEBUG_LOG(("Viewer Pos: New actual screen: %1") + .arg(screenList.indexOf(windowHandle->screen()))); } updateGeometry(); } @@ -1313,14 +1319,24 @@ void OverlayWidget::onScreenResized(int screen) { void OverlayWidget::handleVisibleChanged(bool visible) { if (visible) { + const auto screenList = QGuiApplication::screens(); + DEBUG_LOG(("Viewer Pos: Shown, screen number: %1") + .arg(screenList.indexOf(windowHandle->screen()))); + moveToScreen(); } } void OverlayWidget::handleScreenChanged(QScreen *screen) { - if (isVisible()) { - moveToScreen(); + if (!isVisible()) { + return; } + + const auto screenList = QGuiApplication::screens(); + DEBUG_LOG(("Viewer Pos: Screen changed to: %1") + .arg(screenList.indexOf(screen))); + + moveToScreen(); } void OverlayWidget::onToMessage() { From 1eacaec66b854a22cd9f4321a400138239b0e78b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 21:06:14 +0400 Subject: [PATCH 224/396] Fix build. --- .../SourceFiles/media/view/media_view_overlay_widget.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 1c18feba0..75fff52f4 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -450,7 +450,9 @@ void OverlayWidget::moveToScreen() { .arg(screenList.indexOf(activeWindowScreen))); windowHandle()->setScreen(activeWindowScreen); DEBUG_LOG(("Viewer Pos: New actual screen: %1") - .arg(screenList.indexOf(windowHandle->screen()))); + .arg(windowHandle() + ? screenList.indexOf(windowHandle()->screen()) + : -2)); } updateGeometry(); } @@ -1321,7 +1323,9 @@ void OverlayWidget::handleVisibleChanged(bool visible) { if (visible) { const auto screenList = QGuiApplication::screens(); DEBUG_LOG(("Viewer Pos: Shown, screen number: %1") - .arg(screenList.indexOf(windowHandle->screen()))); + .arg(windowHandle() + ? screenList.indexOf(windowHandle()->screen()) + : -2)); moveToScreen(); } From db89de96a929a9bec22848dae7d188e70fe37af9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 22:07:32 +0400 Subject: [PATCH 225/396] Fix loading of voice chats participants. --- .../SourceFiles/calls/calls_group_members.cpp | 17 ++++++++++------- .../SourceFiles/calls/calls_group_members.h | 4 ---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index a31061f14..271fb30b7 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1483,7 +1483,9 @@ void MembersController::addMuteActionsToContextMenu( const auto muteAction = [&]() -> QAction* { if (muteState == Row::State::Invited || user->isSelf() - || (userIsCallAdmin && row->state() != Row::State::Active)) { + || (muteState == Row::State::Muted + && userIsCallAdmin + && _peer->canManageGroupCall())) { return nullptr; } auto callback = [=] { @@ -1653,6 +1655,13 @@ void GroupMembers::setupList() { resizeToList(); }, _list->lifetime()); + rpl::combine( + _scroll->scrollTopValue(), + _scroll->heightValue() + ) | rpl::start_with_next([=](int scrollTop, int scrollHeight) { + _list->setVisibleTopBottom(scrollTop, scrollTop + scrollHeight); + }, _scroll->lifetime()); + updateControlsGeometry(); } @@ -1754,12 +1763,6 @@ void GroupMembers::setupFakeRoundCorners() { }, lifetime()); } -void GroupMembers::visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) { - setChildVisibleTopBottom(_list, visibleTop, visibleBottom); -} - void GroupMembers::peerListSetTitle(rpl::producer<QString> title) { } diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index 5d5014af7..a0e1e1dd3 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -51,10 +51,6 @@ public: private: using ListWidget = PeerListContent; - void visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) override; - void resizeEvent(QResizeEvent *e) override; // PeerListContentDelegate interface. From e8c3df2abbb5a8b58a2bed572064500dcd49f1ec Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 22:10:13 +0400 Subject: [PATCH 226/396] Version 2.5.8. - Fix OpenAL device closing in calls and voice chats. - Fix video chat rotation when calling from iOS. - Fix scheduling messages without sound. - Remove redundant Cancel button in ScheduleBox. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 7 +++++++ 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 8e62f6314..3a1e36d41 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="2.5.7.0" /> + Version="2.5.8.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 45b954c36..da62aa07e 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,7,0 - PRODUCTVERSION 2,5,7,0 + FILEVERSION 2,5,8,0 + PRODUCTVERSION 2,5,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.7.0" + VALUE "FileVersion", "2.5.8.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.7.0" + VALUE "ProductVersion", "2.5.8.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 79d75ec67..92d723225 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,7,0 - PRODUCTVERSION 2,5,7,0 + FILEVERSION 2,5,8,0 + PRODUCTVERSION 2,5,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.7.0" + VALUE "FileVersion", "2.5.8.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.7.0" + VALUE "ProductVersion", "2.5.8.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 8c4ddc021..6a86f3ef1 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005007; -constexpr auto AppVersionStr = "2.5.7"; +constexpr auto AppVersion = 2005008; +constexpr auto AppVersionStr = "2.5.8"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 1da20bb68..1605f65ef 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005007 +AppVersion 2005008 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.7 -AppVersionStr 2.5.7 +AppVersionStrSmall 2.5.8 +AppVersionStr 2.5.8 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 2.5.7 +AppVersionOriginal 2.5.8 diff --git a/changelog.txt b/changelog.txt index 708152a0c..5a404f8a2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +2.5.8 (29.01.21) + +- Fix OpenAL device closing in calls and voice chats. +- Fix video chat rotation when calling from iOS. +- Fix scheduling messages without sound. +- Remove redundant Cancel button in ScheduleBox. + 2.5.7 (28.01.21) - Delete not only messages, but also groups you created and call history for all sides, without a trace. From 01110a29ad85b99527a228242f4307223e7ad960 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 22:51:11 +0400 Subject: [PATCH 227/396] Version 2.5.8: Fix PopupMenu min width. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index f03209c1f..d94cb3421 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit f03209c1f9c7f9d47eff5740e927d3d41df2e09e +Subproject commit d94cb34219e5795efc0efe24d980c54e40eb42e3 From 5092d8fe63a66878b55da17ce0db44feb5c1311c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 29 Jan 2021 15:46:25 +0400 Subject: [PATCH 228/396] Version 2.5.8: Fix invite link export if absent. --- .../boxes/peers/edit_peer_type_box.cpp | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 8a9967c9a..231b385b8 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -120,7 +120,7 @@ private: void fillPrivaciesButtons( not_null<Ui::VerticalLayout*> parent, - std::optional<Privacy> savedValue = std::nullopt); + Privacy savedValue); void addRoundButton( not_null<Ui::VerticalLayout*> container, Privacy value, @@ -195,6 +195,12 @@ void Controller::createContent() { if (_controls.privacy->value() == Privacy::NoUsername) { checkUsernameAvailability(); } + _controls.inviteLinkWrap->toggle( + (_privacySavedValue != Privacy::HasUsername), + anim::type::instant); + _controls.usernameWrap->toggle( + (_privacySavedValue == Privacy::HasUsername), + anim::type::instant); } void Controller::addRoundButton( @@ -222,7 +228,7 @@ void Controller::addRoundButton( void Controller::fillPrivaciesButtons( not_null<Ui::VerticalLayout*> parent, - std::optional<Privacy> savedValue) { + Privacy savedValue) { const auto canEditUsername = [&] { if (const auto chat = _peer->asChat()) { return chat->canEditUsername(); @@ -245,8 +251,7 @@ void Controller::fillPrivaciesButtons( const auto isPublic = _peer->isChannel() && _peer->asChannel()->hasUsername(); _controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>( - savedValue.value_or( - isPublic ? Privacy::HasUsername : Privacy::NoUsername)); + savedValue); addRoundButton( container, @@ -543,7 +548,8 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { using namespace Settings; AddSkip(container); - AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); + AddSubsectionTitle(container, tr::lng_create_invite_link_title()); + // tr::lng_create_permanent_link_title()); // #TODO links AddPermanentLinkBlock(container, _peer); AddSkip(container); @@ -554,6 +560,16 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { ? tr::lng_group_invite_about_permanent_group() : tr::lng_group_invite_about_permanent_channel())); + if (_peer->wasFullUpdated()) { + const auto link = _peer->isChat() + ? _peer->asChat()->inviteLink() + : _peer->asChannel()->inviteLink(); + if (link.isEmpty()) { + // #TODO links remove this auto-export link. + _peer->session().api().inviteLinks().revokePermanent(_peer); + } + } + return result; } From 72f8d3f485456eb716d3cce5a5f7dd498377c1f3 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 30 Jan 2021 00:05:49 +0400 Subject: [PATCH 229/396] Version 2.5.8: Fix dump_syms invocation. --- Telegram/build/build.bat | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/build/build.bat b/Telegram/build/build.bat index 3f6afd7e1..4c95a91aa 100644 --- a/Telegram/build/build.bat +++ b/Telegram/build/build.bat @@ -41,6 +41,7 @@ if %Build64% neq 0 ( echo Bad environment. Make sure to run from 'x64 Native Tools Command Prompt for VS 2019'. exit /b ) + set "DumpSymsPath=%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) else ( if "%Platform%" neq "x86" ( echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. @@ -52,6 +53,7 @@ if %Build64% neq 0 ( echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. exit /b ) + set "DumpSymsPath=%SolutionPath%\..\..\Libraries\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) FOR /F "tokens=1,2* delims= " %%i in (%FullScriptPath%version) do set "%%i=%%j" @@ -164,13 +166,13 @@ echo. echo Version %AppVersionStrFull% build successfull. Preparing.. echo. -if not exist "%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ( +if not exist "%DumpSymsPath%" ( echo Utility dump_syms not found! exit /b 1 ) echo Dumping debug symbols.. -call "%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" "%ReleasePath%\%BinaryName%.pdb" > "%ReleasePath%\%BinaryName%.sym" +call "%DumpSymsPath%" "%ReleasePath%\%BinaryName%.pdb" > "%ReleasePath%\%BinaryName%.sym" echo Done! set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5" From 87895466dc0146f9539a76fdef188b8707e8d210 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 30 Jan 2021 00:22:40 +0400 Subject: [PATCH 230/396] Version 2.5.8: Fix dump_syms invocation. --- Telegram/build/build.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/build.bat b/Telegram/build/build.bat index 4c95a91aa..9ff991a6a 100644 --- a/Telegram/build/build.bat +++ b/Telegram/build/build.bat @@ -41,7 +41,6 @@ if %Build64% neq 0 ( echo Bad environment. Make sure to run from 'x64 Native Tools Command Prompt for VS 2019'. exit /b ) - set "DumpSymsPath=%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) else ( if "%Platform%" neq "x86" ( echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. @@ -53,7 +52,6 @@ if %Build64% neq 0 ( echo Bad environment. Make sure to run from 'x86 Native Tools Command Prompt for VS 2019'. exit /b ) - set "DumpSymsPath=%SolutionPath%\..\..\Libraries\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) FOR /F "tokens=1,2* delims= " %%i in (%FullScriptPath%version) do set "%%i=%%j" @@ -97,10 +95,12 @@ if %Build64% neq 0 ( set "UpdateFile=tx64upd%AppVersion%" set "SetupFile=tsetup-x64.%AppVersionStrFull%.exe" set "PortableFile=tportable-x64.%AppVersionStrFull%.zip" + set "DumpSymsPath=%SolutionPath%\..\..\Libraries\win64\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) else ( set "UpdateFile=tupdate%AppVersion%" set "SetupFile=tsetup.%AppVersionStrFull%.exe" set "PortableFile=tportable.%AppVersionStrFull%.zip" + set "DumpSymsPath=%SolutionPath%\..\..\Libraries\breakpad\src\tools\windows\dump_syms\Release\dump_syms.exe" ) set "ReleasePath=%SolutionPath%\Release" set "DeployPath=%ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStrFull%" From 0b5213a9cb152acd7915faab5bd8262471483f50 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 30 Jan 2021 04:42:03 +0400 Subject: [PATCH 231/396] Use SNAPCRAFT_PARALLEL_BUILD_COUNT instead of nproc in snap --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 8806abb4a..0268b33b7 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -390,7 +390,7 @@ parts: -I $SNAPCRAFT_STAGE/usr/include \ -L $SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET - make -j$(nproc) + make -j$SNAPCRAFT_PARALLEL_BUILD_COUNT make INSTALL_ROOT="$SNAPCRAFT_PART_INSTALL" install stage: - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2 From 9c86755546223bbee3df516d0261a1bb87cf21e2 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 26 Jan 2021 03:57:50 +0400 Subject: [PATCH 232/396] Take custom scale in account when saving window geometry --- Telegram/SourceFiles/settings.h | 1 + .../details/storage_settings_scheme.cpp | 10 ++++++- Telegram/SourceFiles/storage/localstorage.cpp | 9 ++++++- Telegram/SourceFiles/window/main_window.cpp | 26 +++++++++++++++++-- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index ded46cddf..82a73b0ad 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -73,6 +73,7 @@ struct TWindowPos { int32 moncrc = 0; int maximized = 0; + int scale = 0; int x = 0; int y = 0; int w = 0; diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index 4efc521cf..463c1026f 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -725,9 +725,17 @@ bool ReadSetting( auto position = TWindowPos(); stream >> position.x >> position.y >> position.w >> position.h; stream >> position.moncrc >> position.maximized; + stream >> position.scale; if (!CheckStreamStatus(stream)) return false; - DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); + DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (scale %5%, maximized %6)") + .arg(position.x) + .arg(position.y) + .arg(position.w) + .arg(position.h) + .arg(position.scale) + .arg(Logs::b(position.maximized))); + cSetWindowPos(position); } break; diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 1d80e44c1..73a32f7b9 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -532,8 +532,15 @@ void writeSettings() { auto position = cWindowPos(); data.stream << quint32(dbiWindowPosition) << qint32(position.x) << qint32(position.y) << qint32(position.w) << qint32(position.h); data.stream << qint32(position.moncrc) << qint32(position.maximized); + data.stream << qint32(position.scale); - DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); + DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4 (scale %5%, maximized %6)") + .arg(position.x) + .arg(position.y) + .arg(position.w) + .arg(position.h) + .arg(position.scale) + .arg(Logs::b(position.maximized))); settings.writeEncrypted(data, SettingsKey); } diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 9bbbdadaf..7fa7460e8 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -425,7 +425,21 @@ void MainWindow::initSize() { } auto position = cWindowPos(); - DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); + DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 (scale %5%, maximized %6)") + .arg(position.x) + .arg(position.y) + .arg(position.w) + .arg(position.h) + .arg(position.scale) + .arg(Logs::b(position.maximized))); + + if (position.scale != 0) { + const auto scaleFactor = cScale() / float64(position.scale); + position.x *= scaleFactor; + position.y *= scaleFactor; + position.w *= scaleFactor; + position.h *= scaleFactor; + } const auto primaryScreen = QGuiApplication::primaryScreen(); auto geometryScreen = primaryScreen; @@ -593,6 +607,7 @@ void MainWindow::savePosition(Qt::WindowState state) { realPosition.y = r.y(); realPosition.w = r.width() - (_rightColumn ? _rightColumn->width() : 0); realPosition.h = r.height(); + realPosition.scale = cScale(); realPosition.maximized = 0; realPosition.moncrc = 0; @@ -623,9 +638,16 @@ void MainWindow::savePosition(Qt::WindowState state) { || realPosition.y != savedPosition.y || realPosition.w != savedPosition.w || realPosition.h != savedPosition.h + || realPosition.scale != savedPosition.scale || realPosition.moncrc != savedPosition.moncrc || realPosition.maximized != savedPosition.maximized) { - DEBUG_LOG(("Window Pos: Writing: %1, %2, %3, %4 (maximized %5)").arg(realPosition.x).arg(realPosition.y).arg(realPosition.w).arg(realPosition.h).arg(Logs::b(realPosition.maximized))); + DEBUG_LOG(("Window Pos: Writing: %1, %2, %3, %4 (scale %5%, maximized %6)") + .arg(realPosition.x) + .arg(realPosition.y) + .arg(realPosition.w) + .arg(realPosition.h) + .arg(realPosition.scale) + .arg(Logs::b(realPosition.maximized))); cSetWindowPos(realPosition); Local::writeSettings(); } From f51055d606308105674471eaee76a4d6f5abfe25 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sun, 31 Jan 2021 12:14:22 +0400 Subject: [PATCH 233/396] Ensure the window is not out of available geometry on geometry restoring --- Telegram/SourceFiles/window/main_window.cpp | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 7fa7460e8..fd69d2932 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -466,14 +466,44 @@ void MainWindow::initSize() { for (auto screen : QGuiApplication::screens()) { if (position.moncrc == screenNameChecksum(screen->name())) { auto screenGeometry = screen->geometry(); + auto availableGeometry = screen->availableGeometry(); DEBUG_LOG(("Window Pos: Screen found, screen geometry: %1, %2, %3, %4").arg(screenGeometry.x()).arg(screenGeometry.y()).arg(screenGeometry.width()).arg(screenGeometry.height())); - auto w = screenGeometry.width(), h = screenGeometry.height(); + const auto x = availableGeometry.x() - screenGeometry.x(); + const auto y = availableGeometry.y() - screenGeometry.y(); + const auto w = availableGeometry.width(); + const auto h = availableGeometry.height(); if (w >= st::windowMinWidth && h >= st::windowMinHeight) { - if (position.x < 0) position.x = 0; - if (position.y < 0) position.y = 0; + if (position.x < x) position.x = x; + if (position.y < y) position.y = y; if (position.w > w) position.w = w; if (position.h > h) position.h = h; + const auto rightPoint = position.x + position.w; + if (rightPoint > w) { + const auto distance = rightPoint - w; + const auto newXPos = position.x - distance; + if (newXPos >= 0) { + position.x = newXPos; + } else { + position.x = 0; + const auto newRightPoint = position.x + position.w; + const auto newDistance = newRightPoint - w; + position.w -= newDistance; + } + } + const auto bottomPoint = position.y + position.h; + if (bottomPoint > h) { + const auto distance = bottomPoint - h; + const auto newYPos = position.y - distance; + if (newYPos >= 0) { + position.y = newYPos; + } else { + position.y = 0; + const auto newBottomPoint = position.y + position.h; + const auto newDistance = newBottomPoint - h; + position.h -= newDistance; + } + } position.x += screenGeometry.x(); position.y += screenGeometry.y(); if (position.x + st::windowMinWidth <= screenGeometry.x() + screenGeometry.width() && From 3251b8bf6e740e55777e85d63065a61e43df54fe Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sun, 31 Jan 2021 12:34:35 +0400 Subject: [PATCH 234/396] Don't set geometry for media viewer only on Wayland --- .../media/view/media_view_overlay_widget.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 75fff52f4..f16fde906 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -450,15 +450,13 @@ void OverlayWidget::moveToScreen() { .arg(screenList.indexOf(activeWindowScreen))); windowHandle()->setScreen(activeWindowScreen); DEBUG_LOG(("Viewer Pos: New actual screen: %1") - .arg(windowHandle() - ? screenList.indexOf(windowHandle()->screen()) - : -2)); + .arg(screenList.indexOf(windowHandle()->screen()))); } updateGeometry(); } void OverlayWidget::updateGeometry() { - if (Platform::IsLinux()) { + if (Platform::IsWayland()) { return; } const auto screen = windowHandle() && windowHandle()->screen() @@ -1323,9 +1321,7 @@ void OverlayWidget::handleVisibleChanged(bool visible) { if (visible) { const auto screenList = QGuiApplication::screens(); DEBUG_LOG(("Viewer Pos: Shown, screen number: %1") - .arg(windowHandle() - ? screenList.indexOf(windowHandle()->screen()) - : -2)); + .arg(screenList.indexOf(windowHandle()->screen()))); moveToScreen(); } From d202a0cd065323df21c67cd42d9c12d6371dbee4 Mon Sep 17 00:00:00 2001 From: GitHub Action <action@github.com> Date: Mon, 1 Feb 2021 01:05:40 +0000 Subject: [PATCH 235/396] Update User-Agent for DNS to Chrome 88.0.4324.96. --- .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp index 734f000aa..dbcf6fc16 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp @@ -65,7 +65,7 @@ QByteArray DnsUserAgent() { static const auto kResult = QByteArray( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/87.0.4280.88 Safari/537.36"); + "Chrome/88.0.4324.96 Safari/537.36"); return kResult; } From 2a1096d83c07be41374bc4f7fc4c53c50f38f93c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 12:32:08 +0400 Subject: [PATCH 236/396] Don't reset interface scale to auto on Settings open. --- Telegram/SourceFiles/settings/settings_main.cpp | 2 ++ Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 36911968c..086a19841 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -259,6 +259,8 @@ void SetupInterfaceScale( slider->sectionActivated( ) | rpl::map([=](int section) { return scaleByIndex(section); + }) | rpl::filter([=](int scale) { + return cEvalScale(scale) != cEvalScale(cConfigScale()); }) | rpl::start_with_next([=](int scale) { setScale( (scale == cScreenScale()) ? style::kScaleAuto : scale, diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index 9de847c78..70732c495 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -47,6 +47,10 @@ void DiscreteSlider::setActiveSectionFast(int index) { void DiscreteSlider::finishAnimating() { _a_left.stop(); update(); + _callbackAfterMs = 0; + if (_timerId >= 0) { + activateCallback(); + } } void DiscreteSlider::setSelectOnPress(bool selectOnPress) { From 5feb381cb26fe4dda0e3438f4635e70ccf05fafe Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 15:44:22 +0400 Subject: [PATCH 237/396] Allow showing images from cache in media viewer. Fixes #10237. --- Telegram/SourceFiles/data/data_document.cpp | 41 ++++++++++++++----- .../media/view/media_view_overlay_widget.cpp | 16 +++++++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index c241c6b24..6f0bcf47f 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -47,6 +47,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_animation.h" #include "app.h" +#include <QtCore/QBuffer> + namespace { const auto kAnimatedStickerDimensions = QSize( @@ -320,21 +322,33 @@ void DocumentOpenClickHandler::Open( return; } - const auto openFile = [&] { + const auto media = data->createMediaView(); + const auto openImageInApp = [&] { + if (data->size >= App::kImageSizeLimit) { + return false; + } const auto &location = data->location(true); - if (data->size < App::kImageSizeLimit && location.accessEnable()) { + if (!location.isEmpty() && location.accessEnable()) { const auto guard = gsl::finally([&] { location.accessDisable(); }); const auto path = location.name(); - if (Core::MimeTypeForFile(path).name().startsWith("image/") && QImageReader(path).canRead()) { + if (Core::MimeTypeForFile(path).name().startsWith("image/") + && QImageReader(path).canRead()) { Core::App().showDocument(data, context); - return; + return true; + } + } else if (data->mimeString().startsWith("image/") + && !media->bytes().isEmpty()) { + auto bytes = media->bytes(); + auto buffer = QBuffer(&bytes); + if (QImageReader(&buffer).canRead()) { + Core::App().showDocument(data, context); + return true; } } - LaunchWithWarning(&data->session(), location.name(), context); + return false; }; - const auto media = data->createMediaView(); const auto &location = data->location(true); if (data->isTheme() && media->loaded(true)) { Core::App().showDocument(data, context); @@ -352,11 +366,16 @@ void DocumentOpenClickHandler::Open( } else { Core::App().showDocument(data, context); } - } else if (data->saveFromDataSilent()) { - openFile(); - } else if (data->status == FileReady - || data->status == FileDownloadFailed) { - DocumentSaveClickHandler::Save(origin, data); + } else { + data->saveFromDataSilent(); + if (!openImageInApp()) { + if (!data->filepath(true).isEmpty()) { + LaunchWithWarning(&data->session(), location.name(), context); + } else if (data->status == FileReady + || data->status == FileDownloadFailed) { + DocumentSaveClickHandler::Save(origin, data); + } + } } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index f16fde906..f2088909c 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -197,8 +197,7 @@ void PaintImageProfile(QPainter &p, const QImage &image, QRect rect, QRect fill) }); } -QPixmap PrepareStaticImage(const QString &path) { - auto image = App::readImage(path, nullptr, false); +QPixmap PrepareStaticImage(QImage image) { #if defined Q_OS_MAC && !defined OS_MAC_OLD if (image.width() > kMaxDisplayImageSize || image.height() > kMaxDisplayImageSize) { @@ -212,6 +211,14 @@ QPixmap PrepareStaticImage(const QString &path) { return App::pixmapFromImageInPlace(std::move(image)); } +QPixmap PrepareStaticImage(const QString &path) { + return PrepareStaticImage(App::readImage(path, nullptr, false)); +} + +QPixmap PrepareStaticImage(const QByteArray &bytes) { + return PrepareStaticImage(App::readImage(bytes, nullptr, false)); +} + } // namespace struct OverlayWidget::SharedMedia { @@ -2304,6 +2311,11 @@ void OverlayWidget::displayDocument( _staticContent = PrepareStaticImage(path); _touchbarDisplay.fire(TouchBarItemType::Photo); } + } else if (!_documentMedia->bytes().isEmpty()) { + _staticContent = PrepareStaticImage(_documentMedia->bytes()); + if (!_staticContent.isNull()) { + _touchbarDisplay.fire(TouchBarItemType::Photo); + } } location.accessDisable(); } From 4e5c81dac2096c3773b5d610cc1b3c9fa8713c8d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 16:25:38 +0400 Subject: [PATCH 238/396] Correctly handle 'min' group call participant updates. --- Telegram/Resources/tl/api.tl | 2 +- .../SourceFiles/calls/calls_group_call.cpp | 4 ++++ Telegram/SourceFiles/data/data_group_call.cpp | 22 ++++++++++++++----- Telegram/SourceFiles/data/data_group_call.h | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 1d57ee601..3e84689f1 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1196,7 +1196,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; +groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 4b190dde5..9bdbcc2c6 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -595,6 +595,10 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { const auto handleOtherParticipants = [=]( const MTPDgroupCallParticipant &data) { + if (data.is_min()) { + // No real information about mutedByMe or my custom volume. + return; + } const auto user = _peer->owner().user(data.vuser_id().v); const auto participant = LookupParticipant(_peer, _id, user); if (!participant) { diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 75bf646f1..cc280bb42 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -276,17 +276,25 @@ void GroupCall::applyParticipantsSlice( && ((was ? was->speaking : false) || (!amInCall && (lastActive + speakingAfterActive > now))); - const auto defaultVolume = Calls::Group::kDefaultVolume; + const auto volume = (was && data.is_min()) + ? was->volume + : data.vvolume().value_or(Calls::Group::kDefaultVolume); + const auto mutedByMe = (was && data.is_min()) + ? was->mutedByMe + : data.is_muted_by_you(); + const auto onlyMinLoaded = data.is_min() + && (!was || was->onlyMinLoaded); const auto value = Participant{ .user = user, .date = data.vdate().v, .lastActive = lastActive, .ssrc = uint32(data.vsource().v), - .volume = data.vvolume().value_or(defaultVolume), + .volume = volume, .speaking = canSelfUnmute && (was ? was->speaking : false), .muted = data.is_muted(), - .mutedByMe = data.is_muted_by_you(), + .mutedByMe = mutedByMe, .canSelfUnmute = canSelfUnmute, + .onlyMinLoaded = onlyMinLoaded, }; if (i == end(_participants)) { _userBySsrc.emplace(value.ssrc, user); @@ -358,11 +366,13 @@ void GroupCall::applyActiveUpdate( not_null{ userLoaded }, &Participant::user) : _participants.end(); - if (i == end(_participants)) { + const auto notFound = (i == end(_participants)); + const auto loadByUserId = notFound || i->onlyMinLoaded; + if (loadByUserId) { _unknownSpokenUids[userId] = when; requestUnknownParticipants(); - return; - } else if (!i->canSelfUnmute) { + } + if (notFound || !i->canSelfUnmute) { return; } const auto was = std::make_optional(*i); diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index da039bae2..fd01d3493 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -43,6 +43,7 @@ public: bool muted = false; bool mutedByMe = false; bool canSelfUnmute = false; + bool onlyMinLoaded = false; }; struct ParticipantUpdate { std::optional<Participant> was; From eb11185de7cb5784debda8b9ca8559c6a19c3974 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 16:57:42 +0400 Subject: [PATCH 239/396] Improve escaping of some data in HTML export. --- .../export/output/export_output_html.cpp | 71 +++++-------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 0f62a466e..3935bbdba 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -151,6 +151,7 @@ QByteArray SerializeList(const std::vector<QByteArray> &values) { } return QByteArray(); } + QByteArray MakeLinks(const QByteArray &value) { const auto domain = QByteArray("https://telegram.org/"); auto result = QByteArray(); @@ -190,27 +191,6 @@ QByteArray MakeLinks(const QByteArray &value) { return result; } -void SerializeMultiline( - QByteArray &appendTo, - const QByteArray &value, - int newline) { - const auto data = value.data(); - auto offset = 0; - do { - appendTo.append("> "); - const auto win = (newline > 0 && *(data + newline - 1) == '\r'); - if (win) --newline; - appendTo.append(data + offset, newline - offset).append(kLineBreak); - if (win) ++newline; - offset = newline + 1; - newline = value.indexOf('\n', offset); - } while (newline > 0); - if (const auto size = value.size(); size > offset) { - appendTo.append("> "); - appendTo.append(data + offset, size - offset).append(kLineBreak); - } -} - QByteArray JoinList( const QByteArray &separator, const std::vector<QByteArray> &list) { @@ -294,31 +274,6 @@ QByteArray FormatText( }) | ranges::to_vector); } -QByteArray SerializeKeyValue( - std::vector<std::pair<QByteArray, QByteArray>> &&values) { - auto result = QByteArray(); - for (const auto &[key, value] : values) { - if (value.isEmpty()) { - continue; - } - result.append(key); - if (const auto newline = value.indexOf('\n'); newline >= 0) { - result.append(':').append(kLineBreak); - SerializeMultiline(result, value, newline); - } else { - result.append(": ").append(value).append(kLineBreak); - } - } - return result; -} - -QByteArray SerializeBlockquote( - std::vector<std::pair<QByteArray, QByteArray>> &&values) { - return "<blockquote>" - + SerializeKeyValue(std::move(values)) - + "</blockquote>"; -} - Data::Utf8String FormatUsername(const Data::Utf8String &username) { return username.isEmpty() ? username : ('@' + username); } @@ -766,7 +721,9 @@ QByteArray HtmlWriter::Wrap::pushUserpic(const UserpicData &userpic) { "line-height: " + size)); auto character = [](const QByteArray &from) { const auto utf = QString::fromUtf8(from).trimmed(); - return utf.isEmpty() ? QByteArray() : utf.mid(0, 1).toUtf8(); + return utf.isEmpty() + ? QByteArray() + : SerializeString(utf.mid(0, 1).toUtf8()); }; result.append(character(userpic.firstName)); result.append(character(userpic.lastName)); @@ -997,16 +954,20 @@ auto HtmlWriter::Wrap::pushMessage( const auto serviceText = v::match(message.action.content, [&]( const ActionChatCreate &data) { return serviceFrom - + " created group «" + data.title + "»" + + " created group «" + + SerializeString(data.title) + + "»" + (data.userIds.empty() ? QByteArray() : " with members " + peers.wrapUserNames(data.userIds)); }, [&](const ActionChatEditTitle &data) { return isChannel - ? ("Channel title changed to «" + data.title + "»") + ? ("Channel title changed to «" + + SerializeString(data.title) + + "»") : (serviceFrom - + " changed group title to «" - + data.title + + " fchanged group title to «" + + SerializeString(data.title) + "»"); }, [&](const ActionChatEditPhoto &data) { return isChannel @@ -1029,14 +990,16 @@ auto HtmlWriter::Wrap::pushMessage( + " joined group by link from " + peers.wrapUserName(data.inviterId); }, [&](const ActionChannelCreate &data) { - return "Channel «" + data.title + "» created"; + return "Channel «" + + SerializeString(data.title) + + "» created"; }, [&](const ActionChatMigrateTo &data) { return serviceFrom + " converted this group to a supergroup"; }, [&](const ActionChannelMigrateFrom &data) { return serviceFrom + " converted a basic group to this supergroup " - + "«" + data.title + "»"; + + "«" + SerializeString(data.title) + "»"; }, [&](const ActionPinMessage &data) { return serviceFrom + " pinned " @@ -1062,7 +1025,7 @@ auto HtmlWriter::Wrap::pushMessage( return data.message; }, [&](const ActionBotAllowed &data) { return "You allowed this bot to message you when you logged in on " - + data.domain; + + SerializeString(data.domain); }, [&](const ActionSecureValuesSent &data) { auto list = std::vector<QByteArray>(); for (const auto type : data.types) { From cb2d77d3862dbe5cdf959dfb04146ad036e61475 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 17:51:01 +0400 Subject: [PATCH 240/396] Implement SingleChoiceBox using Ui::GenericBox. --- .../SourceFiles/boxes/single_choice_box.cpp | 52 +++++--------- .../SourceFiles/boxes/single_choice_box.h | 42 ++++------- .../SourceFiles/settings/settings_calls.cpp | 72 +++++++++++-------- .../SourceFiles/settings/settings_calls.h | 13 ++-- 4 files changed, 82 insertions(+), 97 deletions(-) diff --git a/Telegram/SourceFiles/boxes/single_choice_box.cpp b/Telegram/SourceFiles/boxes/single_choice_box.cpp index ea2cf9a10..718687be1 100644 --- a/Telegram/SourceFiles/boxes/single_choice_box.cpp +++ b/Telegram/SourceFiles/boxes/single_choice_box.cpp @@ -16,56 +16,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_layers.h" -SingleChoiceBox::SingleChoiceBox( - QWidget*, - rpl::producer<QString> title, - const std::vector<QString> &optionTexts, - int initialSelection, - Fn<void(int)> callback, - const style::Checkbox *st, - const style::Radio *radioSt) -: _title(std::move(title)) -, _optionTexts(optionTexts) -, _initialSelection(initialSelection) -, _callback(callback) -, _st(st ? *st : st::defaultBoxCheckbox) -, _radioSt(radioSt ? *radioSt : st::defaultRadio) { -} +void SingleChoiceBox( + not_null<Ui::GenericBox*> box, + SingleChoiceBoxArgs &&args) { + box->setTitle(std::move(args.title)); -void SingleChoiceBox::prepare() { - setTitle(std::move(_title)); + box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); }); - addButton(tr::lng_box_ok(), [=] { closeBox(); }); + const auto group = std::make_shared<Ui::RadiobuttonGroup>( + args.initialSelection); - const auto group = std::make_shared<Ui::RadiobuttonGroup>(_initialSelection); - - const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); - content->add(object_ptr<Ui::FixedHeightWidget>( - content, + const auto layout = box->verticalLayout(); + layout->add(object_ptr<Ui::FixedHeightWidget>( + layout, st::boxOptionListPadding.top() + st::autolockButton.margin.top())); auto &&ints = ranges::view::ints(0, ranges::unreachable); - for (const auto &[i, text] : ranges::view::zip(ints, _optionTexts)) { - content->add( + for (const auto &[i, text] : ranges::view::zip(ints, args.options)) { + layout->add( object_ptr<Ui::Radiobutton>( - content, + layout, group, i, text, - _st, - _radioSt), + args.st ? *args.st : st::defaultBoxCheckbox, + args.radioSt ? *args.radioSt : st::defaultRadio), QMargins( st::boxPadding.left() + st::boxOptionListPadding.left(), 0, st::boxPadding.right(), st::boxOptionListSkip)); } + const auto callback = args.callback.value(); group->setChangedCallback([=](int value) { - const auto weak = Ui::MakeWeak(this); - _callback(value); + const auto weak = Ui::MakeWeak(box); + callback(value); if (weak) { - closeBox(); + box->closeBox(); } }); - setDimensionsToContent(st::boxWidth, content); } - diff --git a/Telegram/SourceFiles/boxes/single_choice_box.h b/Telegram/SourceFiles/boxes/single_choice_box.h index 2a83c8e29..4964d3b05 100644 --- a/Telegram/SourceFiles/boxes/single_choice_box.h +++ b/Telegram/SourceFiles/boxes/single_choice_box.h @@ -7,38 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "boxes/abstract_box.h" -#include <vector> - -namespace Ui { -class Radiobutton; -} // namespace Ui +#include "ui/layers/generic_box.h" +#include "base/required.h" namespace style { struct Checkbox; +struct Radio; } // namespace style -class SingleChoiceBox : public Ui::BoxContent { -public: - SingleChoiceBox( - QWidget*, - rpl::producer<QString> title, - const std::vector<QString> &optionTexts, - int initialSelection, - Fn<void(int)> callback, - const style::Checkbox *st = nullptr, - const style::Radio *radioSt = nullptr); - -protected: - void prepare() override; - -private: - rpl::producer<QString> _title; - std::vector<QString> _optionTexts; - int _initialSelection = 0; - Fn<void(int)> _callback; - const style::Checkbox &_st; - const style::Radio &_radioSt; +struct SingleChoiceBoxArgs { + template <typename T> + using required = base::required<T>; + required<rpl::producer<QString>> title; + const std::vector<QString> &options; + int initialSelection = 0; + required<Fn<void(int)>> callback; + const style::Checkbox *st = nullptr; + const style::Radio *radioSt = nullptr; }; +void SingleChoiceBox( + not_null<Ui::GenericBox*> box, + SingleChoiceBoxArgs &&args); diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index 94d981359..dc0d49eff 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -124,11 +124,14 @@ void Calls::setupContent() { call->setCurrentVideoDevice(deviceId); } }); - Ui::show(Box<SingleChoiceBox>( - tr::lng_settings_call_camera(), - options, - currentOption, - save)); + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + SingleChoiceBox(box, { + .title = tr::lng_settings_call_camera(), + .options = options, + .initialSelection = currentOption, + .callback = save, + }); + })); }); const auto bubbleWrap = content->add(object_ptr<Ui::RpWidget>(content)); const auto bubble = content->lifetime().make_state<::Calls::VideoBubble>( @@ -366,7 +369,7 @@ QString CurrentAudioInputName() { : tr::lng_settings_call_device_default(tr::now); } -object_ptr<SingleChoiceBox> ChooseAudioOutputBox( +object_ptr<Ui::GenericBox> ChooseAudioOutputBox( Fn<void(QString id, QString name)> chosen, const style::Checkbox *st, const style::Radio *radioSt) { @@ -390,16 +393,19 @@ object_ptr<SingleChoiceBox> ChooseAudioOutputBox( Core::App().calls().setCurrentAudioDevice(false, deviceId); chosen(deviceId, options[option]); }; - return Box<SingleChoiceBox>( - tr::lng_settings_call_output_device(), - options, - currentOption, - save, - st, - radioSt); + return Box([=](not_null<Ui::GenericBox*> box) { + SingleChoiceBox(box, { + .title = tr::lng_settings_call_output_device(), + .options = options, + .initialSelection = currentOption, + .callback = save, + .st = st, + .radioSt = radioSt, + }); + }); } -object_ptr<SingleChoiceBox> ChooseAudioInputBox( +object_ptr<Ui::GenericBox> ChooseAudioInputBox( Fn<void(QString id, QString name)> chosen, const style::Checkbox *st, const style::Radio *radioSt) { @@ -423,16 +429,19 @@ object_ptr<SingleChoiceBox> ChooseAudioInputBox( Core::App().calls().setCurrentAudioDevice(true, deviceId); chosen(deviceId, options[option]); }; - return Box<SingleChoiceBox>( - tr::lng_settings_call_input_device(), - options, - currentOption, - save, - st, - radioSt); + return Box([=](not_null<Ui::GenericBox*> box) { + SingleChoiceBox(box, { + .title = tr::lng_settings_call_input_device(), + .options = options, + .initialSelection = currentOption, + .callback = save, + .st = st, + .radioSt = radioSt, + }); + }); } - -//object_ptr<SingleChoiceBox> ChooseAudioBackendBox( +// +//object_ptr<Ui::GenericBox> ChooseAudioBackendBox( // const style::Checkbox *st, // const style::Radio *radioSt) { // const auto &settings = Core::App().settings(); @@ -451,13 +460,16 @@ object_ptr<SingleChoiceBox> ChooseAudioInputBox( // Core::App().saveSettings(); // App::restart(); // }; -// return Box<SingleChoiceBox>( -// rpl::single<QString>("Calls audio backend"), -// options, -// currentOption, -// save, -// st, -// radioSt); +// return Box([=](not_null<Ui::GenericBox*> box) { +// SingleChoiceBox(box, { +// .title = rpl::single<QString>("Calls audio backend"), +// .options = options, +// .initialSelection = currentOption, +// .callback = save, +// .st = st, +// .radioSt = radioSt, +// }); +// }); //} } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_calls.h b/Telegram/SourceFiles/settings/settings_calls.h index 068208242..679b167fa 100644 --- a/Telegram/SourceFiles/settings/settings_calls.h +++ b/Telegram/SourceFiles/settings/settings_calls.h @@ -22,14 +22,13 @@ class Call; namespace Ui { class LevelMeter; +class GenericBox; } // namespace Ui namespace Webrtc { class AudioInputTester; } // namespace Webrtc -class SingleChoiceBox; - namespace Settings { class Calls : public Section { @@ -61,17 +60,17 @@ inline constexpr auto kMicTestAnimationDuration = crl::time(200); [[nodiscard]] QString CurrentAudioOutputName(); [[nodiscard]] QString CurrentAudioInputName(); -[[nodiscard]] object_ptr<SingleChoiceBox> ChooseAudioOutputBox( +[[nodiscard]] object_ptr<Ui::GenericBox> ChooseAudioOutputBox( Fn<void(QString id, QString name)> chosen, const style::Checkbox *st = nullptr, const style::Radio *radioSt = nullptr); -[[nodiscard]] object_ptr<SingleChoiceBox> ChooseAudioInputBox( +[[nodiscard]] object_ptr<Ui::GenericBox> ChooseAudioInputBox( Fn<void(QString id, QString name)> chosen, const style::Checkbox *st = nullptr, const style::Radio *radioSt = nullptr); -[[nodiscard]] object_ptr<SingleChoiceBox> ChooseAudioBackendBox( - const style::Checkbox *st = nullptr, - const style::Radio *radioSt = nullptr); +//[[nodiscard]] object_ptr<Ui::GenericBox> ChooseAudioBackendBox( +// const style::Checkbox *st = nullptr, +// const style::Radio *radioSt = nullptr); } // namespace Settings From 34ec1c371ca79a522c31655078955bc0ff91578a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 19:10:59 +0400 Subject: [PATCH 241/396] Return invite-link-only box. --- .../boxes/peers/edit_peer_info_box.cpp | 30 +++++---- .../boxes/peers/edit_peer_type_box.cpp | 64 +++++++++++++------ .../boxes/peers/edit_peer_type_box.h | 9 ++- 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 5869bb67c..54dfa5334 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -332,6 +332,7 @@ private: void showEditLinkedChatBox(); void fillPrivacyTypeButton(); void fillLinkedChatButton(); + void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); void fillManageSection(); @@ -640,8 +641,6 @@ void Controller::refreshHistoryVisibility(anim::type animated) { void Controller::showEditPeerTypeBox( std::optional<rpl::producer<QString>> error) { - Expects(_privacySavedValue.has_value()); - const auto boxCallback = crl::guard(this, [=]( Privacy checked, QString publicLink) { _privacyTypeUpdates.fire(std::move(checked)); @@ -654,7 +653,7 @@ void Controller::showEditPeerTypeBox( _peer, _channelHasLocationOriginalValue, boxCallback, - *_privacySavedValue, + _privacySavedValue, _usernameSavedValue, error), Ui::LayerOption::KeepOther); @@ -800,6 +799,20 @@ void Controller::fillLinkedChatButton() { _linkedChatUpdates.fire_copy(*_linkedChatSavedValue); } +void Controller::fillInviteLinkButton() { + Expects(_controls.buttonsLayout != nullptr); + + const auto buttonCallback = [=] { + Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther); + }; + + AddButtonWithText( + _controls.buttonsLayout, + tr::lng_profile_invite_link_section(), + rpl::single(QString()), //Empty text. + buttonCallback); +} + void Controller::fillSignaturesButton() { Expects(_controls.buttonsLayout != nullptr); @@ -880,13 +893,6 @@ void Controller::fillManageSection() { ? channel->canEditUsername() : chat->canEditUsername(); }(); - const auto canEditInviteLink = [&] { - return isChannel - ? (channel->amCreator() - || (channel->adminRights() & ChatAdminRight::f_invite_users)) - : (chat->amCreator() - || (chat->adminRights() & ChatAdminRight::f_invite_users)); - }(); const auto canEditSignatures = [&] { return isChannel ? (channel->canEditSignatures() && !channel->isMegagroup()) @@ -953,6 +959,8 @@ void Controller::fillManageSection() { if (canEditUsername) { fillPrivacyTypeButton(); + } else if (canEditInviteLinks) { + fillInviteLinkButton(); } if (canViewOrEditLinkedChat) { fillLinkedChatButton(); @@ -965,7 +973,7 @@ void Controller::fillManageSection() { } if (canEditPreHistoryHidden || canEditSignatures - || canEditInviteLink + || canEditInviteLinks || canViewOrEditLinkedChat || canEditUsername) { AddSkip( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 231b385b8..bc33af502 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -64,7 +64,7 @@ public: not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, bool useLocationPhrases, - Privacy privacySavedValue, + std::optional<Privacy> privacySavedValue, std::optional<QString> usernameSavedValue); void createContent(); @@ -72,7 +72,9 @@ public: void setFocusUsername(); rpl::producer<QString> getTitle() { - return _isGroup + return !_privacySavedValue + ? tr::lng_create_invite_link_title() + : _isGroup ? tr::lng_manage_peer_group_type() : tr::lng_manage_peer_channel_type(); } @@ -120,7 +122,7 @@ private: void fillPrivaciesButtons( not_null<Ui::VerticalLayout*> parent, - Privacy savedValue); + std::optional<Privacy> savedValue); void addRoundButton( not_null<Ui::VerticalLayout*> container, Privacy value, @@ -132,7 +134,7 @@ private: not_null<PeerData*> _peer; MTP::Sender _api; - Privacy _privacySavedValue = Privacy(); + std::optional<Privacy> _privacySavedValue; std::optional<QString> _usernameSavedValue; bool _useLocationPhrases = false; @@ -153,7 +155,7 @@ Controller::Controller( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, bool useLocationPhrases, - Privacy privacySavedValue, + std::optional<Privacy> privacySavedValue, std::optional<QString> usernameSavedValue) : _peer(peer) , _api(&_peer->session().mtp()) @@ -172,10 +174,14 @@ void Controller::createContent() { fillPrivaciesButtons(_wrap, _privacySavedValue); // Skip. - _wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap)); + if (_privacySavedValue) { + _wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap)); + } // _wrap->add(createInviteLinkBlock()); - _wrap->add(createUsernameEdit()); + if (_privacySavedValue) { + _wrap->add(createUsernameEdit()); + } //using namespace Settings; // #TODO links //AddSkip(_wrap.get()); @@ -192,15 +198,20 @@ void Controller::createContent() { //AddSkip(_wrap.get()); //AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); - if (_controls.privacy->value() == Privacy::NoUsername) { - checkUsernameAvailability(); + if (_controls.privacy) { + if (_controls.privacy->value() == Privacy::NoUsername) { + checkUsernameAvailability(); + } + const auto forShowing = _privacySavedValue.value_or(Privacy::NoUsername); + _controls.inviteLinkWrap->toggle( + (forShowing != Privacy::HasUsername), + anim::type::instant); + _controls.usernameWrap->toggle( + (forShowing == Privacy::HasUsername), + anim::type::instant); + } else { + _controls.inviteLinkWrap->show(anim::type::instant); } - _controls.inviteLinkWrap->toggle( - (_privacySavedValue != Privacy::HasUsername), - anim::type::instant); - _controls.usernameWrap->toggle( - (_privacySavedValue == Privacy::HasUsername), - anim::type::instant); } void Controller::addRoundButton( @@ -228,7 +239,7 @@ void Controller::addRoundButton( void Controller::fillPrivaciesButtons( not_null<Ui::VerticalLayout*> parent, - Privacy savedValue) { + std::optional<Privacy> savedValue) { const auto canEditUsername = [&] { if (const auto chat = _peer->asChat()) { return chat->canEditUsername(); @@ -251,7 +262,8 @@ void Controller::fillPrivaciesButtons( const auto isPublic = _peer->isChannel() && _peer->asChannel()->hasUsername(); _controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>( - savedValue); + savedValue.value_or( + isPublic ? Privacy::HasUsername : Privacy::NoUsername)); addRoundButton( container, @@ -546,9 +558,11 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { const auto container = result->entity(); using namespace Settings; - AddSkip(container); + if (_privacySavedValue) { + AddSkip(container); - AddSubsectionTitle(container, tr::lng_create_invite_link_title()); + AddSubsectionTitle(container, tr::lng_create_invite_link_title()); + } // tr::lng_create_permanent_link_title()); // #TODO links AddPermanentLinkBlock(container, _peer); @@ -585,7 +599,7 @@ EditPeerTypeBox::EditPeerTypeBox( not_null<PeerData*> peer, bool useLocationPhrases, std::optional<FnMut<void(Privacy, QString)>> savedCallback, - Privacy privacySaved, + std::optional<Privacy> privacySaved, std::optional<QString> usernameSaved, std::optional<rpl::producer<QString>> usernameError) : _peer(peer) @@ -596,6 +610,12 @@ EditPeerTypeBox::EditPeerTypeBox( , _usernameError(usernameError) { } +EditPeerTypeBox::EditPeerTypeBox( + QWidget*, + not_null<PeerData*> peer) +: EditPeerTypeBox(nullptr, peer, {}, {}, {}, {}, {}) { +} + void EditPeerTypeBox::setInnerFocus() { _focusRequests.fire({}); } @@ -641,8 +661,10 @@ void EditPeerTypeBox::prepare() { : QString()); // We don't need username with private type. closeBox(); }); + addButton(tr::lng_cancel(), [=] { closeBox(); }); + } else { + addButton(tr::lng_close(), [=] { closeBox(); }); } - addButton(tr::lng_cancel(), [=] { closeBox(); }); setDimensionsToContent(st::boxWideWidth, content.data()); setInnerWidget(std::move(content)); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 8aae656e2..0d4a828b1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -37,10 +37,15 @@ public: not_null<PeerData*> peer, bool useLocationPhrases, std::optional<FnMut<void(Privacy, QString)>> savedCallback, - Privacy privacySaved, + std::optional<Privacy> privacySaved, std::optional<QString> usernameSaved, std::optional<rpl::producer<QString>> usernameError = {}); + // For invite link only. + EditPeerTypeBox( + QWidget*, + not_null<PeerData*> peer); + protected: void prepare() override; void setInnerFocus() override; @@ -50,7 +55,7 @@ private: bool _useLocationPhrases = false; std::optional<FnMut<void(Privacy, QString)>> _savedCallback; - Privacy _privacySavedValue = Privacy(); + std::optional<Privacy> _privacySavedValue; std::optional<QString> _usernameSavedValue; std::optional<rpl::producer<QString>> _usernameError; From 5538c5eace0ec5d3ae99f55f567b88ae442c6295 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 1 Feb 2021 22:30:00 +0400 Subject: [PATCH 242/396] Add 'Invite via Link' button to Add Members box. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/boxes.style | 23 ++++++++++ Telegram/SourceFiles/boxes/peer_list_box.cpp | 14 ++++-- Telegram/SourceFiles/boxes/peer_list_box.h | 6 +++ .../boxes/peers/add_participants_box.cpp | 46 +++++++++++++++++++ .../boxes/peers/add_participants_box.h | 2 + .../boxes/peers/edit_peer_type_box.cpp | 15 +++--- .../SourceFiles/boxes/single_choice_box.h | 2 +- .../SourceFiles/calls/calls_group_panel.cpp | 4 ++ 9 files changed, 101 insertions(+), 12 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index eb1d8f846..ba9334a8e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -827,6 +827,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_profile_delete_contact" = "Delete"; "lng_profile_set_group_photo" = "Set Photo"; "lng_profile_add_participant" = "Add Members"; +"lng_profile_add_via_link" = "Invite via Link"; "lng_profile_view_channel" = "View Channel"; "lng_profile_view_discussion" = "View discussion"; "lng_profile_join_channel" = "Join Channel"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 53d827c6c..1c76ebaeb 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -906,6 +906,29 @@ pollResultsShowMore: SettingsButton(defaultSettingsButton) { ripple: defaultRippleAnimation; } +inviteViaLinkButton: SettingsButton(defaultSettingsButton) { + textFg: lightButtonFg; + textFgOver: lightButtonFgOver; + textBg: windowBg; + textBgOver: windowBgOver; + + font: font(14px semibold); + + height: 20px; + padding: margins(74px, 8px, 8px, 9px); + + ripple: defaultRippleAnimation; +} +inviteViaLinkIcon: icon {{ "info/edit/group_manage_links", lightButtonFg }}; +inviteViaLinkIconPosition: point(23px, 2px); +peerListWithInviteViaLink: PeerList(peerListBox) { + padding: margins( + 0px, + 0px, + 0px, + membersMarginBottom); +} + scheduleHeight: 95px; scheduleDateTop: 38px; scheduleDateField: InputField(defaultInputField) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 281de0b95..08fd4104c 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -1161,11 +1161,7 @@ void PeerListContent::enterEventHook(QEvent *e) { void PeerListContent::leaveEventHook(QEvent *e) { setMouseTracking(false); - if (_mouseSelection) { - setSelected(Selected()); - _mouseSelection = false; - _lastMousePosition = std::nullopt; - } + mouseLeftGeometry(); } void PeerListContent::mouseMoveEvent(QMouseEvent *e) { @@ -1500,6 +1496,14 @@ void PeerListContent::clearSelection() { setSelected(Selected()); } +void PeerListContent::mouseLeftGeometry() { + if (_mouseSelection) { + setSelected(Selected()); + _mouseSelection = false; + _lastMousePosition = std::nullopt; + } +} + void PeerListContent::loadProfilePhotos() { if (_visibleTop >= _visibleBottom) return; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 6a2b87786..81069bca3 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -261,6 +261,7 @@ public: virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0; virtual void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) = 0; virtual void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) = 0; + virtual void peerListMouseLeftGeometry() = 0; virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0; virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0; virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0; @@ -541,6 +542,8 @@ public: void setHideEmpty(bool hide); void refreshRows(); + void mouseLeftGeometry(); + void setSearchMode(PeerListSearchMode mode); void changeCheckState( not_null<PeerListRow*> row, @@ -804,6 +807,9 @@ public: void peerListSetSearchMode(PeerListSearchMode mode) override { _content->setSearchMode(mode); } + void peerListMouseLeftGeometry() override { + _content->mouseLeftGeometry(); + } void peerListSortRows( Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) override { _content->reorderRows([&]( diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 9ac503df3..6a60fe641 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/add_participants_box.h" #include "boxes/peers/edit_participant_box.h" +#include "boxes/peers/edit_peer_type_box.h" #include "boxes/confirm_box.h" #include "lang/lang_keys.h" #include "data/data_channel.h" @@ -18,15 +19,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "history/history.h" #include "dialogs/dialogs_indexed_list.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/padding_wrap.h" #include "base/unixtime.h" #include "main/main_session.h" #include "mtproto/mtproto_config.h" #include "mainwidget.h" #include "mainwindow.h" #include "window/window_session_controller.h" +#include "info/profile/info_profile_icon.h" #include "apiwrap.h" #include "facades.h" // Ui::showPeerHistory #include "app.h" +#include "styles/style_boxes.h" namespace { @@ -68,6 +73,9 @@ AddParticipantsBoxController::AddParticipantsBoxController( : ContactsBoxController(&peer->session()) , _peer(peer) , _alreadyIn(std::move(alreadyIn)) { + if (needsInviteLinkButton()) { + setStyleOverrides(&st::peerListWithInviteViaLink); + } subscribeToMigration(); } @@ -166,6 +174,44 @@ void AddParticipantsBoxController::updateTitle() { ).arg(session().serverConfig().megagroupSizeMax); delegate()->peerListSetTitle(tr::lng_profile_add_participant()); delegate()->peerListSetAdditionalTitle(rpl::single(additional)); + + addInviteLinkButton(); +} + +bool AddParticipantsBoxController::needsInviteLinkButton() { + if (!_peer) { + return false; + } else if (const auto channel = _peer->asChannel()) { + return channel->canHaveInviteLink(); + } + return _peer->asChat()->canHaveInviteLink(); +} + +void AddParticipantsBoxController::addInviteLinkButton() { + if (!needsInviteLinkButton()) { + return; + } + auto button = object_ptr<Ui::PaddingWrap<Ui::SettingsButton>>( + nullptr, + object_ptr<Ui::SettingsButton>( + nullptr, + tr::lng_profile_add_via_link(), + st::inviteViaLinkButton), + style::margins(0, st::membersMarginTop, 0, 0)); + object_ptr<Info::Profile::FloatingIcon>( + button->entity(), + st::inviteViaLinkIcon, + st::inviteViaLinkIconPosition); + button->entity()->setClickedCallback([=] { + Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther); + }); + button->entity()->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::Enter); + }) | rpl::start_with_next([=] { + delegate()->peerListMouseLeftGeometry(); + }, button->lifetime()); + delegate()->peerListSetAboveWidget(std::move(button)); } bool AddParticipantsBoxController::inviteSelectedUsers( diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.h b/Telegram/SourceFiles/boxes/peers/add_participants_box.h index 7bba41119..8098134b7 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.h @@ -44,6 +44,7 @@ protected: void prepareViewHook() override; std::unique_ptr<PeerListRow> createRow( not_null<UserData*> user) override; + virtual bool needsInviteLinkButton(); private: static void Start( @@ -52,6 +53,7 @@ private: base::flat_set<not_null<UserData*>> &&alreadyIn, bool justCreated); + void addInviteLinkButton(); bool inviteSelectedUsers(not_null<PeerListBox*> box) const; void subscribeToMigration(); int alreadyInCount() const; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index bc33af502..d587ecbca 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -133,6 +133,8 @@ private: QString inviteLinkText(); not_null<PeerData*> _peer; + bool _linkOnly = false; + MTP::Sender _api; std::optional<Privacy> _privacySavedValue; std::optional<QString> _usernameSavedValue; @@ -158,6 +160,7 @@ Controller::Controller( std::optional<Privacy> privacySavedValue, std::optional<QString> usernameSavedValue) : _peer(peer) +, _linkOnly(!privacySavedValue.has_value()) , _api(&_peer->session().mtp()) , _privacySavedValue(privacySavedValue) , _usernameSavedValue(usernameSavedValue) @@ -174,12 +177,12 @@ void Controller::createContent() { fillPrivaciesButtons(_wrap, _privacySavedValue); // Skip. - if (_privacySavedValue) { + if (!_linkOnly) { _wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap)); } // _wrap->add(createInviteLinkBlock()); - if (_privacySavedValue) { + if (!_linkOnly) { _wrap->add(createUsernameEdit()); } @@ -198,7 +201,9 @@ void Controller::createContent() { //AddSkip(_wrap.get()); //AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); - if (_controls.privacy) { + if (_linkOnly) { + _controls.inviteLinkWrap->show(anim::type::instant); + } else { if (_controls.privacy->value() == Privacy::NoUsername) { checkUsernameAvailability(); } @@ -209,8 +214,6 @@ void Controller::createContent() { _controls.usernameWrap->toggle( (forShowing == Privacy::HasUsername), anim::type::instant); - } else { - _controls.inviteLinkWrap->show(anim::type::instant); } } @@ -248,7 +251,7 @@ void Controller::fillPrivaciesButtons( } Unexpected("Peer type in Controller::createPrivaciesEdit."); }(); - if (!canEditUsername) { + if (!canEditUsername || _linkOnly) { return; } diff --git a/Telegram/SourceFiles/boxes/single_choice_box.h b/Telegram/SourceFiles/boxes/single_choice_box.h index 4964d3b05..b33d13575 100644 --- a/Telegram/SourceFiles/boxes/single_choice_box.h +++ b/Telegram/SourceFiles/boxes/single_choice_box.h @@ -22,7 +22,7 @@ struct SingleChoiceBoxArgs { required<rpl::producer<QString>> title; const std::vector<QString> &options; int initialSelection = 0; - required<Fn<void(int)>> callback; + Fn<void(int)> callback; const style::Checkbox *st = nullptr; const style::Radio *radioSt = nullptr; }; diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 9a752a35d..dae98cb5b 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -99,6 +99,10 @@ private: std::unique_ptr<PeerListRow> createRow( not_null<UserData*> user) override; + bool needsInviteLinkButton() override { + return false; + } + const not_null<const base::flat_set<not_null<UserData*>>*> _inGroup; rpl::producer<not_null<UserData*>> _discoveredInGroup; From f7b72bffe2385756aa8a2f502991d11a2db0cb9b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 2 Feb 2021 17:39:51 +0400 Subject: [PATCH 243/396] Fix build. --- Telegram/SourceFiles/boxes/single_choice_box.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/single_choice_box.cpp b/Telegram/SourceFiles/boxes/single_choice_box.cpp index 718687be1..ec26c105a 100644 --- a/Telegram/SourceFiles/boxes/single_choice_box.cpp +++ b/Telegram/SourceFiles/boxes/single_choice_box.cpp @@ -46,7 +46,7 @@ void SingleChoiceBox( st::boxPadding.right(), st::boxOptionListSkip)); } - const auto callback = args.callback.value(); + const auto callback = args.callback; group->setChangedCallback([=](int value) { const auto weak = Ui::MakeWeak(box); callback(value); From fe8bc306459c080ba7e65c67b7ae01a046127eaa Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 2 Feb 2021 18:30:12 +0400 Subject: [PATCH 244/396] Use GCancellable to prevent crash in notificationShown --- .../linux/notifications_manager_linux.cpp | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 7df885b33..7afdd560c 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -272,6 +272,7 @@ public: private: GDBusConnection *_dbusConnection = nullptr; base::weak_ptr<Manager> _manager; + GCancellable *_cancellable = nullptr; QString _title; QString _body; @@ -316,6 +317,7 @@ NotificationData::NotificationData( NotificationId id, bool hideReplyButton) : _manager(manager) +, _cancellable(g_cancellable_new()) , _title(title) , _imageKey(GetImageKey(CurrentServerInformationValue().specVersion)) , _id(id) { @@ -461,6 +463,9 @@ NotificationData::~NotificationData() { g_variant_unref(value); } } + + g_cancellable_cancel(_cancellable); + g_object_unref(_cancellable); } void NotificationData::show() { @@ -509,7 +514,7 @@ void NotificationData::show() { nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - nullptr, + _cancellable, notificationShown, this); } @@ -518,6 +523,18 @@ void NotificationData::notificationShown( GObject *source_object, GAsyncResult *res, gpointer user_data) { + GError *error = nullptr; + + auto reply = g_dbus_connection_call_finish( + reinterpret_cast<GDBusConnection*>(source_object), + res, + &error); + + if (error && error->code == G_IO_ERROR_CANCELLED) { + g_error_free(error); + return; + } + const auto notificationData = reinterpret_cast<NotificationData*>( user_data); @@ -525,13 +542,6 @@ void NotificationData::notificationShown( return; } - GError *error = nullptr; - - auto reply = g_dbus_connection_call_finish( - notificationData->_dbusConnection, - res, - &error); - if (!error) { g_variant_get(reply, "(u)", ¬ificationData->_notificationId); g_variant_unref(reply); From bcc333c2e120414a06a982a8d99624fcdcd1513d Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 2 Feb 2021 17:03:00 +0400 Subject: [PATCH 245/396] Use QWindow::setCursor instead of QGuiApplication::setCursorOverride --- .../SourceFiles/window/window_title_qt.cpp | 162 ++++++------------ Telegram/SourceFiles/window/window_title_qt.h | 12 +- 2 files changed, 62 insertions(+), 112 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 53eae8e56..675159a6f 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_title_qt.h" #include "platform/platform_specific.h" -#include "base/platform/base_platform_info.h" #include "ui/platform/ui_platform_utility.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -16,11 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "styles/style_window.h" #include "styles/style_calls.h" // st::callShadow -#include "base/call_delayed.h" -#include <QtGui/QGuiApplication> +#include <QtCore/QCoreApplication> #include <QtGui/QWindow> -#include <QtWidgets/QApplication> namespace Window { namespace { @@ -79,7 +76,7 @@ TitleWidgetQt::TitleWidgetQt(QWidget *parent) } TitleWidgetQt::~TitleWidgetQt() { - restoreCursor(); + window()->windowHandle()->unsetCursor(); if (!_windowWasFrameless) { toggleFramelessWindow(false); @@ -90,10 +87,6 @@ TitleWidgetQt::~TitleWidgetQt() { } } -void TitleWidgetQt::toggleFramelessWindow(bool enabled) { - window()->windowHandle()->setFlag(Qt::FramelessWindowHint, enabled); -} - void TitleWidgetQt::init() { connect( window()->windowHandle(), @@ -125,17 +118,15 @@ void TitleWidgetQt::paintEvent(QPaintEvent *e) { Painter(this).fillRect(rect(), active ? _st.bgActive : _st.bg); } +void TitleWidgetQt::toggleFramelessWindow(bool enabled) { + window()->windowHandle()->setFlag(Qt::FramelessWindowHint, enabled); +} + void TitleWidgetQt::updateWindowExtents() { if (hasShadow()) { - if (!_maximizedState) { - Platform::SetWindowExtents( - window()->windowHandle(), - ShadowExtents()); - } else { - Platform::SetWindowExtents( - window()->windowHandle(), - QMargins()); - } + Platform::SetWindowExtents( + window()->windowHandle(), + resizeArea()); _extentsSet = true; } else if (_extentsSet) { @@ -222,18 +213,7 @@ void TitleWidgetQt::resizeEvent(QResizeEvent *e) { void TitleWidgetQt::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { - if ((crl::now() - _pressedForMoveTime) - < QApplication::doubleClickInterval()) { - if (_maximizedState) { - window()->setWindowState(Qt::WindowNoState); - } else { - window()->setWindowState(Qt::WindowMaximized); - } - } else { - _pressedForMove = true; - _pressedForMoveTime = crl::now(); - _pressedForMovePoint = e->windowPos().toPoint(); - } + _mousePressed = true; } else if (e->button() == Qt::RightButton) { Platform::ShowWindowMenu(window()->windowHandle()); } @@ -241,7 +221,21 @@ void TitleWidgetQt::mousePressEvent(QMouseEvent *e) { void TitleWidgetQt::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { - _pressedForMove = false; + _mousePressed = false; + } +} + +void TitleWidgetQt::mouseMoveEvent(QMouseEvent *e) { + if (_mousePressed) { + startMove(); + } +} + +void TitleWidgetQt::mouseDoubleClickEvent(QMouseEvent *e) { + if (_maximizedState) { + window()->setWindowState(Qt::WindowNoState); + } else { + window()->setWindowState(Qt::WindowMaximized); } } @@ -253,34 +247,18 @@ bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { const auto mouseEvent = static_cast<QMouseEvent*>(e); const auto currentPoint = mouseEvent->windowPos().toPoint(); const auto edges = edgesFromPos(currentPoint); - const auto dragDistance = QApplication::startDragDistance(); if (e->type() == QEvent::MouseMove && mouseEvent->buttons() == Qt::NoButton) { - if (_pressedForMove) { - _pressedForMove = false; - } - updateCursor(edges); } - if (e->type() == QEvent::MouseMove - && _pressedForMove - && ((currentPoint - _pressedForMovePoint).manhattanLength() - >= dragDistance)) { - return startMove(); - } - if (e->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton - && !_maximizedState) { + && edges) { return startResize(edges); } } - } else if (e->type() == QEvent::Leave) { - if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { - restoreCursor(); - } } else if (e->type() == QEvent::Move || e->type() == QEvent::Resize) { if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { @@ -346,83 +324,59 @@ void TitleWidgetQt::updateButtonsState() { : nullptr); } -int TitleWidgetQt::getResizeArea(Qt::Edge edge) const { - if (!hasShadow()) { - return st::windowResizeArea; +QMargins TitleWidgetQt::resizeArea() const { + if (_maximizedState) { + return QMargins(); + } else if (!hasShadow()) { + return QMargins( + st::windowResizeArea, + st::windowResizeArea, + st::windowResizeArea, + st::windowResizeArea); } - if (edge == Qt::LeftEdge) { - return ShadowExtents().left(); - } else if (edge == Qt::RightEdge) { - return ShadowExtents().right(); - } else if (edge == Qt::TopEdge) { - return ShadowExtents().top(); - } else if (edge == Qt::BottomEdge) { - return ShadowExtents().bottom(); - } - - return 0; + return ShadowExtents(); } -Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) { - if (pos.x() <= getResizeArea(Qt::LeftEdge)) { - if (pos.y() <= getResizeArea(Qt::TopEdge)) { +Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) const { + if (pos.x() <= resizeArea().left()) { + if (pos.y() <= resizeArea().top()) { return Qt::LeftEdge | Qt::TopEdge; - } else if (pos.y() - >= (window()->height() - getResizeArea(Qt::BottomEdge))) { + } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { return Qt::LeftEdge | Qt::BottomEdge; } return Qt::LeftEdge; - } else if (pos.x() - >= (window()->width() - getResizeArea(Qt::RightEdge))) { - if (pos.y() <= getResizeArea(Qt::TopEdge)) { + } else if (pos.x() >= (window()->width() - resizeArea().right())) { + if (pos.y() <= resizeArea().top()) { return Qt::RightEdge | Qt::TopEdge; - } else if (pos.y() - >= (window()->height() - getResizeArea(Qt::BottomEdge))) { + } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { return Qt::RightEdge | Qt::BottomEdge; } return Qt::RightEdge; - } else if (pos.y() <= getResizeArea(Qt::TopEdge)) { + } else if (pos.y() <= resizeArea().top()) { return Qt::TopEdge; - } else if (pos.y() - >= (window()->height() - getResizeArea(Qt::BottomEdge))) { + } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { return Qt::BottomEdge; } else { return Qt::Edges(); } } -void TitleWidgetQt::restoreCursor() { - if (_cursorOverriden) { - _cursorOverriden = false; - QGuiApplication::restoreOverrideCursor(); - } -} - void TitleWidgetQt::updateCursor(Qt::Edges edges) { - if (!edges || _maximizedState) { - restoreCursor(); - return; - } else if (!QGuiApplication::overrideCursor()) { - _cursorOverriden = false; - } - if (!_cursorOverriden) { - _cursorOverriden = true; - QGuiApplication::setOverrideCursor(QCursor()); - } - if (((edges & Qt::LeftEdge) && (edges & Qt::TopEdge)) || ((edges & Qt::RightEdge) && (edges & Qt::BottomEdge))) { - QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeFDiagCursor)); + window()->windowHandle()->setCursor(QCursor(Qt::SizeFDiagCursor)); } else if (((edges & Qt::LeftEdge) && (edges & Qt::BottomEdge)) || ((edges & Qt::RightEdge) && (edges & Qt::TopEdge))) { - QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeBDiagCursor)); + window()->windowHandle()->setCursor(QCursor(Qt::SizeBDiagCursor)); } else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) { - QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeHorCursor)); + window()->windowHandle()->setCursor(QCursor(Qt::SizeHorCursor)); } else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) { - QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeVerCursor)); + window()->windowHandle()->setCursor(QCursor(Qt::SizeVerCursor)); + } else { + window()->windowHandle()->unsetCursor(); } } @@ -441,17 +395,15 @@ bool TitleWidgetQt::startMove() { } bool TitleWidgetQt::startResize(Qt::Edges edges) { - if (edges) { - if (Platform::StartSystemResize(window()->windowHandle(), edges)) { - return true; - } + if (Platform::StartSystemResize(window()->windowHandle(), edges)) { + return true; + } #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - if (window()->windowHandle()->startSystemResize(edges)) { - return true; - } -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED + if (window()->windowHandle()->startSystemResize(edges)) { + return true; } +#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED return false; } diff --git a/Telegram/SourceFiles/window/window_title_qt.h b/Telegram/SourceFiles/window/window_title_qt.h index 00b65cc09..62a1f508e 100644 --- a/Telegram/SourceFiles/window/window_title_qt.h +++ b/Telegram/SourceFiles/window/window_title_qt.h @@ -34,6 +34,8 @@ protected: void resizeEvent(QResizeEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseDoubleClickEvent(QMouseEvent *e) override; bool eventFilter(QObject *obj, QEvent *e) override; @@ -49,10 +51,9 @@ private: void toggleFramelessWindow(bool enabled); bool hasShadow() const; - int getResizeArea(Qt::Edge edge) const; - Qt::Edges edgesFromPos(const QPoint &pos); + QMargins resizeArea() const; + Qt::Edges edgesFromPos(const QPoint &pos) const; void updateCursor(Qt::Edges edges); - void restoreCursor(); bool startMove(); bool startResize(Qt::Edges edges); @@ -65,11 +66,8 @@ private: bool _maximizedState = false; bool _activeState = false; bool _windowWasFrameless = false; - bool _cursorOverriden = false; bool _extentsSet = false; - bool _pressedForMove = false; - crl::time _pressedForMoveTime = 0; - QPoint _pressedForMovePoint; + bool _mousePressed = false; }; From 76457c1e52494746623e36fb9adb636b43cbfa52 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Wed, 3 Feb 2021 09:34:37 +0400 Subject: [PATCH 246/396] Restore QGuiApplication::setOverrideCursor usage QWindow::setCursor doesn't work as expected... --- .../SourceFiles/window/window_title_qt.cpp | 36 +++++++++++++++---- Telegram/SourceFiles/window/window_title_qt.h | 2 ++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 675159a6f..8b3e26df7 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_calls.h" // st::callShadow #include <QtCore/QCoreApplication> +#include <QtGui/QGuiApplication> #include <QtGui/QWindow> namespace Window { @@ -76,7 +77,7 @@ TitleWidgetQt::TitleWidgetQt(QWidget *parent) } TitleWidgetQt::~TitleWidgetQt() { - window()->windowHandle()->unsetCursor(); + restoreCursor(); if (!_windowWasFrameless) { toggleFramelessWindow(false); @@ -259,6 +260,10 @@ bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { return startResize(edges); } } + } else if (e->type() == QEvent::Leave) { + if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { + restoreCursor(); + } } else if (e->type() == QEvent::Move || e->type() == QEvent::Resize) { if (obj->isWidgetType() && window() == static_cast<QWidget*>(obj)) { @@ -365,18 +370,35 @@ Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) const { } void TitleWidgetQt::updateCursor(Qt::Edges edges) { + if (!edges) { + restoreCursor(); + return; + } else if (!QGuiApplication::overrideCursor()) { + _cursorOverriden = false; + } + + if (!_cursorOverriden) { + _cursorOverriden = true; + QGuiApplication::setOverrideCursor(QCursor()); + } + if (((edges & Qt::LeftEdge) && (edges & Qt::TopEdge)) || ((edges & Qt::RightEdge) && (edges & Qt::BottomEdge))) { - window()->windowHandle()->setCursor(QCursor(Qt::SizeFDiagCursor)); + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeFDiagCursor)); } else if (((edges & Qt::LeftEdge) && (edges & Qt::BottomEdge)) || ((edges & Qt::RightEdge) && (edges & Qt::TopEdge))) { - window()->windowHandle()->setCursor(QCursor(Qt::SizeBDiagCursor)); + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeBDiagCursor)); } else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) { - window()->windowHandle()->setCursor(QCursor(Qt::SizeHorCursor)); + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeHorCursor)); } else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) { - window()->windowHandle()->setCursor(QCursor(Qt::SizeVerCursor)); - } else { - window()->windowHandle()->unsetCursor(); + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeVerCursor)); + } +} + +void TitleWidgetQt::restoreCursor() { + if (_cursorOverriden) { + _cursorOverriden = false; + QGuiApplication::restoreOverrideCursor(); } } diff --git a/Telegram/SourceFiles/window/window_title_qt.h b/Telegram/SourceFiles/window/window_title_qt.h index 62a1f508e..e248ae97d 100644 --- a/Telegram/SourceFiles/window/window_title_qt.h +++ b/Telegram/SourceFiles/window/window_title_qt.h @@ -54,6 +54,7 @@ private: QMargins resizeArea() const; Qt::Edges edgesFromPos(const QPoint &pos) const; void updateCursor(Qt::Edges edges); + void restoreCursor(); bool startMove(); bool startResize(Qt::Edges edges); @@ -66,6 +67,7 @@ private: bool _maximizedState = false; bool _activeState = false; bool _windowWasFrameless = false; + bool _cursorOverriden = false; bool _extentsSet = false; bool _mousePressed = false; From 153c949a88509b145c47b011f3e5cfbf19160dd5 Mon Sep 17 00:00:00 2001 From: Nicholas Guriev <nicholas@guriev.su> Date: Wed, 3 Feb 2021 15:23:23 +0300 Subject: [PATCH 247/396] Clean up DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION --- Telegram/SourceFiles/calls/calls_call.cpp | 5 - Telegram/cmake/lib_tgcalls.cmake | 212 ++++--- Telegram/cmake/lib_tgvoip.cmake | 648 +--------------------- Telegram/lib_webrtc | 2 +- cmake | 2 +- 5 files changed, 107 insertions(+), 762 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 74bd55bdb..bdd8fdd53 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -48,10 +48,7 @@ constexpr auto kHangupTimeoutMs = 5000; constexpr auto kSha256Size = 32; const auto kDefaultVersion = "2.4.4"_q; -#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION const auto RegisterTag = tgcalls::Register<tgcalls::InstanceImpl>(); -//const auto RegisterTagReference = tgcalls::Register<tgcalls::InstanceImplReference>(); -#endif // DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION const auto RegisterTagLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>(); void AppendEndpoint( @@ -380,7 +377,6 @@ void Call::setupOutgoingVideo() { _videoOutgoing->setState(Webrtc::VideoState::Inactive); } else if (state != Webrtc::VideoState::Inactive) { // Paused not supported right now. -#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION Assert(state == Webrtc::VideoState::Active); if (!_videoCapture) { _videoCapture = _delegate->getVideoCapture(); @@ -390,7 +386,6 @@ void Call::setupOutgoingVideo() { _instance->setVideoCapture(_videoCapture); } _videoCapture->setState(tgcalls::VideoState::Active); -#endif // DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION } else if (_videoCapture) { _videoCapture->setState(tgcalls::VideoState::Inactive); } diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index cde4f6144..7a7e68ed0 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -25,103 +25,101 @@ PRIVATE Instance.h ) -if (NOT DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION) - nice_target_sources(lib_tgcalls ${tgcalls_loc} - PRIVATE - AudioDeviceHelper.cpp - AudioDeviceHelper.h - CodecSelectHelper.cpp - CodecSelectHelper.h - CryptoHelper.cpp - CryptoHelper.h - EncryptedConnection.cpp - EncryptedConnection.h - InstanceImpl.cpp - InstanceImpl.h - LogSinkImpl.cpp - LogSinkImpl.h - Manager.cpp - Manager.h - MediaManager.cpp - MediaManager.h - Message.cpp - Message.h - NetworkManager.cpp - NetworkManager.h - ThreadLocalObject.h - VideoCaptureInterface.cpp - VideoCaptureInterface.h - VideoCaptureInterfaceImpl.cpp - VideoCaptureInterfaceImpl.h - VideoCapturerInterface.h +nice_target_sources(lib_tgcalls ${tgcalls_loc} +PRIVATE + AudioDeviceHelper.cpp + AudioDeviceHelper.h + CodecSelectHelper.cpp + CodecSelectHelper.h + CryptoHelper.cpp + CryptoHelper.h + EncryptedConnection.cpp + EncryptedConnection.h + InstanceImpl.cpp + InstanceImpl.h + LogSinkImpl.cpp + LogSinkImpl.h + Manager.cpp + Manager.h + MediaManager.cpp + MediaManager.h + Message.cpp + Message.h + NetworkManager.cpp + NetworkManager.h + ThreadLocalObject.h + VideoCaptureInterface.cpp + VideoCaptureInterface.h + VideoCaptureInterfaceImpl.cpp + VideoCaptureInterfaceImpl.h + VideoCapturerInterface.h - group/GroupInstanceImpl.cpp - group/GroupInstanceImpl.h + group/GroupInstanceImpl.cpp + group/GroupInstanceImpl.h - platform/PlatformInterface.h + platform/PlatformInterface.h - # Android - platform/android/AndroidContext.cpp - platform/android/AndroidContext.h - platform/android/AndroidInterface.cpp - platform/android/AndroidInterface.h - platform/android/VideoCameraCapturer.cpp - platform/android/VideoCameraCapturer.h - platform/android/VideoCapturerInterfaceImpl.cpp - platform/android/VideoCapturerInterfaceImpl.h + # Android + platform/android/AndroidContext.cpp + platform/android/AndroidContext.h + platform/android/AndroidInterface.cpp + platform/android/AndroidInterface.h + platform/android/VideoCameraCapturer.cpp + platform/android/VideoCameraCapturer.h + platform/android/VideoCapturerInterfaceImpl.cpp + platform/android/VideoCapturerInterfaceImpl.h - # iOS / macOS - platform/darwin/DarwinInterface.h - platform/darwin/DarwinInterface.mm - platform/darwin/GLVideoView.h - platform/darwin/GLVideoView.mm - platform/darwin/TGRTCCVPixelBuffer.h - platform/darwin/TGRTCCVPixelBuffer.mm - platform/darwin/TGRTCDefaultVideoDecoderFactory.h - platform/darwin/TGRTCDefaultVideoDecoderFactory.mm - platform/darwin/TGRTCDefaultVideoEncoderFactory.h - platform/darwin/TGRTCDefaultVideoEncoderFactory.mm - platform/darwin/TGRTCVideoDecoderH264.h - platform/darwin/TGRTCVideoDecoderH264.mm - platform/darwin/TGRTCVideoDecoderH265.h - platform/darwin/TGRTCVideoDecoderH265.mm - platform/darwin/TGRTCVideoEncoderH264.h - platform/darwin/TGRTCVideoEncoderH264.mm - platform/darwin/TGRTCVideoEncoderH265.h - platform/darwin/TGRTCVideoEncoderH265.mm - platform/darwin/VideoCameraCapturer.h - platform/darwin/VideoCameraCapturer.mm - platform/darwin/VideoCameraCapturerMac.h - platform/darwin/VideoCameraCapturerMac.mm - platform/darwin/VideoCapturerInterfaceImpl.h - platform/darwin/VideoCapturerInterfaceImpl.mm - platform/darwin/VideoMetalView.h - platform/darwin/VideoMetalView.mm - platform/darwin/VideoMetalViewMac.h - platform/darwin/VideoMetalViewMac.mm + # iOS / macOS + platform/darwin/DarwinInterface.h + platform/darwin/DarwinInterface.mm + platform/darwin/GLVideoView.h + platform/darwin/GLVideoView.mm + platform/darwin/TGRTCCVPixelBuffer.h + platform/darwin/TGRTCCVPixelBuffer.mm + platform/darwin/TGRTCDefaultVideoDecoderFactory.h + platform/darwin/TGRTCDefaultVideoDecoderFactory.mm + platform/darwin/TGRTCDefaultVideoEncoderFactory.h + platform/darwin/TGRTCDefaultVideoEncoderFactory.mm + platform/darwin/TGRTCVideoDecoderH264.h + platform/darwin/TGRTCVideoDecoderH264.mm + platform/darwin/TGRTCVideoDecoderH265.h + platform/darwin/TGRTCVideoDecoderH265.mm + platform/darwin/TGRTCVideoEncoderH264.h + platform/darwin/TGRTCVideoEncoderH264.mm + platform/darwin/TGRTCVideoEncoderH265.h + platform/darwin/TGRTCVideoEncoderH265.mm + platform/darwin/VideoCameraCapturer.h + platform/darwin/VideoCameraCapturer.mm + platform/darwin/VideoCameraCapturerMac.h + platform/darwin/VideoCameraCapturerMac.mm + platform/darwin/VideoCapturerInterfaceImpl.h + platform/darwin/VideoCapturerInterfaceImpl.mm + platform/darwin/VideoMetalView.h + platform/darwin/VideoMetalView.mm + platform/darwin/VideoMetalViewMac.h + platform/darwin/VideoMetalViewMac.mm - # POSIX + # POSIX - # Teleram Desktop - platform/tdesktop/DesktopInterface.cpp - platform/tdesktop/DesktopInterface.h - platform/tdesktop/VideoCapturerInterfaceImpl.cpp - platform/tdesktop/VideoCapturerInterfaceImpl.h - platform/tdesktop/VideoCapturerTrackSource.cpp - platform/tdesktop/VideoCapturerTrackSource.h - platform/tdesktop/VideoCameraCapturer.cpp - platform/tdesktop/VideoCameraCapturer.h + # Teleram Desktop + platform/tdesktop/DesktopInterface.cpp + platform/tdesktop/DesktopInterface.h + platform/tdesktop/VideoCapturerInterfaceImpl.cpp + platform/tdesktop/VideoCapturerInterfaceImpl.h + platform/tdesktop/VideoCapturerTrackSource.cpp + platform/tdesktop/VideoCapturerTrackSource.h + platform/tdesktop/VideoCameraCapturer.cpp + platform/tdesktop/VideoCameraCapturer.h - # All - reference/InstanceImplReference.cpp - reference/InstanceImplReference.h - ) + # All + reference/InstanceImplReference.cpp + reference/InstanceImplReference.h +) - target_link_libraries(lib_tgcalls - PRIVATE - desktop-app::external_webrtc - ) -endif() +target_link_libraries(lib_tgcalls +PRIVATE + desktop-app::external_webrtc +) target_compile_definitions(lib_tgcalls PRIVATE @@ -143,25 +141,23 @@ elseif (APPLE) PRIVATE WEBRTC_MAC ) - if (NOT DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION) - remove_target_sources(lib_tgcalls ${tgcalls_loc} - platform/darwin/GLVideoView.h - platform/darwin/GLVideoView.mm - platform/darwin/VideoCameraCapturer.h - platform/darwin/VideoCameraCapturer.mm - platform/darwin/VideoMetalView.h - platform/darwin/VideoMetalView.mm - platform/darwin/VideoMetalViewMac.h - platform/darwin/VideoMetalViewMac.mm - platform/tdesktop/DesktopInterface.cpp - platform/tdesktop/DesktopInterface.h - platform/tdesktop/VideoCapturerTrackSource.cpp - platform/tdesktop/VideoCapturerTrackSource.h - platform/tdesktop/VideoCapturerInterfaceImpl.cpp - platform/tdesktop/VideoCapturerInterfaceImpl.h - ) - endif() -else() + remove_target_sources(lib_tgcalls ${tgcalls_loc} + platform/darwin/GLVideoView.h + platform/darwin/GLVideoView.mm + platform/darwin/VideoCameraCapturer.h + platform/darwin/VideoCameraCapturer.mm + platform/darwin/VideoMetalView.h + platform/darwin/VideoMetalView.mm + platform/darwin/VideoMetalViewMac.h + platform/darwin/VideoMetalViewMac.mm + platform/tdesktop/DesktopInterface.cpp + platform/tdesktop/DesktopInterface.h + platform/tdesktop/VideoCapturerTrackSource.cpp + platform/tdesktop/VideoCapturerTrackSource.h + platform/tdesktop/VideoCapturerInterfaceImpl.cpp + platform/tdesktop/VideoCapturerInterfaceImpl.h + ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(lib_tgcalls PRIVATE WEBRTC_LINUX diff --git a/Telegram/cmake/lib_tgvoip.cmake b/Telegram/cmake/lib_tgvoip.cmake index 684f927b6..74d4f260d 100644 --- a/Telegram/cmake/lib_tgvoip.cmake +++ b/Telegram/cmake/lib_tgvoip.cmake @@ -173,655 +173,9 @@ if (NOT TGVOIP_FOUND) PUBLIC ${tgvoip_loc} ) - - if (DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION) - target_include_directories(lib_tgvoip_bundled - PRIVATE - ${tgvoip_loc}/webrtc_dsp - ) - target_compile_definitions(lib_tgvoip_bundled - PRIVATE - TGVOIP_USE_DESKTOP_DSP_BUNDLED - WEBRTC_APM_DEBUG_DUMP=0 - WEBRTC_NS_FLOAT - ) - - nice_target_sources(lib_tgvoip_bundled ${tgvoip_loc} - PRIVATE - # WebRTC APM - webrtc_dsp/system_wrappers/include/field_trial.h - webrtc_dsp/system_wrappers/include/cpu_features_wrapper.h - webrtc_dsp/system_wrappers/include/asm_defines.h - webrtc_dsp/system_wrappers/include/metrics.h - webrtc_dsp/system_wrappers/include/compile_assert_c.h - webrtc_dsp/system_wrappers/source/field_trial.cc - webrtc_dsp/system_wrappers/source/metrics.cc - webrtc_dsp/system_wrappers/source/cpu_features.cc - webrtc_dsp/typedefs.h - webrtc_dsp/absl/strings/internal/memutil.h - webrtc_dsp/absl/strings/internal/memutil.cc - webrtc_dsp/absl/strings/string_view.cc - webrtc_dsp/absl/strings/ascii.h - webrtc_dsp/absl/strings/ascii.cc - webrtc_dsp/absl/strings/string_view.h - webrtc_dsp/absl/types/optional.h - webrtc_dsp/absl/types/bad_optional_access.h - webrtc_dsp/absl/types/bad_optional_access.cc - webrtc_dsp/absl/types/optional.cc - webrtc_dsp/absl/memory/memory.h - webrtc_dsp/absl/meta/type_traits.h - webrtc_dsp/absl/algorithm/algorithm.h - webrtc_dsp/absl/container/inlined_vector.h - webrtc_dsp/absl/base/policy_checks.h - webrtc_dsp/absl/base/port.h - webrtc_dsp/absl/base/config.h - webrtc_dsp/absl/base/internal/raw_logging.cc - webrtc_dsp/absl/base/internal/throw_delegate.cc - webrtc_dsp/absl/base/internal/invoke.h - webrtc_dsp/absl/base/internal/inline_variable.h - webrtc_dsp/absl/base/internal/atomic_hook.h - webrtc_dsp/absl/base/internal/identity.h - webrtc_dsp/absl/base/internal/raw_logging.h - webrtc_dsp/absl/base/internal/throw_delegate.h - webrtc_dsp/absl/base/attributes.h - webrtc_dsp/absl/base/macros.h - webrtc_dsp/absl/base/optimization.h - webrtc_dsp/absl/base/log_severity.h - webrtc_dsp/absl/utility/utility.h - webrtc_dsp/rtc_base/string_to_number.h - webrtc_dsp/rtc_base/constructormagic.h - webrtc_dsp/rtc_base/race_checker.cc - webrtc_dsp/rtc_base/strings/string_builder.h - webrtc_dsp/rtc_base/strings/string_builder.cc - webrtc_dsp/rtc_base/event_tracer.h - webrtc_dsp/rtc_base/stringencode.h - webrtc_dsp/rtc_base/memory/aligned_malloc.cc - webrtc_dsp/rtc_base/memory/aligned_malloc.h - webrtc_dsp/rtc_base/timeutils.cc - webrtc_dsp/rtc_base/event.h - webrtc_dsp/rtc_base/ignore_wundef.h - webrtc_dsp/rtc_base/stringutils.h - webrtc_dsp/rtc_base/arraysize.h - webrtc_dsp/rtc_base/platform_file.cc - webrtc_dsp/rtc_base/swap_queue.h - webrtc_dsp/rtc_base/string_to_number.cc - webrtc_dsp/rtc_base/trace_event.h - webrtc_dsp/rtc_base/checks.h - webrtc_dsp/rtc_base/deprecation.h - webrtc_dsp/rtc_base/thread_checker_impl.cc - webrtc_dsp/rtc_base/sanitizer.h - webrtc_dsp/rtc_base/scoped_ref_ptr.h - webrtc_dsp/rtc_base/logging.h - webrtc_dsp/rtc_base/logging_mac.h - webrtc_dsp/rtc_base/logging_mac.mm - webrtc_dsp/rtc_base/timeutils.h - webrtc_dsp/rtc_base/atomicops.h - webrtc_dsp/rtc_base/stringencode.cc - webrtc_dsp/rtc_base/stringutils.cc - webrtc_dsp/rtc_base/checks.cc - webrtc_dsp/rtc_base/numerics/safe_minmax.h - webrtc_dsp/rtc_base/numerics/safe_conversions.h - webrtc_dsp/rtc_base/numerics/safe_conversions_impl.h - webrtc_dsp/rtc_base/numerics/safe_compare.h - webrtc_dsp/rtc_base/system/unused.h - webrtc_dsp/rtc_base/system/inline.h - webrtc_dsp/rtc_base/system/ignore_warnings.h - webrtc_dsp/rtc_base/system/asm_defines.h - webrtc_dsp/rtc_base/system/rtc_export.h - webrtc_dsp/rtc_base/system/arch.h - webrtc_dsp/rtc_base/platform_thread.cc - webrtc_dsp/rtc_base/platform_thread.h - webrtc_dsp/rtc_base/platform_thread_types.h - webrtc_dsp/rtc_base/protobuf_utils.h - webrtc_dsp/rtc_base/thread_annotations.h - webrtc_dsp/rtc_base/gtest_prod_util.h - webrtc_dsp/rtc_base/function_view.h - webrtc_dsp/rtc_base/criticalsection.h - webrtc_dsp/rtc_base/criticalsection.cc - webrtc_dsp/rtc_base/platform_thread_types.cc - webrtc_dsp/rtc_base/refcount.h - webrtc_dsp/rtc_base/event.cc - webrtc_dsp/rtc_base/thread_checker_impl.h - webrtc_dsp/rtc_base/event_tracer.cc - webrtc_dsp/rtc_base/compile_assert_c.h - webrtc_dsp/rtc_base/logging_webrtc.cc - webrtc_dsp/rtc_base/type_traits.h - webrtc_dsp/rtc_base/platform_file.h - webrtc_dsp/rtc_base/refcounter.h - webrtc_dsp/rtc_base/logging_mac.h - webrtc_dsp/rtc_base/thread_checker.h - webrtc_dsp/rtc_base/race_checker.h - webrtc_dsp/rtc_base/refcountedobject.h - webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc - webrtc_dsp/third_party/rnnoise/src/rnn_activations.h - webrtc_dsp/third_party/rnnoise/src/kiss_fft.h - webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc - webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.h - webrtc_dsp/api/audio/audio_frame.cc - webrtc_dsp/api/audio/echo_canceller3_config.h - webrtc_dsp/api/audio/echo_control.h - webrtc_dsp/api/audio/audio_frame.h - webrtc_dsp/api/audio/echo_canceller3_config.cc - webrtc_dsp/api/audio/echo_canceller3_factory.h - webrtc_dsp/api/audio/echo_canceller3_factory.cc - webrtc_dsp/api/array_view.h - webrtc_dsp/modules/third_party/fft/fft.h - webrtc_dsp/modules/third_party/fft/fft.c - webrtc_dsp/modules/audio_coding/codecs/isac/bandwidth_info.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/include/isac.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/settings.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_float_type.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/codec.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/structs.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h - webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.h - webrtc_dsp/modules/audio_processing/rms_level.cc - webrtc_dsp/modules/audio_processing/echo_detector/moving_max.h - webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.h - webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.h - webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc - webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc - webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc - webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc - webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.h - webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.h - webrtc_dsp/modules/audio_processing/splitting_filter.cc - webrtc_dsp/modules/audio_processing/gain_control_impl.cc - webrtc_dsp/modules/audio_processing/rms_level.h - webrtc_dsp/modules/audio_processing/ns/ns_core.h - webrtc_dsp/modules/audio_processing/ns/nsx_core.c - webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c - webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c - webrtc_dsp/modules/audio_processing/ns/defines.h - webrtc_dsp/modules/audio_processing/ns/noise_suppression.h - webrtc_dsp/modules/audio_processing/ns/ns_core.c - webrtc_dsp/modules/audio_processing/ns/nsx_core.h - webrtc_dsp/modules/audio_processing/ns/windows_private.h - webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.h - webrtc_dsp/modules/audio_processing/ns/noise_suppression.c - webrtc_dsp/modules/audio_processing/ns/nsx_defines.h - webrtc_dsp/modules/audio_processing/residual_echo_detector.h - webrtc_dsp/modules/audio_processing/audio_processing_impl.h - webrtc_dsp/modules/audio_processing/audio_buffer.cc - webrtc_dsp/modules/audio_processing/typing_detection.cc - webrtc_dsp/modules/audio_processing/render_queue_item_verifier.h - webrtc_dsp/modules/audio_processing/include/audio_generator.h - webrtc_dsp/modules/audio_processing/include/config.h - webrtc_dsp/modules/audio_processing/include/audio_frame_view.h - webrtc_dsp/modules/audio_processing/include/mock_audio_processing.h - webrtc_dsp/modules/audio_processing/include/gain_control.h - webrtc_dsp/modules/audio_processing/include/audio_generator_factory.h - webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc - webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc - webrtc_dsp/modules/audio_processing/include/aec_dump.cc - webrtc_dsp/modules/audio_processing/include/aec_dump.h - webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.h - webrtc_dsp/modules/audio_processing/include/audio_processing.h - webrtc_dsp/modules/audio_processing/include/audio_processing.cc - webrtc_dsp/modules/audio_processing/include/config.cc - webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.h - webrtc_dsp/modules/audio_processing/agc2/biquad_filter.h - webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc - webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc - webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.h - webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc - webrtc_dsp/modules/audio_processing/agc2/signal_classifier.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc - webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc - webrtc_dsp/modules/audio_processing/agc2/limiter.cc - webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc - webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/test_utils.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_info.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/ring_buffer.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/common.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.h - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc - webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc - webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc - webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc - webrtc_dsp/modules/audio_processing/agc2/down_sampler.h - webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc - webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc - webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc - webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc - webrtc_dsp/modules/audio_processing/agc2/saturation_protector.h - webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc - webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc - webrtc_dsp/modules/audio_processing/agc2/agc2_common.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.h - webrtc_dsp/modules/audio_processing/agc2/vad_with_level.h - webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.h - webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.h - webrtc_dsp/modules/audio_processing/agc2/gain_applier.h - webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc - webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.h - webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc - webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc - webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc - webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.h - webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc - webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.h - webrtc_dsp/modules/audio_processing/agc2/limiter.h - webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc - webrtc_dsp/modules/audio_processing/transient/moving_moments.cc - webrtc_dsp/modules/audio_processing/transient/transient_detector.h - webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc - webrtc_dsp/modules/audio_processing/transient/transient_suppressor.h - webrtc_dsp/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h - webrtc_dsp/modules/audio_processing/transient/common.h - webrtc_dsp/modules/audio_processing/transient/wpd_node.h - webrtc_dsp/modules/audio_processing/transient/moving_moments.h - webrtc_dsp/modules/audio_processing/transient/wpd_tree.h - webrtc_dsp/modules/audio_processing/transient/wpd_node.cc - webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc - webrtc_dsp/modules/audio_processing/transient/transient_detector.cc - webrtc_dsp/modules/audio_processing/transient/dyadic_decimator.h - webrtc_dsp/modules/audio_processing/low_cut_filter.cc - webrtc_dsp/modules/audio_processing/noise_suppression_impl.h - webrtc_dsp/modules/audio_processing/level_estimator_impl.cc - webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc - webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc - webrtc_dsp/modules/audio_processing/aec/aec_resampler.h - webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc - webrtc_dsp/modules/audio_processing/aec/echo_cancellation.h - webrtc_dsp/modules/audio_processing/aec/aec_core.cc - webrtc_dsp/modules/audio_processing/aec/aec_core.h - webrtc_dsp/modules/audio_processing/aec/aec_core_optimized_methods.h - webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc - webrtc_dsp/modules/audio_processing/aec/aec_common.h - webrtc_dsp/modules/audio_processing/voice_detection_impl.h - webrtc_dsp/modules/audio_processing/voice_detection_impl.cc - webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc - webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc - webrtc_dsp/modules/audio_processing/agc/agc.cc - webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc - webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc - webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.h - webrtc_dsp/modules/audio_processing/agc/legacy/gain_control.h - webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.h - webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c - webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c - webrtc_dsp/modules/audio_processing/agc/utility.cc - webrtc_dsp/modules/audio_processing/agc/mock_agc.h - webrtc_dsp/modules/audio_processing/agc/loudness_histogram.h - webrtc_dsp/modules/audio_processing/agc/gain_map_internal.h - webrtc_dsp/modules/audio_processing/agc/utility.h - webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.h - webrtc_dsp/modules/audio_processing/agc/agc.h - webrtc_dsp/modules/audio_processing/common.h - webrtc_dsp/modules/audio_processing/audio_processing_impl.cc - webrtc_dsp/modules/audio_processing/audio_buffer.h - webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.h - webrtc_dsp/modules/audio_processing/splitting_filter.h - webrtc_dsp/modules/audio_processing/low_cut_filter.h - webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.h - webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc - webrtc_dsp/modules/audio_processing/gain_controller2.cc - webrtc_dsp/modules/audio_processing/three_band_filter_bank.h - webrtc_dsp/modules/audio_processing/residual_echo_detector.cc - webrtc_dsp/modules/audio_processing/echo_cancellation_impl.h - webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc - webrtc_dsp/modules/audio_processing/level_estimator_impl.h - webrtc_dsp/modules/audio_processing/gain_controller2.h - webrtc_dsp/modules/audio_processing/aecm/aecm_core.h - webrtc_dsp/modules/audio_processing/aecm/aecm_defines.h - webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc - webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc - webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.h - webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc - webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc - webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.h - webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.h - webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc - webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.h - webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.h - webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc - webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc - webrtc_dsp/modules/audio_processing/aec3/aec_state.h - webrtc_dsp/modules/audio_processing/aec3/suppression_filter.h - webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc - webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc - webrtc_dsp/modules/audio_processing/aec3/subtractor.cc - webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.h - webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.h - webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.h - webrtc_dsp/modules/audio_processing/aec3/matched_filter.h - webrtc_dsp/modules/audio_processing/aec3/subtractor_output.h - webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.h - webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc - webrtc_dsp/modules/audio_processing/aec3/aec3_fft.h - webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.h - webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/suppression_filter.cc - webrtc_dsp/modules/audio_processing/aec3/block_processor.cc - webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.h - webrtc_dsp/modules/audio_processing/aec3/subtractor.h - webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.h - webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.h - webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/aec_state.cc - webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.cc - webrtc_dsp/modules/audio_processing/aec3/fft_data.h - webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc - webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.h - webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.h - webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/erl_estimator.h - webrtc_dsp/modules/audio_processing/aec3/echo_remover.h - webrtc_dsp/modules/audio_processing/aec3/block_framer.cc - webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc - webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc - webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.h - webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.h - webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc - webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc - webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.h - webrtc_dsp/modules/audio_processing/aec3/moving_average.h - webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.h - webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc - webrtc_dsp/modules/audio_processing/aec3/suppression_gain.cc - webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc - webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.h - webrtc_dsp/modules/audio_processing/aec3/suppression_gain.h - webrtc_dsp/modules/audio_processing/aec3/moving_average.cc - webrtc_dsp/modules/audio_processing/aec3/erle_estimator.h - webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.h - webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc - webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/block_processor.h - webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.h - webrtc_dsp/modules/audio_processing/aec3/matched_filter.cc - webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.h - webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.h - webrtc_dsp/modules/audio_processing/aec3/skew_estimator.h - webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc - webrtc_dsp/modules/audio_processing/aec3/render_buffer.h - webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc - webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc - webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.h - webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/vector_buffer.h - webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.h - webrtc_dsp/modules/audio_processing/aec3/echo_audibility.h - webrtc_dsp/modules/audio_processing/aec3/fft_buffer.h - webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc - webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc - webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/aec3_common.h - webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc - webrtc_dsp/modules/audio_processing/aec3/vector_math.h - webrtc_dsp/modules/audio_processing/aec3/decimator.h - webrtc_dsp/modules/audio_processing/aec3/frame_blocker.h - webrtc_dsp/modules/audio_processing/aec3/block_framer.h - webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.h - webrtc_dsp/modules/audio_processing/aec3/delay_estimate.h - webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_model.h - webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.h - webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.h - webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc - webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc - webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.h - webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc - webrtc_dsp/modules/audio_processing/aec3/decimator.cc - webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.h - webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc - webrtc_dsp/modules/audio_processing/gain_control_impl.h - webrtc_dsp/modules/audio_processing/typing_detection.h - webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc - webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.h - webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc - webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc - webrtc_dsp/modules/audio_processing/vad/vad_audio_proc_internal.h - webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc - webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc - webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.h - webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.h - webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc - webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc - webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.h - webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc - webrtc_dsp/modules/audio_processing/vad/gmm.h - webrtc_dsp/modules/audio_processing/vad/common.h - webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.h - webrtc_dsp/modules/audio_processing/vad/voice_gmm_tables.h - webrtc_dsp/modules/audio_processing/vad/noise_gmm_tables.h - webrtc_dsp/modules/audio_processing/vad/pitch_internal.h - webrtc_dsp/modules/audio_processing/vad/gmm.cc - webrtc_dsp/modules/audio_processing/vad/standalone_vad.h - webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.h - webrtc_dsp/modules/audio_processing/utility/delay_estimator_internal.h - webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc - webrtc_dsp/modules/audio_processing/utility/ooura_fft.h - webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc - webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc - webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc - webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.h - webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc - webrtc_dsp/modules/audio_processing/utility/delay_estimator.h - webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_common.h - webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.h - webrtc_dsp/common_audio/mocks/mock_smoothing_filter.h - webrtc_dsp/common_audio/wav_file.h - webrtc_dsp/common_audio/window_generator.cc - webrtc_dsp/common_audio/channel_buffer.cc - webrtc_dsp/common_audio/fir_filter_factory.cc - webrtc_dsp/common_audio/sparse_fir_filter.h - webrtc_dsp/common_audio/fir_filter_sse.h - webrtc_dsp/common_audio/window_generator.h - webrtc_dsp/common_audio/ring_buffer.h - webrtc_dsp/common_audio/fir_filter.h - webrtc_dsp/common_audio/include/audio_util.h - webrtc_dsp/common_audio/wav_header.cc - webrtc_dsp/common_audio/real_fourier_ooura.cc - webrtc_dsp/common_audio/audio_util.cc - webrtc_dsp/common_audio/real_fourier_ooura.h - webrtc_dsp/common_audio/fir_filter_sse.cc - webrtc_dsp/common_audio/smoothing_filter.h - webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc - webrtc_dsp/common_audio/resampler/sinc_resampler.h - webrtc_dsp/common_audio/resampler/resampler.cc - webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc - webrtc_dsp/common_audio/resampler/include/push_resampler.h - webrtc_dsp/common_audio/resampler/include/resampler.h - webrtc_dsp/common_audio/resampler/push_sinc_resampler.h - webrtc_dsp/common_audio/resampler/push_resampler.cc - webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.h - webrtc_dsp/common_audio/resampler/sinc_resampler.cc - webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc - webrtc_dsp/common_audio/fir_filter_factory.h - webrtc_dsp/common_audio/audio_converter.h - webrtc_dsp/common_audio/wav_file.cc - webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c - webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h - webrtc_dsp/common_audio/third_party/fft4g/fft4g.c - webrtc_dsp/common_audio/third_party/fft4g/fft4g.h - webrtc_dsp/common_audio/audio_converter.cc - webrtc_dsp/common_audio/real_fourier.cc - webrtc_dsp/common_audio/channel_buffer.h - webrtc_dsp/common_audio/real_fourier.h - webrtc_dsp/common_audio/sparse_fir_filter.cc - webrtc_dsp/common_audio/smoothing_filter.cc - webrtc_dsp/common_audio/fir_filter_c.cc - webrtc_dsp/common_audio/ring_buffer.c - webrtc_dsp/common_audio/fir_filter_c.h - webrtc_dsp/common_audio/signal_processing/complex_fft_tables.h - webrtc_dsp/common_audio/signal_processing/complex_fft.c - webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c - webrtc_dsp/common_audio/signal_processing/levinson_durbin.c - webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc - webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c - webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c - webrtc_dsp/common_audio/signal_processing/energy.c - webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c - webrtc_dsp/common_audio/signal_processing/downsample_fast.c - webrtc_dsp/common_audio/signal_processing/splitting_filter1.c - webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c - webrtc_dsp/common_audio/signal_processing/spl_init.c - webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c - webrtc_dsp/common_audio/signal_processing/cross_correlation.c - webrtc_dsp/common_audio/signal_processing/include/signal_processing_library.h - webrtc_dsp/common_audio/signal_processing/include/real_fft.h - webrtc_dsp/common_audio/signal_processing/include/spl_inl.h - webrtc_dsp/common_audio/signal_processing/division_operations.c - webrtc_dsp/common_audio/signal_processing/auto_correlation.c - webrtc_dsp/common_audio/signal_processing/get_scaling_square.c - webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.h - webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.h - webrtc_dsp/common_audio/signal_processing/resample.c - webrtc_dsp/common_audio/signal_processing/min_max_operations.c - webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c - webrtc_dsp/common_audio/signal_processing/filter_ar.c - webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c - webrtc_dsp/common_audio/signal_processing/resample_fractional.c - webrtc_dsp/common_audio/signal_processing/real_fft.c - webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c - webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c - webrtc_dsp/common_audio/signal_processing/randomization_functions.c - webrtc_dsp/common_audio/signal_processing/copy_set_operations.c - webrtc_dsp/common_audio/signal_processing/resample_by_2.c - webrtc_dsp/common_audio/signal_processing/get_hanning_window.c - webrtc_dsp/common_audio/signal_processing/resample_48khz.c - webrtc_dsp/common_audio/signal_processing/spl_inl.c - webrtc_dsp/common_audio/signal_processing/spl_sqrt.c - webrtc_dsp/common_audio/wav_header.h - webrtc_dsp/common_audio/vad/vad_sp.c - webrtc_dsp/common_audio/vad/vad.cc - webrtc_dsp/common_audio/vad/webrtc_vad.c - webrtc_dsp/common_audio/vad/vad_core.h - webrtc_dsp/common_audio/vad/include/vad.h - webrtc_dsp/common_audio/vad/include/webrtc_vad.h - webrtc_dsp/common_audio/vad/vad_gmm.h - webrtc_dsp/common_audio/vad/vad_filterbank.c - webrtc_dsp/common_audio/vad/vad_core.c - webrtc_dsp/common_audio/vad/vad_sp.h - webrtc_dsp/common_audio/vad/vad_filterbank.h - webrtc_dsp/common_audio/vad/vad_gmm.c - - # ARM/NEON sources - # TODO check if there's a good way to make these compile with ARM ports of TDesktop - # webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c - # webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc - # webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc - # webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h - # webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc - # webrtc_dsp/common_audio/fir_filter_neon.cc - # webrtc_dsp/common_audio/resampler/sinc_resampler_neon.cc - # webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S - # webrtc_dsp/common_audio/fir_filter_neon.h - # webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c - # webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S - # webrtc_dsp/common_audio/signal_processing/include/spl_inl_armv7.h - # webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c - # webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c - # webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S - ) - - if (WIN32) - target_compile_definitions(lib_tgvoip_bundled - PUBLIC - WEBRTC_WIN - ) - elseif (APPLE) - target_compile_definitions(lib_tgvoip_bundled - PUBLIC - WEBRTC_POSIX - WEBRTC_MAC - ) - else() - target_compile_definitions(lib_tgvoip_bundled - PUBLIC - WEBRTC_POSIX - WEBRTC_LINUX - ) - endif() - - else() - target_link_libraries(lib_tgvoip_bundled - PRIVATE - desktop-app::external_webrtc - ) - endif() - target_link_libraries(lib_tgvoip_bundled PRIVATE + desktop-app::external_webrtc desktop-app::external_opus ) diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index af7269454..60d5c43da 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit af7269454c371b7e1045429d60cd728f7580ee4a +Subproject commit 60d5c43daf882a6c03944a3e6198b5f35b654a0e diff --git a/cmake b/cmake index 561273a2f..695fabda6 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 561273a2f88306072750283b28d763959cd58652 +Subproject commit 695fabda6830b58bdc02d09db70531d5dececcd0 From 57ca6e23b9af44419898f3b43e47546d8073cd4f Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Wed, 3 Feb 2021 14:43:31 +0400 Subject: [PATCH 248/396] Port Qt-based title widget to lib_ui --- .../icons/calls/call_shadow_left.png | Bin 100 -> 0 bytes .../icons/calls/call_shadow_left@2x.png | Bin 125 -> 0 bytes .../icons/calls/call_shadow_left@3x.png | Bin 139 -> 0 bytes .../Resources/icons/calls/call_shadow_top.png | Bin 103 -> 0 bytes .../icons/calls/call_shadow_top@2x.png | Bin 127 -> 0 bytes .../icons/calls/call_shadow_top@3x.png | Bin 141 -> 0 bytes .../icons/calls/call_shadow_top_left.png | Bin 295 -> 0 bytes .../icons/calls/call_shadow_top_left@2x.png | Bin 559 -> 0 bytes .../icons/calls/call_shadow_top_left@3x.png | Bin 927 -> 0 bytes Telegram/SourceFiles/calls/calls.style | 14 ---------- .../SourceFiles/calls/calls_group_panel.cpp | 25 +++++++----------- .../SourceFiles/calls/calls_group_panel.h | 4 +-- Telegram/SourceFiles/calls/calls_panel.cpp | 23 +++++++--------- Telegram/SourceFiles/calls/calls_panel.h | 4 +-- .../SourceFiles/media/view/media_view_pip.cpp | 2 +- Telegram/SourceFiles/window/main_window.cpp | 2 +- .../SourceFiles/window/window_title_qt.cpp | 2 +- 17 files changed, 27 insertions(+), 49 deletions(-) delete mode 100644 Telegram/Resources/icons/calls/call_shadow_left.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_left@2x.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_left@3x.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top@2x.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top@3x.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top_left.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top_left@2x.png delete mode 100644 Telegram/Resources/icons/calls/call_shadow_top_left@3x.png diff --git a/Telegram/Resources/icons/calls/call_shadow_left.png b/Telegram/Resources/icons/calls/call_shadow_left.png deleted file mode 100644 index 74864ad4b58577ca3a3d1856559ea719998b84c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^5<twz!3HFctjk*sq_jO<978x}=5`r!F(?QemhyV} zKYq>S&IOG=oClave$R06yz;H^)?a3(FUic47BRliXODFQYGd$p^>bP0l+XkK{<R)! diff --git a/Telegram/Resources/icons/calls/call_shadow_left@2x.png b/Telegram/Resources/icons/calls/call_shadow_left@2x.png deleted file mode 100644 index 6a0e6e4c537f3e8ab7f3005d58f347ea1afbd90f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^20+Zj!3HGdHpD9eDHl%{#}J9BXM3&r8XQEJ+Rxwn ze<JVurdye+XD0|a9AH)8^klf<cFBNqTYIjjlhCb8)3#pwVQgLPnYVjeW!?U%_FL<g Z@XlJt_-eZ>=Q^Ny44$rjF6*2UngGhbD!Kpw diff --git a/Telegram/Resources/icons/calls/call_shadow_left@3x.png b/Telegram/Resources/icons/calls/call_shadow_left@3x.png deleted file mode 100644 index 30257ebe7755d12085cb5c309c4fad0dd42aa4cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^9ze{@!2~4t3*6feq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c73q4qIEGZ*N=ivcNN{2G@$osqqbA{yHkoCER8yw-9aWwy k3_d)EMR=n78CfJ4gajDgI;9(g0yQ#ty85}Sb4q9e0F2NjR{#J2 diff --git a/Telegram/Resources/icons/calls/call_shadow_top.png b/Telegram/Resources/icons/calls/call_shadow_top.png deleted file mode 100644 index 653e0afa994e839c76ca58e1a20333f70799a448..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^j6f{G!3HF)&rH7sr1U&p978x}=AL%sVld!1a-g^S z$A9tb-4}xPoY534U<$ZVck;}_Rb_df_8w5kT*`Q0HIwMytuLj4IvG4&{an^LB{Ts5 D!GI#Y diff --git a/Telegram/Resources/icons/calls/call_shadow_top@2x.png b/Telegram/Resources/icons/calls/call_shadow_top@2x.png deleted file mode 100644 index 47c672ca3b028dbf40d89d5aa5825b95344fc3d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^Oh9bF!3HF)SyndzDK}3S#}J9BdnXw3F(_~_dpG{K zj}$-JG121Wl{=0c91Uy?M>K>d+UmF(eVyYHI@zjormCo72uu0?l}USU$Sv8CG->+w bKRmJqnM^Htf2OejO=R$N^>bP0l+XkK@!lpq diff --git a/Telegram/Resources/icons/calls/call_shadow_top@3x.png b/Telegram/Resources/icons/calls/call_shadow_top@3x.png deleted file mode 100644 index 350db0481b414a97a62b64e5b4fce8198d667177..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^%s}kH!2~4tT)p8Aq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c73q7rIEGZ*N=j;A6mxTP6N>cl@liRYbY#QEj!<)p$SWUJ nSs$KhG1n8^ba*DKKmvo#Nyck0LQ7qMS{XcD{an^LB{Ts5gEA{X diff --git a/Telegram/Resources/icons/calls/call_shadow_top_left.png b/Telegram/Resources/icons/calls/call_shadow_top_left.png deleted file mode 100644 index baba49364f09da2655e03782fd31be9da4594658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 295 zcmV+?0oeYDP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0002)Nkl<ZNQtGC z;SPi_2!l76@Bhf>uNtfcakpfN-YxxTVa_qeZ)~hNXY1VB5$cMUfGE;=&cG6YgDi$! zop}W)fof;M5>SbnnS9UOJOG(EgxvjlXIOOe$pOU7Y`FXFS-E1J3!s|G!fUPJ?l%y- zQN=D6=6E2E4M+r4G07=H<?en7AII^4#JE}j<sd=;!f$|7eMlpUJ%C_z5dmssTTPBJ zFo=7RPEjV#pcqw*NC9LI)J5ZQj8y}|5xq89xltEwYl_%GbvI(=Z3kaK-EWbxAV<Y8 tRY<k0{5Ns-WtD*4s@s5ngtq`Y>jc4T2IpqmK|lZi002ovPDHLkV1fWgeZv3% diff --git a/Telegram/Resources/icons/calls/call_shadow_top_left@2x.png b/Telegram/Resources/icons/calls/call_shadow_top_left@2x.png deleted file mode 100644 index 0d9672fb51b6dd970b3413917c45f15c5453534d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559 zcmV+~0?_@5P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0005@Nkl<ZXo2mT z;f{ka41){o{a^X+7gCAh*hvH2q_b4Px>Ec#O%U3)F~-mMV(o7mSk>o^n{M0IHrH(c zt0vX!+W^=NJ6C<y5<q0C`dxNh2>?y(_1ivWOh##dtVwi^=2E_!B?DxP+oO73kp#e2 z4jHciMSY$qfU-TjqteIbZvjxuN9PQR8D~Ht01G)XjLfSr89{)^JTg7T82i3UFbG8P zBLOH-s6`Mez)}*kL{&`BpAnFS$QTa+keGRPWYK5`U`c3Ao^AfDM57fzNv6m=1F-<! z0<i@R0qkJ5caixB>b%Y~@0pCiN;%*q?A(syxSU5@N&$#qXbwax?`@;g{0xD%R!1IO z09Y8K>f=R#K~y3T0(e5nX19+8IgX<h0QHASY#%TRh-cdNDdkjE`h6fuUK9y63h16G z05p-e>L?0cn?YD5$|(ILMtzm%!FinLi+rR2qKl$LR$*)<WA~&0cAwjGWea(w6_wk^ z$|!?F=DlcmV^_>m0G7}e!h?!T-%^DT1T)CYD-ac>--)9!tSJl727nAJhE$`dFQc|! zXjsIe)n18!44|4q$*s&)(JjA`Rn1g%0f@?`RmP-J1gdf)@Z<`vDz%0FB3Nq<Fu*$& xpah^Ac_L!BJO`dsdl`Ki<m>+{@elB}egPU73!TJ&h#UX_002ovPDHLkV1h57@K68% diff --git a/Telegram/Resources/icons/calls/call_shadow_top_left@3x.png b/Telegram/Resources/icons/calls/call_shadow_top_left@3x.png deleted file mode 100644 index 260bb2658094fd232fe80415fdbfa6e624f5214e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 927 zcmV;Q17Q4#P)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#K}keGRCwC#THBJVFbpd+|NqNf=44LB z>x=B%+b((NP+-79ieeick9*wXEyl4wf@4YyX-nv@*K778NZ-WLTwsx)KLeN>WjR4r zV3kngPb9se0>No3fHmA+?pMzoBGfbxl@)i97S&|lzJBG=oxnPu!trm^;+bgoRYZZH z>0fYY1!KYKq2ejRqY4C9dpjw*o}nr*&LLhlT7U%~uo|>Aon-Vm+;Rw*N`%tXNsAS$ zi(KwJ&l3|>rPZ{?4B6C@!1A6l3HxVSYU2m%jmByTFY`m~1aMxJqxrz_gr+k=0?QFg zU<?2~hY2#0Lo)S_l%l0K3ZQIUaRAKNHfxvccJnClS;9Mh54Oj$oKDYhfVnFPV@5Rw zJnOQG)ae(wd)FMpyjkfL19sR0KZVXjq*02l@LW{jIzy8EROJnLO0bcwq8!S9*+tkn z>wr}$ld)8SW*cm#tpgVHS!+mU+><SP>~Z|i0?p!MiH|!NV-z{{#wuVg=9YR4_#vZ< zA(0!*{jnye0CRPi2`LdvsO2c&IS$OlnX1fP#ipXo3=}XHb=!aiD^58>7#^%i)%QD% zF}X!JZCAKPC+)#<%dw1gwFcDE1BYI?dOn|Y`dxqV+OTec#@3{!h=o0A^bsjWfK`_* zOn7V$ONdm`&i|uIlhc94YZ*1ZqBIp1TcE8JWJ*2O(1CNa`wOiqR9Re?bOhYdZ7n8M zftDtZ@W4jzI%I#hIK{cL?u}P!yd<ayaV<LrSmaHYtH}sdnt}~^1DG1>e`%7;38g(y zP0D3JO1-~lnrdI|1o??%x}b6!2@Eg;>g9M%ZQ-HZQm<Q=d05XCG(1#4l%<W;5t+!C z)eHkP#-#|7&*{K$gEH8baI4k+cc{HD{wQ6bSgh)WxDCefjDfc#E^m?2%YEm>E?f0! zS&ExsXZO3aW&N1?IfS|{V~X&h$Sd5PonXKi@CMfmd#2g>_|kDU7__^BfxbvX-{DOe zTky^0AC{t}H1(+bUZ6+M^j>YBLWdaZ$(26OJiZoK(cVT&*vwc&$+OkIL5f4XVHPmj zfeZ$^*pSW{I=o%+GlA`03~$D6U^lQESkw3sU;yU*jCdo?|6>3E002ovPDHLkV1l1} Bw%Gsx diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 446780c1b..0dfade9b7 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -22,20 +22,6 @@ CallSignalBars { inactiveOpacity: double; } -callRadius: 6px; -callShadow: Shadow { - left: icon {{ "calls/call_shadow_left", windowShadowFg }}; - topLeft: icon {{ "calls/call_shadow_top_left", windowShadowFg }}; - top: icon {{ "calls/call_shadow_top", windowShadowFg }}; - topRight: icon {{ "calls/call_shadow_top_left-flip_horizontal", windowShadowFg }}; - right: icon {{ "calls/call_shadow_left-flip_horizontal", windowShadowFg }}; - bottomRight: icon {{ "calls/call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }}; - bottom: icon {{ "calls/call_shadow_top-flip_vertical", windowShadowFg }}; - bottomLeft: icon {{ "calls/call_shadow_top_left-flip_vertical", windowShadowFg }}; - extend: margins(9px, 8px, 9px, 10px); - fallback: windowShadowFgFallback; -} - callWidthMin: 300px; callHeightMin: 440px; callWidth: 720px; diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index dae98cb5b..93a00f41f 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_group_common.h" #include "calls/calls_group_members.h" #include "calls/calls_group_settings.h" +#include "ui/platform/ui_platform_window_title.h" #include "ui/widgets/buttons.h" #include "ui/widgets/window.h" #include "ui/widgets/call_button.h" @@ -38,10 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_calls.h" #include "styles/style_layers.h" -#ifdef Q_OS_WIN -#include "ui/platform/win/ui_window_title_win.h" -#endif // Q_OS_WIN - #include <QtWidgets/QDesktopWidget> #include <QtWidgets/QApplication> #include <QtGui/QWindow> @@ -304,11 +301,11 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call) , _peer(call->peer()) , _window(std::make_unique<Ui::Window>(Core::App().getModalParent())) , _layerBg(std::make_unique<Ui::LayerManager>(_window->body())) -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC , _controls(std::make_unique<Ui::Platform::TitleControls>( - _window.get(), + _window->body(), st::groupCallTitle)) -#endif // Q_OS_WIN +#endif // !Q_OS_MAC , _members(widget(), call) , _settings(widget(), st::groupCallSettings) , _mute(std::make_unique<Ui::CallMuteButton>( @@ -749,9 +746,9 @@ void GroupPanel::kickMemberSure(not_null<UserData*> user) { void GroupPanel::initLayout() { initGeometry(); -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC _controls->raise(); -#endif // Q_OS_WIN +#endif // !Q_OS_MAC } void GroupPanel::showControls() { @@ -783,14 +780,12 @@ int GroupPanel::computeMembersListTop() const { } std::optional<QRect> GroupPanel::computeTitleRect() const { -#ifdef Q_OS_WIN +#ifdef Q_OS_MAC + return QRect(70, 0, widget()->width() - 70, 28); +#else // Q_OS_MAC const auto controls = _controls->geometry(); return QRect(0, 0, controls.x(), controls.height()); -#elif defined Q_OS_MAC // Q_OS_WIN - return QRect(70, 0, widget()->width() - 70, 28); -#else // Q_OS_WIN || Q_OS_MAC - return std::nullopt; -#endif // Q_OS_WIN || Q_OS_MAC +#endif // !Q_OS_MAC } void GroupPanel::updateControlsGeometry() { diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index 32cd06caa..1887f3dbb 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -110,9 +110,9 @@ private: const std::unique_ptr<Ui::Window> _window; const std::unique_ptr<Ui::LayerManager> _layerBg; -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC std::unique_ptr<Ui::Platform::TitleControls> _controls; -#endif // Q_OS_WIN +#endif // !Q_OS_MAC rpl::lifetime _callLifetime; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index d64c829d9..2c3f5e6c6 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_signal_bars.h" #include "calls/calls_userpic.h" #include "calls/calls_video_bubble.h" +#include "ui/platform/ui_platform_window_title.h" #include "ui/widgets/call_button.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -45,10 +46,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_calls.h" #include "styles/style_chat.h" -#ifdef Q_OS_WIN -#include "ui/platform/win/ui_window_title_win.h" -#endif // Q_OS_WIN - #include <QtWidgets/QDesktopWidget> #include <QtWidgets/QApplication> #include <QtGui/QWindow> @@ -189,12 +186,12 @@ Panel::Panel(not_null<Call*> call) : _call(call) , _user(call->user()) , _window(std::make_unique<Ui::Window>(Core::App().getModalParent())) -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC , _controls(std::make_unique<Ui::Platform::TitleControls>( - _window.get(), + _window->body(), st::callTitle, [=](bool maximized) { toggleFullScreen(maximized); })) -#endif // Q_OS_WIN +#endif // !Q_OS_MAC , _bodySt(&st::callBodyLayout) , _answerHangupRedial(widget(), st::callAnswer, &st::callHangup) , _decline(widget(), object_ptr<Ui::CallButton>(widget(), st::callHangup)) @@ -270,11 +267,11 @@ void Panel::initWindow() { if (!widget()->rect().contains(widgetPoint)) { return Flag::None | Flag(0); } -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC if (_controls->geometry().contains(widgetPoint)) { return Flag::None | Flag(0); } -#endif // Q_OS_WIN +#endif // !Q_OS_MAC const auto buttonWidth = st::callCancel.button.width; const auto buttonsWidth = buttonWidth * 4; const auto inControls = (_fingerprint @@ -595,9 +592,9 @@ void Panel::initLayout() { updateControlsGeometry(); }, widget()->lifetime()); -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC _controls->raise(); -#endif // Q_OS_WIN +#endif // !Q_OS_MAC } void Panel::showControls() { @@ -669,10 +666,10 @@ void Panel::updateControlsGeometry() { refreshIncomingGeometry(); } if (_fingerprint) { -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC const auto minRight = _controls->geometry().width() + st::callFingerprintTop; -#else // Q_OS_WIN +#else // !Q_OS_MAC const auto minRight = 0; #endif // _controls const auto desired = (widget()->width() - _fingerprint->width()) / 2; diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index ec8d7e4ea..4c6e13b81 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -108,9 +108,9 @@ private: const std::unique_ptr<Ui::Window> _window; std::unique_ptr<Incoming> _incoming; -#ifdef Q_OS_WIN +#ifndef Q_OS_MAC std::unique_ptr<Ui::Platform::TitleControls> _controls; -#endif // Q_OS_WIN +#endif // !Q_OS_MAC QSize _incomingFrameSize; diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 3059d8993..83e716456 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -28,9 +28,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/text/format_values.h" #include "window/window_controller.h" +#include "styles/style_widgets.h" #include "styles/style_window.h" #include "styles/style_media_view.h" -#include "styles/style_calls.h" // st::callShadow #include <QtGui/QWindow> #include <QtGui/QScreen> diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index fd69d2932..05afa18fb 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -35,8 +35,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" // session->content()->windowShown(). #include "facades.h" #include "app.h" +#include "styles/style_widgets.h" #include "styles/style_window.h" -#include "styles/style_calls.h" // st::callShadow #include <QtWidgets/QDesktopWidget> #include <QtCore/QMimeData> diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 8b3e26df7..d9703107f 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -13,8 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "core/core_settings.h" #include "core/application.h" +#include "styles/style_widgets.h" #include "styles/style_window.h" -#include "styles/style_calls.h" // st::callShadow #include <QtCore/QCoreApplication> #include <QtGui/QGuiApplication> From 2dd99a535ca72d087dd5f5e8282cf9179a93f00d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 30 Jan 2021 13:26:46 +0300 Subject: [PATCH 249/396] United enum classes of arc directions into single enum class. --- Telegram/SourceFiles/calls/calls_group_members.cpp | 2 +- Telegram/SourceFiles/calls/calls_volume_item.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 271fb30b7..70939e9b0 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -194,7 +194,7 @@ private: st::groupCallStatusSpeakerArcsAnimation, kSpeakerThreshold, volume, - Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) { + Ui::Paint::ArcsAnimation::Direction::Right)) { } const style::icon &speaker; const std::unique_ptr<Ui::Paint::ArcsAnimation> arcs; diff --git a/Telegram/SourceFiles/calls/calls_volume_item.cpp b/Telegram/SourceFiles/calls/calls_volume_item.cpp index a147b3d00..3d1cc249c 100644 --- a/Telegram/SourceFiles/calls/calls_volume_item.cpp +++ b/Telegram/SourceFiles/calls/calls_volume_item.cpp @@ -64,7 +64,7 @@ MenuVolumeItem::MenuVolumeItem( st::groupCallSpeakerArcsAnimation, kSpeakerThreshold, _localMuted ? 0. : (startVolume / float(maxVolume)), - Ui::Paint::ArcsAnimation::HorizontalDirection::Right)) { + Ui::Paint::ArcsAnimation::Direction::Right)) { initResizeHook(parent->sizeValue()); enableMouseSelecting(); From 2d50c617039a57f2771c2d38ac57db5c42275527 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 30 Jan 2021 14:43:46 +0300 Subject: [PATCH 250/396] Added ability to set dividers to MediaSlider. --- .../ui/widgets/continuous_sliders.cpp | 31 +++++++++++++++++++ .../ui/widgets/continuous_sliders.h | 9 ++++++ 2 files changed, 40 insertions(+) diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index 7813d2aed..e98a7a991 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -219,6 +219,10 @@ void MediaSlider::disablePaint(bool disabled) { _paintDisabled = disabled; } +void MediaSlider::addDivider(float64 atValue, const QSize &size) { + _dividers.push_back(Divider{ atValue, size }); +} + void MediaSlider::paintEvent(QPaintEvent *e) { if (_paintDisabled) { return; @@ -285,6 +289,33 @@ void MediaSlider::paintEvent(QPaintEvent *e) { p.setBrush(horizontal ? inactiveFg : activeFg); p.drawRoundedRect(endRect, radius, radius); } + if (!_dividers.empty()) { + p.setClipRect(rect()); + for (const auto ÷r : _dividers) { + const auto dividerValue = horizontal + ? divider.atValue + : (1. - divider.atValue); + const auto dividerMid = std::round(from + + dividerValue * length); + const auto &size = divider.size; + const auto rect = horizontal + ? QRect( + dividerMid - size.width() / 2, + (height() - size.height()) / 2, + size.width(), + size.height()) + : QRect( + (width() - size.height()) / 2, + dividerMid - size.width() / 2, + size.height(), + size.width()); + p.setBrush(((value < dividerValue) == horizontal) + ? inactiveFg + : activeFg); + const auto dividerRadius = size.width() / 2.; + p.drawRoundedRect(rect, dividerRadius, dividerRadius); + } + } const auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); if (markerSizeRatio > 0) { const auto position = qRound(markerFrom + value * markerLength) - (horizontal ? (_st.seekSize.width() / 2) : (_st.seekSize.height() / 2)); diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h index 80c7e4194..1141b8fa9 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h @@ -179,10 +179,17 @@ public: }); } + void addDivider(float64 atValue, const QSize &size); + protected: void paintEvent(QPaintEvent *e) override; private: + struct Divider { + const float64 atValue; + const QSize size; + }; + QSize getSeekDecreaseSize() const override; float64 getOverDuration() const override; @@ -190,6 +197,8 @@ private: bool _alwaysDisplayMarker = false; bool _paintDisabled = false; + std::vector<Divider> _dividers; + }; } // namespace Ui From 813470ff25038704882e3475db73d076965712f1 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 28 Jan 2021 08:46:40 +0300 Subject: [PATCH 251/396] Replaced submenu for playback speed with slider. --- Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/core/core_settings.h | 9 +- .../SourceFiles/media/view/media_view.style | 2 + .../view/media_view_playback_controls.cpp | 224 ++++++++++++++---- .../media/view/media_view_playback_controls.h | 3 +- 5 files changed, 188 insertions(+), 51 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ba9334a8e..7f89d6e4d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1721,7 +1721,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_downloads" = "Downloads"; "lng_mediaview_video_loading" = "Loading - {percent}"; "lng_mediaview_playback_speed" = "Playback speed"; -"lng_mediaview_playback_speed_normal" = "Normal"; "lng_mediaview_rotate_video" = "Rotate video"; "lng_theme_preview_title" = "Theme Preview"; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 67ffc1f25..b3735451d 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -507,10 +507,15 @@ public: [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] float64 DefaultDialogsWidthRatio(); [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { - return int(std::round(std::clamp(speed * 4., 2., 8.))) - 2; + return int(std::round(std::clamp(speed, 0.5, 2.0) * 100)); } [[nodiscard]] static float64 DeserializePlaybackSpeed(qint32 speed) { - return (std::clamp(speed, 0, 6) + 2) / 4.; + if (speed < 10) { + // The old values in settings. + return (std::clamp(speed, 0, 6) + 2) / 4.; + } else { + return std::clamp(speed, 50, 200) / 100.; + } } void resetOnLastLogout(); diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 4f0344438..40efafa80 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -301,3 +301,5 @@ pipCloseIcon: icon {{ "player_pip_close", mediaviewPlaybackIconFg }}; pipCloseIconOver: icon {{ "player_pip_close", mediaviewPlaybackIconFgOver }}; pipEnlargeIcon: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFg }}; pipEnlargeIconOver: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFgOver }}; + +speedSliderDividerSize: size(2px, 8px); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 5c6cd0994..e850b8555 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/continuous_sliders.h" #include "ui/effects/fade_animation.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/menu/menu_item_base.h" #include "ui/widgets/popup_menu.h" #include "ui/text/format_values.h" #include "ui/cached_round_corners.h" @@ -21,6 +22,166 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { namespace View { +namespace { + +constexpr auto kMinSpeed = 50; +constexpr auto kMaxSpeed = 200; + +constexpr float64 SpeedShiftToValue(float64 value) { + const auto valueAsSpeedF = value * 100.; + const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round. + return float64(valueAsSpeed) / (kMaxSpeed - kMinSpeed); +}; + +constexpr float64 SpeedToValue(float64 value) { + const auto valueAsSpeedF = value * 100.; + const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round. + return float64(valueAsSpeed - kMinSpeed) / (kMaxSpeed - kMinSpeed); +}; + +constexpr auto kSpeedStickedValues = + std::array<std::pair<float64, float64>, 5>{{ + { SpeedToValue(0.75), SpeedShiftToValue(0.03) }, + { SpeedToValue(1.00), SpeedShiftToValue(0.05) }, + { SpeedToValue(1.25), SpeedShiftToValue(0.03) }, + { SpeedToValue(1.50), SpeedShiftToValue(0.03) }, + { SpeedToValue(1.75), SpeedShiftToValue(0.03) }, + }}; + +class MenuSpeedItem final : public Ui::Menu::ItemBase { +public: + MenuSpeedItem( + not_null<RpWidget*> parent, + const style::Menu &st, + float64 startSpeed); + + not_null<QAction*> action() const override; + bool isEnabled() const override; + + [[nodiscard]] rpl::producer<float64> changeSpeedRequests() const; + +protected: + int contentHeight() const override; + +private: + QRect _itemRect; + QRect _textRect; + + const style::MediaSlider &_sliderSt; + const base::unique_qptr<Ui::MediaSlider> _slider; + const not_null<QAction*> _dummyAction; + const int _height; + + rpl::event_stream<float64> _changeSpeedRequests; + +}; + +MenuSpeedItem::MenuSpeedItem( + not_null<RpWidget*> parent, + const style::Menu &st, + float64 startSpeed) +: Ui::Menu::ItemBase(parent, st) +, _sliderSt(st::mediaviewPlayback) +, _slider(base::make_unique_q<Ui::MediaSlider>( + this, + _sliderSt)) +, _dummyAction(new QAction(parent)) +, _height(st.itemPadding.top() * 2 + + st.itemStyle.font->height + + _sliderSt.seekSize.height() + + st.itemPadding.bottom() * 2) { + + initResizeHook(parent->sizeValue()); + enableMouseSelecting(); + enableMouseSelecting(_slider.get()); + + const auto computeSpeed = [=](float64 value) { + return anim::interpolate(kMinSpeed, kMaxSpeed, value) / 100.; + }; + const auto speedString = [=](float64 value) { + return u"%1: %2x"_q + .arg(tr::lng_mediaview_playback_speed(tr::now)) + .arg(computeSpeed(value)); + }; + + _slider->setAlwaysDisplayMarker(true); + _slider->setValue((std::round(startSpeed * 100.) - kMinSpeed) + / (kMaxSpeed - kMinSpeed)); + + _slider->addDivider( + kSpeedStickedValues[1].first, + st::speedSliderDividerSize); + + { + const auto goodWidth = st.itemPadding.left() + + st.itemPadding.right() + + st.itemStyle.font->width(speedString(0.9)); + setMinWidth(std::clamp(goodWidth, st.widthMin, st.widthMax)); + } + + sizeValue( + ) | rpl::start_with_next([=](const QSize &size) { + const auto geometry = QRect(QPoint(), size); + _itemRect = geometry - st.itemPadding; + + const auto height = _itemRect.height(); + _textRect = _itemRect + - style::margins(0, 0, 0, height - st.itemStyle.font->height); + + const auto sliderGeometry = _itemRect + - style::margins(0, height - _sliderSt.seekSize.height(), 0, 0); + _slider->setGeometry(sliderGeometry); + }, lifetime()); + + paintRequest( + ) | rpl::start_with_next([=](const QRect &clip) { + Painter p(this); + + const auto selected = isSelected(); + p.fillRect(clip, selected ? st.itemBgOver : st.itemBg); + + const auto value = _slider->value(); + + p.setFont(st.itemStyle.font); + p.drawText(_textRect, speedString(value), style::al_left); + }, lifetime()); + + _slider->setChangeProgressCallback([=](float64 value) { + update(_textRect); + }); + + _slider->setChangeFinishedCallback([=](float64 value) { + _changeSpeedRequests.fire_copy(computeSpeed(value)); + }); + + _slider->setAdjustCallback([=](float64 value) { + for (const auto &snap : kSpeedStickedValues) { + if (value > (snap.first - snap.second) + && value < (snap.first + snap.second)) { + return snap.first; + } + } + return value; + }); +} + +not_null<QAction*> MenuSpeedItem::action() const { + return _dummyAction; +} + +bool MenuSpeedItem::isEnabled() const { + return true; +} + +int MenuSpeedItem::contentHeight() const { + return _height; +} + +rpl::producer<float64> MenuSpeedItem::changeSpeedRequests() const { + return _changeSpeedRequests.events(); +} + +} // namespace PlaybackControls::PlaybackControls( QWidget *parent, @@ -37,7 +198,7 @@ PlaybackControls::PlaybackControls( , _pictureInPicture(this, st::mediaviewPipButton) , _playedAlready(this, st::mediaviewPlayProgressLabel) , _toPlayLeft(this, st::mediaviewPlayProgressLabel) -, _speedMenuStyle(st::mediaviewControlsPopupMenu) +, _menuStyle(st::mediaviewControlsPopupMenu) , _fadeAnimation(std::make_unique<Ui::FadeAnimation>(this)) { _fadeAnimation->show(); _fadeAnimation->setFinishedCallback([=] { @@ -175,61 +336,32 @@ void PlaybackControls::fadeUpdated(float64 opacity) { _volumeController->setFadeOpacity(opacity); } -void PlaybackControls::validateSpeedMenuStyle() { - auto &st = _speedMenuStyle.menu; - const auto &check = st::mediaviewMenuCheck; - const auto normal = tr::lng_mediaview_playback_speed_normal(tr::now); - const auto itemHeight = st.itemPadding.top() - + st.itemStyle.font->height - + st.itemPadding.bottom(); - const auto itemWidth = st.itemPadding.left() - + st.itemStyle.font->width(normal) - + st.itemPadding.right(); - if (itemWidth + st.itemPadding.right() + check.width() > st.widthMin) { - st.widthMin = itemWidth + st.itemPadding.right() + check.width(); - } - const auto realWidth = std::clamp(itemWidth, st.widthMin, st.widthMax); - st.itemIconPosition = QPoint( - realWidth - st.itemPadding.right() - check.width(), - (itemHeight - check.height()) / 2); -} - void PlaybackControls::showMenu() { if (_menu) { _menu = nullptr; return; } - validateSpeedMenuStyle(); + _menu.emplace(this, _menuStyle); + + { + auto speedItem = base::make_unique_q<MenuSpeedItem>( + _menu, + _menuStyle.menu, + _delegate->playbackControlsCurrentSpeed()); + speedItem->changeSpeedRequests( + ) | rpl::start_with_next([=](float64 speed) { + updatePlaybackSpeed(speed); + }, speedItem->lifetime()); + _menu->addAction(std::move(speedItem)); + } + + _menu->addSeparator(); - auto submenu = std::make_unique<Ui::PopupMenu>( - this, - _speedMenuStyle); - const auto addSpeed = [&](float64 speed, QString text = QString()) { - if (text.isEmpty()) { - text = QString::number(speed); - } - const auto checked = (speed == _delegate->playbackControlsCurrentSpeed()); - const auto action = submenu->addAction( - text, - [=] { updatePlaybackSpeed(speed); }, - checked ? &st::mediaviewMenuCheck : nullptr); - }; - addSpeed(0.5); - addSpeed(0.75); - addSpeed(1., tr::lng_mediaview_playback_speed_normal(tr::now)); - addSpeed(1.25); - addSpeed(1.5); - addSpeed(1.75); - addSpeed(2.); - _menu.emplace(this, st::mediaviewControlsPopupMenu); _menu->addAction(tr::lng_mediaview_rotate_video(tr::now), [=] { _delegate->playbackControlsRotate(); }); - _menu->addSeparator(); - _menu->addAction( - tr::lng_mediaview_playback_speed(tr::now), - std::move(submenu)); + _menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomLeft); _menu->popup(mapToGlobal(_menuToggle->geometry().topLeft())); } diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index 6e6617538..0b5b71c13 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -84,7 +84,6 @@ private: void updatePlayPauseResumeState(const Player::TrackState &state); void updateTimeTexts(const Player::TrackState &state); void refreshTimeTexts(); - void validateSpeedMenuStyle(); void showMenu(); not_null<Delegate*> _delegate; @@ -112,7 +111,7 @@ private: object_ptr<Ui::LabelSimple> _toPlayLeft; object_ptr<Ui::LabelSimple> _downloadProgress = { nullptr }; - style::PopupMenu _speedMenuStyle; + const style::PopupMenu &_menuStyle; base::unique_qptr<Ui::PopupMenu> _menu; std::unique_ptr<Ui::FadeAnimation> _fadeAnimation; From 4695ccfdb851c0b918b598dec511c4d026dc58bf Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 30 Jan 2021 16:25:40 +0300 Subject: [PATCH 252/396] Fixed mouse hiding in OverlayWidget when menu of controls is displayed. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 1 + .../SourceFiles/media/view/media_view_playback_controls.cpp | 4 ++++ .../SourceFiles/media/view/media_view_playback_controls.h | 1 + 3 files changed, 6 insertions(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index f2088909c..7f66e2edb 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1274,6 +1274,7 @@ void OverlayWidget::activateControls() { void OverlayWidget::onHideControls(bool force) { if (!force) { if (!_dropdown->isHidden() + || (_streamed && _streamed->controls.hasMenu()) || _menu || _mousePressed || (_fullScreenVideo diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index e850b8555..9d873f804 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -568,6 +568,10 @@ void PlaybackControls::mousePressEvent(QMouseEvent *e) { e->accept(); // Don't pass event to the Media::View::OverlayWidget. } +bool PlaybackControls::hasMenu() const { + return _menu != nullptr; +} + PlaybackControls::~PlaybackControls() = default; } // namespace View diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index 0b5b71c13..6128f2a63 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -57,6 +57,7 @@ public: void updatePlayback(const Player::TrackState &state); void setLoadingProgress(int ready, int total); void setInFullScreen(bool inFullScreen); + [[nodiscard]] bool hasMenu() const; ~PlaybackControls(); From 4ad08376616e422bdace1775b2d131c400ffe8d3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 31 Jan 2021 05:55:55 +0300 Subject: [PATCH 253/396] Fixed ability to attach file while editing message in sections. --- .../view/controls/history_view_compose_controls.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index c34332fbe..5acb563bf 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -767,7 +767,14 @@ rpl::producer<MessageToEdit> ComposeControls::editRequests() const { } rpl::producer<> ComposeControls::attachRequests() const { - return _attachToggle->clicks() | rpl::to_empty; + return _attachToggle->clicks( + ) | rpl::to_empty | rpl::filter([=] { + if (isEditingMessage()) { + Ui::show(Box<InformBox>(tr::lng_edit_caption_attach(tr::now))); + return false; + } + return true; + }); } void ComposeControls::setMimeDataHook(MimeDataHook hook) { From b13e5ddce90ed672d086388be0f16931ce0f496e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 31 Jan 2021 06:22:54 +0300 Subject: [PATCH 254/396] Moved scroll keys from sections to compose controls. --- .../history_view_compose_controls.cpp | 22 +++++++++++++++++++ .../controls/history_view_compose_controls.h | 4 ++++ .../view/history_view_replies_section.cpp | 14 +++++------- .../view/history_view_scheduled_section.cpp | 8 ++++--- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 5acb563bf..1fcead364 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -721,6 +721,11 @@ rpl::producer<not_null<QKeyEvent*>> ComposeControls::keyEvents() const { }); } +auto ComposeControls::scrollKeyEvents() const +-> rpl::producer<not_null<QKeyEvent*>> { + return _scrollKeyEvents.events(); +} + auto ComposeControls::sendContentRequests(SendRequestType requestType) const { auto filter = rpl::filter([=] { const auto type = (_mode == Mode::Normal) @@ -947,6 +952,7 @@ void ComposeControls::init() { initSendButton(); initWriteRestriction(); initVoiceRecordBar(); + initKeyHandler(); _botCommandStart->setClickedCallback([=] { setText({ "/" }); }); @@ -1048,6 +1054,22 @@ void ComposeControls::drawRestrictedWrite(Painter &p, const QString &error) { style::al_center); } +void ComposeControls::initKeyHandler() { + _wrap->events( + ) | rpl::filter([=](not_null<QEvent*> event) { + return (event->type() == QEvent::KeyPress); + }) | rpl::start_with_next([=](not_null<QEvent*> e) { + auto keyEvent = static_cast<QKeyEvent*>(e.get()); + const auto key = keyEvent->key(); + if ((key == Qt::Key_Up) + || (key == Qt::Key_Down) + || (key == Qt::Key_PageUp) + || (key == Qt::Key_PageDown)) { + _scrollKeyEvents.fire(std::move(keyEvent)); + } + }, _wrap->lifetime()); +} + void ComposeControls::initField() { _field->setMaxHeight(st::historyComposeFieldMaxHeight); updateSubmitSettings(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index d6328a055..2e04bb290 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -122,6 +122,8 @@ public: [[nodiscard]] rpl::producer<InlineChosen> inlineResultChosen() const; [[nodiscard]] rpl::producer<SendActionUpdate> sendActionUpdates() const; [[nodiscard]] rpl::producer<not_null<QEvent*>> viewportEvents() const; + [[nodiscard]] auto scrollKeyEvents() const + -> rpl::producer<not_null<QKeyEvent*>>; using MimeDataHook = Fn<bool( not_null<const QMimeData*> data, @@ -189,6 +191,7 @@ private: void initWriteRestriction(); void initVoiceRecordBar(); void initAutocomplete(); + void initKeyHandler(); void updateSubmitSettings(); void updateSendButtonType(); void updateHeight(); @@ -290,6 +293,7 @@ private: rpl::event_stream<InlineChosen> _inlineResultChosen; rpl::event_stream<SendActionUpdate> _sendActionUpdates; rpl::event_stream<QString> _sendCommandRequests; + rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents; TextUpdateEvents _textUpdateEvents = TextUpdateEvents() | TextUpdateEvent::SaveDraft diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 5b8ee8c4a..25f9e2566 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -488,6 +488,11 @@ void RepliesWidget::setupComposeControls() { showAtPosition(pos); }, lifetime()); + _composeControls->scrollKeyEvents( + ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { + _scroll->keyPressEvent(e); + }, lifetime()); + _composeControls->keyEvents( ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { if (e->key() == Qt::Key_Up) { @@ -501,15 +506,6 @@ void RepliesWidget::setupComposeControls() { _scroll->keyPressEvent(e); } e->accept(); - } else if (e->key() == Qt::Key_Down) { - _scroll->keyPressEvent(e); - e->accept(); - } else if (e->key() == Qt::Key_PageDown) { - _scroll->keyPressEvent(e); - e->accept(); - } else if (e->key() == Qt::Key_PageUp) { - _scroll->keyPressEvent(e); - e->accept(); } }, lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 9faee54c7..690315db9 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -231,6 +231,11 @@ void ScheduledWidget::setupComposeControls() { showAtPosition(pos); }, lifetime()); + _composeControls->scrollKeyEvents( + ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { + _scroll->keyPressEvent(e); + }, lifetime()); + _composeControls->keyEvents( ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { if (e->key() == Qt::Key_Up) { @@ -246,9 +251,6 @@ void ScheduledWidget::setupComposeControls() { _scroll->keyPressEvent(e); } e->accept(); - } else if (e->key() == Qt::Key_Down) { - _scroll->keyPressEvent(e); - e->accept(); } }, lifetime()); From 062c451c2725640d2b926e218102bfd7c2b2b33f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 31 Jan 2021 07:22:46 +0300 Subject: [PATCH 255/396] Refactored handle of last editable message on Up arrow in sections. --- .../SourceFiles/data/data_replies_list.cpp | 19 ---------------- Telegram/SourceFiles/data/data_replies_list.h | 2 -- .../data/data_scheduled_messages.cpp | 22 ------------------- .../data/data_scheduled_messages.h | 2 -- .../history_view_compose_controls.cpp | 20 +++++++++-------- .../controls/history_view_compose_controls.h | 4 +++- .../history/view/history_view_list_widget.cpp | 20 ++++++++++++++++- .../history/view/history_view_list_widget.h | 3 ++- .../view/history_view_replies_section.cpp | 15 +++---------- .../view/history_view_scheduled_section.cpp | 17 +++----------- 10 files changed, 41 insertions(+), 83 deletions(-) diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index e184460b4..b2004fa79 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_replies_list.h" -#include "base/unixtime.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_service.h" @@ -627,22 +626,4 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) { return (list.size() == skipped); } -HistoryItem *RepliesList::lastEditableMessage() { - const auto message = [&](MsgId msgId) { - return _history->owner().message(_history->channelId(), msgId); - }; - - const auto now = base::unixtime::now(); - auto proj = [&](MsgId msgId) { - if (const auto item = message(msgId)) { - return item->allowsEdit(now); - } - return false; - }; - const auto it = ranges::find_if(_list, std::move(proj)); - return (it == end(_list)) - ? nullptr - : _history->owner().groups().findItemToEdit(message(*it)).get(); -} - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_replies_list.h b/Telegram/SourceFiles/data/data_replies_list.h index 945e70da8..36932b82b 100644 --- a/Telegram/SourceFiles/data/data_replies_list.h +++ b/Telegram/SourceFiles/data/data_replies_list.h @@ -31,8 +31,6 @@ public: [[nodiscard]] rpl::producer<int> fullCount() const; - [[nodiscard]] HistoryItem *lastEditableMessage(); - private: struct Viewer; diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 754d78ec8..f6bb51527 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -546,26 +546,4 @@ int32 ScheduledMessages::countListHash(const List &list) const { return HashFinalize(hash); } -HistoryItem *ScheduledMessages::lastEditableMessage( - not_null<History*> history) { - const auto i = _data.find(history); - if (i == end(_data)) { - return nullptr; - } - auto &list = i->second; - - sort(list); - const auto items = ranges::view::reverse(list.items); - - const auto now = base::unixtime::now(); - auto proj = [&](const OwnedItem &item) { - return item->allowsEdit(now); - }; - - const auto it = ranges::find_if(items, std::move(proj)); - return (it == end(items)) - ? nullptr - : history->owner().groups().findItemToEdit((*it).get()).get(); -} - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.h b/Telegram/SourceFiles/data/data_scheduled_messages.h index 650434c2c..b6656f23f 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.h +++ b/Telegram/SourceFiles/data/data_scheduled_messages.h @@ -32,8 +32,6 @@ public: [[nodiscard]] HistoryItem *lookupItem(PeerId peer, MsgId msg) const; [[nodiscard]] HistoryItem *lookupItem(FullMsgId itemId) const; [[nodiscard]] int count(not_null<History*> history) const; - [[nodiscard]] HistoryItem *lastEditableMessage( - not_null<History*> history); void checkEntitiesAndUpdate(const MTPDmessage &data); void apply(const MTPDupdateNewScheduledMessage &update); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 1fcead364..92b1f5146 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -712,20 +712,16 @@ rpl::producer<> ComposeControls::cancelRequests() const { return _cancelRequests.events(); } -rpl::producer<not_null<QKeyEvent*>> ComposeControls::keyEvents() const { - return _wrap->events( - ) | rpl::map([=](not_null<QEvent*> e) -> not_null<QKeyEvent*> { - return static_cast<QKeyEvent*>(e.get()); - }) | rpl::filter([=](not_null<QEvent*> event) { - return (event->type() == QEvent::KeyPress); - }); -} - auto ComposeControls::scrollKeyEvents() const -> rpl::producer<not_null<QKeyEvent*>> { return _scrollKeyEvents.events(); } +auto ComposeControls::editLastMessageRequests() const +-> rpl::producer<not_null<QKeyEvent*>> { + return _editLastMessageRequests.events(); +} + auto ComposeControls::sendContentRequests(SendRequestType requestType) const { auto filter = rpl::filter([=] { const auto type = (_mode == Mode::Normal) @@ -1061,6 +1057,12 @@ void ComposeControls::initKeyHandler() { }) | rpl::start_with_next([=](not_null<QEvent*> e) { auto keyEvent = static_cast<QKeyEvent*>(e.get()); const auto key = keyEvent->key(); + if (key == Qt::Key_Up) { + if (!isEditingMessage()) { + _editLastMessageRequests.fire(std::move(keyEvent)); + return; + } + } if ((key == Qt::Key_Up) || (key == Qt::Key_Down) || (key == Qt::Key_PageUp) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 2e04bb290..6570d560e 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -118,12 +118,13 @@ public: [[nodiscard]] rpl::producer<FileChosen> fileChosen() const; [[nodiscard]] rpl::producer<PhotoChosen> photoChosen() const; [[nodiscard]] rpl::producer<Data::MessagePosition> scrollRequests() const; - [[nodiscard]] rpl::producer<not_null<QKeyEvent*>> keyEvents() const; [[nodiscard]] rpl::producer<InlineChosen> inlineResultChosen() const; [[nodiscard]] rpl::producer<SendActionUpdate> sendActionUpdates() const; [[nodiscard]] rpl::producer<not_null<QEvent*>> viewportEvents() const; [[nodiscard]] auto scrollKeyEvents() const -> rpl::producer<not_null<QKeyEvent*>>; + [[nodiscard]] auto editLastMessageRequests() const + -> rpl::producer<not_null<QKeyEvent*>>; using MimeDataHook = Fn<bool( not_null<const QMimeData*> data, @@ -294,6 +295,7 @@ private: rpl::event_stream<SendActionUpdate> _sendActionUpdates; rpl::event_stream<QString> _sendCommandRequests; rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents; + rpl::event_stream<not_null<QKeyEvent*>> _editLastMessageRequests; TextUpdateEvents _textUpdateEvents = TextUpdateEvents() | TextUpdateEvent::SaveDraft diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index c639b9bcf..fe21642b3 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/history_view_list_widget.h" +#include "base/unixtime.h" #include "history/history_message.h" #include "history/history_item_components.h" #include "history/history_item_text.h" @@ -2725,10 +2726,27 @@ rpl::producer<FullMsgId> ListWidget::editMessageRequested() const { return _requestedToEditMessage.events(); } -void ListWidget::editMessageRequestNotify(FullMsgId item) { +void ListWidget::editMessageRequestNotify(FullMsgId item) const { _requestedToEditMessage.fire(std::move(item)); } +bool ListWidget::lastMessageEditRequestNotify() const { + const auto now = base::unixtime::now(); + auto proj = [&](not_null<Element*> view) { + return view->data()->allowsEdit(now); + }; + const auto &list = ranges::view::reverse(_items); + const auto it = ranges::find_if(list, std::move(proj)); + if (it == end(list)) { + return false; + } else { + const auto item = + session().data().groups().findItemToEdit((*it)->data()).get(); + editMessageRequestNotify(item->fullId()); + return true; + } +} + rpl::producer<FullMsgId> ListWidget::replyToMessageRequested() const { return _requestedToReplyToMessage.events(); } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index d01e5dd06..95a2174cd 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -206,7 +206,8 @@ public: bool tooltipWindowActive() const override; [[nodiscard]] rpl::producer<FullMsgId> editMessageRequested() const; - void editMessageRequestNotify(FullMsgId item); + void editMessageRequestNotify(FullMsgId item) const; + [[nodiscard]] bool lastMessageEditRequestNotify() const; [[nodiscard]] rpl::producer<FullMsgId> replyToMessageRequested() const; void replyToMessageRequestNotify(FullMsgId item); [[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 25f9e2566..9044de419 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -493,19 +493,10 @@ void RepliesWidget::setupComposeControls() { _scroll->keyPressEvent(e); }, lifetime()); - _composeControls->keyEvents( + _composeControls->editLastMessageRequests( ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { - if (e->key() == Qt::Key_Up) { - if (!_composeControls->isEditingMessage()) { - if (const auto item = _replies->lastEditableMessage()) { - _inner->editMessageRequestNotify(item->fullId()); - } else { - _scroll->keyPressEvent(e); - } - } else { - _scroll->keyPressEvent(e); - } - e->accept(); + if (!_inner->lastMessageEditRequestNotify()) { + _scroll->keyPressEvent(e); } }, lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 690315db9..f2d11d08e 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -236,21 +236,10 @@ void ScheduledWidget::setupComposeControls() { _scroll->keyPressEvent(e); }, lifetime()); - _composeControls->keyEvents( + _composeControls->editLastMessageRequests( ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { - if (e->key() == Qt::Key_Up) { - if (!_composeControls->isEditingMessage()) { - const auto item = session().data().scheduledMessages() - .lastEditableMessage(_history); - if (item) { - _inner->editMessageRequestNotify(item->fullId()); - } else { - _scroll->keyPressEvent(e); - } - } else { - _scroll->keyPressEvent(e); - } - e->accept(); + if (!_inner->lastMessageEditRequestNotify()) { + _scroll->keyPressEvent(e); } }, lifetime()); From 0b98cfbfec4bc35cd4f60881338ee344f1dbc4ec Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 31 Jan 2021 07:33:24 +0300 Subject: [PATCH 256/396] Added ability to attach file with shortcut in sections. --- .../view/controls/history_view_compose_controls.cpp | 11 +++++++++-- .../view/controls/history_view_compose_controls.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 92b1f5146..097504ce5 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -768,8 +768,10 @@ rpl::producer<MessageToEdit> ComposeControls::editRequests() const { } rpl::producer<> ComposeControls::attachRequests() const { - return _attachToggle->clicks( - ) | rpl::to_empty | rpl::filter([=] { + return rpl::merge( + _attachToggle->clicks() | rpl::to_empty, + _attachRequests.events() + ) | rpl::filter([=] { if (isEditingMessage()) { Ui::show(Box<InformBox>(tr::lng_edit_caption_attach(tr::now))); return false; @@ -1057,6 +1059,11 @@ void ComposeControls::initKeyHandler() { }) | rpl::start_with_next([=](not_null<QEvent*> e) { auto keyEvent = static_cast<QKeyEvent*>(e.get()); const auto key = keyEvent->key(); + const auto isCtrl = keyEvent->modifiers() == Qt::ControlModifier; + if (key == Qt::Key_O && isCtrl) { + _attachRequests.fire({}); + return; + } if (key == Qt::Key_Up) { if (!isEditingMessage()) { _editLastMessageRequests.fire(std::move(keyEvent)); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 6570d560e..fbb2975e8 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -296,6 +296,7 @@ private: rpl::event_stream<QString> _sendCommandRequests; rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents; rpl::event_stream<not_null<QKeyEvent*>> _editLastMessageRequests; + rpl::event_stream<> _attachRequests; TextUpdateEvents _textUpdateEvents = TextUpdateEvents() | TextUpdateEvent::SaveDraft From f1236edf5b3ec0119c211f4df91eafb2b60b4d87 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 31 Jan 2021 23:49:25 +0300 Subject: [PATCH 257/396] Added ability to select message for reply with Ctrl+Up/Down in sections. --- .../view/controls/compose_controls_common.h | 9 ++++ .../history_view_compose_controls.cpp | 42 ++++++++++++++++ .../controls/history_view_compose_controls.h | 4 ++ .../history/view/history_view_list_widget.cpp | 48 +++++++++++++++++++ .../history/view/history_view_list_widget.h | 4 ++ .../view/history_view_replies_section.cpp | 15 ++++++ 6 files changed, 122 insertions(+) diff --git a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h index 355070749..25a9a2744 100644 --- a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h +++ b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h @@ -40,4 +40,13 @@ struct SetHistoryArgs { rpl::producer<std::optional<QString>> writeRestriction; }; +struct ReplyNextRequest { + enum class Direction { + Next, + Previous, + }; + const FullMsgId replyId; + const Direction direction; +}; + } // namespace HistoryView::Controls diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 097504ce5..85da5e24d 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/controls/history_view_compose_controls.h" #include "base/event_filter.h" +#include "base/platform/base_platform_info.h" #include "base/qt_signal_producer.h" #include "base/unixtime.h" #include "chat_helpers/emoji_suggestions_widget.h" @@ -64,6 +65,11 @@ constexpr auto kMouseEvents = { QEvent::MouseButtonRelease }; +constexpr auto kCommonModifiers = 0 + | Qt::ShiftModifier + | Qt::MetaModifier + | Qt::ControlModifier; + using FileChosen = ComposeControls::FileChosen; using PhotoChosen = ComposeControls::PhotoChosen; using MessageToEdit = ComposeControls::MessageToEdit; @@ -722,6 +728,11 @@ auto ComposeControls::editLastMessageRequests() const return _editLastMessageRequests.events(); } +auto ComposeControls::replyNextRequests() const +-> rpl::producer<ReplyNextRequest> { + return _replyNextRequests.events(); +} + auto ComposeControls::sendContentRequests(SendRequestType requestType) const { auto filter = rpl::filter([=] { const auto type = (_mode == Mode::Normal) @@ -1077,6 +1088,37 @@ void ComposeControls::initKeyHandler() { _scrollKeyEvents.fire(std::move(keyEvent)); } }, _wrap->lifetime()); + + base::install_event_filter(_wrap.get(), _field, [=](not_null<QEvent*> e) { + using Result = base::EventFilterResult; + if (e->type() != QEvent::KeyPress) { + return Result::Continue; + } + const auto k = static_cast<QKeyEvent*>(e.get()); + + if ((k->modifiers() & kCommonModifiers) == Qt::ControlModifier) { + const auto isUp = (k->key() == Qt::Key_Up); + const auto isDown = (k->key() == Qt::Key_Down); + if (isUp || isDown) { + if (Platform::IsMac()) { + // Cmd + Up is used instead of Home. + if ((isUp && (!_field->textCursor().atStart())) + // Cmd + Down is used instead of End. + || (isDown && (!_field->textCursor().atEnd()))) { + return Result::Continue; + } + } + _replyNextRequests.fire({ + .replyId = replyingToMessage(), + .direction = (isDown + ? ReplyNextRequest::Direction::Next + : ReplyNextRequest::Direction::Previous) + }); + return Result::Cancel; + } + } + return Result::Continue; + }); } void ComposeControls::initField() { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index fbb2975e8..c107a3f0c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -82,6 +82,7 @@ public: using VoiceToSend = Controls::VoiceToSend; using SendActionUpdate = Controls::SendActionUpdate; using SetHistoryArgs = Controls::SetHistoryArgs; + using ReplyNextRequest = Controls::ReplyNextRequest; using FieldHistoryAction = Ui::InputField::HistoryAction; enum class Mode { @@ -125,6 +126,8 @@ public: -> rpl::producer<not_null<QKeyEvent*>>; [[nodiscard]] auto editLastMessageRequests() const -> rpl::producer<not_null<QKeyEvent*>>; + [[nodiscard]] auto replyNextRequests() const + -> rpl::producer<ReplyNextRequest>; using MimeDataHook = Fn<bool( not_null<const QMimeData*> data, @@ -297,6 +300,7 @@ private: rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents; rpl::event_stream<not_null<QKeyEvent*>> _editLastMessageRequests; rpl::event_stream<> _attachRequests; + rpl::event_stream<ReplyNextRequest> _replyNextRequests; TextUpdateEvents _textUpdateEvents = TextUpdateEvents() | TextUpdateEvent::SaveDraft diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index fe21642b3..45048eeb3 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -525,6 +525,11 @@ void ListWidget::updateHighlightedMessage() { _highlightedMessageId = FullMsgId(); } +void ListWidget::clearHighlightedMessage() { + _highlightedMessageId = FullMsgId(); + updateHighlightedMessage(); +} + void ListWidget::checkUnreadBarCreation() { if (!_bar.element) { if (auto data = _delegate->listMessagesBar(_items); data.bar.element) { @@ -2759,6 +2764,49 @@ rpl::producer<FullMsgId> ListWidget::readMessageRequested() const { return _requestedToReadMessage.events(); } +rpl::producer<FullMsgId> ListWidget::showMessageRequested() const { + return _requestedToShowMessage.events(); +} + +void ListWidget::replyNextMessage(FullMsgId fullId, bool next) { + const auto reply = [&](Element *view) { + if (view) { + const auto newFullId = view->data()->fullId(); + replyToMessageRequestNotify(newFullId); + _requestedToShowMessage.fire_copy(newFullId); + } else { + replyToMessageRequestNotify(FullMsgId()); + clearHighlightedMessage(); + } + }; + const auto replyFirst = [&] { + reply(next ? nullptr : _items.back().get()); + }; + if (!fullId) { + replyFirst(); + return; + } + + auto proj = [&](not_null<Element*> view) { + return view->data()->fullId() == fullId; + }; + const auto &list = ranges::view::reverse(_items); + const auto it = ranges::find_if(list, std::move(proj)); + if (it == end(list)) { + replyFirst(); + return; + } else { + const auto nextIt = it + (next ? -1 : 1); + if (nextIt == end(list)) { + return; + } else if (next && (it == begin(list))) { + reply(nullptr); + } else { + reply(nextIt->get()); + } + } +} + ListWidget::~ListWidget() = default; void ConfirmDeleteSelectedItems(not_null<ListWidget*> widget) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 95a2174cd..8d6cf9169 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -211,6 +211,8 @@ public: [[nodiscard]] rpl::producer<FullMsgId> replyToMessageRequested() const; void replyToMessageRequestNotify(FullMsgId item); [[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const; + [[nodiscard]] rpl::producer<FullMsgId> showMessageRequested() const; + void replyNextMessage(FullMsgId fullId, bool next = true); // ElementDelegate interface. Context elementContext() override; @@ -448,6 +450,7 @@ private: void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo); void updateHighlightedMessage(); + void clearHighlightedMessage(); // This function finds all history items that are displayed and calls template method // for each found message (in given direction) in the passed history with passed top offset. @@ -555,6 +558,7 @@ private: rpl::event_stream<FullMsgId> _requestedToEditMessage; rpl::event_stream<FullMsgId> _requestedToReplyToMessage; rpl::event_stream<FullMsgId> _requestedToReadMessage; + rpl::event_stream<FullMsgId> _requestedToShowMessage; rpl::lifetime _viewerLifetime; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 9044de419..e76676f22 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -221,6 +221,13 @@ RepliesWidget::RepliesWidget( replyToMessage(fullId); }, _inner->lifetime()); + _inner->showMessageRequested( + ) | rpl::start_with_next([=](auto fullId) { + if (const auto item = session().data().message(fullId)) { + showAtPosition(item->position()); + } + }, _inner->lifetime()); + _composeControls->sendActionUpdates( ) | rpl::start_with_next([=](ComposeControls::SendActionUpdate &&data) { session().sendProgressManager().update( @@ -500,6 +507,14 @@ void RepliesWidget::setupComposeControls() { } }, lifetime()); + _composeControls->replyNextRequests( + ) | rpl::start_with_next([=](ComposeControls::ReplyNextRequest &&data) { + using Direction = ComposeControls::ReplyNextRequest::Direction; + _inner->replyNextMessage( + data.replyId, + data.direction == Direction::Next); + }, lifetime()); + _composeControls->setMimeDataHook([=]( not_null<const QMimeData*> data, Ui::InputField::MimeAction action) { From 03a5619d61617679661ef8af80fd02041c7fa98f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 1 Feb 2021 04:00:58 +0300 Subject: [PATCH 258/396] Added ability to reschedule multiple messages. Fixed #9851. --- Telegram/Resources/langs/lang.strings | 1 + .../view/history_view_context_menu.cpp | 64 ++++++++++++++----- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7f89d6e4d..dd78fbb46 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1583,6 +1583,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_copy_selected_items" = "Copy Selected as Text"; "lng_context_forward_selected" = "Forward Selected"; "lng_context_send_now_selected" = "Send selected now"; +"lng_context_reschedule_selected" = "Reschedule Selected"; "lng_context_delete_selected" = "Delete Selected"; "lng_context_clear_selection" = "Clear Selection"; "lng_send_image_empty" = "Could not send an empty file: {name}"; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index dcabec08a..20f399061 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -443,31 +443,61 @@ bool AddSendNowMessageAction( return true; } -bool AddRescheduleMessageAction( +bool AddRescheduleAction( not_null<Ui::PopupMenu*> menu, const ContextMenuRequest &request, not_null<ListWidget*> list) { - if (!HasEditMessageAction(request, list) - || !request.item->isScheduled()) { + const auto owner = &request.navigation->session().data(); + + const auto goodSingle = !(!HasEditMessageAction(request, list) + || !request.item->isScheduled()); + const auto goodMany = [&] { + if (goodSingle) { + return false; + } + if (!request.overSelection || request.selectedItems.empty()) { + return false; + } + return true; + }(); + if (!goodSingle && !goodMany) { return false; } - const auto owner = &request.item->history()->owner(); - const auto itemId = request.item->fullId(); - menu->addAction(tr::lng_context_reschedule(tr::now), [=] { - const auto item = owner->message(itemId); - if (!item) { + auto ids = goodSingle + ? MessageIdsList{ request.item->fullId() } + : ExtractIdsList(request.selectedItems); + ranges::sort(ids, [&](const FullMsgId &a, const FullMsgId &b) { + const auto itemA = owner->message(a); + const auto itemB = owner->message(b); + return (itemA && itemB) && (itemA->position() < itemB->position()); + }); + + auto text = ((ids.size() == 1) + ? tr::lng_context_reschedule + : tr::lng_context_reschedule_selected)(tr::now); + + menu->addAction(std::move(text), [=] { + const auto firstItem = owner->message(ids.front()); + if (!firstItem) { return; } + list->cancelSelection(); const auto callback = [=](Api::SendOptions options) { - if (const auto item = owner->message(itemId)) { + for (const auto &id : ids) { + const auto item = owner->message(id); + if (!item && !item->isScheduled()) { + continue; + } if (!item->media() || !item->media()->webpage()) { options.removeWebPageId = true; } Api::RescheduleMessage(item, options); + // Increase the scheduled date by 1ms to keep the order. + options.scheduled += 1; } }; - const auto peer = item->history()->peer; + const auto peer = firstItem->history()->peer; const auto sendMenuType = !peer ? SendMenu::Type::Disabled : peer->isSelf() @@ -477,9 +507,10 @@ bool AddRescheduleMessageAction( : SendMenu::Type::Scheduled; using S = Data::ScheduledMessages; - const auto date = (item->date() == S::kScheduledUntilOnlineTimestamp) + const auto itemDate = firstItem->date(); + const auto date = (itemDate == S::kScheduledUntilOnlineTimestamp) ? HistoryView::DefaultScheduleTime() - : item->date() + 600; + : itemDate + 600; const auto box = Ui::show( HistoryView::PrepareScheduleBox( @@ -490,9 +521,10 @@ bool AddRescheduleMessageAction( Ui::LayerOption::KeepOther); owner->itemRemoved( - itemId - ) | rpl::start_with_next([=] { - box->closeBox(); + ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) { + if (ranges::contains(ids, item->fullId())) { + box->closeBox(); + } }, box->lifetime()); }); return true; @@ -825,7 +857,7 @@ void AddMessageActions( AddDeleteAction(menu, request, list); AddReportAction(menu, request, list); AddSelectionAction(menu, request, list); - AddRescheduleMessageAction(menu, request, list); + AddRescheduleAction(menu, request, list); } void AddCopyLinkAction( From 0cb8f2cc8502345f5634a037adfe33bd2095c112 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 1 Feb 2021 05:02:50 +0300 Subject: [PATCH 259/396] Added ability to toggle silent broadcast from sections. Fixed #8655. --- .../history_view_compose_controls.cpp | 52 ++++++++++++++++--- .../controls/history_view_compose_controls.h | 5 ++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 85da5e24d..b75d559a6 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/format_values.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" +#include "ui/special_buttons.h" #include "window/window_session_controller.h" #include "mainwindow.h" @@ -663,6 +664,10 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { if (!channel->mgInfo->botStatus) { session().api().requestBots(channel); } + } else if (hasSilentBroadcastToggle()) { + _silent = std::make_unique<Ui::SilentToggle>( + _wrap.get(), + peer->asChannel()); } session().local().readDraftsWithCursors(_history); applyDraft(); @@ -1297,6 +1302,17 @@ void ComposeControls::updateFieldPlaceholder() { updateSendButtonType(); } +void ComposeControls::updateSilentBroadcast() { + if (!_silent || !_history) { + return; + } + const auto &peer = _history->peer; + if (!session().data().notifySilentPostsUnknown(peer)) { + _silent->setChecked(session().data().notifySilentPosts(peer)); + updateFieldPlaceholder(); + } +} + void ComposeControls::fieldChanged() { if (!_inlineBot && !_header->isEditingMessage() @@ -1720,14 +1736,15 @@ void ComposeControls::finishAnimating() { void ComposeControls::updateControlsGeometry(QSize size) { // _attachToggle -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel - // (_attachDocument|_attachPhoto) _field _botCommandStart _tabbedSelectorToggle _send + // (_attachDocument|_attachPhoto) _field (_silent|_botCommandStart) _tabbedSelectorToggle _send const auto fieldWidth = size.width() - _attachToggle->width() - st::historySendRight - _send->width() - _tabbedSelectorToggle->width() - - (_botCommandShown ? _botCommandStart->width() : 0); + - (_botCommandShown ? _botCommandStart->width() : 0) + - (_silent ? _silent->width() : 0); { const auto oldFieldHeight = _field->height(); _field->resizeToWidth(fieldWidth); @@ -1758,6 +1775,9 @@ void ComposeControls::updateControlsGeometry(QSize size) { _tabbedSelectorToggle->moveToRight(right, buttonsTop); right += _tabbedSelectorToggle->width(); _botCommandStart->moveToRight(right, buttonsTop); + if (_silent) { + _silent->moveToRight(right, buttonsTop); + } _voiceRecordBar->resizeToWidth(size.width()); _voiceRecordBar->moveToLeft( @@ -2155,12 +2175,20 @@ void ComposeControls::initWebpageProcess() { session().changes().peerUpdates( Data::PeerUpdate::Flag::Rights + | Data::PeerUpdate::Flag::Notifications ) | rpl::filter([=](const Data::PeerUpdate &update) { return (update.peer.get() == peer); - }) | rpl::start_with_next([=] { - checkPreview(); - updateStickersByEmoji(); - updateFieldPlaceholder(); + }) | rpl::map([](const Data::PeerUpdate &update) { + return update.flags; + }) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) { + if (flags & Data::PeerUpdate::Flag::Rights) { + checkPreview(); + updateStickersByEmoji(); + updateFieldPlaceholder(); + } + if (flags & Data::PeerUpdate::Flag::Notifications) { + updateSilentBroadcast(); + } }, lifetime); base::ObservableViewer( @@ -2263,6 +2291,18 @@ bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const { return false; } +bool ComposeControls::hasSilentBroadcastToggle() const { + if (!_history) { + return false; + } + const auto &peer = _history->peer; + return peer + && peer->isChannel() + && !peer->isMegagroup() + && peer->canWrite() + && !session().data().notifySilentPostsUnknown(peer); +} + void ComposeControls::updateInlineBotQuery() { if (!_history) { return; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index c107a3f0c..52d49fc55 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -49,6 +49,7 @@ namespace Ui { class SendButton; class IconButton; class EmojiButton; +class SilentToggle; } // namespace Ui namespace Main { @@ -218,6 +219,7 @@ private: void checkAutocomplete(); void updateStickersByEmoji(); void updateFieldPlaceholder(); + void updateSilentBroadcast(); void editMessage(not_null<HistoryItem*> item); void escape(); @@ -235,6 +237,8 @@ private: void clearInlineBot(); void inlineBotChanged(); + bool hasSilentBroadcastToggle() const; + // Look in the _field for the inline bot and query string. void updateInlineBotQuery(); @@ -279,6 +283,7 @@ private: const not_null<Ui::EmojiButton*> _tabbedSelectorToggle; const not_null<Ui::InputField*> _field; const not_null<Ui::IconButton*> _botCommandStart; + std::unique_ptr<Ui::SilentToggle> _silent; std::unique_ptr<InlineBots::Layout::Widget> _inlineResults; std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel; From 0aaa88cb7950959bb3b9afdb231887ed3ec11a68 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 1 Feb 2021 09:33:32 +0300 Subject: [PATCH 260/396] Added animation to toggle silent broadcast button. --- .../icons/send_control_silent_on.png | Bin 572 -> 0 bytes .../icons/send_control_silent_on@2x.png | Bin 1051 -> 0 bytes .../icons/send_control_silent_on@3x.png | Bin 1895 -> 0 bytes Telegram/SourceFiles/ui/chat/chat.style | 10 ++- Telegram/SourceFiles/ui/special_buttons.cpp | 72 +++++++++++++----- Telegram/SourceFiles/ui/special_buttons.h | 12 ++- 6 files changed, 71 insertions(+), 23 deletions(-) delete mode 100644 Telegram/Resources/icons/send_control_silent_on.png delete mode 100644 Telegram/Resources/icons/send_control_silent_on@2x.png delete mode 100644 Telegram/Resources/icons/send_control_silent_on@3x.png diff --git a/Telegram/Resources/icons/send_control_silent_on.png b/Telegram/Resources/icons/send_control_silent_on.png deleted file mode 100644 index a22728951ad2f05e5377445cec1087a7195df183..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>k}@P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00065Nkl<ZNQt$U zKWj2U5QpdffQXGYiOm@-lJX#hV5f*5K<hxt5Q3H|<Pa=UrAT3_^9x)dM?meZ%|{5P zvj~Yz3J;?3o)j<Mn?#Ivk^Eqay_wl(XLpu8MMRK4lo~ZbQIx*}wA*c2EEaNea}$0+ z5@Cy$%cZEQD$C_kbX^aXvsOX9UKhhKzI-c{O5(b%sOrCPP1D46T`3lep&+d6<m3c^ z$z+0QnurLBqHuL}#U^z+9dupi^E}_|{~(o0F&qv7czu1PTrLN`<Kts8nGC9`a&T~f zWm(K-vj7qbAR@$KF&xJMU_PI7dV0DsIhV@?kV2sl4zSvmhld9MeBZ~itc{Rnvk5@A z+YJv85uTr)0eF9Z2f(&%G)-HJ%Vx6xJUu-{1NgoVK($)M^E?3h{XU6AVy)U$rI7$D zi3s_8p6PT7z-TmLZ*Om7?5_YK!qL$YkB^T4jK^aR4-W(1?d|RE01;t-f1msNdjMWu zUdZS3f$#G2a(94;kVqsL3<dyro=2rp`RNWs2Z#uoreWK5&}Fq+EgGQJYHd3>L<GyS zf^NIMzD7}$?SRjdN0s9^q|@o20lR8Cosvu@x1T{&Gfk7pWCFn1*;#bJ#l^*!m7brU zuf-UKL8H+K*G1ng9*@)O^>}-G<L>T`csw2|FVd6w8%6%Bh^i0y@Csj;PnInJ0000< KMNUMnLSTXjRsXaA diff --git a/Telegram/Resources/icons/send_control_silent_on@2x.png b/Telegram/Resources/icons/send_control_silent_on@2x.png deleted file mode 100644 index 810b0947009cfc2096dafdffdd312cc9f05bee93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1051 zcmV+$1mydPP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm000BxNkl<ZXo2mS z%}Z)s6vo%-L#?E=K}7U{2_XeV31$$6H46lxVI@tAK<E#Un$)O4Ac!Nuc}YbSBpMZB z&?v-41r5Xu6zQm7na>?ue0go#y`~;DxC{1Zul+phclO@rEI1qyfqrl_)VhFj;~zk+ zLofh|L_&7Eos!8UB@zj;*=!_{NCfeM5W{A(;d5B6Rw2BE0eF3VWdP#wc*T9V97UxA zC@jlyTt$5viD<Kuf@5Q2w6?ZJGcz*<=Q)m}Xf#SMFE17I{QEX>9C~_s{+OfF>2Q60 z%^aJr;_~tm)6>({xdS^pJBUOg&}y{>=O-p6kV>VBF8=>vv)LdPi-iMNU0wapER{-O zVPT<Qo>r?xBoZkJ!sqirCX-bjptrXd>2x~J?BnAD8yg!1^OZ^^?(gq$a&m&j#YHF- z3N$x2LnIPmaBvVyOG`LCJ^d0wB9T;n2j=JJ!Sg(0Xti3=*x1PAH#If=)pT-l62V}Q z(X`v`!rX!J@o|JgA;$3d_!v^Dw4|+<N~Q34JdCDJr>i^w5uvZI4|jKWjN$F=ExNnA zONRKo6IWMP!T=Bv+S}XV^Z6Kvr>7^3jEs~7F*P;C=#7q!3Ijky*xue|41wQm!ra_k zNf2jeXL(v%TU){bTwGif1OR}yw>PY;tb7~5WHROHcsw5A0G^+p^UOZKW^+%F<G7+V zl}csa`cNn&9Kiegd!E_s>@1?uC<EbiIw2B?iqaH|#mqwy4B&GR5mYJ_9v&W;Ij`3X zxm;edZq)#Y2wh!W@caGDd@vY<LZPUC03t$5OAGe*_nDk<I1G(OQ~v-&gocI&?C$O| zd5J^<Mx*gd-Or6+%>Z&^eSIC7OeQZklgYqrHW$oSC=_{L<(dN!5ex<c5{U$}W^Zq= zU?V7(%i;BU>j!{{pjN994u_d_E|&{!ZEZ}xNF;*O>8u|BBErDH00Mykv-alZreGuZ zosVEu0Eh@OnG9~Xn_2((_=w@*;cwMY6M)?Da5x-{Ml2SCUazlzfZXx0SS-k9vw1q{ zbQ;Uc%fbR|Zf=$hkQ*kGiD?A0*(@v;i!gVfQ1Rse5E1lxJz}vKqv>!sif#mp0{E(U zA;ax<qobpve1KXMpNl{sfd2me@&IZb;cysgwfg4*0KXeSgTYY#Tc}#YY&J8E;K9Mc z&jBDJ7>!0GlSu%8-|t6jYisoY3U#2l;Ca4i9gRkV^Ye3bc6R<Lukrv#M@L@*xLmGE z`&Ax5rBWdtk2A%`VlfO24b?wD4&w0e5IoO==XtnXE+L8+?k8Wh;@_Y8TE~wE_yyqB Vog5&cKLr2)002ovPDHLkV1g{^-r)cM diff --git a/Telegram/Resources/icons/send_control_silent_on@3x.png b/Telegram/Resources/icons/send_control_silent_on@3x.png deleted file mode 100644 index 4eaa2d384e57e2efef1e42e02ff7b204a45af7cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000LrNkl<Zc%1E< zUr1AL7{{Nj<5t6ITT;tEbg|k}Ct)Rl;s~$8p_HN*dQ+GLITl72Aql^$5RKJUbScO} z%PTKLSZ05qLNXO(g@u0zr{!j1?qB=!{awiX<<IS$ZBEbBec`>?-t#=q`MmFW&U4=P z903u5k_sy!-vK1Ll7Kjr1jL~vAPyw~afsnNfWLL@*fBbP{ya$}5^_47^zq|I`tjoj z-(e9Fq1DyZ!D6xCZ&@rB)YaAT9UK2}Dk>@v2m}yHfj|Hi6&3pz5W_GS9v%(}C#d1! zVK5BCOMJX1X_ZPvN~Myuk5Z{5l}fck5p5LjfKpRaNho9wLnstdYHI2ZMf5-4ym<Xf zZnv8R0s(6ufj~fRw|j>o+Nc9W^iQ-0-MxF4E?v4rLqkKcIX!TIG#U-*^?JH<=MJ4X zae@_+NF<^Q7cNkCb~f2;HnQ981Ry5gu_&sfRbOAfe#hzT>_kdRN>s<|bUO6)^@Y7_ zaVZc8ps%kFI-M@@?qX$SW$<`B8v-5}7=TnN<vdm@mE!5sr||pzF@gEN{C+=rdwY?W zmzNlT3JMCayu2JT*pZPDC=?1#8^y)Nu-R;J0Tk5FpFeT+>Q(*$efaQ!HPET4DX7(I zPCII~8q?F$afRgf`*HvNeFy}CxC0UdDRC($Cx`m``$?nGgzaOu+o`g$lHR?0$7)NV zP*8t=KNS`h64Bb7?9-=D^zGX>ve|6p^ZAHj7|O`Vppz$0(%G|T>EOYGoH0Ip_>gYj zz8%TwM5Tyn%49MO4Gpo5rqk)f<;#~N%1KY2Jb}SrfLg5%o5!S5Db#8;3<d+n$HzHm z)4hB5__@S_O)M6pr>BQCNT1J#n>TO9eY%UqVl*{1VSau-tnWY|5YYf_8z3TrP$-1a zXyhEt?aB-J`T2PJ_H9_dU%!4uPEHPgfP$^LxtX(`>FVl&NF<6oq>PLVym;{<tlvkE z9`O%|h)`Qw3%A?N3hCv`ml4%U(YCCtER2qhhV<!hIG|Fg_y<HpxOnj*e*OBz3Tbq7 z6vvMrk2|E|;$k?R&X7KhMq^?CA|hzDT6n!)R!Gy+)5y)ujXR`p`S8n^FF1PiXkq{= zFE8f=1ORrs9VI0taRsDOsX|uhUauGB<>gzl9k=C<qM{;V816j)xm-?Wvze-@s^aCE zCMPG!Y&LKDonaU%Dk|EN?YICE3We)caU?o)=ny@7_Kfs;eLUO~5z(txuR{J_SXjup zAjhRGPjQJvf{BTVkW-vXZEbCFsg=&1I|qluvC00#!~`S~3BNfgJv|+Zi;J6rTU=a3 zOG^vq5=T60r6Wg<gpDCRJ)K`bGMOyo=*`d1gJBrd)YQP`a<M`(o6Sf|ON%)MhG9ZJ zf;b!w$Ye5p0m<d^O$%uNV6|G;`)IXVEG;duLK+(zLuO`X%yC$))=f4&9uMSldEx^i zT6>0?ot<TcG&3_3^D5!42Sl{?c)+!6VYk~+T3Wh40THbw^})eGR!A<F3)R)t`x6im zK_n8vWHPZr^7(u)7!3Op5D`Hj5TL!iofQ%Qw70iMR4cW#wCpWF!DcWR;Pd%dA(>33 zh-#&#rlt+H_5>gzLUnaDXRS0iI2cjUcjd~J^(Bx!1Bi%FT3QOb-OdVWe0&^PSy`O6 zN=iy#x7+s?AR<CuULIy<W>_K3&dwq~KcCZ9Zf-6nCnxt7AR<C$W+ukQ##kXOEiFN- z)pFWWC=~oZi^Urd5g{!t4Q8{M6_U&4LQPE#r>!ln79=JhqP1G7tE-C@lHc#gty{OY zd=9%3P;m6y+S*tl0l<R?54L3wF5v+Mn_jQytd)9tdLR~y6AzF?BH4C8M1-oUs<2x; zLA`$c8ix-bPAou;jg299SmO<d=)d}gO%{s<+1c5N0jQy&A?(iTb^szGoIZV;^Bp6r z)e4P9vt?fqzaJDz4Gj(S`0-;Bi9{j)Uszb6Q>RYFYMxaJg@Sr}d+E%XGhzGu{{5RO zD=Wk9cton0$X>sGJ!IZVH2wMW2eq}eJ9~*WHa14IrIRQM1OjMoZr&L{H*Vb66*DK4 zj7DQDwNi9|R#sN{KF=-b`Sa(|)=JR<;wsj5gWkM(6U|;(bbz?FZFY-3fBqclYkDzf z_k?b0wHi}XQ>>8Qzkk1f0THdO1&xf1goLxayo`c^g8dDMXze}gfq{Vya6BFl%F4<j zwZUKT9UUD}<&BPxj+p$Uq@<v;vlGBtO;lfBAMvrgeipv4u&}NDgYtU4bnV(T`u_bp zIUEi$nM_d~&)W=ADwSAWU5#k=tgfy?sZ{b2Cx3v52(7KH5dms#ZRIOQegO#t0vL@( z*6-$Hd_>qAfPzh@)1kY&8*_7WSXo)Y+}s?xySsPt)638U7^IX`Xz~78c%mx_h(k$0 h97+P>P!bS_{sry290_+TN;&`l002ovPDHLkV1hJdbj<(& diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index e112415b2..c34328a02 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -418,8 +418,14 @@ historySilentToggle: IconButton(historyBotKeyboardShow) { icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; iconOver: icon {{ "send_control_silent_off", historyComposeIconFgOver }}; } -historySilentToggleOn: icon {{ "send_control_silent_on", historyComposeIconFg }}; -historySilentToggleOnOver: icon {{ "send_control_silent_on", historyComposeIconFgOver }}; + +historySilentToggleCrossLine: CrossLineAnimation { + fg: historyComposeIconFg; + icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; + startPosition: point(5px, 3px); + endPosition: point(21px, 18px); + stroke: 2px; +} historyReplySkip: 51px; historyReplyNameFg: windowActiveTextFg; diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index a353e5465..a3bad8c0a 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -43,6 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { +constexpr auto kAnimationDuration = crl::time(120); + QString CropTitle(not_null<PeerData*> peer) { if (peer->isChat() || peer->isMegagroup()) { return tr::lng_create_group_crop(tr::now); @@ -858,19 +860,43 @@ void UserpicButton::prepareUserpicPixmap() { //} SilentToggle::SilentToggle(QWidget *parent, not_null<ChannelData*> channel) -: IconButton(parent, st::historySilentToggle) +: RippleButton(parent, st::historySilentToggle.ripple) +, _st(st::historySilentToggle) +, _colorOver(st::historyComposeIconFgOver->c) , _channel(channel) -, _checked(channel->owner().notifySilentPosts(_channel)) { +, _checked(channel->owner().notifySilentPosts(_channel)) +, _crossLine(st::historySilentToggleCrossLine) { Expects(!channel->owner().notifySilentPostsUnknown(_channel)); - if (_checked) { - refreshIconOverrides(); - } + resize(_st.width, _st.height); + + style::PaletteChanged( + ) | rpl::start_with_next([=] { + _crossLine.invalidate(); + }, lifetime()); + + paintRequest( + ) | rpl::start_with_next([=](const QRect &clip) { + Painter p(this); + paintRipple(p, _st.rippleAreaPosition, nullptr); + + _crossLine.paint( + p, + (width() - _st.icon.width()) / 2, + (height() - _st.icon.height()) / 2, + _crossLineAnimation.value(_checked ? 1. : 0.), + // Since buttons of the compose controls have no duration + // for the over animation, we can skip this animation here. + isOver() + ? std::make_optional<QColor>(_colorOver) + : std::nullopt); + }, lifetime()); + setMouseTracking(true); } void SilentToggle::mouseMoveEvent(QMouseEvent *e) { - IconButton::mouseMoveEvent(e); + RippleButton::mouseMoveEvent(e); if (rect().contains(e->pos())) { Ui::Tooltip::Show(1000, this); } else { @@ -881,28 +907,22 @@ void SilentToggle::mouseMoveEvent(QMouseEvent *e) { void SilentToggle::setChecked(bool checked) { if (_checked != checked) { _checked = checked; - refreshIconOverrides(); + _crossLineAnimation.start( + [=] { update(); }, + _checked ? 0. : 1., + _checked ? 1. : 0., + kAnimationDuration); } } -void SilentToggle::refreshIconOverrides() { - const auto iconOverride = _checked - ? &st::historySilentToggleOn - : nullptr; - const auto iconOverOverride = _checked - ? &st::historySilentToggleOnOver - : nullptr; - setIconOverride(iconOverride, iconOverOverride); -} - void SilentToggle::leaveEventHook(QEvent *e) { - IconButton::leaveEventHook(e); + RippleButton::leaveEventHook(e); Ui::Tooltip::Hide(); } void SilentToggle::mouseReleaseEvent(QMouseEvent *e) { setChecked(!_checked); - IconButton::mouseReleaseEvent(e); + RippleButton::mouseReleaseEvent(e); Ui::Tooltip::Show(0, this); _channel->owner().updateNotifySettings( _channel, @@ -924,4 +944,18 @@ bool SilentToggle::tooltipWindowActive() const { return Ui::AppInFocus() && InFocusChain(window()); } +QPoint SilentToggle::prepareRippleStartPosition() const { + const auto result = mapFromGlobal(QCursor::pos()) + - _st.rippleAreaPosition; + const auto rect = QRect(0, 0, _st.rippleAreaSize, _st.rippleAreaSize); + return rect.contains(result) + ? result + : DisabledRippleStartPosition(); +} + +QImage SilentToggle::prepareRippleMask() const { + return RippleAnimation::ellipseMask( + QSize(_st.rippleAreaSize, _st.rippleAreaSize)); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/special_buttons.h b/Telegram/SourceFiles/ui/special_buttons.h index 4cc13286f..fa9f86686 100644 --- a/Telegram/SourceFiles/ui/special_buttons.h +++ b/Telegram/SourceFiles/ui/special_buttons.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/tooltip.h" #include "ui/effects/animations.h" +#include "ui/effects/cross_line.h" #include "styles/style_window.h" #include "styles/style_widgets.h" @@ -183,7 +184,7 @@ private: //}; class SilentToggle - : public Ui::IconButton + : public Ui::RippleButton , public Ui::AbstractTooltipShower { public: SilentToggle(QWidget *parent, not_null<ChannelData*> channel); @@ -203,12 +204,19 @@ protected: void mouseReleaseEvent(QMouseEvent *e) override; void leaveEventHook(QEvent *e) override; + QImage prepareRippleMask() const override; + QPoint prepareRippleStartPosition() const override; + private: - void refreshIconOverrides(); + const style::IconButton &_st; + const QColor &_colorOver; not_null<ChannelData*> _channel; bool _checked = false; + Ui::CrossLineAnimation _crossLine; + Ui::Animations::Simple _crossLineAnimation; + }; } // namespace Ui From 683d78c64a2aaf2ea9fe1c1412092fa4f9346686 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 2 Feb 2021 08:34:54 +0300 Subject: [PATCH 261/396] Added missed passing of resize event to parent of OverlayWidget. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 7f66e2edb..99ab39a96 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -478,6 +478,7 @@ void OverlayWidget::updateGeometry() { void OverlayWidget::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); + OverlayParent::resizeEvent(e); } void OverlayWidget::updateControlsGeometry() { From 019e691fbbc814f58a94622e6f35049805cd4364 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 04:17:03 +0300 Subject: [PATCH 262/396] Moved some session dependent methods to SessionController. MainWindow::showAddContact(), MainWindow::showNewGroup(), MainWindow::showNewChannel(). --- .../boxes/peer_list_controllers.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 37 -------------- Telegram/SourceFiles/mainwindow.h | 3 -- .../platform/linux/main_window_linux.cpp | 30 +++++++++--- .../platform/mac/main_window_mac.mm | 48 +++++++++++++++---- .../SourceFiles/window/window_main_menu.cpp | 14 +++--- .../window/window_session_controller.cpp | 19 ++++++++ .../window/window_session_controller.h | 4 ++ 9 files changed, 95 insertions(+), 64 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index b745c48eb..eef91d413 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -113,7 +113,7 @@ object_ptr<Ui::BoxContent> PrepareContactsBox( box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addLeftButton( tr::lng_profile_add_contact(), - [=] { controller->widget()->showAddContact(); }); + [=] { controller->showAddContact(); }); }; return Box<PeerListBox>( std::make_unique<ContactsBoxController>( diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index a4c87531c..31596585d 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2281,7 +2281,7 @@ void InnerWidget::refreshEmptyLabel() { resizeEmptyLabel(); _empty->setClickHandlerFilter([=](const auto &...) { if (_emptyState == EmptyState::NoContacts) { - App::wnd()->showAddContact(); + _controller->showAddContact(); } else if (_emptyState == EmptyState::EmptyFolder) { editOpenedFilter(); } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 188213ead..24a2fc724 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_domain.h" #include "mainwidget.h" #include "boxes/confirm_box.h" -#include "boxes/add_contact_box.h" #include "boxes/connection_box.h" #include "storage/storage_account.h" #include "storage/localstorage.h" @@ -669,42 +668,6 @@ void MainWindow::updateTrayMenu(bool force) { psTrayMenuUpdated(); } -void MainWindow::showAddContact() { - if (isHidden()) { - showFromTray(); - } - - if (const auto controller = sessionController()) { - Ui::show( - Box<AddContactBox>(&controller->session()), - Ui::LayerOption::KeepOther); - } -} - -void MainWindow::showNewGroup() { - if (isHidden()) { - showFromTray(); - } - - if (const auto controller = sessionController()) { - Ui::show( - Box<GroupInfoBox>(controller, GroupInfoBox::Type::Group), - Ui::LayerOption::KeepOther); - } -} - -void MainWindow::showNewChannel() { - if (isHidden()) { - showFromTray(); - } - - if (const auto controller = sessionController()) { - Ui::show( - Box<GroupInfoBox>(controller, GroupInfoBox::Type::Channel), - Ui::LayerOption::KeepOther); - } -} - void MainWindow::showLogoutConfirmation() { if (isHidden()) { showFromTray(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 6e2a05fa6..f2a952528 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -53,9 +53,6 @@ public: void setupMain(); void showSettings(); - void showAddContact(); - void showNewGroup(); - void showNewChannel(); void setInnerFocus(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 9bdec9036..0abc1995d 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -932,6 +932,12 @@ void MainWindow::updateGlobalMenuHook() { #else // DESKTOP_APP_DISABLE_DBUS_INTEGRATION void MainWindow::createGlobalMenu() { + const auto ensureWindowShown = [=] { + if (isHidden()) { + showFromTray(); + } + }; + psMainMenu = new QMenu(this); auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now)); @@ -1061,20 +1067,32 @@ void MainWindow::createGlobalMenu() { psAddContact = tools->addAction( tr::lng_mac_menu_add_contact(tr::now), - App::wnd(), - [=] { App::wnd()->showAddContact(); }); + this, + [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showAddContact(); + }); tools->addSeparator(); psNewGroup = tools->addAction( tr::lng_mac_menu_new_group(tr::now), - App::wnd(), - [=] { App::wnd()->showNewGroup(); }); + this, + [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showNewGroup(); + }); psNewChannel = tools->addAction( tr::lng_mac_menu_new_channel(tr::now), - App::wnd(), - [=] { App::wnd()->showNewChannel(); }); + this, + [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showNewChannel(); + }); auto help = psMainMenu->addMenu(tr::lng_linux_menu_help(tr::now)); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 08630c158..3d4919191 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -684,6 +684,12 @@ void MainWindow::initShadows() { } void MainWindow::createGlobalMenu() { + const auto ensureWindowShown = [=] { + if (isHidden()) { + showFromTray(); + } + }; + auto main = psMainMenu.addMenu(qsl("Telegram")); auto about = main->addAction(tr::lng_mac_menu_about_telegram(tr::now, lt_telegram, qsl("Telegram"))); connect(about, &QAction::triggered, about, [] { @@ -738,16 +744,40 @@ void MainWindow::createGlobalMenu() { } Ui::show(PrepareContactsBox(sessionController())); })); - psAddContact = window->addAction(tr::lng_mac_menu_add_contact(tr::now), App::wnd(), [=] { - App::wnd()->showAddContact(); - }); + { + auto callback = [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showAddContact(); + }; + psAddContact = window->addAction( + tr::lng_mac_menu_add_contact(tr::now), + this, + std::move(callback)); + } window->addSeparator(); - psNewGroup = window->addAction(tr::lng_mac_menu_new_group(tr::now), App::wnd(), [=] { - App::wnd()->showNewGroup(); - }); - psNewChannel = window->addAction(tr::lng_mac_menu_new_channel(tr::now), App::wnd(), [=] { - App::wnd()->showNewChannel(); - }); + { + auto callback = [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showNewGroup(); + }; + psNewGroup = window->addAction( + tr::lng_mac_menu_new_group(tr::now), + this, + std::move(callback)); + } + { + auto callback = [=] { + Expects(sessionController() != nullptr); + ensureWindowShown(); + sessionController()->showNewChannel(); + }; + psNewChannel = window->addAction( + tr::lng_mac_menu_new_channel(tr::now), + this, + std::move(callback)); + } window->addSeparator(); psShowTelegram = window->addAction(tr::lng_mac_menu_show(tr::now), App::wnd(), [=] { showFromTray(); diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 5edad0b6f..b8840574e 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -894,13 +894,13 @@ void MainMenu::parentResized() { void MainMenu::refreshMenu() { _menu->clearActions(); + const auto controller = _controller; if (!_controller->session().supportMode()) { - const auto controller = _controller; - _menu->addAction(tr::lng_create_group_title(tr::now), [] { - App::wnd()->showNewGroup(); + _menu->addAction(tr::lng_create_group_title(tr::now), [=] { + controller->showNewGroup(); }, &st::mainMenuNewGroup, &st::mainMenuNewGroupOver); - _menu->addAction(tr::lng_create_channel_title(tr::now), [] { - App::wnd()->showNewChannel(); + _menu->addAction(tr::lng_create_channel_title(tr::now), [=] { + controller->showNewChannel(); }, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver); _menu->addAction(tr::lng_menu_contacts(tr::now), [=] { Ui::show(PrepareContactsBox(controller)); @@ -911,8 +911,8 @@ void MainMenu::refreshMenu() { }, &st::mainMenuCalls, &st::mainMenuCallsOver); } } else { - _menu->addAction(tr::lng_profile_add_contact(tr::now), [] { - App::wnd()->showAddContact(); + _menu->addAction(tr::lng_profile_add_contact(tr::now), [=] { + controller->showAddContact(); }, &st::mainMenuContacts, &st::mainMenuContactsOver); const auto fix = std::make_shared<QPointer<QAction>>(); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index c0aef5a3f..82da3e84f 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_session_controller.h" +#include "boxes/add_contact_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "boxes/peer_list_controllers.h" #include "window/window_controller.h" @@ -1140,6 +1141,24 @@ void SessionController::setActiveChatsFilter(FilterId id) { } } +void SessionController::showAddContact() { + _window->show( + Box<AddContactBox>(&session()), + Ui::LayerOption::KeepOther); +} + +void SessionController::showNewGroup() { + _window->show( + Box<GroupInfoBox>(this, GroupInfoBox::Type::Group), + Ui::LayerOption::KeepOther); +} + +void SessionController::showNewChannel() { + _window->show( + Box<GroupInfoBox>(this, GroupInfoBox::Type::Channel), + Ui::LayerOption::KeepOther); +} + SessionController::~SessionController() = default; } // namespace Window diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 64f3b8b66..d973fe3da 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -325,6 +325,10 @@ public: Dialogs::Key chat, QDate requestedDate); + void showAddContact(); + void showNewGroup(); + void showNewChannel(); + void showPassportForm(const Passport::FormRequest &request); void clearPassportForm(); From 1f80c297ec692a5cafef1735aa2a554633d6ae25 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 04:58:27 +0300 Subject: [PATCH 263/396] Removed App:wnd for opening settings. Removed unused App::showSettings from facades. --- Telegram/SourceFiles/core/local_url_handlers.cpp | 1 + Telegram/SourceFiles/facades.cpp | 6 ------ Telegram/SourceFiles/facades.h | 1 - Telegram/SourceFiles/intro/intro_widget.cpp | 6 ++++-- Telegram/SourceFiles/intro/intro_widget.h | 2 ++ Telegram/SourceFiles/mainwindow.cpp | 8 +++++--- .../platform/linux/main_window_linux.cpp | 7 +++++-- .../SourceFiles/platform/mac/main_window_mac.mm | 16 ++++++++++++---- Telegram/SourceFiles/window/window_main_menu.cpp | 4 ++-- 9 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 19af8f2ba..a0be0a20a 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -346,6 +346,7 @@ bool ResolveSettings( if (!controller) { return false; } + controller->window().activate(); const auto section = match->captured(1).mid(1).toLower(); if (section.isEmpty()) { controller->window().showSettings(); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index bc542e29e..c35948c2e 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -229,12 +229,6 @@ void searchByHashtag(const QString &tag, PeerData *inPeer) { } } -void showSettings() { - if (auto w = App::wnd()) { - w->showSettings(); - } -} - } // namespace App namespace Ui { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 353d26352..a9ef5be1b 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -48,7 +48,6 @@ void activateBotCommand( int row, int column); void searchByHashtag(const QString &tag, PeerData *inPeer); -void showSettings(); } // namespace App diff --git a/Telegram/SourceFiles/intro/intro_widget.cpp b/Telegram/SourceFiles/intro/intro_widget.cpp index 0d2afd9b7..d9ef403d5 100644 --- a/Telegram/SourceFiles/intro/intro_widget.cpp +++ b/Telegram/SourceFiles/intro/intro_widget.cpp @@ -121,8 +121,6 @@ Widget::Widget( _next->entity()->setClickedCallback([=] { getStep()->submit(); }); - _settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); }); - if (_changeLanguage) { _changeLanguage->finishAnimating(); } @@ -153,6 +151,10 @@ Widget::Widget( } } +rpl::producer<> Widget::showSettingsRequested() const { + return _settings->entity()->clicks() | rpl::to_empty; +} + not_null<Media::Player::FloatDelegate*> Widget::floatPlayerDelegate() { return static_cast<Media::Player::FloatDelegate*>(this); } diff --git a/Telegram/SourceFiles/intro/intro_widget.h b/Telegram/SourceFiles/intro/intro_widget.h index 001409dfc..59be97a2e 100644 --- a/Telegram/SourceFiles/intro/intro_widget.h +++ b/Telegram/SourceFiles/intro/intro_widget.h @@ -99,6 +99,8 @@ public: void setInnerFocus(); + [[nodiscard]] rpl::producer<> showSettingsRequested() const; + ~Widget(); protected: diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 24a2fc724..743fa3fd0 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -289,6 +289,11 @@ void MainWindow::setupIntro(Intro::EnterPoint point) { destroyLayer(); auto created = object_ptr<Intro::Widget>(bodyWidget(), &account(), point); + created->showSettingsRequested( + ) | rpl::start_with_next([=] { + showSettings(); + }, created->lifetime()); + clearWidgets(); _intro = std::move(created); if (_passcodeLock) { @@ -342,9 +347,6 @@ void MainWindow::setupMain() { } void MainWindow::showSettings() { - if (isHidden()) { - showFromTray(); - } if (_passcodeLock) { return; } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 0abc1995d..8c022e7d7 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -1043,8 +1043,11 @@ void MainWindow::createGlobalMenu() { auto prefs = edit->addAction( tr::lng_mac_menu_preferences(tr::now), - App::wnd(), - [=] { App::wnd()->showSettings(); }, + this, + [=] { + ensureWindowShown(); + controller().showSettings(); + }, QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); prefs->setMenuRole(QAction::PreferencesRole); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 3d4919191..37b2e7ef8 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -699,10 +699,18 @@ void MainWindow::createGlobalMenu() { about->setMenuRole(QAction::AboutQtRole); main->addSeparator(); - QAction *prefs = main->addAction(tr::lng_mac_menu_preferences(tr::now), App::wnd(), [=] { - App::wnd()->showSettings(); - }, QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); - prefs->setMenuRole(QAction::PreferencesRole); + { + auto callback = [=] { + ensureWindowShown(); + controller().showSettings(); + }; + main->addAction( + tr::lng_mac_menu_preferences(tr::now), + this, + std::move(callback), + QKeySequence(Qt::ControlModifier | Qt::Key_Comma)) + ->setMenuRole(QAction::PreferencesRole); + } QMenu *file = psMainMenu.addMenu(tr::lng_mac_menu_file(tr::now)); psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now)); diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index b8840574e..8a6cd9aa4 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -930,8 +930,8 @@ void MainMenu::refreshMenu() { _controller->session().supportTemplates().reload(); }, &st::mainMenuReload, &st::mainMenuReloadOver); } - _menu->addAction(tr::lng_menu_settings(tr::now), [] { - App::wnd()->showSettings(); + _menu->addAction(tr::lng_menu_settings(tr::now), [=] { + controller->showSettings(); }, &st::mainMenuSettings, &st::mainMenuSettingsOver); _nightThemeAction = std::make_shared<QPointer<QAction>>(); From b4af8055210edca5de2e352db2dca038ed6ea63d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 05:49:26 +0300 Subject: [PATCH 264/396] Moved showLogoutConfirmation from MainWindow to Window::Controller. --- Telegram/SourceFiles/mainwindow.cpp | 33 ------------------- Telegram/SourceFiles/mainwindow.h | 2 -- .../platform/linux/main_window_linux.cpp | 11 ++++--- .../platform/mac/main_window_mac.mm | 14 +++++--- .../SourceFiles/settings/settings_common.cpp | 2 +- .../SourceFiles/window/window_controller.cpp | 30 +++++++++++++++++ .../SourceFiles/window/window_controller.h | 2 ++ .../window/window_lock_widgets.cpp | 2 +- 8 files changed, 51 insertions(+), 45 deletions(-) diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 743fa3fd0..318f43e95 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -670,39 +670,6 @@ void MainWindow::updateTrayMenu(bool force) { psTrayMenuUpdated(); } -void MainWindow::showLogoutConfirmation() { - if (isHidden()) { - showFromTray(); - } - - const auto account = Core::App().passcodeLocked() - ? nullptr - : sessionController() - ? &sessionController()->session().account() - : nullptr; - const auto weak = base::make_weak(account); - const auto callback = [=] { - if (account && !weak) { - return; - } - if (account - && account->sessionExists() - && Core::App().exportManager().inProgress(&account->session())) { - Ui::hideLayer(); - Core::App().exportManager().stopWithConfirmation([=] { - Core::App().logout(account); - }); - } else { - Core::App().logout(account); - } - }; - Ui::show(Box<ConfirmBox>( - tr::lng_sure_logout(tr::now), - tr::lng_settings_logout(tr::now), - st::attentionBoxButton, - callback)); -} - bool MainWindow::takeThirdSectionFromLayer() { return _layer ? _layer->takeToThirdSection() : false; } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index f2a952528..f47d01689 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -103,8 +103,6 @@ public: not_null<PhotoData*> photo); void hideMediaPreview(); - void showLogoutConfirmation(); - void updateControlsGeometry() override; protected: diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 8c022e7d7..3247ca4e1 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -942,10 +942,13 @@ void MainWindow::createGlobalMenu() { auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now)); - psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now)); - connect(psLogout, &QAction::triggered, psLogout, [] { - if (App::wnd()) App::wnd()->showLogoutConfirmation(); - }); + psLogout = file->addAction( + tr::lng_mac_menu_logout(tr::now), + this, + [=] { + ensureWindowShown(); + controller().showLogoutConfirmation(); + }); auto quit = file->addAction( tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, qsl("Telegram")), diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 37b2e7ef8..77fb8de4a 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -713,10 +713,16 @@ void MainWindow::createGlobalMenu() { } QMenu *file = psMainMenu.addMenu(tr::lng_mac_menu_file(tr::now)); - psLogout = file->addAction(tr::lng_mac_menu_logout(tr::now)); - connect(psLogout, &QAction::triggered, psLogout, [] { - if (App::wnd()) App::wnd()->showLogoutConfirmation(); - }); + { + auto callback = [=] { + ensureWindowShown(); + controller().showLogoutConfirmation(); + }; + psLogout = file->addAction( + tr::lng_mac_menu_logout(tr::now), + this, + std::move(callback)); + } QMenu *edit = psMainMenu.addMenu(tr::lng_mac_menu_edit(tr::now)); psUndo = edit->addAction(tr::lng_mac_menu_undo(tr::now), this, SLOT(psMacUndo()), QKeySequence::Undo); diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp index 43951b443..f94456ee6 100644 --- a/Telegram/SourceFiles/settings/settings_common.cpp +++ b/Telegram/SourceFiles/settings/settings_common.cpp @@ -210,7 +210,7 @@ void FillMenu( } addAction( tr::lng_settings_logout(tr::now), - [=] { window->widget()->showLogoutConfirmation(); }); + [=] { window->showLogoutConfirmation(); }); } } diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index eb514d7e0..f76b5d697 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_updates.h" #include "core/application.h" #include "core/click_handler_types.h" +#include "export/export_manager.h" #include "platform/platform_window_title.h" #include "main/main_account.h" #include "main/main_domain.h" @@ -329,4 +330,33 @@ QPoint Controller::getPointForCallPanelCenter() const { : _widget.windowHandle()->screen()->geometry().center(); } +void Controller::showLogoutConfirmation() { + const auto account = Core::App().passcodeLocked() + ? nullptr + : sessionController() + ? &sessionController()->session().account() + : nullptr; + const auto weak = base::make_weak(account); + const auto callback = [=] { + if (account && !weak) { + return; + } + if (account + && account->sessionExists() + && Core::App().exportManager().inProgress(&account->session())) { + Ui::hideLayer(); + Core::App().exportManager().stopWithConfirmation([=] { + Core::App().logout(account); + }); + } else { + Core::App().logout(account); + } + }; + show(Box<ConfirmBox>( + tr::lng_sure_logout(tr::now), + tr::lng_settings_logout(tr::now), + st::attentionBoxButton, + callback)); +} + } // namespace Window diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index 975626974..b5cb6710e 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -46,6 +46,8 @@ public: void setupIntro(); void setupMain(); + void showLogoutConfirmation(); + void showSettings(); [[nodiscard]] int verticalShadowTop() const; diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index 0e471a702..d13581ec3 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -120,7 +120,7 @@ PasscodeLockWidget::PasscodeLockWidget( _submit->setClickedCallback([=] { submit(); }); _logout->setClickedCallback([=] { - window->widget()->showLogoutConfirmation(); + window->showLogoutConfirmation(); }); } From fb9a34a069191427dbb58c95ea87b5489dc2c3c0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 05:55:01 +0300 Subject: [PATCH 265/396] Removed App::wnd for opening about box. --- .../platform/linux/main_window_linux.cpp | 9 +++------ .../platform/mac/main_window_mac.mm | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 3247ca4e1..5f5a94bfb 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -1107,12 +1107,9 @@ void MainWindow::createGlobalMenu() { tr::now, lt_telegram, qsl("Telegram")), - [] { - if (App::wnd() && App::wnd()->isHidden()) { - App::wnd()->showFromTray(); - } - - Ui::show(Box<AboutBox>()); + [=] { + ensureWindowShown(); + controller().show(Box<AboutBox>()); }); about->setMenuRole(QAction::AboutQtRole); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 77fb8de4a..36a26926d 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -691,12 +691,19 @@ void MainWindow::createGlobalMenu() { }; auto main = psMainMenu.addMenu(qsl("Telegram")); - auto about = main->addAction(tr::lng_mac_menu_about_telegram(tr::now, lt_telegram, qsl("Telegram"))); - connect(about, &QAction::triggered, about, [] { - if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); - Ui::show(Box<AboutBox>()); - }); - about->setMenuRole(QAction::AboutQtRole); + { + auto callback = [=] { + ensureWindowShown(); + controller().show(Box<AboutBox>()); + }; + main->addAction( + tr::lng_mac_menu_about_telegram( + tr::now, + lt_telegram, + qsl("Telegram")), + std::move(callback)) + ->setMenuRole(QAction::AboutQtRole); + } main->addSeparator(); { From 0783a682dcbad8565aced7de1fd7360351381939 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 05:58:59 +0300 Subject: [PATCH 266/396] Removed App:wnd from Platform::MainWindow for macOS. --- .../SourceFiles/platform/mac/main_window_mac.mm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 36a26926d..6adde8c14 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -800,9 +800,10 @@ void MainWindow::createGlobalMenu() { std::move(callback)); } window->addSeparator(); - psShowTelegram = window->addAction(tr::lng_mac_menu_show(tr::now), App::wnd(), [=] { - showFromTray(); - }); + psShowTelegram = window->addAction( + tr::lng_mac_menu_show(tr::now), + this, + [=] { showFromTray(); }); updateGlobalMenu(); } @@ -864,7 +865,9 @@ void MainWindow::psMacClearFormat() { } void MainWindow::updateGlobalMenuHook() { - if (!App::wnd() || !positionInited()) return; + if (!positionInited()) { + return; + } auto focused = QApplication::focusWidget(); bool canUndo = false, canRedo = false, canCut = false, canCopy = false, canPaste = false, canDelete = false, canSelectAll = false; @@ -895,7 +898,7 @@ void MainWindow::updateGlobalMenuHook() { _canApplyMarkdown = canApplyMarkdown; - App::wnd()->updateIsActive(); + updateIsActive(); const auto logged = (sessionController() != nullptr); const auto inactive = !logged || controller().locked(); const auto support = logged && account().session().supportMode(); @@ -911,7 +914,7 @@ void MainWindow::updateGlobalMenuHook() { ForceDisabled(psAddContact, inactive); ForceDisabled(psNewGroup, inactive || support); ForceDisabled(psNewChannel, inactive || support); - ForceDisabled(psShowTelegram, App::wnd()->isActive()); + ForceDisabled(psShowTelegram, isActive()); ForceDisabled(psBold, !canApplyMarkdown); ForceDisabled(psItalic, !canApplyMarkdown); From 0b4d0b83c2edb36ef2617a44f5d946e8c3893bfc Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Feb 2021 06:31:11 +0300 Subject: [PATCH 267/396] Removed App::wnd from classes that have pointer to Window::Controller. --- .../SourceFiles/boxes/sticker_set_box.cpp | 16 ++++----- .../chat_helpers/field_autocomplete.cpp | 18 ++++------ .../chat_helpers/gifs_list_widget.cpp | 36 +++++++++---------- .../chat_helpers/stickers_list_widget.cpp | 14 ++++---- .../dialogs/dialogs_inner_widget.cpp | 2 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 8 +++-- .../admin_log/history_admin_log_inner.cpp | 2 +- .../history/history_inner_widget.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 10 +++--- .../history/view/history_view_list_widget.cpp | 2 +- .../inline_bots/inline_results_inner.cpp | 30 ++++++++-------- .../window/themes/window_theme_editor.cpp | 10 +++--- 12 files changed, 71 insertions(+), 79 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index b4eafa102..381b5aad9 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -473,11 +473,9 @@ void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) { int index = stickerFromGlobalPos(e->globalPos()); if (index >= 0 && index < _pack.size() && index != _previewShown) { _previewShown = index; - if (const auto w = App::wnd()) { - w->showMediaPreview( - Data::FileOriginStickerSet(_setId, _setAccess), - _pack[_previewShown]); - } + _controller->widget()->showMediaPreview( + Data::FileOriginStickerSet(_setId, _setAccess), + _pack[_previewShown]); } } } @@ -536,11 +534,9 @@ void StickerSetBox::Inner::showPreview() { int index = stickerFromGlobalPos(QCursor::pos()); if (index >= 0 && index < _pack.size()) { _previewShown = index; - if (const auto w = App::wnd()) { - w->showMediaPreview( - Data::FileOriginStickerSet(_setId, _setAccess), - _pack[_previewShown]); - } + _controller->widget()->showMediaPreview( + Data::FileOriginStickerSet(_setId, _setAccess), + _pack[_previewShown]); } } diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 87ee66884..e771b2ef3 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -1280,11 +1280,9 @@ void FieldAutocomplete::Inner::selectByMouse(QPoint globalPosition) { if (_down >= 0 && _sel >= 0 && _down != _sel) { _down = _sel; if (_down >= 0 && _down < _srows->size()) { - if (const auto w = App::wnd()) { - w->showMediaPreview( - (*_srows)[_down].document->stickerSetOrigin(), - (*_srows)[_down].document); - } + _controller->widget()->showMediaPreview( + (*_srows)[_down].document->stickerSetOrigin(), + (*_srows)[_down].document); } } } @@ -1302,12 +1300,10 @@ void FieldAutocomplete::Inner::onParentGeometryChanged() { void FieldAutocomplete::Inner::showPreview() { if (_down >= 0 && _down < _srows->size()) { - if (const auto w = App::wnd()) { - w->showMediaPreview( - (*_srows)[_down].document->stickerSetOrigin(), - (*_srows)[_down].document); - _previewShown = true; - } + _controller->widget()->showMediaPreview( + (*_srows)[_down].document->stickerSetOrigin(), + (*_srows)[_down].document); + _previewShown = true; } } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index c00104e21..39b9cb636 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -1078,16 +1078,14 @@ void GifsListWidget::updateSelected() { _pressed = _selected; if (row >= 0 && col >= 0) { auto layout = _rows[row].items[col]; - if (const auto w = App::wnd()) { - if (const auto previewDocument = layout->getPreviewDocument()) { - w->showMediaPreview( - Data::FileOriginSavedGifs(), - previewDocument); - } else if (const auto previewPhoto = layout->getPreviewPhoto()) { - w->showMediaPreview( - Data::FileOrigin(), - previewPhoto); - } + if (const auto previewDocument = layout->getPreviewDocument()) { + controller()->widget()->showMediaPreview( + Data::FileOriginSavedGifs(), + previewDocument); + } else if (const auto previewPhoto = layout->getPreviewPhoto()) { + controller()->widget()->showMediaPreview( + Data::FileOrigin(), + previewPhoto); } } } @@ -1104,16 +1102,14 @@ void GifsListWidget::showPreview() { int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; if (row < _rows.size() && col < _rows[row].items.size()) { auto layout = _rows[row].items[col]; - if (const auto w = App::wnd()) { - if (const auto previewDocument = layout->getPreviewDocument()) { - _previewShown = w->showMediaPreview( - Data::FileOriginSavedGifs(), - previewDocument); - } else if (const auto previewPhoto = layout->getPreviewPhoto()) { - _previewShown = w->showMediaPreview( - Data::FileOrigin(), - previewPhoto); - } + if (const auto previewDocument = layout->getPreviewDocument()) { + _previewShown = controller()->widget()->showMediaPreview( + Data::FileOriginSavedGifs(), + previewDocument); + } else if (const auto previewPhoto = layout->getPreviewPhoto()) { + _previewShown = controller()->widget()->showMediaPreview( + Data::FileOrigin(), + previewPhoto); } } } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 8a92680ad..a53206cde 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -2883,9 +2883,9 @@ void StickersListWidget::setSelected(OverState newSelected) { const auto &set = sets[sticker->section]; Assert(sticker->index >= 0 && sticker->index < set.stickers.size()); const auto document = set.stickers[sticker->index].document; - if (const auto w = App::wnd()) { - w->showMediaPreview(document->stickerSetOrigin(), document); - } + controller()->widget()->showMediaPreview( + document->stickerSetOrigin(), + document); } } } @@ -2898,10 +2898,10 @@ void StickersListWidget::showPreview() { const auto &set = sets[sticker->section]; Assert(sticker->index >= 0 && sticker->index < set.stickers.size()); const auto document = set.stickers[sticker->index].document; - if (const auto w = App::wnd()) { - w->showMediaPreview(document->stickerSetOrigin(), document); - _previewShown = true; - } + controller()->widget()->showMediaPreview( + document->stickerSetOrigin(), + document); + _previewShown = true; } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 31596585d..38c1daaa0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -414,7 +414,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { Painter p(this); const auto r = e->rect(); - if (App::wnd()->contentOverlapped(this, r)) { + if (_controller->widget()->contentOverlapped(this, r)) { return; } const auto activeEntry = _controller->activeChatEntryCurrent(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 508e325aa..379c5341f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -711,7 +711,7 @@ void Widget::animationCallback() { updateControlsVisibility(true); if (!_filter->hasFocus()) { - if (App::wnd()) App::wnd()->setInnerFocus(); + controller()->widget()->setInnerFocus(); } } } @@ -957,7 +957,7 @@ void Widget::onChooseByDrag() { } void Widget::showMainMenu() { - App::wnd()->showMainMenu(); + controller()->widget()->showMainMenu(); } void Widget::searchMessages( @@ -1704,7 +1704,9 @@ void Widget::keyPressEvent(QKeyEvent *e) { } void Widget::paintEvent(QPaintEvent *e) { - if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; + if (controller()->widget()->contentOverlapped(this, e)) { + return; + } Painter p(this); QRect r(e->rect()); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index bc26d4a18..0df987662 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1515,7 +1515,7 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but if (_selectedItem && !_pressWasInactive) { if (_selectedText.from == _selectedText.to) { _selectedItem = nullptr; - App::wnd()->setInnerFocus(); + _controller->widget()->setInnerFocus(); } } } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 93ef22236..704d5d95d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1392,7 +1392,7 @@ void HistoryInner::mouseActionFinish( auto sel = _selected.cbegin()->second; if (sel != FullSelection && sel.from == sel.to) { _selected.clear(); - App::wnd()->setInnerFocus(); + _controller->widget()->setInnerFocus(); } } } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8cad61a24..130041ebb 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -260,9 +260,11 @@ HistoryWidget::HistoryWidget( connect(_field, &Ui::InputField::changed, [=] { fieldChanged(); }); - connect(App::wnd()->windowHandle(), &QWindow::visibleChanged, this, [=] { - windowIsVisibleChanged(); - }); + connect( + controller->widget()->windowHandle(), + &QWindow::visibleChanged, + this, + [=] { windowIsVisibleChanged(); }); initTabbedSelector(); @@ -2006,7 +2008,7 @@ void HistoryWidget::showHistory( update(); controller()->floatPlayerAreaUpdated(); - crl::on_main(App::wnd(), [] { App::wnd()->setInnerFocus(); }); + crl::on_main(this, [=] { controller()->widget()->setInnerFocus(); }); } void HistoryWidget::clearDelayedShowAt() { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 45048eeb3..08c77e2ae 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2254,7 +2254,7 @@ void ListWidget::mouseActionFinish( } else if (_selectedTextItem && !_pressWasInactive) { if (_selectedTextRange.from == _selectedTextRange.to) { clearTextSelection(); - App::wnd()->setInnerFocus(); + _controller->widget()->setInnerFocus(); } } } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index e44caf6fe..9c837b05e 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -713,14 +713,14 @@ void Inner::updateSelected() { _pressed = _selected; if (row >= 0 && col >= 0) { auto layout = _rows.at(row).items.at(col); - if (const auto w = App::wnd()) { - if (const auto previewDocument = layout->getPreviewDocument()) { - w->showMediaPreview( - Data::FileOrigin(), - previewDocument); - } else if (auto previewPhoto = layout->getPreviewPhoto()) { - w->showMediaPreview(Data::FileOrigin(), previewPhoto); - } + if (const auto previewDocument = layout->getPreviewDocument()) { + _controller->widget()->showMediaPreview( + Data::FileOrigin(), + previewDocument); + } else if (auto previewPhoto = layout->getPreviewPhoto()) { + _controller->widget()->showMediaPreview( + Data::FileOrigin(), + previewPhoto); } } } @@ -740,12 +740,14 @@ void Inner::showPreview() { int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; if (row < _rows.size() && col < _rows.at(row).items.size()) { auto layout = _rows.at(row).items.at(col); - if (const auto w = App::wnd()) { - if (const auto previewDocument = layout->getPreviewDocument()) { - _previewShown = w->showMediaPreview(Data::FileOrigin(), previewDocument); - } else if (const auto previewPhoto = layout->getPreviewPhoto()) { - _previewShown = w->showMediaPreview(Data::FileOrigin(), previewPhoto); - } + if (const auto previewDocument = layout->getPreviewDocument()) { + _previewShown = _controller->widget()->showMediaPreview( + Data::FileOrigin(), + previewDocument); + } else if (const auto previewPhoto = layout->getPreviewPhoto()) { + _previewShown = _controller->widget()->showMediaPreview( + Data::FileOrigin(), + previewPhoto); } } } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index 58fe1adad..c1802bd6c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -858,8 +858,8 @@ void Editor::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { if (!_select->getQuery().isEmpty()) { _select->clearQuery(); - } else if (auto window = App::wnd()) { - window->setInnerFocus(); + } else { + _window->widget()->setInnerFocus(); } } else if (e->key() == Qt::Key_Down) { _inner->selectSkip(1); @@ -904,10 +904,8 @@ void Editor::closeWithConfirmation() { } void Editor::closeEditor() { - if (const auto window = App::wnd()) { - window->showRightColumn(nullptr); - Background()->clearEditingTheme(); - } + _window->widget()->showRightColumn(nullptr); + Background()->clearEditingTheme(); } } // namespace Theme From 0cd8453b000184bba7a3a330d8eb9fe6040b46f1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 17:14:02 +0400 Subject: [PATCH 268/396] Support different invite link syntax. --- Telegram/SourceFiles/core/local_url_handlers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index a0be0a20a..9d47fa80d 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -521,8 +521,8 @@ QString TryConvertUrlToLocal(QString url) { auto telegramMeMatch = regex_match(qsl("^(https?://)?(www\\.)?(telegram\\.(me|dog)|t\\.me)/(.+)$"), url, matchOptions); if (telegramMeMatch) { auto query = telegramMeMatch->capturedRef(5); - if (auto joinChatMatch = regex_match(qsl("^joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) { - return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(1)); + if (auto joinChatMatch = regex_match(qsl("^(joinchat/|\\+|\\%20)([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) { + return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(2)); } else if (auto stickerSetMatch = regex_match(qsl("^addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) { return qsl("tg://addstickers?set=") + url_encode(stickerSetMatch->captured(1)); } else if (auto themeMatch = regex_match(qsl("^addtheme/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) { From 37b8551760752a6a04f26b9fa0b81da4061ffa5c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 17:35:52 +0400 Subject: [PATCH 269/396] Ignore 400: LOCATION_NOT_AVAILABLE in export. Fix #6807. --- Telegram/SourceFiles/export/export_api_wrap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index b317617e0..c16fb12c9 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -399,7 +399,8 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) { MTP_int(0), MTP_bytes())); } else if (result.type() == qstr("LOCATION_INVALID") - || result.type() == qstr("VERSION_INVALID")) { + || result.type() == qstr("VERSION_INVALID") + || result.type() == qstr("LOCATION_NOT_AVAILABLE")) { filePartUnavailable(); } else if (result.code() == 400 && result.type().startsWith(qstr("FILE_REFERENCE_"))) { From 7cd626c9fdd2d28d4c86b7d12c0e739d93637802 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 18:11:06 +0400 Subject: [PATCH 270/396] Fix build, update lib_ui. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index d94cb3421..77856c3a2 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit d94cb34219e5795efc0efe24d980c54e40eb42e3 +Subproject commit 77856c3a21870e656b6e9ae48bf9c9a19a2a86c3 From a2187a1d2bbf285c93c498d2012d161d90b580eb Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 19:12:31 +0400 Subject: [PATCH 271/396] Fix pinned message reset on message being sent. Fixes #10304 --- Telegram/SourceFiles/history/history_widget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 130041ebb..1da6e2c10 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1632,6 +1632,8 @@ void HistoryWidget::fastShowAtEnd(not_null<History*> history) { clearAllLoadRequests(); setMsgId(ShowAtUnreadMsgId); + _pinnedClickedId = FullMsgId(); + _minPinnedId = std::nullopt; if (_history->isReadyFor(_showAtMsgId)) { historyLoaded(); } else { From 11b965e82e35f97e0ccb6b2be923226b2a1763e1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 19:26:49 +0400 Subject: [PATCH 272/396] Skip image context actions for not-loaded photos. --- .../history/admin_log/history_admin_log_inner.cpp | 15 +++++++++------ .../SourceFiles/history/history_inner_widget.cpp | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 0df987662..069ec5b1a 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1088,12 +1088,15 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } if (lnkPhoto) { const auto photo = lnkPhoto->photo(); - _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { - savePhotoToFile(photo); - })); - _menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - copyContextImage(photo); - }); + const auto media = photo->activeMediaView(); + if (!photo->isNull() && media && media->loaded()) { + _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { + savePhotoToFile(photo); + })); + _menu->addAction(tr::lng_context_copy_image(tr::now), [=] { + copyContextImage(photo); + }); + } if (photo->hasAttachedStickers()) { const auto controller = _controller; auto callback = [=] { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 704d5d95d..495376acc 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1579,12 +1579,15 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } }; const auto addPhotoActions = [&](not_null<PhotoData*> photo) { - _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { - savePhotoToFile(photo); - })); - _menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - copyContextImage(photo); - }); + const auto media = photo->activeMediaView(); + if (!photo->isNull() && media && media->loaded()) { + _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { + savePhotoToFile(photo); + })); + _menu->addAction(tr::lng_context_copy_image(tr::now), [=] { + copyContextImage(photo); + }); + } if (photo->hasAttachedStickers()) { _menu->addAction(tr::lng_context_attached_stickers(tr::now), [=] { session->api().attachedStickers().requestAttachedStickerSets( From d782ea63f8f5e9d651b3859c0b802444ae3a085e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 19:58:57 +0400 Subject: [PATCH 273/396] Fix audio file forward inconsistencies. --- Telegram/SourceFiles/history/view/history_view_message.cpp | 7 ++----- .../history/view/media/history_view_media_grouped.cpp | 4 ++++ .../history/view/media/history_view_media_grouped.h | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index e2392905a..ea03bb705 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2113,12 +2113,9 @@ bool Message::displayForwardedFrom() const { } } const auto media = this->media(); - return item->Has<HistoryMessageVia>() - || !media + return !media || !media->isDisplayed() - || !media->hideForwardedFrom() - || (forwarded->originalSender - && forwarded->originalSender->isChannel()); + || !media->hideForwardedFrom(); } return false; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index c55c98d16..ca07460d8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -682,6 +682,10 @@ bool GroupedMedia::needsBubble() const { return _needBubble; } +bool GroupedMedia::hideForwardedFrom() const { + return main()->hideForwardedFrom(); +} + bool GroupedMedia::computeNeedBubble() const { if (!_caption.isEmpty() || _mode == Mode::Column) { return true; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h index 442f9a5b1..6d2692b20 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -91,6 +91,7 @@ public: bool customHighlight() const override { return true; } + bool hideForwardedFrom() const override; void stopAnimation() override; void checkAnimation() override; From e8affa85b0a403ac818a5e032644ba53d2e52fb5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 4 Feb 2021 20:42:32 +0400 Subject: [PATCH 274/396] Try to open localized changelog. --- Telegram/SourceFiles/boxes/about_box.cpp | 3 +- Telegram/SourceFiles/core/application.cpp | 33 +++++++++++++++++++ Telegram/SourceFiles/core/application.h | 1 + Telegram/SourceFiles/core/changelogs.cpp | 2 +- .../SourceFiles/window/window_main_menu.cpp | 2 +- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/boxes/about_box.cpp b/Telegram/SourceFiles/boxes/about_box.cpp index 31798dc59..c43cb3afa 100644 --- a/Telegram/SourceFiles/boxes/about_box.cpp +++ b/Telegram/SourceFiles/boxes/about_box.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "core/click_handler_types.h" #include "core/update_checker.h" +#include "core/application.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -109,7 +110,7 @@ void AboutBox::showVersionHistory() { Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard.")); } else { - UrlClickHandler::Open(qsl("https://desktop.telegram.org/changelog")); + UrlClickHandler::Open(Core::App().changelogLink()); } } diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 045146b0c..719f9fbe3 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -764,6 +764,39 @@ bool Application::openInternalUrl(const QString &url, QVariant context) { return openCustomUrl("internal:", InternalUrlHandlers(), url, context); } +QString Application::changelogLink() const { + const auto base = u"https://desktop.telegram.org/changelog"_q; + const auto languages = { + "id", + "de", + "fr", + "nl", + "pl", + "tr", + "uk", + "fa", + "ru", + "ms", + "es", + "it", + "uz", + "pt-br", + "be", + "ar", + "ko", + }; + const auto current = _langpack->id().replace("-raw", ""); + if (current.isEmpty()) { + return base; + } + for (const auto language : languages) { + if (current == language || current.split(u'-')[0] == language) { + return base + "?setln=" + language; + } + } + return base; +} + bool Application::openCustomUrl( const QString &protocol, const std::vector<LocalUrlHandler> &handlers, diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 15a5fcb1a..4342f315f 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -227,6 +227,7 @@ public: void checkStartUrl(); bool openLocalUrl(const QString &url, QVariant context); bool openInternalUrl(const QString &url, QVariant context); + [[nodiscard]] QString changelogLink() const; // Float player. void setDefaultFloatPlayerDelegate( diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 52d1c8d4c..87b2f7098 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -185,7 +185,7 @@ void Changelogs::addLocalLogs() { lt_changes, tr::lng_new_version_minor(tr::now), lt_link, - qsl("https://desktop.telegram.org/changelog")); + Core::App().changelogLink()); addLocalLog(text.trimmed()); } } diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 8a6cd9aa4..cbb07f8c3 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -616,7 +616,7 @@ MainMenu::MainMenu( qsl("https://desktop.telegram.org"))); _telegram->setLinksTrusted(); _version->setRichText(textcmdLink(1, tr::lng_settings_current_version(tr::now, lt_version, currentVersionText())) + QChar(' ') + QChar(8211) + QChar(' ') + textcmdLink(2, tr::lng_menu_about(tr::now))); - _version->setLink(1, std::make_shared<UrlClickHandler>(qsl("https://desktop.telegram.org/changelog"))); + _version->setLink(1, std::make_shared<UrlClickHandler>(Core::App().changelogLink())); _version->setLink(2, std::make_shared<LambdaClickHandler>([] { Ui::show(Box<AboutBox>()); })); _controller->session().downloaderTaskFinished( From ce1b94eb169bef3b0f90ec9397ebee602e28a998 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 5 Feb 2021 13:17:26 +0400 Subject: [PATCH 275/396] Send PDFs only as files. Fixes #10294. --- Telegram/SourceFiles/storage/localimageloader.cpp | 3 ++- Telegram/SourceFiles/storage/storage_media_prepare.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index bad70f0a6..81038b936 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -875,7 +875,8 @@ void FileLoadTask::process(Args &&args) { } } else if (isAnimation) { attributes.push_back(MTP_documentAttributeAnimated()); - } else if (_type != SendMediaType::File) { + } else if (filemime.startsWith(u"image/"_q) + && _type != SendMediaType::File) { auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; { diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.cpp b/Telegram/SourceFiles/storage/storage_media_prepare.cpp index 23955072b..b08b4d77d 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.cpp +++ b/Telegram/SourceFiles/storage/storage_media_prepare.cpp @@ -40,7 +40,7 @@ bool ValidPhotoForAlbum( const QString &mime) { if (image.animated || Core::IsMimeSticker(mime) - || (mime == u"application/pdf"_q)) { + || !mime.startsWith(u"image/")) { return false; } const auto width = image.data.width(); From 36acf60f7e63386b4ba500540281b224c7c2977b Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 5 Feb 2021 02:06:21 +0400 Subject: [PATCH 276/396] Add XDG Desktop Portal based file dialog implementation from Qt This allows to use portal dialogs more flexibly (e.g. fallback mechanism) This also allows to have any changes we want for portal dialogs without patchig Qt No more need to override QT_QPA_PLATFORM to use portal dialogs --- Telegram/CMakeLists.txt | 4 + .../platform/linux/file_utilities_linux.cpp | 16 + .../platform/linux/linux_gtk_file_dialog.cpp | 20 +- .../platform/linux/linux_xdp_file_dialog.cpp | 536 ++++++++++++++++++ .../platform/linux/linux_xdp_file_dialog.h | 136 +++++ .../platform/linux/specific_linux.cpp | 21 +- 6 files changed, 700 insertions(+), 33 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 663453b10..85f82779c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -840,6 +840,8 @@ PRIVATE platform/linux/linux_open_with_dialog.h platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h + platform/linux/linux_xdp_file_dialog.cpp + platform/linux/linux_xdp_file_dialog.h platform/linux/linux_xlib_helper.cpp platform/linux/linux_xlib_helper.h platform/linux/file_utilities_linux.cpp @@ -1132,6 +1134,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) platform/linux/linux_gsd_media_keys.h platform/linux/linux_notification_service_watcher.cpp platform/linux/linux_notification_service_watcher.h + platform/linux/linux_xdp_file_dialog.cpp + platform/linux/linux_xdp_file_dialog.h platform/linux/notifications_manager_linux.cpp ) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index d13b80488..b9ebef8c0 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gtk_integration.h" #include "platform/linux/specific_linux.h" +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_xdp_file_dialog.h" +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION + #include <QtGui/QDesktopServices> extern "C" { @@ -77,6 +81,18 @@ bool Get( if (parent) { parent = parent->window(); } +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (XDP::Use(type)) { + return XDP::Get( + parent, + files, + remoteContent, + caption, + filter, + type, + startFile); + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION if (const auto integration = GtkIntegration::Instance()) { if (integration->fileDialogSupported() && integration->useFileDialog(type)) { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index d9adb924d..f03fcdaea 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -640,24 +640,10 @@ bool Supported() { } bool Use(Type type) { - // use gtk file dialog on gtk-based desktop environments - // or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3) - // or if portals are used and operation is to open folder - // and portal doesn't support folder choosing - const auto sandboxedOrCustomPortal = InFlatpak() - || InSnap() - || UseXDGDesktopPortal(); - - const auto neededForPortal = (type == Type::ReadFolder) - && !CanOpenDirectoryWithPortal(); - - const auto neededNonForced = DesktopEnvironment::IsGtkBased() - || (sandboxedOrCustomPortal && neededForPortal); - - const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal; - return IsGtkIntegrationForced() - || (neededNonForced && !excludeNonForced); + || DesktopEnvironment::IsGtkBased() + // use as a fallback for portal dialog + || UseXDGDesktopPortal(); } bool Get( diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp new file mode 100644 index 000000000..5d53d1ab2 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp @@ -0,0 +1,536 @@ +/* +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 "platform/linux/linux_xdp_file_dialog.h" + +#include "platform/linux/specific_linux.h" +#include "storage/localstorage.h" +#include "base/qt_adapters.h" + +#include <QtCore/qeventloop.h> + +#include <QtDBus/QtDBus> +#include <QDBusConnection> +#include <QDBusMessage> +#include <QDBusPendingCall> +#include <QDBusPendingCallWatcher> +#include <QDBusPendingReply> + +#include <QFile> +#include <QMetaType> +#include <QMimeType> +#include <QMimeDatabase> +#include <QRandomGenerator> +#include <QWindow> + +namespace Platform { +namespace FileDialog { +namespace XDP { +namespace { + +const char *filterRegExp = +"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + +QStringList makeFilterList(const QString &filter) { + QString f(filter); + + if (f.isEmpty()) + return QStringList(); + + QString sep(QLatin1String(";;")); + int i = f.indexOf(sep, 0); + if (i == -1) { + if (f.indexOf(QLatin1Char('\n'), 0) != -1) { + sep = QLatin1Char('\n'); + i = f.indexOf(sep, 0); + } + } + + return f.split(sep); +} + +} // namespace + +bool Use(Type type) { + return UseXDGDesktopPortal() + && (type != Type::ReadFolder || CanOpenDirectoryWithPortal()); +} + +bool Get( + QPointer<QWidget> parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + Type type, + QString startFile) { + XDPFileDialog dialog(parent, caption, QString(), filter); + + dialog.setModal(true); + if (type == Type::ReadFile || type == Type::ReadFiles) { + dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + } else if (type == Type::ReadFolder) { + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::Directory); + dialog.setOption(QFileDialog::ShowDirsOnly); + } else { + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + } + if (startFile.isEmpty() || startFile.at(0) != '/') { + startFile = cDialogLastPath() + '/' + startFile; + } + dialog.selectFile(startFile); + + int res = dialog.exec(); + + QString path = dialog.directory().path(); + if (path != cDialogLastPath()) { + cSetDialogLastPath(path); + Local::writeSettings(); + } + + if (res == QDialog::Accepted) { + QStringList selectedFilesStrings; + ranges::transform( + dialog.selectedFiles(), + ranges::back_inserter(selectedFilesStrings), + [](const QUrl &url) { return url.path(); }); + + if (type == Type::ReadFiles) { + files = selectedFilesStrings; + } else { + files = selectedFilesStrings.mid(0, 1); + } + return true; + } + + files = QStringList(); + remoteContent = QByteArray(); + return false; +} + +QDBusArgument &operator <<(QDBusArgument &arg, const XDPFileDialog::FilterCondition &filterCondition) { + arg.beginStructure(); + arg << filterCondition.type << filterCondition.pattern; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator >>(const QDBusArgument &arg, XDPFileDialog::FilterCondition &filterCondition) { + uint type; + QString filterPattern; + arg.beginStructure(); + arg >> type >> filterPattern; + filterCondition.type = (XDPFileDialog::ConditionType)type; + filterCondition.pattern = filterPattern; + arg.endStructure(); + + return arg; +} + +QDBusArgument &operator <<(QDBusArgument &arg, const XDPFileDialog::Filter filter) { + arg.beginStructure(); + arg << filter.name << filter.filterConditions; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator >>(const QDBusArgument &arg, XDPFileDialog::Filter &filter) { + QString name; + XDPFileDialog::FilterConditionList filterConditions; + arg.beginStructure(); + arg >> name >> filterConditions; + filter.name = name; + filter.filterConditions = filterConditions; + arg.endStructure(); + + return arg; +} + +class XDPFileDialogPrivate { +public: + XDPFileDialogPrivate() { + } + + WId winId = 0; + bool directoryMode = false; + bool modal = false; + bool multipleFiles = false; + bool saveFile = false; + QString acceptLabel; + QString directory; + QString title; + QStringList nameFilters; + QStringList mimeTypesFilters; + // maps user-visible name for portal to full name filter + QMap<QString, QString> userVisibleToNameFilter; + QString selectedMimeTypeFilter; + QString selectedNameFilter; + QStringList selectedFiles; +}; + +XDPFileDialog::XDPFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) +: QDialog(parent) +, d_ptr(new XDPFileDialogPrivate()) +, _windowTitle(caption) +, _initialDirectory(directory) { + Q_D(XDPFileDialog); + + auto filters = makeFilterList(filter); + const int numFilters = filters.count(); + _nameFilters.reserve(numFilters); + for (int i = 0; i < numFilters; ++i) { + _nameFilters << filters[i].simplified(); + } + + accepted( + ) | rpl::start_with_next([=] { + Q_EMIT accept(); + }, _lifetime); + + rejected( + ) | rpl::start_with_next([=] { + Q_EMIT reject(); + }, _lifetime); +} + +XDPFileDialog::~XDPFileDialog() { +} + +void XDPFileDialog::initializeDialog() { + Q_D(XDPFileDialog); + + if (_fileMode == QFileDialog::ExistingFiles) + d->multipleFiles = true; + + if (_fileMode == QFileDialog::Directory || _options.testFlag(QFileDialog::ShowDirsOnly)) + d->directoryMode = true; + +#if 0 // it is commented in GtkFileDialog for some reason, do the same + if (options()->isLabelExplicitlySet(QFileDialog::Accept)) + d->acceptLabel = options()->labelText(QFileDialog::Accept); +#endif + + if (!_windowTitle.isEmpty()) + d->title = _windowTitle; + + if (_acceptMode == QFileDialog::AcceptSave) + d->saveFile = true; + + if (!_nameFilters.isEmpty()) + d->nameFilters = _nameFilters; + +#if 0 // what is the right way to implement this? + if (!options()->mimeTypeFilters().isEmpty()) + d->mimeTypesFilters = options()->mimeTypeFilters(); + + if (!options()->initiallySelectedMimeTypeFilter().isEmpty()) + d->selectedMimeTypeFilter = options()->initiallySelectedMimeTypeFilter(); +#endif + + const auto initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front(); + if (!initialNameFilter.isEmpty()) + d->selectedNameFilter = initialNameFilter; + + setDirectory(_initialDirectory); +} + +void XDPFileDialog::openPortal() { + Q_D(XDPFileDialog); + + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.FileChooser"), + d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile")); + QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16); + + QVariantMap options; + if (!d->acceptLabel.isEmpty()) + options.insert(QLatin1String("accept_label"), d->acceptLabel); + + options.insert(QLatin1String("modal"), d->modal); + options.insert(QLatin1String("multiple"), d->multipleFiles); + options.insert(QLatin1String("directory"), d->directoryMode); + + if (d->saveFile) { + if (!d->directory.isEmpty()) + options.insert(QLatin1String("current_folder"), QFile::encodeName(d->directory).append('\0')); + + if (!d->selectedFiles.isEmpty()) + options.insert(QLatin1String("current_file"), QFile::encodeName(d->selectedFiles.first()).append('\0')); + } + + // Insert filters + qDBusRegisterMetaType<FilterCondition>(); + qDBusRegisterMetaType<FilterConditionList>(); + qDBusRegisterMetaType<Filter>(); + qDBusRegisterMetaType<FilterList>(); + + FilterList filterList; + auto selectedFilterIndex = filterList.size() - 1; + + d->userVisibleToNameFilter.clear(); + + if (!d->mimeTypesFilters.isEmpty()) { + for (const QString &mimeTypefilter : d->mimeTypesFilters) { + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForName(mimeTypefilter); + + // Creates e.g. (1, "image/png") + FilterCondition filterCondition; + filterCondition.type = MimeType; + filterCondition.pattern = mimeTypefilter; + + // Creates e.g. [((1, "image/png"))] + FilterConditionList filterConditions; + filterConditions << filterCondition; + + // Creates e.g. [("Images", [((1, "image/png"))])] + Filter filter; + filter.name = mimeType.comment(); + filter.filterConditions = filterConditions; + + filterList << filter; + + if (!d->selectedMimeTypeFilter.isEmpty() && d->selectedMimeTypeFilter == mimeTypefilter) + selectedFilterIndex = filterList.size() - 1; + } + } else if (!d->nameFilters.isEmpty()) { + for (const QString &nameFilter : d->nameFilters) { + // Do parsing: + // Supported format is ("Images (*.png *.jpg)") + QRegularExpression regexp(QString::fromLatin1(filterRegExp)); + QRegularExpressionMatch match = regexp.match(nameFilter); + if (match.hasMatch()) { + QString userVisibleName = match.captured(1); + QStringList filterStrings = match.captured(2).split(QLatin1Char(' '), base::QStringSkipEmptyParts); + + if (filterStrings.isEmpty()) { + LOG(("XDP File Dialog Error: Filter %1 is empty and will be ignored.").arg(userVisibleName)); + continue; + } + + FilterConditionList filterConditions; + for (const QString &filterString : filterStrings) { + FilterCondition filterCondition; + filterCondition.type = GlobalPattern; + filterCondition.pattern = filterString; + filterConditions << filterCondition; + } + + Filter filter; + filter.name = userVisibleName; + filter.filterConditions = filterConditions; + + filterList << filter; + + d->userVisibleToNameFilter.insert(userVisibleName, nameFilter); + + if (!d->selectedNameFilter.isEmpty() && d->selectedNameFilter == nameFilter) + selectedFilterIndex = filterList.size() - 1; + } + } + } + + if (!filterList.isEmpty()) + options.insert(QLatin1String("filters"), QVariant::fromValue(filterList)); + + if (selectedFilterIndex != -1) + options.insert(QLatin1String("current_filter"), QVariant::fromValue(filterList[selectedFilterIndex])); + + options.insert(QLatin1String("handle_token"), QStringLiteral("qt%1").arg(QRandomGenerator::global()->generate())); + + // TODO choices a(ssa(ss)s) + // List of serialized combo boxes to add to the file chooser. + + message << parentWindowId << d->title << options; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) { + QDBusPendingReply<QDBusObjectPath> reply = *watcher; + if (reply.isError()) { + _reject.fire({}); + } else { + QDBusConnection::sessionBus().connect( + nullptr, + reply.value().path(), + QLatin1String("org.freedesktop.portal.Request"), + QLatin1String("Response"), + this, + SLOT(gotResponse(uint,QVariantMap))); + } + }); +} + +bool XDPFileDialog::defaultNameFilterDisables() const { + return false; +} + +void XDPFileDialog::setDirectory(const QUrl &directory) { + Q_D(XDPFileDialog); + + d->directory = directory.path(); +} + +QUrl XDPFileDialog::directory() const { + Q_D(const XDPFileDialog); + + return d->directory; +} + +void XDPFileDialog::selectFile(const QUrl &filename) { + Q_D(XDPFileDialog); + + d->selectedFiles << filename.path(); +} + +QList<QUrl> XDPFileDialog::selectedFiles() const { + Q_D(const XDPFileDialog); + + QList<QUrl> files; + for (const QString &file : d->selectedFiles) { + files << QUrl(file); + } + return files; +} + +void XDPFileDialog::setFilter() { + Q_D(XDPFileDialog); +} + +void XDPFileDialog::selectMimeTypeFilter(const QString &filter) { + Q_D(XDPFileDialog); +} + +QString XDPFileDialog::selectedMimeTypeFilter() const { + Q_D(const XDPFileDialog); + return d->selectedMimeTypeFilter; +} + +void XDPFileDialog::selectNameFilter(const QString &filter) { + Q_D(XDPFileDialog); +} + +QString XDPFileDialog::selectedNameFilter() const { + Q_D(const XDPFileDialog); + return d->selectedNameFilter; +} + +int XDPFileDialog::exec() { + Q_D(XDPFileDialog); + + bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_DeleteOnClose, false); + + bool wasShowModal = testAttribute(Qt::WA_ShowModal); + setAttribute(Qt::WA_ShowModal, true); + setResult(0); + + show(); + + QPointer<QDialog> guard = this; + + // HACK we have to avoid returning until we emit that the dialog was accepted or rejected + QEventLoop loop; + rpl::lifetime lifetime; + + accepted( + ) | rpl::start_with_next([&] { + loop.quit(); + }, lifetime); + + rejected( + ) | rpl::start_with_next([&] { + loop.quit(); + }, lifetime); + + loop.exec(); + + if (guard.isNull()) + return QDialog::Rejected; + + setAttribute(Qt::WA_ShowModal, wasShowModal); + + return result(); +} + +void XDPFileDialog::setVisible(bool visible) { + if (visible) { + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) { + return; + } + } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) { + return; + } + + if (visible) { + showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr); + } else { + hideHelper(); + } + + // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below + // updates the state correctly, but skips showing the non-native version: + setAttribute(Qt::WA_DontShowOnScreen); + + QDialog::setVisible(visible); +} + +void XDPFileDialog::hideHelper() { + Q_D(XDPFileDialog); +} + +void XDPFileDialog::showHelper(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { + Q_D(XDPFileDialog); + + initializeDialog(); + + d->modal = windowModality != Qt::NonModal; + d->winId = parent ? parent->winId() : 0; + + openPortal(); +} + +void XDPFileDialog::gotResponse(uint response, const QVariantMap &results) { + Q_D(XDPFileDialog); + + if (!response) { + if (results.contains(QLatin1String("uris"))) + d->selectedFiles = results.value(QLatin1String("uris")).toStringList(); + + if (results.contains(QLatin1String("current_filter"))) { + const Filter selectedFilter = qdbus_cast<Filter>(results.value(QStringLiteral("current_filter"))); + if (!selectedFilter.filterConditions.empty() && selectedFilter.filterConditions[0].type == MimeType) { + // s.a. XDPFileDialog::openPortal which basically does the inverse + d->selectedMimeTypeFilter = selectedFilter.filterConditions[0].pattern; + d->selectedNameFilter.clear(); + } else { + d->selectedNameFilter = d->userVisibleToNameFilter.value(selectedFilter.name); + d->selectedMimeTypeFilter.clear(); + } + } + _accept.fire({}); + } else { + _reject.fire({}); + } +} + +rpl::producer<> XDPFileDialog::accepted() { + return _accept.events(); +} + +rpl::producer<> XDPFileDialog::rejected() { + return _reject.events(); +} + +} // namespace XDP +} // namespace FileDialog +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.h b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.h new file mode 100644 index 000000000..57bc43e56 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.h @@ -0,0 +1,136 @@ +/* +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 "core/file_utilities.h" + +#include <QFileDialog> +#include <QVector> + +namespace Platform { +namespace FileDialog { +namespace XDP { + +class XDPFileDialogPrivate; +using Type = ::FileDialog::internal::Type; + +bool Use(Type type = Type::ReadFile); +bool Get( + QPointer<QWidget> parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + Type type, + QString startFile); + +// This is a patched copy of file dialog from qxdgdesktopportal theme plugin. +// It allows using XDP file dialog flexibly, +// without relying on QT_QPA_PLATFORMTHEME variable. +// +// XDP file dialog is a dialog obtained via a DBus service +// provided by the current desktop environment. +class XDPFileDialog : public QDialog { + Q_OBJECT + Q_DECLARE_PRIVATE(XDPFileDialog) +public: + enum ConditionType : uint { + GlobalPattern = 0, + MimeType = 1 + }; + // Filters a(sa(us)) + // Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])] + struct FilterCondition { + ConditionType type; + QString pattern; // E.g. '*ico' or 'image/png' + }; + typedef QVector<FilterCondition> FilterConditionList; + + struct Filter { + QString name; // E.g. 'Images' or 'Text + FilterConditionList filterConditions;; // E.g. [(0, '*.ico'), (1, 'image/png')] or [(0, '*.txt')] + }; + typedef QVector<Filter> FilterList; + + XDPFileDialog( + QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &directory = QString(), + const QString &filter = QString()); + ~XDPFileDialog(); + + void setVisible(bool visible) override; + + void setWindowTitle(const QString &windowTitle) { + _windowTitle = windowTitle; + } + void setAcceptMode(QFileDialog::AcceptMode acceptMode) { + _acceptMode = acceptMode; + } + void setFileMode(QFileDialog::FileMode fileMode) { + _fileMode = fileMode; + } + void setOption(QFileDialog::Option option, bool on = true) { + if (on) { + _options |= option; + } else { + _options &= ~option; + } + } + + bool defaultNameFilterDisables() const; + QUrl directory() const; + void setDirectory(const QUrl &directory); + void selectFile(const QUrl &filename); + QList<QUrl> selectedFiles() const; + void setFilter(); + void selectNameFilter(const QString &filter); + QString selectedNameFilter() const; + void selectMimeTypeFilter(const QString &filter); + QString selectedMimeTypeFilter() const; + + int exec() override; + +private Q_SLOTS: + void gotResponse(uint response, const QVariantMap &results); + +private: + void initializeDialog(); + void openPortal(); + + void showHelper(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent); + void hideHelper(); + + rpl::producer<> accepted(); + rpl::producer<> rejected(); + + QScopedPointer<XDPFileDialogPrivate> d_ptr; + + // Options + QFileDialog::Options _options; + QString _windowTitle = "Choose file"; + QString _initialDirectory; + QStringList _initialFiles; + QStringList _nameFilters; + QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen; + QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile; + + rpl::event_stream<> _accept; + rpl::event_stream<> _reject; + rpl::lifetime _lifetime; +}; + +} // namespace XDP +} // namespace FileDialog +} // namespace Platform + +Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterCondition); +Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterConditionList); +Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::Filter); +Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterList); + diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 6f2acd99e..cc091af3f 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -29,8 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <QtWidgets/QDesktopWidget> #include <QtCore/QStandardPaths> #include <QtCore/QProcess> -#include <QtCore/QVersionNumber> -#include <QtCore/QLibraryInfo> #include <QtGui/QWindow> #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -596,18 +594,16 @@ bool AreQtPluginsBundled() { bool UseXDGDesktopPortal() { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION static const auto Result = [&] { - const auto onlyIn = AreQtPluginsBundled() - // it is handled by Qt for flatpak and snap - && !InFlatpak() - && !InSnap(); + if (InFlatpak() || InSnap()) { + return true; + } const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL"); const auto portalPresent = IsXDGDesktopPortalPresent(); const auto neededForKde = DesktopEnvironment::IsKDE() && IsXDGDesktopPortalKDEPresent(); - return onlyIn - && portalPresent + return portalPresent && (neededForKde || envVar); }(); @@ -620,12 +616,7 @@ bool UseXDGDesktopPortal() { bool CanOpenDirectoryWithPortal() { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION static const auto Result = [&] { -#ifdef DESKTOP_APP_QT_PATCHED return FileChooserPortalVersion() >= 3; -#else // DESKTOP_APP_QT_PATCHED - return QLibraryInfo::version() >= QVersionNumber(5, 15, 0) - && FileChooserPortalVersion() >= 3; -#endif // !DESKTOP_APP_QT_PATCHED }(); return Result; @@ -1037,14 +1028,12 @@ void start() { } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - // this can give us a chance to use - // a proper file dialog for current session + // Tell the user when XDP file dialog is used DEBUG_LOG(("Checking for XDG Desktop Portal...")); if (IsXDGDesktopPortalPresent()) { DEBUG_LOG(("XDG Desktop Portal is present!")); if (UseXDGDesktopPortal()) { LOG(("Using XDG Desktop Portal.")); - qputenv("QT_QPA_PLATFORMTHEME", "xdgdesktopportal"); } else { DEBUG_LOG(("Not using XDG Desktop Portal.")); } From 8fd1d16db6756734a812a8dab3a5dcb513d18ffc Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 5 Feb 2021 11:41:12 +0400 Subject: [PATCH 277/396] Fix accept/reject lifetime in gtk file dialog --- .../platform/linux/linux_gtk_file_dialog.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index f03fcdaea..2b9b1b0cc 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -110,7 +110,6 @@ private: rpl::event_stream<> _accept; rpl::event_stream<> _reject; - rpl::lifetime _lifetime; }; @@ -208,16 +207,17 @@ void QGtkDialog::exec() { } else { // block input to the window, allow input to other GTK dialogs QEventLoop loop; + rpl::lifetime lifetime; accept( - ) | rpl::start_with_next([=, &loop] { + ) | rpl::start_with_next([&] { loop.quit(); - }, _lifetime); + }, lifetime); reject( - ) | rpl::start_with_next([=, &loop] { + ) | rpl::start_with_next([&] { loop.quit(); - }, _lifetime); + }, lifetime); loop.exec(); } From 9b70f24e91ad9b56534eb91d5e574fda56297335 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 5 Feb 2021 16:22:02 +0400 Subject: [PATCH 278/396] Adjust some tabs in gtk file dialog --- .../platform/linux/linux_gtk_file_dialog.cpp | 3 ++- .../platform/linux/linux_gtk_file_dialog.h | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index 2b9b1b0cc..708e07005 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -115,7 +115,8 @@ private: class GtkFileDialog : public QDialog { public: - GtkFileDialog(QWidget *parent = nullptr, + GtkFileDialog( + QWidget *parent = nullptr, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString()); diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h index 0891a0e10..7f3c5ffb4 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h @@ -18,13 +18,13 @@ using Type = ::FileDialog::internal::Type; bool Supported(); bool Use(Type type = Type::ReadFile); bool Get( - QPointer<QWidget> parent, - QStringList &files, - QByteArray &remoteContent, - const QString &caption, - const QString &filter, - Type type, - QString startFile); + QPointer<QWidget> parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + Type type, + QString startFile); } // namespace Gtk } // namespace FileDialog From 03e8d28456fbcb6f639ecf4e0d45491612052386 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 9 Feb 2021 15:26:33 +0400 Subject: [PATCH 279/396] Check for null manager type --- .../platform/linux/notifications_manager_linux.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 7afdd560c..eb7ca59e5 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -722,13 +722,13 @@ void Create(Window::Notifications::System *system) { using ManagerType = Window::Notifications::ManagerType; if ((Core::App().settings().nativeNotifications() && Supported()) || Enforced()) { - if (*system->managerType() != ManagerType::Native) { + if (!system->managerType().has_value() + || *system->managerType() != ManagerType::Native) { system->setManager(std::make_unique<Manager>(system)); } - } else { - if (*system->managerType() != ManagerType::Default) { - system->setManager(nullptr); - } + } else if (!system->managerType().has_value() + || *system->managerType() != ManagerType::Default) { + system->setManager(nullptr); } }; From f2b434d82bef8a5e006357bf521c5ea6c620ef1d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 4 Feb 2021 19:50:46 +0300 Subject: [PATCH 280/396] Fixed text color in playback speed item. --- .../SourceFiles/media/view/media_view_playback_controls.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 9d873f804..89f1e8c26 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -101,7 +101,7 @@ MenuSpeedItem::MenuSpeedItem( const auto speedString = [=](float64 value) { return u"%1: %2x"_q .arg(tr::lng_mediaview_playback_speed(tr::now)) - .arg(computeSpeed(value)); + .arg(QString::number(computeSpeed(value), 'f', 2)); }; _slider->setAlwaysDisplayMarker(true); @@ -142,6 +142,7 @@ MenuSpeedItem::MenuSpeedItem( const auto value = _slider->value(); + p.setPen(selected ? st.itemFgOver : st.itemFg); p.setFont(st.itemStyle.font); p.drawText(_textRect, speedString(value), style::al_left); }, lifetime()); From 776c099a257a7a9ff14903106529d63adf20cc6b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 4 Feb 2021 20:41:03 +0300 Subject: [PATCH 281/396] Limited maximum number of selected messages for rescheduling to 20. --- .../SourceFiles/history/view/history_view_context_menu.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 20f399061..73ca05504 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -57,6 +57,7 @@ namespace { // If we can't cloud-export link for such time we export it locally. constexpr auto kExportLocalTimeout = crl::time(1000); +constexpr auto kRescheduleLimit = 20; //void AddToggleGroupingAction( // #feed // not_null<Ui::PopupMenu*> menu, @@ -458,6 +459,9 @@ bool AddRescheduleAction( if (!request.overSelection || request.selectedItems.empty()) { return false; } + if (request.selectedItems.size() > kRescheduleLimit) { + return false; + } return true; }(); if (!goodSingle && !goodMany) { @@ -492,7 +496,7 @@ bool AddRescheduleAction( options.removeWebPageId = true; } Api::RescheduleMessage(item, options); - // Increase the scheduled date by 1ms to keep the order. + // Increase the scheduled date by 1s to keep the order. options.scheduled += 1; } }; From 90f90a4ca3865185259875e6e4f134e81377dbb5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 4 Feb 2021 20:55:43 +0300 Subject: [PATCH 282/396] Fixed accepting of Enter key in box of voice message discarding. --- .../history/view/controls/history_view_voice_record_bar.cpp | 4 +++- .../history/view/controls/history_view_voice_record_bar.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp index 4bc2f6a01..3796889c6 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp @@ -1602,7 +1602,7 @@ void VoiceRecordBar::installListenStateFilter() { _listen->playPause(); return Result::Cancel; } - if (isEnter) { + if (isEnter && !_warningShown) { requestToSendWithOptions({}); return Result::Cancel; } @@ -1633,6 +1633,7 @@ void VoiceRecordBar::showDiscardBox( hideAnimated(); } close(); + _warningShown = false; if (callback) { callback(); } @@ -1644,6 +1645,7 @@ void VoiceRecordBar::showDiscardBox( tr::lng_record_lock_discard(tr::now), st::attentionBoxButton, std::move(sure))); + _warningShown = true; } } // namespace HistoryView::Controls diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h index ff8e8cdbc..941f39af2 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h @@ -143,6 +143,8 @@ private: Fn<bool()> _startRecordingFilter; + bool _warningShown = false; + rpl::variable<bool> _recording = false; rpl::variable<bool> _inField = false; rpl::variable<bool> _lockShowing = false; From d60a89f3549bb8a46fa7f8411a84b0ea78895887 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 19:23:45 +0400 Subject: [PATCH 283/396] Update tgcalls. --- Telegram/ThirdParty/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index bea2e2189..71addf5b4 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit bea2e218959244590fa57b62803bf2a7a5e15bc7 +Subproject commit 71addf5b41cb6bb6844f75e977edae0020938930 From 39742d22d9aa67f74255def6f0f499d9791e4dfe Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 12:58:19 +0400 Subject: [PATCH 284/396] Apply volume_by_admin flag in voice chats. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 10 +++++++++- Telegram/SourceFiles/calls/calls_group_common.h | 1 + Telegram/SourceFiles/data/data_group_call.cpp | 8 +++++++- Telegram/SourceFiles/data/data_group_call.h | 1 + 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 9bdbcc2c6..5f03074c1 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -377,10 +377,15 @@ void GroupCall::applySelfInCallLocally() { const auto lastActive = (i != end(participants)) ? i->lastActive : TimeId(0); + const auto volume = (i != end(participants)) + ? i->volume + : Group::kDefaultVolume; const auto canSelfUnmute = (muted() != MuteState::ForceMuted); const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) | (_mySsrc ? Flag(0) : Flag::f_left) + | Flag::f_volume // Without flag the volume is reset to 100%. + | Flag::f_volume_by_admin // Self volume can only be set by admin. | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0)); call->applyUpdateChecked( MTP_updateGroupCallParticipants( @@ -393,7 +398,7 @@ void GroupCall::applySelfInCallLocally() { MTP_int(date), MTP_int(lastActive), MTP_int(_mySsrc), - MTP_int(Group::kDefaultVolume))), // volume + MTP_int(volume))), MTP_int(0)).c_updateGroupCallParticipants()); } @@ -410,6 +415,9 @@ void GroupCall::applyParticipantLocally( using Flag = MTPDgroupCallParticipant::Flag; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | Flag::f_volume // Without flag the volume is reset to 100%. + | ((participant->applyVolumeFromMin && !volume) + ? Flag::f_volume_by_admin + : Flag(0)) | (participant->lastActive ? Flag::f_active_date : Flag(0)) | (!mute ? Flag(0) diff --git a/Telegram/SourceFiles/calls/calls_group_common.h b/Telegram/SourceFiles/calls/calls_group_common.h index 22ac7d856..7f76fd398 100644 --- a/Telegram/SourceFiles/calls/calls_group_common.h +++ b/Telegram/SourceFiles/calls/calls_group_common.h @@ -19,6 +19,7 @@ struct MuteRequest { bool mute = false; bool locallyOnly = false; }; + struct VolumeRequest { not_null<UserData*> user; int volume = kDefaultVolume; diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index cc280bb42..38ccd2e6a 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -276,9 +276,14 @@ void GroupCall::applyParticipantsSlice( && ((was ? was->speaking : false) || (!amInCall && (lastActive + speakingAfterActive > now))); - const auto volume = (was && data.is_min()) + const auto volume = (was + && !was->applyVolumeFromMin + && data.is_min()) ? was->volume : data.vvolume().value_or(Calls::Group::kDefaultVolume); + const auto applyVolumeFromMin = (was && data.is_min()) + ? was->applyVolumeFromMin + : (data.is_min() || data.is_volume_by_admin()); const auto mutedByMe = (was && data.is_min()) ? was->mutedByMe : data.is_muted_by_you(); @@ -290,6 +295,7 @@ void GroupCall::applyParticipantsSlice( .lastActive = lastActive, .ssrc = uint32(data.vsource().v), .volume = volume, + .applyVolumeFromMin = applyVolumeFromMin, .speaking = canSelfUnmute && (was ? was->speaking : false), .muted = data.is_muted(), .mutedByMe = mutedByMe, diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index fd01d3493..b1d6bac88 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -38,6 +38,7 @@ public: TimeId lastActive = 0; uint32 ssrc = 0; int volume = 0; + bool applyVolumeFromMin = true; bool sounding = false; bool speaking = false; bool muted = false; From 5cea5fc4e60f328504edcc5e8b11900f0ebbd7ba Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 8 Feb 2021 16:07:38 +0400 Subject: [PATCH 285/396] Fix window size in Tablet Mode on Windows 10. --- Telegram/SourceFiles/platform/win/main_window_win.cpp | 2 +- Telegram/lib_base | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 24e3a0f2d..d2454f0b1 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -324,7 +324,7 @@ bool MainWindow::initSizeFromSystem() { if (!screen) { return false; } - setGeometry(screen->geometry()); + setGeometry(screen->availableGeometry()); return true; } diff --git a/Telegram/lib_base b/Telegram/lib_base index 2bf29ab1a..f1e416808 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 2bf29ab1a5458003c8ed250886e08c61cce5ff72 +Subproject commit f1e4168081428fa451d2f50eee7b1c448268c43a From e85394b5209e0ebf281c43023d85a145f3b9beb2 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 14:01:29 +0400 Subject: [PATCH 286/396] Fix build with updated scheme. --- Telegram/Resources/tl/api.tl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 3e84689f1..741268feb 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1196,7 +1196,7 @@ groupCall#55903081 flags:# join_muted:flags.1?true can_change_join_muted:flags.2 inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; +groupCallParticipant#64c62a15 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true user_id:int date:int active_date:flags.3?int source:int volume:flags.7?int = GroupCallParticipant; phone.groupCall#66ab0bfc call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string users:Vector<User> = phone.GroupCall; From bff3291631b738e3239d5860386fdfe9a2755177 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 11 Feb 2021 21:58:25 +0300 Subject: [PATCH 287/396] Fixed clearing of text field in sections when sending files. --- .../history_view_compose_controls.cpp | 23 +++++++++++++++++++ .../controls/history_view_compose_controls.h | 2 ++ .../view/history_view_replies_section.cpp | 21 +++-------------- .../view/history_view_scheduled_section.cpp | 21 +++-------------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index b75d559a6..9b2bc1b74 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -2373,4 +2373,27 @@ void ComposeControls::applyInlineBotQuery( } } +Fn<void()> ComposeControls::restoreTextCallback( + const QString &insertTextOnCancel) const { + const auto cursor = _field->textCursor(); + const auto position = cursor.position(); + const auto anchor = cursor.anchor(); + const auto text = getTextWithAppliedMarkdown(); + + _field->setTextWithTags({}); + + return crl::guard(_field, [=] { + _field->setTextWithTags(text); + auto cursor = _field->textCursor(); + cursor.setPosition(anchor); + if (position != anchor) { + cursor.setPosition(position, QTextCursor::KeepAnchor); + } + _field->setTextCursor(cursor); + if (!insertTextOnCancel.isEmpty()) { + _field->textCursor().insertText(insertTextOnCancel); + } + }); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 52d49fc55..0919baff7 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -172,6 +172,8 @@ public: void applyDraft( FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear); + Fn<void()> restoreTextCallback(const QString &insertTextOnCancel) const; + private: enum class TextUpdateEvent { SaveDraft = (1 << 0), diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index e76676f22..a8a96f894 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -626,19 +626,14 @@ bool RepliesWidget::confirmSendingFiles( return false; } - //const auto cursor = _field->textCursor(); - //const auto position = cursor.position(); - //const auto anchor = cursor.anchor(); - const auto text = _composeControls->getTextWithAppliedMarkdown();//_field->getTextWithTags(); using SendLimit = SendFilesBox::SendLimit; auto box = Box<SendFilesBox>( controller(), std::move(list), - text, + _composeControls->getTextWithAppliedMarkdown(), _history->peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many, Api::SendType::Normal, SendMenu::Type::SilentOnly); // #TODO replies schedule - _composeControls->setText({}); const auto replyTo = replyToId(); box->setConfirmedCallback(crl::guard(this, [=]( @@ -654,18 +649,8 @@ bool RepliesWidget::confirmSendingFiles( options, ctrlShiftEnter); })); - box->setCancelledCallback(crl::guard(this, [=] { - _composeControls->setText(text); - //auto cursor = _field->textCursor(); - //cursor.setPosition(anchor); - //if (position != anchor) { - // cursor.setPosition(position, QTextCursor::KeepAnchor); - //} - //_field->setTextCursor(cursor); - //if (!insertTextOnCancel.isEmpty()) { - // _field->textCursor().insertText(insertTextOnCancel); - //} - })); + box->setCancelledCallback(_composeControls->restoreTextCallback( + insertTextOnCancel)); //ActivateWindow(controller()); const auto shown = Ui::show(std::move(box)); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index f2d11d08e..017956038 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -350,21 +350,16 @@ bool ScheduledWidget::confirmSendingFiles( return false; } - //const auto cursor = _field->textCursor(); - //const auto position = cursor.position(); - //const auto anchor = cursor.anchor(); - const auto text = _composeControls->getTextWithAppliedMarkdown();//_field->getTextWithTags(); using SendLimit = SendFilesBox::SendLimit; auto box = Box<SendFilesBox>( controller(), std::move(list), - text, + _composeControls->getTextWithAppliedMarkdown(), _history->peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many, CanScheduleUntilOnline(_history->peer) ? Api::SendType::ScheduledToUser : Api::SendType::Scheduled, SendMenu::Type::Disabled); - //_field->setTextWithTags({}); box->setConfirmedCallback(crl::guard(this, [=]( Ui::PreparedList &&list, @@ -379,18 +374,8 @@ bool ScheduledWidget::confirmSendingFiles( options, ctrlShiftEnter); })); - //box->setCancelledCallback(crl::guard(this, [=] { - // _field->setTextWithTags(text); - // auto cursor = _field->textCursor(); - // cursor.setPosition(anchor); - // if (position != anchor) { - // cursor.setPosition(position, QTextCursor::KeepAnchor); - // } - // _field->setTextCursor(cursor); - // if (!insertTextOnCancel.isEmpty()) { - // _field->textCursor().insertText(insertTextOnCancel); - // } - //})); + box->setCancelledCallback(_composeControls->restoreTextCallback( + insertTextOnCancel)); //ActivateWindow(controller()); const auto shown = Ui::show(std::move(box)); From c3c1759f3c487f83fc86a5b1653e28dd221c052e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 17 Feb 2021 01:25:57 +0300 Subject: [PATCH 288/396] Fixed updating number of sessions after terminating any of them. --- Telegram/SourceFiles/api/api_authorizations.cpp | 16 +++++++++++++--- Telegram/SourceFiles/boxes/sessions_box.cpp | 8 ++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index ea8bef14a..b0a99e929 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -128,9 +128,19 @@ void Authorizations::requestTerminate( const auto send = [&](auto request) { _api.request( std::move(request) - ).done( - std::move(done) - ).fail( + ).done([=, done = std::move(done)](const MTPBool &result) { + done(result); + if (mtpIsTrue(result)) { + if (hash) { + _list.erase( + ranges::remove(_list, *hash, &Entry::hash), + end(_list)); + } else { + _list.clear(); + } + _listChanges.fire({}); + } + }).fail( std::move(fail) ).send(); }; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 9f447dcfa..198c0e587 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -262,13 +262,13 @@ void SessionsContent::terminateOne(uint64 hash) { const auto weak = Ui::MakeWeak(this); auto callback = [=] { auto done = crl::guard(weak, [=](const MTPBool &result) { + if (mtpIsFalse(result)) { + return; + } _inner->terminatingOne(hash, false); - const auto getHash = [](const Entry &entry) { - return entry.hash; - }; const auto removeByHash = [&](std::vector<Entry> &list) { list.erase( - ranges::remove(list, hash, getHash), + ranges::remove(list, hash, &Entry::hash), end(list)); }; removeByHash(_data.incomplete); From 223681d2da0e73b7bcbd46e0833bcbd4f37095e3 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 14:40:55 +0400 Subject: [PATCH 289/396] Fix default chat rights for creator. --- .../boxes/peers/add_participants_box.cpp | 14 +++++--- .../boxes/peers/add_participants_box.h | 2 +- .../boxes/peers/edit_participant_box.cpp | 30 ++++++++-------- .../boxes/peers/edit_participant_box.h | 4 +-- .../boxes/peers/edit_participants_box.cpp | 24 ++++++++----- .../boxes/peers/edit_participants_box.h | 4 +-- Telegram/SourceFiles/data/data_chat.cpp | 11 +++--- Telegram/SourceFiles/data/data_chat.h | 36 +++++++++---------- 8 files changed, 71 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 6a60fe641..73ff07e32 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -329,15 +329,21 @@ Main::Session &AddSpecialBoxController::session() const { } void AddSpecialBoxController::subscribeToMigration() { + const auto chat = _peer->asChat(); + if (!chat) { + return; + } SubscribeToMigration( - _peer, + chat, lifetime(), - [=](not_null<ChannelData*> channel) { migrate(channel); }); + [=](not_null<ChannelData*> channel) { migrate(chat, channel); }); } -void AddSpecialBoxController::migrate(not_null<ChannelData*> channel) { +void AddSpecialBoxController::migrate( + not_null<ChatData*> chat, + not_null<ChannelData*> channel) { _peer = channel; - _additional.migrate(channel); + _additional.migrate(chat, channel); } std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow( diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.h b/Telegram/SourceFiles/boxes/peers/add_participants_box.h index 8098134b7..ee626f161 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.h @@ -121,7 +121,7 @@ private: std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const; void subscribeToMigration(); - void migrate(not_null<ChannelData*> channel); + void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel); not_null<PeerData*> _peer; MTP::Sender _api; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index df67a6255..1ea91afc5 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -208,10 +208,10 @@ EditAdminBox::EditAdminBox( , _oldRank(rank) { } -MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) { - const auto defaultRights = peer->isChat() - ? ChatData::DefaultAdminRights() - : peer->isMegagroup() +MTPChatAdminRights EditAdminBox::defaultRights() const { + const auto flags = peer()->isChat() + ? peer()->asChat()->defaultAdminRights(user()) + : peer()->isMegagroup() ? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users @@ -223,7 +223,7 @@ MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) { | Flag::f_edit_messages | Flag::f_delete_messages | Flag::f_invite_users); - return MTP_chatAdminRights(MTP_flags(defaultRights)); + return MTP_chatAdminRights(MTP_flags(flags)); } void EditAdminBox::prepare() { @@ -242,7 +242,7 @@ void EditAdminBox::prepare() { const auto chat = peer()->asChat(); const auto channel = peer()->asChannel(); - const auto prepareRights = hadRights ? _oldRights : Defaults(peer()); + const auto prepareRights = hadRights ? _oldRights : defaultRights(); const auto disabledByDefaults = (channel && !channel->isMegagroup()) ? MTPDchatAdminRights::Flags(0) : DisabledByDefaultRestrictions(peer()); @@ -264,12 +264,12 @@ void EditAdminBox::prepare() { result.emplace( disabledByDefaults, tr::lng_rights_permission_for_all(tr::now)); - if (const auto channel = peer()->asChannel()) { - if (amCreator() && user()->isSelf()) { - result.emplace( - ~Flag::f_anonymous, - tr::lng_rights_permission_cant_edit(tr::now)); - } else if (!channel->amCreator()) { + if (amCreator() && user()->isSelf()) { + result.emplace( + ~Flag::f_anonymous, + tr::lng_rights_permission_cant_edit(tr::now)); + } else if (const auto channel = peer()->asChannel()) { + if (!channel->amCreator()) { result.emplace( ~channel->adminRights(), tr::lng_rights_permission_cant_edit(tr::now)); @@ -611,9 +611,9 @@ void EditRestrictedBox::prepare() { const auto defaultRestrictions = chat ? chat->defaultRestrictions() : channel->defaultRestrictions(); - const auto prepareRights = (_oldRights.c_chatBannedRights().vflags().v + const auto prepareRights = _oldRights.c_chatBannedRights().vflags().v ? _oldRights - : Defaults(peer())); + : defaultRights(); const auto prepareFlags = FixDependentRestrictions( prepareRights.c_chatBannedRights().vflags().v | defaultRestrictions @@ -680,7 +680,7 @@ void EditRestrictedBox::prepare() { } } -MTPChatBannedRights EditRestrictedBox::Defaults(not_null<PeerData*> peer) { +MTPChatBannedRights EditRestrictedBox::defaultRights() const { return MTP_chatBannedRights(MTP_flags(0), MTP_int(0)); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 371d7b98b..474c166c1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -89,7 +89,7 @@ private: using Flag = MTPDchatAdminRights::Flag; using Flags = MTPDchatAdminRights::Flags; - static MTPChatAdminRights Defaults(not_null<PeerData*> peer); + [[nodiscard]] MTPChatAdminRights defaultRights() const; not_null<Ui::InputField*> addRankInput(); void transferOwnership(); @@ -144,7 +144,7 @@ private: using Flag = MTPDchatBannedRights::Flag; using Flags = MTPDchatBannedRights::Flags; - static MTPChatBannedRights Defaults(not_null<PeerData*> peer); + [[nodiscard]] MTPChatBannedRights defaultRights() const; bool canSave() const { return !!_saveCallback; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 4adf4993f..f777344c0 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -224,7 +224,7 @@ Fn<void( const MTPDchatAdminRights &data) { return data.vflags().v; }); - if (flags == ChatData::DefaultAdminRights() && rank.isEmpty()) { + if (flags == chat->defaultAdminRights(user) && rank.isEmpty()) { saveChatAdmin(true); } else if (!flags) { saveChatAdmin(false); @@ -370,7 +370,7 @@ auto ParticipantsAdditionalData::adminRights( if (const auto chat = _peer->asChat()) { return _admins.contains(user) ? std::make_optional(MTPChatAdminRights(MTP_chatAdminRights( - MTP_flags(ChatData::DefaultAdminRights())))) + MTP_flags(chat->defaultAdminRights(user))))) : std::nullopt; } const auto i = _adminRights.find(user); @@ -671,14 +671,16 @@ UserData *ParticipantsAdditionalData::applyBanned( return user; } -void ParticipantsAdditionalData::migrate(not_null<ChannelData*> channel) { +void ParticipantsAdditionalData::migrate( + not_null<ChatData*> chat, + not_null<ChannelData*> channel) { _peer = channel; fillFromChannel(channel); for (const auto user : _admins) { _adminRights.emplace( user, - MTP_chatAdminRights(MTP_flags(ChatData::DefaultAdminRights()))); + MTP_chatAdminRights(MTP_flags(chat->defaultAdminRights(user)))); if (channel->amCreator()) { _adminCanEdit.emplace(user); } @@ -1889,15 +1891,21 @@ void ParticipantsBoxController::refreshCustomStatus( } void ParticipantsBoxController::subscribeToMigration() { + const auto chat = _peer->asChat(); + if (!chat) { + return; + } SubscribeToMigration( - _peer, + chat, lifetime(), - [=](not_null<ChannelData*> channel) { migrate(channel); }); + [=](not_null<ChannelData*> channel) { migrate(chat, channel); }); } -void ParticipantsBoxController::migrate(not_null<ChannelData*> channel) { +void ParticipantsBoxController::migrate( + not_null<ChatData*> chat, + not_null<ChannelData*> channel) { _peer = channel; - _additional.migrate(channel); + _additional.migrate(chat, channel); subscribeToCreatorChange(channel); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index dfcb1ae6d..cff017b31 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -101,7 +101,7 @@ public: [[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const; [[nodiscard]] UserData *restrictedBy(not_null<UserData*> user) const; - void migrate(not_null<ChannelData*> channel); + void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel); private: UserData *applyCreator(const MTPDchannelParticipantCreator &data); @@ -242,7 +242,7 @@ private: void recomputeTypeFor(not_null<UserData*> user); void subscribeToMigration(); - void migrate(not_null<ChannelData*> channel); + void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel); void subscribeToCreatorChange(not_null<ChannelData*> channel); void fullListRefresh(); diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 8f4f29659..b0ec13d23 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -48,14 +48,17 @@ void ChatData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) { }); } -auto ChatData::DefaultAdminRights() -> AdminRights { +auto ChatData::defaultAdminRights(not_null<UserData*> user) -> AdminRights { + const auto isCreator = (creator == user->bareId()) + || (user->isSelf() && amCreator()); using Flag = AdminRight; return Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_pin_messages - | Flag::f_manage_call; + | Flag::f_manage_call + | (isCreator ? Flag::f_add_admins : Flag(0)); } bool ChatData::canWrite() const { @@ -335,7 +338,7 @@ void ApplyChatUpdate( } if (user->isSelf()) { chat->setAdminRights(MTP_chatAdminRights(mtpIsTrue(update.vis_admin()) - ? MTP_flags(ChatData::DefaultAdminRights()) + ? MTP_flags(chat->defaultAdminRights(user)) : MTP_flags(0))); } if (mtpIsTrue(update.vis_admin())) { @@ -457,7 +460,7 @@ void ApplyChatUpdate( chat->admins.emplace(user); if (user->isSelf()) { chat->setAdminRights(MTP_chatAdminRights( - MTP_flags(ChatData::DefaultAdminRights()))); + MTP_flags(chat->defaultAdminRights(user)))); } }, [](const MTPDchatParticipant &) { }); diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 52cc3e1de..d1c5d4343 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -45,7 +45,7 @@ public: void setName(const QString &newName); void invalidateParticipants(); - bool noParticipantInfo() const { + [[nodiscard]] bool noParticipantInfo() const { return (count > 0 || amIn()) && participants.empty(); } @@ -58,10 +58,10 @@ public: void removeFlags(MTPDchat::Flags which) { _flags.remove(which); } - auto flags() const { + [[nodiscard]] auto flags() const { return _flags.current(); } - auto flagsValue() const { + [[nodiscard]] auto flagsValue() const { return _flags.value(); } @@ -74,58 +74,58 @@ public: void removeFullFlags(MTPDchatFull::Flags which) { _fullFlags.remove(which); } - auto fullFlags() const { + [[nodiscard]] auto fullFlags() const { return _fullFlags.current(); } - auto fullFlagsValue() const { + [[nodiscard]] auto fullFlagsValue() const { return _fullFlags.value(); } - auto adminRights() const { + [[nodiscard]] auto adminRights() const { return _adminRights.current(); } - auto adminRightsValue() const { + [[nodiscard]] auto adminRightsValue() const { return _adminRights.value(); } void setAdminRights(const MTPChatAdminRights &rights); - bool hasAdminRights() const { + [[nodiscard]] bool hasAdminRights() const { return (adminRights() != 0); } - auto defaultRestrictions() const { + [[nodiscard]] auto defaultRestrictions() const { return _defaultRestrictions.current(); } - auto defaultRestrictionsValue() const { + [[nodiscard]] auto defaultRestrictionsValue() const { return _defaultRestrictions.value(); } void setDefaultRestrictions(const MTPChatBannedRights &rights); - bool isForbidden() const { + [[nodiscard]] bool isForbidden() const { return flags() & MTPDchat_ClientFlag::f_forbidden; } - bool amIn() const { + [[nodiscard]] bool amIn() const { return !isForbidden() && !isDeactivated() && !haveLeft() && !wasKicked(); } - bool haveLeft() const { + [[nodiscard]] bool haveLeft() const { return flags() & MTPDchat::Flag::f_left; } - bool wasKicked() const { + [[nodiscard]] bool wasKicked() const { return flags() & MTPDchat::Flag::f_kicked; } - bool amCreator() const { + [[nodiscard]] bool amCreator() const { return flags() & MTPDchat::Flag::f_creator; } - bool isDeactivated() const { + [[nodiscard]] bool isDeactivated() const { return flags() & MTPDchat::Flag::f_deactivated; } - bool isMigrated() const { + [[nodiscard]] bool isMigrated() const { return flags() & MTPDchat::Flag::f_migrated_to; } - static AdminRights DefaultAdminRights(); + [[nodiscard]] AdminRights defaultAdminRights(not_null<UserData*> user); // Like in ChannelData. bool canWrite() const; From 3cd05a34d900a3ee0844fa0075eec73f7f019644 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 14:47:09 +0400 Subject: [PATCH 290/396] In legacy groups admins can't remove admins. --- Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index f777344c0..c74dfa173 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -359,7 +359,9 @@ bool ParticipantsAdditionalData::canRemoveUser( if (canRestrictUser(user)) { return true; } else if (const auto chat = _peer->asChat()) { - return !user->isSelf() && chat->invitedByMe.contains(user); + return !user->isSelf() + && chat->invitedByMe.contains(user) + && (chat->amCreator() || !_admins.contains(user)); } return false; } From c935f1bb1643e198285c40a10665081577ece942 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 14:51:53 +0400 Subject: [PATCH 291/396] Always show check marks on outgoing service messages. --- Telegram/SourceFiles/history/history_item.cpp | 2 +- Telegram/SourceFiles/history/history_service.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index c23a6e2df..f5857ca09 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -775,7 +775,7 @@ void HistoryItem::sendFailed() { } bool HistoryItem::needCheck() const { - return out() || (id < 0 && history()->peer->isSelf()); + return (out() && !isEmpty()) || (id < 0 && history()->peer->isSelf()); } bool HistoryItem::unread() const { diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 4d6dd048d..8f7f733fe 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -792,9 +792,7 @@ bool HistoryService::updateDependencyItem() { } bool HistoryService::needCheck() const { - return out() - && ((GetDependentData() != nullptr) - || Has<HistoryServiceSelfDestruct>()); + return out() && !isEmpty(); } QString HistoryService::inDialogsText(DrawInDialog way) const { From 9a0edbd0c51f23696dd4a749aa4e334304b10a81 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 15:03:53 +0400 Subject: [PATCH 292/396] Update skin-colored animated emoji. --- .../chat_helpers/stickers_emoji_pack.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 1cfec6d02..53e0aff6d 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -45,46 +45,46 @@ constexpr auto kRefreshTimeout = 7200 * crl::time(1000); static const auto color1 = Lottie::ColorReplacements{ { - { 0xf77e41U, 0xca907aU }, - { 0xffb139U, 0xedc5a5U }, - { 0xffd140U, 0xf7e3c3U }, - { 0xffdf79U, 0xfbefd6U }, + { 0xf77e41U, 0xcb7b55U }, + { 0xffb139U, 0xf6b689U }, + { 0xffd140U, 0xffcda7U }, + { 0xffdf79U, 0xffdfc5U }, }, 1, }; static const auto color2 = Lottie::ColorReplacements{ { - { 0xf77e41U, 0xaa7c60U }, - { 0xffb139U, 0xc8a987U }, - { 0xffd140U, 0xddc89fU }, - { 0xffdf79U, 0xe6d6b2U }, + { 0xf77e41U, 0xa45a38U }, + { 0xffb139U, 0xdf986bU }, + { 0xffd140U, 0xedb183U }, + { 0xffdf79U, 0xf4c3a0U }, }, 2, }; static const auto color3 = Lottie::ColorReplacements{ { - { 0xf77e41U, 0x8c6148U }, - { 0xffb139U, 0xad8562U }, - { 0xffd140U, 0xc49e76U }, - { 0xffdf79U, 0xd4b188U }, + { 0xf77e41U, 0x703a17U }, + { 0xffb139U, 0xab673dU }, + { 0xffd140U, 0xc37f4eU }, + { 0xffdf79U, 0xd89667U }, }, 3, }; static const auto color4 = Lottie::ColorReplacements{ { - { 0xf77e41U, 0x6e3c2cU }, - { 0xffb139U, 0x925a34U }, - { 0xffd140U, 0xa16e46U }, - { 0xffdf79U, 0xac7a52U }, + { 0xf77e41U, 0x4a2409U }, + { 0xffb139U, 0x7d3e0eU }, + { 0xffd140U, 0x965529U }, + { 0xffdf79U, 0xa96337U }, }, 4, }; static const auto color5 = Lottie::ColorReplacements{ { - { 0xf77e41U, 0x291c12U }, - { 0xffb139U, 0x472a22U }, - { 0xffd140U, 0x573b30U }, - { 0xffdf79U, 0x68493cU }, + { 0xf77e41U, 0x200f0aU }, + { 0xffb139U, 0x412924U }, + { 0xffd140U, 0x593d37U }, + { 0xffdf79U, 0x63453fU }, }, 5, }; From 07f94cc184825c3088734037f9ae3a75de24940d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 15:20:58 +0400 Subject: [PATCH 293/396] Fix skin-colored animated emoji refresh. --- .../SourceFiles/chat_helpers/stickers_emoji_pack.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 53e0aff6d..a6fe22c1c 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -247,7 +247,7 @@ void EmojiPack::applySet(const MTPDmessages_stickerSet &data) { was.erase(i); } } - for (const auto &[emoji, Document] : was) { + for (const auto &[emoji, document] : was) { refreshItems(emoji); } } @@ -260,6 +260,13 @@ void EmojiPack::refreshAll() { void EmojiPack::refreshItems(EmojiPtr emoji) { const auto i = _items.find(IsolatedEmoji{ { emoji } }); + if (!emoji->colored()) { + if (const auto count = emoji->variantsCount()) { + for (auto i = 0; i != count; ++i) { + refreshItems(emoji->variant(i + 1)); + } + } + } if (i == end(_items)) { return; } From 93a88b54ad065d43ce4ebd7ea503ab44ef3e6990 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 15:24:53 +0400 Subject: [PATCH 294/396] Fix sending images from clipboard. Regression was introduced in ce1b94eb1. --- Telegram/SourceFiles/storage/storage_media_prepare.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.cpp b/Telegram/SourceFiles/storage/storage_media_prepare.cpp index b08b4d77d..3a8f6f61c 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.cpp +++ b/Telegram/SourceFiles/storage/storage_media_prepare.cpp @@ -40,7 +40,7 @@ bool ValidPhotoForAlbum( const QString &mime) { if (image.animated || Core::IsMimeSticker(mime) - || !mime.startsWith(u"image/")) { + || (!mime.isEmpty() && !mime.startsWith(u"image/"))) { return false; } const auto width = image.data.width(); From dcebefe2bbc5a62ac9fa68b6ae78e2c9419b0b9a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 18:30:58 +0400 Subject: [PATCH 295/396] Fix layout of round video messages. --- Telegram/SourceFiles/history/view/media/history_view_gif.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 334c6b893..a95e45df2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -155,11 +155,11 @@ QSize Gif::countOptimalSize() { _thumbh = th; auto maxWidth = qMax(tw, st::minPhotoSize); auto minHeight = qMax(th, st::minPhotoSize); - accumulate_max(maxWidth, _parent->minWidthForMedia()); if (!activeCurrentStreamed()) { accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { + accumulate_max(maxWidth, _parent->minWidthForMedia()); if (!_caption.isEmpty()) { auto captionw = maxWidth - st::msgPadding.left() - st::msgPadding.right(); minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); @@ -212,11 +212,11 @@ QSize Gif::countCurrentSize(int newWidth) { newWidth = qMax(tw, st::minPhotoSize); auto newHeight = qMax(th, st::minPhotoSize); - accumulate_max(newWidth, _parent->minWidthForMedia()); if (!activeCurrentStreamed()) { accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); } if (_parent->hasBubble()) { + accumulate_max(newWidth, _parent->minWidthForMedia()); if (!_caption.isEmpty()) { auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right(); newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); From 8ec60e03214b869d5ab770daea68e5f8db5940bb Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 19:23:16 +0400 Subject: [PATCH 296/396] Show all .webp as stickers. Allow opening stickers not from stickerpacks in media viewer. --- Telegram/SourceFiles/data/data_types.cpp | 9 +++++++++ Telegram/SourceFiles/data/data_types.h | 7 +------ .../history/view/media/history_view_sticker.cpp | 6 ++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp index abd615534..644caebe6 100644 --- a/Telegram/SourceFiles/data/data_types.cpp +++ b/Telegram/SourceFiles/data/data_types.cpp @@ -168,3 +168,12 @@ TimeId DateFromMessage(const MTPmessage &message) { return message.vdate().v; }); } + +bool GoodStickerDimensions(int width, int height) { + // Show all .webp (except very large ones) as stickers, + // allow to open them in media viewer to see details. + constexpr auto kLargetsStickerSide = 2560; + return (width > 0) + && (height > 0) + && (width * height <= kLargetsStickerSide * kLargetsStickerSide); +} diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index ffed002f9..8089daad4 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -325,12 +325,7 @@ enum DocumentType { }; inline constexpr auto kStickerSideSize = 512; - -[[nodiscard]] inline bool GoodStickerDimensions(int width, int height) { - return (width > 0 && width <= kStickerSideSize) - && (height > 0 && height <= kStickerSideSize) - && (width == kStickerSideSize || height == kStickerSideSize); -} +[[nodiscard]] bool GoodStickerDimensions(int width, int height); using MediaKey = QPair<uint64, uint64>; diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 68df01812..442575dbc 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -273,6 +273,12 @@ void Sticker::refreshLink() { _link = std::make_shared<LambdaClickHandler>([document = _data] { StickerSetBox::Show(App::wnd()->sessionController(), document); }); + } else if (sticker + && !_parent->data()->isSending() + && !_parent->data()->hasFailed()) { + _link = std::make_shared<DocumentOpenClickHandler>( + _data, + _parent->data()->fullId()); } } From 7977331d8b619cdf8d1cfbac550e11a68d1036e0 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 15 Feb 2021 17:22:44 +0400 Subject: [PATCH 297/396] Read DESKTOPINTEGRATION variable instead of TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION Since it's widely used (by AppImages, for instance) --- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index cc091af3f..12e98b37f 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -1063,8 +1063,8 @@ void finish() { } void InstallLauncher(bool force) { - static const auto DisabledByEnv = qEnvironmentVariableIsSet( - "TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION"); + static const auto DisabledByEnv = !qEnvironmentVariableIsEmpty( + "DESKTOPINTEGRATION"); // don't update desktop file for alpha version or if updater is disabled if ((cAlphaVersion() || Core::UpdaterDisabled() || DisabledByEnv) From 153b91248dc76227443f0dfb1e4fd0a03c042142 Mon Sep 17 00:00:00 2001 From: Il'ya <Ilya@marshal.by> Date: Sun, 14 Feb 2021 17:13:19 +0300 Subject: [PATCH 298/396] set nproc for number of jobs to build webrtc in docker --- Telegram/build/docker/centos_env/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 45e2f0313..f649c9b96 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -601,7 +601,7 @@ RUN cmake3 -B out/Release . \ -DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \ -DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include -RUN cmake3 --build out/Release -- -j8 +RUN cmake3 --build out/Release -- -j$(nproc) RUN cmake3 -B out/Debug . \ -DCMAKE_BUILD_TYPE=Debug \ @@ -611,7 +611,7 @@ RUN cmake3 -B out/Debug . \ -DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \ -DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include -RUN cmake3 --build out/Debug -- -j8 +RUN cmake3 --build out/Debug -- -j$(nproc) FROM builder From 88951e9e5c827b3698ec5bf1f987678193215808 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 13 Feb 2021 17:41:27 +0400 Subject: [PATCH 299/396] Fix saving last path in confined environments --- .../platform/linux/linux_xdp_file_dialog.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp index 5d53d1ab2..e3988783f 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_xdp_file_dialog.h" +#include "platform/platform_file_utilities.h" #include "platform/linux/specific_linux.h" #include "storage/localstorage.h" #include "base/qt_adapters.h" @@ -68,6 +69,12 @@ bool Get( const QString &filter, Type type, QString startFile) { + static const auto docRegExp = QRegularExpression("^/run/user/\\d+/doc"); + if (cDialogLastPath().isEmpty() + || cDialogLastPath().contains(docRegExp)) { + InitLastPath(); + } + XDPFileDialog dialog(parent, caption, QString(), filter); dialog.setModal(true); @@ -85,16 +92,11 @@ bool Get( if (startFile.isEmpty() || startFile.at(0) != '/') { startFile = cDialogLastPath() + '/' + startFile; } + dialog.setDirectory(QFileInfo(startFile).absoluteDir().absolutePath()); dialog.selectFile(startFile); int res = dialog.exec(); - QString path = dialog.directory().path(); - if (path != cDialogLastPath()) { - cSetDialogLastPath(path); - Local::writeSettings(); - } - if (res == QDialog::Accepted) { QStringList selectedFilesStrings; ranges::transform( @@ -107,6 +109,18 @@ bool Get( } else { files = selectedFilesStrings.mid(0, 1); } + + QString path = files.isEmpty() + ? QString() + : QFileInfo(files.back()).absoluteDir().absolutePath(); + + if (!path.isEmpty() + && !path.contains(docRegExp) + && path != cDialogLastPath()) { + cSetDialogLastPath(path); + Local::writeSettings(); + } + return true; } From 294f849775a03ae9286eef6ab84bb0385d4e70fc Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 13 Feb 2021 17:51:23 +0400 Subject: [PATCH 300/396] Init last path with gtk dialog --- .../SourceFiles/platform/linux/linux_gtk_file_dialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index 708e07005..be65f7af6 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_gtk_file_dialog.h" +#include "platform/platform_file_utilities.h" #include "platform/linux/linux_gtk_integration_p.h" #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_desktop_environment.h" @@ -655,6 +656,10 @@ bool Get( const QString &filter, Type type, QString startFile) { + if (cDialogLastPath().isEmpty()) { + InitLastPath(); + } + GtkFileDialog dialog(parent, caption, QString(), filter); dialog.setModal(true); From da74fe4248c3e1ad5fb1f55d322db4941273759d Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 13 Feb 2021 15:29:37 +0400 Subject: [PATCH 301/396] Call moveToScreen right before showFullScreen for Wayland --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 99ab39a96..24662c7b8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2425,6 +2425,7 @@ void OverlayWidget::displayFinished() { updateControls(); if (isHidden()) { Ui::Platform::UpdateOverlayed(this); + moveToScreen(); if (Platform::IsLinux()) { showFullScreen(); } else { From 7e4dff25e9e0beced8aeaf1887ca09479baf9f8f Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 13 Feb 2021 15:33:50 +0400 Subject: [PATCH 302/396] Log media viewer geometry --- .../media/view/media_view_overlay_widget.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 24662c7b8..e0492c29a 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -473,10 +473,21 @@ void OverlayWidget::updateGeometry() { if (geometry() == available) { return; } + DEBUG_LOG(("Viewer Pos: Setting %1, %2, %3, %4") + .arg(available.x()) + .arg(available.y()) + .arg(available.width()) + .arg(available.height())); setGeometry(available); } void OverlayWidget::resizeEvent(QResizeEvent *e) { + const auto newGeometry = geometry(); + DEBUG_LOG(("Viewer Pos: Resized to %1, %2, %3, %4") + .arg(newGeometry.x()) + .arg(newGeometry.y()) + .arg(newGeometry.width()) + .arg(newGeometry.height())); updateControlsGeometry(); OverlayParent::resizeEvent(e); } From 3a659b4b54f9e3be253a2e9d082361704203c9dd Mon Sep 17 00:00:00 2001 From: Nicholas Guriev <nicholas@guriev.su> Date: Tue, 9 Feb 2021 08:55:41 +0300 Subject: [PATCH 303/396] Add -mxgot flag to td_scheme on MIPS64 This fixes "relocation truncated to fit ..." error on final linking. It should not introduce a lot of overhead since the code is used only in network communications. This errors appears sometimes and I do not know exact conditions, I think it is related to large size of an object file with the schema. More docs see at https://gcc.gnu.org/onlinedocs/gcc/MIPS-Options.html#index-mxgot-1 --- Telegram/cmake/td_scheme.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Telegram/cmake/td_scheme.cmake b/Telegram/cmake/td_scheme.cmake index 81778c27f..e4c68a602 100644 --- a/Telegram/cmake/td_scheme.cmake +++ b/Telegram/cmake/td_scheme.cmake @@ -33,3 +33,12 @@ PUBLIC desktop-app::lib_base desktop-app::lib_tl ) + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "mips64") + # Sometimes final linking may fail with error "relocation truncated to fit" + # due to large scheme size. + target_compile_options(td_scheme + PRIVATE + -mxgot + ) +endif() From e109da037e26c9bcbdfb7a25347389474771a0db Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 6 Feb 2021 18:16:44 +0400 Subject: [PATCH 304/396] Add a private method to get control widget by enum to TitleWidgetQt --- .../SourceFiles/window/window_title_qt.cpp | 63 +++++++++---------- Telegram/SourceFiles/window/window_title_qt.h | 1 + 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index d9703107f..6d89f14a4 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -110,6 +110,16 @@ bool TitleWidgetQt::hasShadow() const { && Ui::Platform::TranslucentWindowsSupported(center); } +Ui::IconButton *TitleWidgetQt::controlWidget(Control control) const { + switch (control) { + case Control::Minimize: return _minimize; + case Control::Maximize: return _maximizeRestore; + case Control::Close: return _close; + } + + return nullptr; +} + void TitleWidgetQt::paintEvent(QPaintEvent *e) { auto active = isActiveWindow(); if (_activeState != active) { @@ -141,22 +151,24 @@ void TitleWidgetQt::updateControlsPosition() { const auto controlsLeft = controlsLayout.left; const auto controlsRight = controlsLayout.right; - if (ranges::contains(controlsLeft, Control::Minimize) - || ranges::contains(controlsRight, Control::Minimize)) { + const auto controlPresent = [&](Control control) { + return ranges::contains(controlsLeft, control) + || ranges::contains(controlsRight, control); + }; + + if (controlPresent(Control::Minimize)) { _minimize->show(); } else { _minimize->hide(); } - if (ranges::contains(controlsLeft, Control::Maximize) - || ranges::contains(controlsRight, Control::Maximize)) { + if (controlPresent(Control::Maximize)) { _maximizeRestore->show(); } else { _maximizeRestore->hide(); } - if (ranges::contains(controlsLeft, Control::Close) - || ranges::contains(controlsRight, Control::Close)) { + if (controlPresent(Control::Close)) { _close->show(); } else { _close->hide(); @@ -175,35 +187,18 @@ void TitleWidgetQt::updateControlsPositionBySide( auto position = 0; for (const auto &control : preparedControls) { - switch (control) { - case Control::Minimize: - if (right) { - _minimize->moveToRight(position, 0); - } else { - _minimize->moveToLeft(position, 0); - } - - position += _minimize->width(); - break; - case Control::Maximize: - if (right) { - _maximizeRestore->moveToRight(position, 0); - } else { - _maximizeRestore->moveToLeft(position, 0); - } - - position += _maximizeRestore->width(); - break; - case Control::Close: - if (right) { - _close->moveToRight(position, 0); - } else { - _close->moveToLeft(position, 0); - } - - position += _close->width(); - break; + const auto widget = controlWidget(control); + if (!widget) { + continue; } + + if (right) { + widget->moveToRight(position, 0); + } else { + widget->moveToLeft(position, 0); + } + + position += widget->width(); } } diff --git a/Telegram/SourceFiles/window/window_title_qt.h b/Telegram/SourceFiles/window/window_title_qt.h index e248ae97d..9bed67470 100644 --- a/Telegram/SourceFiles/window/window_title_qt.h +++ b/Telegram/SourceFiles/window/window_title_qt.h @@ -51,6 +51,7 @@ private: void toggleFramelessWindow(bool enabled); bool hasShadow() const; + Ui::IconButton *controlWidget(Control control) const; QMargins resizeArea() const; Qt::Edges edgesFromPos(const QPoint &pos) const; void updateCursor(Qt::Edges edges); From 3637fec397d6c02f9e375d62741273f836c9ce08 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sun, 7 Feb 2021 17:32:35 +0400 Subject: [PATCH 305/396] Ensure controls aren't duplictated --- Telegram/SourceFiles/window/window_title_qt.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 6d89f14a4..83e58a163 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -27,6 +27,16 @@ namespace { return st::callShadow.extend; } +template <typename T> +void RemoveDuplicates(std::vector<T> &v) { + auto end = v.end(); + for (auto it = v.begin(); it != end; ++it) { + end = std::remove(it + 1, end, *it); + } + + v.erase(end, v.end()); +} + } // namespace TitleWidgetQt::TitleWidgetQt(QWidget *parent) @@ -181,10 +191,12 @@ void TitleWidgetQt::updateControlsPosition() { void TitleWidgetQt::updateControlsPositionBySide( const std::vector<Control> &controls, bool right) { - const auto preparedControls = right + auto preparedControls = right ? (ranges::view::reverse(controls) | ranges::to_vector) : controls; + RemoveDuplicates(preparedControls); + auto position = 0; for (const auto &control : preparedControls) { const auto widget = controlWidget(control); From d44f076f0baeb7696d19ddb6a8fce17c860c0a9f Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 12 Feb 2021 16:58:14 +0400 Subject: [PATCH 306/396] Take in account device pixel ratio when setting window extents --- Telegram/SourceFiles/window/window_title_qt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 83e58a163..ce05afb86 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -147,7 +147,7 @@ void TitleWidgetQt::updateWindowExtents() { if (hasShadow()) { Platform::SetWindowExtents( window()->windowHandle(), - resizeArea()); + resizeArea() * cIntRetinaFactor()); _extentsSet = true; } else if (_extentsSet) { From 69b41fadef1abf029469d355a8e0fdcb4d61b8b8 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 11:19:45 +0400 Subject: [PATCH 307/396] Version 2.5.9. - Add 'Invite via Link' button to Add Members box. - Fix window size in Windows 10 Tablet Mode. - Fix layout of round video messages in channels. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 6 ++++++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 3a1e36d41..e9de2ee72 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="2.5.8.0" /> + Version="2.5.9.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index da62aa07e..369fcaf7f 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,8,0 - PRODUCTVERSION 2,5,8,0 + FILEVERSION 2,5,9,0 + PRODUCTVERSION 2,5,9,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.8.0" + VALUE "FileVersion", "2.5.9.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.8.0" + VALUE "ProductVersion", "2.5.9.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 92d723225..cc346bd9e 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,8,0 - PRODUCTVERSION 2,5,8,0 + FILEVERSION 2,5,9,0 + PRODUCTVERSION 2,5,9,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.8.0" + VALUE "FileVersion", "2.5.9.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.8.0" + VALUE "ProductVersion", "2.5.9.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 6a86f3ef1..fd2ad5779 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005008; -constexpr auto AppVersionStr = "2.5.8"; +constexpr auto AppVersion = 2005009; +constexpr auto AppVersionStr = "2.5.9"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 1605f65ef..1513201f9 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005008 +AppVersion 2005009 AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.8 -AppVersionStr 2.5.8 +AppVersionStrSmall 2.5.9 +AppVersionStr 2.5.9 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 2.5.8 +AppVersionOriginal 2.5.9 diff --git a/changelog.txt b/changelog.txt index 5a404f8a2..4122791cb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +2.5.9 (17.02.21) + +- Add 'Invite via Link' button to Add Members box. +- Fix window size in Windows 10 Tablet Mode. +- Fix layout of round video messages in channels. + 2.5.8 (29.01.21) - Fix OpenAL device closing in calls and voice chats. From 8500bf6073629d3f7b45dd3060f13afba34dcf0b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 15:58:57 +0400 Subject: [PATCH 308/396] Version 2.5.9: Fix reading settings from old apps. --- .../SourceFiles/storage/details/storage_settings_scheme.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index 463c1026f..a703d96f3 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -725,7 +725,9 @@ bool ReadSetting( auto position = TWindowPos(); stream >> position.x >> position.y >> position.w >> position.h; stream >> position.moncrc >> position.maximized; - stream >> position.scale; + if (version >= 2005009) { + stream >> position.scale; + } if (!CheckStreamStatus(stream)) return false; DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (scale %5%, maximized %6)") From 48fea47d16376bafc901b5c601de25230fea30f5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 17:33:24 +0400 Subject: [PATCH 309/396] Version 2.5.9: Fix build for Microsoft Store. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/build/build.bat | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index e9de2ee72..ee001697c 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -12,7 +12,7 @@ Version="2.5.9.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> - <PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName> + <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> <Description>Telegram Desktop official messenger</Description> <Logo>Assets\logo\logo.png</Logo> </Properties> diff --git a/Telegram/build/build.bat b/Telegram/build/build.bat index 9ff991a6a..80cc5167d 100644 --- a/Telegram/build/build.bat +++ b/Telegram/build/build.bat @@ -268,8 +268,11 @@ if %BuildUWP% neq 0 ( mkdir "%DeployPath%" move "%ReleasePath%\%BinaryName%.pdb" "%DeployPath%\" - move "%ReleasePath%\%BinaryName%.x86.appx" "%DeployPath%\" - move "%ReleasePath%\%BinaryName%.x64.appx" "%DeployPath%\" + if %Build64% equ 0 ( + move "%ReleasePath%\%BinaryName%.x86.appx" "%DeployPath%\" + ) else ( + move "%ReleasePath%\%BinaryName%.x64.appx" "%DeployPath%\" + ) move "%ReleasePath%\%BinaryName%.exe" "%DeployPath%\" if "%AlphaBetaParam%" equ "" ( From 093d89db83669a244f84b99c447e013eb58cb4ba Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 19:13:37 +0400 Subject: [PATCH 310/396] Fix display of stickers in media viewer. --- .../media/view/media_view_overlay_widget.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index e0492c29a..7607cd2ff 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3098,7 +3098,10 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { const auto r = e->rect(); const auto region = e->region(); const auto contentShown = _photo || documentContentShown(); - const auto bgRegion = contentShown + const auto opaqueContentShown = contentShown + && (!_document + || (!_document->isVideoMessage() && !_document->sticker())); + const auto bgRegion = opaqueContentShown ? (region - contentRect()) : region; @@ -3385,9 +3388,9 @@ void OverlayWidget::paintTransformedStaticContent(Painter &p) { const auto rect = contentRect(); PainterHighQualityEnabler hq(p); - if ((!_document || !_documentMedia->getStickerLarge()) - && (_staticContent.isNull() - || _staticContent.hasAlpha())) { + if ((!_document + || (!_document->sticker() && !_document->isVideoMessage())) + && (_staticContent.isNull() || _staticContent.hasAlpha())) { p.fillRect(rect, _transparentBrush); } if (_staticContent.isNull()) { From baccec623d45dbfd1132d5f808192f0f3ad87647 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 19:15:49 +0400 Subject: [PATCH 311/396] Allow opening large (> 512px) stickers in media viewer. --- .../SourceFiles/history/view/media/history_view_sticker.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 442575dbc..802d8c0dc 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -274,8 +274,13 @@ void Sticker::refreshLink() { StickerSetBox::Show(App::wnd()->sessionController(), document); }); } else if (sticker + && (_data->dimensions.width() > kStickerSideSize + || _data->dimensions.height() > kStickerSideSize) && !_parent->data()->isSending() && !_parent->data()->hasFailed()) { + // In case we have a .webp file that is displayed as a sticker, but + // that doesn't fit in 512x512, we assume it may be a regular large + // .webp image and we allow to open it in media viewer. _link = std::make_shared<DocumentOpenClickHandler>( _data, _parent->data()->fullId()); From 70570e098754fe795ea78433485ce533026a29fd Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 19:19:00 +0400 Subject: [PATCH 312/396] Always make sure that settings are saved. --- Telegram/SourceFiles/core/application.cpp | 10 ++++++++-- Telegram/SourceFiles/core/application.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 719f9fbe3..2990e596f 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -152,6 +152,10 @@ Application::Application(not_null<Launcher*> launcher) } Application::~Application() { + if (_saveSettingsTimer && _saveSettingsTimer->isActive()) { + Local::writeSettings(); + } + // Depend on activeWindow() for now :( Shortcuts::Finish(); @@ -465,7 +469,9 @@ bool Application::eventFilter(QObject *object, QEvent *e) { } void Application::saveSettingsDelayed(crl::time delay) { - _saveSettingsTimer.callOnce(delay); + if (_saveSettingsTimer) { + _saveSettingsTimer->callOnce(delay); + } } void Application::saveSettings() { @@ -533,7 +539,7 @@ void Application::badMtprotoConfigurationError() { void Application::startLocalStorage() { Local::start(); - _saveSettingsTimer.setCallback([=] { saveSettings(); }); + _saveSettingsTimer.emplace([=] { saveSettings(); }); } void Application::startEmojiImageLoader() { diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 4342f315f..8fe0c986a 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -368,7 +368,7 @@ private: crl::time _shouldLockAt = 0; base::Timer _autoLockTimer; - base::Timer _saveSettingsTimer; + std::optional<base::Timer> _saveSettingsTimer; struct LeaveSubscription { LeaveSubscription( From b3660f1ed882450c9ac6527ebe08f7c10d9b2c9f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 19:20:05 +0400 Subject: [PATCH 313/396] Version 2.5.9: Move window position to Core::Settings. --- Telegram/SourceFiles/core/core_settings.cpp | 59 ++++++++++++++++++- Telegram/SourceFiles/core/core_settings.h | 19 ++++++ Telegram/SourceFiles/mainwindow.cpp | 2 +- Telegram/SourceFiles/settings.cpp | 1 - Telegram/SourceFiles/settings.h | 12 ---- .../details/storage_settings_scheme.cpp | 15 ++--- .../storage/details/storage_settings_scheme.h | 2 +- Telegram/SourceFiles/storage/localstorage.cpp | 13 ---- Telegram/SourceFiles/window/main_window.cpp | 19 +++--- Telegram/lib_ui | 2 +- 10 files changed, 100 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index ace2fcd29..7e1ccb578 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -17,6 +17,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" namespace Core { +namespace { + +[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) { + QDataStream stream(data); + stream.setVersion(QDataStream::Qt_5_1); + + auto result = WindowPosition(); + stream + >> result.x + >> result.y + >> result.w + >> result.h + >> result.moncrc + >> result.maximized + >> result.scale; + return result; +} + +[[nodiscard]] QByteArray Serialize(const WindowPosition &position) { + auto result = QByteArray(); + const auto size = 7 * sizeof(qint32); + result.reserve(size); + { + QDataStream stream(&result, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_5_1); + stream + << qint32(position.x) + << qint32(position.y) + << qint32(position.w) + << qint32(position.h) + << qint32(position.moncrc) + << qint32(position.maximized) + << qint32(position.scale); + } + DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4" + " (scale %5%, maximized %6)") + .arg(position.x) + .arg(position.y) + .arg(position.w) + .arg(position.h) + .arg(position.scale) + .arg(Logs::b(position.maximized))); + return result; +} + +} // namespace Settings::Settings() : _sendSubmitWay(Ui::InputSubmitSettings::Enter) @@ -27,6 +73,8 @@ Settings::Settings() QByteArray Settings::serialize() const { const auto themesAccentColors = _themesAccentColors.serialize(); + const auto windowPosition = Serialize(_windowPosition); + auto size = Serialize::bytearraySize(themesAccentColors) + sizeof(qint32) * 5 + Serialize::stringSize(_downloadPath.current()) @@ -40,6 +88,7 @@ QByteArray Settings::serialize() const { size += Serialize::stringSize(key) + Serialize::stringSize(value); } size += Serialize::bytearraySize(_videoPipGeometry); + size += Serialize::bytearraySize(windowPosition); auto result = QByteArray(); result.reserve(size); @@ -115,7 +164,8 @@ QByteArray Settings::serialize() const { << _groupCallPushToTalkShortcut << qint64(_groupCallPushToTalkDelay) << qint32(0) // Call audio backend - << qint32(_disableCalls ? 1 : 0); + << qint32(_disableCalls ? 1 : 0) + << windowPosition; } return result; } @@ -188,6 +238,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay; qint32 callAudioBackend = 0; qint32 disableCalls = _disableCalls ? 1 : 0; + QByteArray windowPosition; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -289,6 +340,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> disableCalls; } + if (!stream.atEnd()) { + stream >> windowPosition; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -389,6 +443,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _groupCallPushToTalkShortcut = groupCallPushToTalkShortcut; _groupCallPushToTalkDelay = groupCallPushToTalkDelay; _disableCalls = (disableCalls == 1); + if (!windowPosition.isEmpty()) { + _windowPosition = Deserialize(windowPosition); + } } bool Settings::chatWide() const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index b3735451d..76320ad9e 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -28,6 +28,18 @@ enum class Backend; namespace Core { +struct WindowPosition { + WindowPosition() = default; + + int32 moncrc = 0; + int maximized = 0; + int scale = 0; + int x = 0; + int y = 0; + int w = 0; + int h = 0; +}; + class Settings final { public: enum class ScreenCorner { @@ -503,6 +515,12 @@ public: [[nodiscard]] rpl::producer<Window::ControlsLayout> windowControlsLayoutChanges() const { return _windowControlsLayout.changes(); } + [[nodiscard]] const WindowPosition &windowPosition() const { + return _windowPosition; + } + void setWindowPosition(const WindowPosition &position) { + _windowPosition = position; + } [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] float64 DefaultDialogsWidthRatio(); @@ -585,6 +603,7 @@ private: rpl::variable<std::optional<bool>> _systemDarkMode = std::nullopt; rpl::variable<bool> _systemDarkModeEnabled = false; rpl::variable<Window::ControlsLayout> _windowControlsLayout; + WindowPosition _windowPosition; // per-window bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 318f43e95..fdeaa0af8 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -170,7 +170,7 @@ void MainWindow::createTrayIconMenu() { void MainWindow::applyInitialWorkMode() { Global::RefWorkMode().setForced(Global::WorkMode().value(), true); - if (cWindowPos().maximized) { + if (Core::App().settings().windowPosition().maximized) { DEBUG_LOG(("Window Pos: First show, setting maximized.")); setWindowState(Qt::WindowMaximized); } diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 09065ecb5..c58e3b211 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -40,7 +40,6 @@ bool gSendToMenu = false; bool gUseExternalVideoPlayer = false; bool gUseFreeType = false; bool gAutoUpdate = true; -TWindowPos gWindowPos; LaunchMode gLaunchMode = LaunchModeNormal; bool gSeenTrayTooltip = false; bool gRestartingUpdate = false, gRestarting = false, gRestartingToSettings = false, gWriteProtected = false; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 82a73b0ad..e8d7ab684 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -68,18 +68,6 @@ inline const QString &cDialogHelperPathFinal() { DeclareSetting(bool, AutoUpdate); -struct TWindowPos { - TWindowPos() = default; - - int32 moncrc = 0; - int maximized = 0; - int scale = 0; - int x = 0; - int y = 0; - int w = 0; - int h = 0; -}; -DeclareSetting(TWindowPos, WindowPos); DeclareSetting(bool, SeenTrayTooltip); DeclareSetting(bool, RestartingUpdate); DeclareSetting(bool, Restarting); diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index a703d96f3..f41dcbd83 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -721,16 +721,16 @@ bool ReadSetting( context.legacyRead = true; } break; - case dbiWindowPosition: { - auto position = TWindowPos(); + case dbiWindowPositionOld: { + auto position = Core::WindowPosition(); + if (!CheckStreamStatus(stream)) { + return false; + } stream >> position.x >> position.y >> position.w >> position.h; stream >> position.moncrc >> position.maximized; - if (version >= 2005009) { - stream >> position.scale; - } if (!CheckStreamStatus(stream)) return false; - DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (scale %5%, maximized %6)") + DEBUG_LOG(("Window Pos: Read from legacy storage %1, %2, %3, %4 (scale %5%, maximized %6)") .arg(position.x) .arg(position.y) .arg(position.w) @@ -738,7 +738,8 @@ bool ReadSetting( .arg(position.scale) .arg(Logs::b(position.maximized))); - cSetWindowPos(position); + Core::App().settings().setWindowPosition(position); + context.legacyRead = true; } break; case dbiLoggedPhoneNumberOld: { // deprecated diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.h b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h index d5384f25e..0dc94b817 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.h +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h @@ -91,7 +91,7 @@ enum { dbiDesktopNotifyOld = 0x0b, dbiAutoUpdate = 0x0c, dbiLastUpdateCheck = 0x0d, - dbiWindowPosition = 0x0e, + dbiWindowPositionOld = 0x0e, dbiConnectionTypeOld = 0x0f, // 0x10 reserved dbiDefaultAttach = 0x11, diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 73a32f7b9..4700a0ffa 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -529,19 +529,6 @@ void writeSettings() { data.stream << quint32(dbiLanguagesKey) << quint64(_languagesKey); } - auto position = cWindowPos(); - data.stream << quint32(dbiWindowPosition) << qint32(position.x) << qint32(position.y) << qint32(position.w) << qint32(position.h); - data.stream << qint32(position.moncrc) << qint32(position.maximized); - data.stream << qint32(position.scale); - - DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4 (scale %5%, maximized %6)") - .arg(position.x) - .arg(position.y) - .arg(position.w) - .arg(position.h) - .arg(position.scale) - .arg(Logs::b(position.maximized))); - settings.writeEncrypted(data, SettingsKey); } diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 05afa18fb..6ed2698c3 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -292,7 +292,7 @@ void MainWindow::handleVisibleChanged(bool visible) { setWindowState(Qt::WindowMaximized); } } else { - _maximizedBeforeHide = cWindowPos().maximized; + _maximizedBeforeHide = Core::App().settings().windowPosition().maximized; } handleVisibleChangedHook(visible); @@ -424,8 +424,9 @@ void MainWindow::initSize() { return; } - auto position = cWindowPos(); - DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 (scale %5%, maximized %6)") + auto position = Core::App().settings().windowPosition(); + DEBUG_LOG(("Window Pos: Initializing first %1, %2, %3, %4 " + "(scale %5%, maximized %6)") .arg(position.x) .arg(position.y) .arg(position.w) @@ -625,7 +626,7 @@ void MainWindow::savePosition(Qt::WindowState state) { return; } - auto savedPosition = cWindowPos(); + const auto &savedPosition = Core::App().settings().windowPosition(); auto realPosition = savedPosition; if (state == Qt::WindowMaximized) { @@ -657,7 +658,11 @@ void MainWindow::savePosition(Qt::WindowState state) { } if (chosen) { auto screenGeometry = chosen->geometry(); - DEBUG_LOG(("Window Pos: Screen found, geometry: %1, %2, %3, %4").arg(screenGeometry.x()).arg(screenGeometry.y()).arg(screenGeometry.width()).arg(screenGeometry.height())); + DEBUG_LOG(("Window Pos: Screen found, geometry: %1, %2, %3, %4" + ).arg(screenGeometry.x() + ).arg(screenGeometry.y() + ).arg(screenGeometry.width() + ).arg(screenGeometry.height())); realPosition.x -= screenGeometry.x(); realPosition.y -= screenGeometry.y(); realPosition.moncrc = screenNameChecksum(chosen->name()); @@ -678,8 +683,8 @@ void MainWindow::savePosition(Qt::WindowState state) { .arg(realPosition.h) .arg(realPosition.scale) .arg(Logs::b(realPosition.maximized))); - cSetWindowPos(realPosition); - Local::writeSettings(); + Core::App().settings().setWindowPosition(realPosition); + Core::App().saveSettingsDelayed(); } } } diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 77856c3a2..e14bc4681 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 77856c3a21870e656b6e9ae48bf9c9a19a2a86c3 +Subproject commit e14bc4681d69c1b538b8c5af51501077ae5a8a86 From a9c08552c898f611599126d431fb092934a82fd4 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Wed, 17 Feb 2021 22:04:52 +0400 Subject: [PATCH 314/396] Check if resize area is null --- .../SourceFiles/window/window_title_qt.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index ce05afb86..558e0d14e 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -351,29 +351,33 @@ QMargins TitleWidgetQt::resizeArea() const { } Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) const { - if (pos.x() <= resizeArea().left()) { - if (pos.y() <= resizeArea().top()) { + const auto area = resizeArea(); + + if (area.isNull()) { + return Qt::Edges(); + } else if (pos.x() <= area.left()) { + if (pos.y() <= area.top()) { return Qt::LeftEdge | Qt::TopEdge; - } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { + } else if (pos.y() >= (window()->height() - area.bottom())) { return Qt::LeftEdge | Qt::BottomEdge; } return Qt::LeftEdge; - } else if (pos.x() >= (window()->width() - resizeArea().right())) { - if (pos.y() <= resizeArea().top()) { + } else if (pos.x() >= (window()->width() - area.right())) { + if (pos.y() <= area.top()) { return Qt::RightEdge | Qt::TopEdge; - } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { + } else if (pos.y() >= (window()->height() - area.bottom())) { return Qt::RightEdge | Qt::BottomEdge; } return Qt::RightEdge; - } else if (pos.y() <= resizeArea().top()) { + } else if (pos.y() <= area.top()) { return Qt::TopEdge; - } else if (pos.y() >= (window()->height() - resizeArea().bottom())) { + } else if (pos.y() >= (window()->height() - area.bottom())) { return Qt::BottomEdge; - } else { - return Qt::Edges(); } + + return Qt::Edges(); } void TitleWidgetQt::updateCursor(Qt::Edges edges) { From f1ee5b5704172de5b75646c402f30eea9ec1aed3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 04:37:06 +0400 Subject: [PATCH 315/396] Expose gtk integration to lib_base --- .github/workflows/linux.yml | 2 +- Telegram/CMakeLists.txt | 12 +- .../platform/linux/linux_gdk_helper.cpp | 1 + .../platform/linux/linux_gtk_file_dialog.cpp | 2 +- .../platform/linux/linux_gtk_integration.cpp | 424 ++++-------------- .../platform/linux/linux_gtk_integration.h | 15 - .../linux/linux_gtk_integration_dummy.cpp | 19 - .../platform/linux/linux_gtk_integration_p.h | 31 -- .../platform/linux/linux_xlib_helper.cpp | 38 -- .../platform/linux/linux_xlib_helper.h | 24 - .../platform/linux/main_window_linux.cpp | 2 +- .../platform/linux/specific_linux.cpp | 87 +--- .../platform/linux/specific_linux.h | 2 - Telegram/cmake/telegram_options.cmake | 5 - 14 files changed, 114 insertions(+), 550 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/linux_xlib_helper.h diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a39e31d76..ce1109d86 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -63,7 +63,7 @@ jobs: - "" - "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" - - "TDESKTOP_DISABLE_GTK_INTEGRATION" + - "DESKTOP_APP_DISABLE_GTK_INTEGRATION" env: UPLOAD_ARTIFACT: "false" diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 85f82779c..600e9e37b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -104,7 +104,7 @@ if (LINUX) ) endif() - if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION) + if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION) find_package(PkgConfig REQUIRED) if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY) @@ -842,8 +842,6 @@ PRIVATE platform/linux/linux_wayland_integration.h platform/linux/linux_xdp_file_dialog.cpp platform/linux/linux_xdp_file_dialog.h - platform/linux/linux_xlib_helper.cpp - platform/linux/linux_xlib_helper.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp @@ -1128,7 +1126,7 @@ if (NOT LINUX) ) endif() -if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) +if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.h @@ -1145,12 +1143,12 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) ) endif() -if (LINUX AND DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) +if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_wayland_integration.cpp) nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp) endif() -if (LINUX AND TDESKTOP_DISABLE_GTK_INTEGRATION) +if (DESKTOP_APP_DISABLE_GTK_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_gdk_helper.cpp platform/linux/linux_gdk_helper.h @@ -1160,8 +1158,6 @@ if (LINUX AND TDESKTOP_DISABLE_GTK_INTEGRATION) platform/linux/linux_gtk_integration.cpp platform/linux/linux_open_with_dialog.cpp platform/linux/linux_open_with_dialog.h - platform/linux/linux_xlib_helper.cpp - platform/linux/linux_xlib_helper.h ) nice_target_sources(Telegram ${src_loc} diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp index 08b1fad2f..e7440da76 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_gdk_helper.h" +#include "base/platform/linux/base_linux_gtk_integration_p.h" #include "platform/linux/linux_gtk_integration_p.h" extern "C" { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index be65f7af6..96c725c7b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -642,7 +642,7 @@ bool Supported() { } bool Use(Type type) { - return IsGtkIntegrationForced() + return qEnvironmentVariableIsSet("TDESKTOP_USE_GTK_FILE_DIALOG") || DesktopEnvironment::IsGtkBased() // use as a fallback for portal dialog || UseXDGDesktopPortal(); diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 59fcd12d9..75b2cd18e 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -7,149 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_gtk_integration.h" +#include "base/platform/linux/base_linux_gtk_integration.h" +#include "base/platform/linux/base_linux_gtk_integration_p.h" #include "platform/linux/linux_gtk_integration_p.h" -#include "base/platform/base_platform_info.h" -#include "platform/linux/linux_xlib_helper.h" #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_gtk_file_dialog.h" #include "platform/linux/linux_open_with_dialog.h" #include "platform/linux/specific_linux.h" +#include "ui/platform/ui_platform_utility.h" #include "core/sandbox.h" #include "core/core_settings.h" #include "core/application.h" -#include "main/main_domain.h" -#include "mainwindow.h" namespace Platform { namespace internal { using namespace Platform::Gtk; +using BaseGtkIntegration = base::Platform::GtkIntegration; namespace { -bool GtkTriedToInit = false; -bool GtkLoaded = false; +bool Loaded = false; -bool LoadLibrary(QLibrary &lib, const char *name, int version) { -#ifdef LINK_TO_GTK - return true; -#else // LINK_TO_GTK - DEBUG_LOG(("Loading '%1' with version %2...").arg( - QLatin1String(name)).arg(version)); - lib.setFileNameAndVersion(QLatin1String(name), version); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' with version %2!").arg( - QLatin1String(name)).arg(version)); - return true; - } - lib.setFileNameAndVersion(QLatin1String(name), QString()); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name))); - return true; - } - LOG(("Could not load '%1' with version %2 :(").arg( - QLatin1String(name)).arg(version)); - return false; -#endif // !LINK_TO_GTK -} - -void GtkMessageHandler( - const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer unused_data) { - // Silence false-positive Gtk warnings (we are using Xlib to set - // the WM_TRANSIENT_FOR hint). - if (message != qstr("GtkDialog mapped without a transient parent. " - "This is discouraged.")) { - // For other messages, call the default handler. - g_log_default_handler(log_domain, log_level, message, unused_data); - } -} - -bool SetupGtkBase(QLibrary &lib_gtk) { - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false; - - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide", gtk_widget_hide)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_get_window", gtk_widget_get_window)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_get_type", gtk_window_get_type)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_set_title", gtk_window_set_title)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_action", gtk_file_chooser_set_action)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_new", gtk_image_new)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false; - - if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_type", gtk_dialog_get_type)) return false; - if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_run", gtk_dialog_run)) return false; - - if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false; - - if (LOAD_GTK_SYMBOL(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) { - // We work only with Wayland and X11 GDK backends. - // Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call. - // See https://github.com/telegramdesktop/tdesktop/issues/3176 - // See https://github.com/telegramdesktop/tdesktop/issues/3162 - if(IsWayland()) { - DEBUG_LOG(("Limit allowed GDK backends to wayland,x11")); - gdk_set_allowed_backends("wayland,x11"); - } else { - DEBUG_LOG(("Limit allowed GDK backends to x11,wayland")); - gdk_set_allowed_backends("x11,wayland"); - } - } - - // gtk_init will reset the Xlib error handler, - // and that causes Qt applications to quit on X errors. - // Therefore, we need to manually restore it. - XErrorHandlerRestorer handlerRestorer; - - DEBUG_LOG(("Library gtk functions loaded!")); - GtkTriedToInit = true; - if (!gtk_init_check(0, 0)) { - gtk_init_check = nullptr; - DEBUG_LOG(("Failed to gtk_init_check(0, 0)!")); - return false; - } - DEBUG_LOG(("Checked gtk with gtk_init_check!")); - - // Use our custom log handler. - g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, GtkMessageHandler, nullptr); - - return true; +QLibrary &Library() { + return BaseGtkIntegration::Instance()->library(); } bool GetImageFromClipboardSupported() { @@ -166,47 +47,6 @@ bool GetImageFromClipboardSupported() { && (gdk_atom_intern != nullptr); } -template <typename T> -std::optional<T> GtkSetting(const QString &propertyName) { - const auto integration = GtkIntegration::Instance(); - if (!integration - || !integration->loaded() - || gtk_settings_get_default == nullptr) { - return std::nullopt; - } - auto settings = gtk_settings_get_default(); - T value; - g_object_get(settings, propertyName.toUtf8(), &value, nullptr); - return value; -} - -bool IconThemeShouldBeSet() { - // change the icon theme only if - // it isn't already set by a platformtheme plugin - // if QT_QPA_PLATFORMTHEME=(gtk2|gtk3), then force-apply the icon theme - static const auto Result = - // QGenericUnixTheme - (QIcon::themeName() == qstr("hicolor") - && QIcon::fallbackThemeName() == qstr("hicolor")) - // QGnomeTheme - || (QIcon::themeName() == qstr("Adwaita") - && QIcon::fallbackThemeName() == qstr("gnome")) - // qt5ct - || (QIcon::themeName().isEmpty() - && QIcon::fallbackThemeName().isEmpty()) - || IsGtkIntegrationForced(); - - return Result; -} - -bool CursorSizeShouldBeSet() { - // change the cursor size only on Wayland and if it wasn't already set - static const auto Result = IsWayland() - && qEnvironmentVariableIsEmpty("XCURSOR_SIZE"); - - return Result; -} - void SetScaleFactor() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); @@ -225,61 +65,6 @@ void SetScaleFactor() { }); } -void SetIconTheme() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - const auto integration = GtkIntegration::Instance(); - if (!integration || !IconThemeShouldBeSet()) { - return; - } - - const auto themeName = integration->getStringSetting( - qsl("gtk-icon-theme-name")); - - const auto fallbackThemeName = integration->getStringSetting( - qsl("gtk-fallback-icon-theme")); - - if (!themeName.has_value() || !fallbackThemeName.has_value()) { - return; - } - - DEBUG_LOG(("Setting GTK icon theme")); - - QIcon::setThemeName(*themeName); - QIcon::setFallbackThemeName(*fallbackThemeName); - - DEBUG_LOG(("New icon theme: %1").arg(QIcon::themeName())); - DEBUG_LOG(("New fallback icon theme: %1").arg( - QIcon::fallbackThemeName())); - - SetApplicationIcon(Window::CreateIcon()); - if (App::wnd()) { - App::wnd()->setWindowIcon(Window::CreateIcon()); - } - - Core::App().domain().notifyUnreadBadgeChanged(); - }); -} - -void SetCursorSize() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - const auto integration = GtkIntegration::Instance(); - if (!integration || !CursorSizeShouldBeSet()) { - return; - } - - const auto newCursorSize = integration->getIntSetting( - qsl("gtk-cursor-theme-size")); - - if (!newCursorSize.has_value()) { - return; - } - - DEBUG_LOG(("Setting GTK cursor size")); - qputenv("XCURSOR_SIZE", QByteArray::number(*newCursorSize)); - DEBUG_LOG(("New cursor size: %1").arg(*newCursorSize)); - }); -} - void DarkModeChanged() { Core::Sandbox::Instance().customEnterFromEventLoop([] { Core::App().settings().setSystemDarkMode(IsDarkMode()); @@ -299,10 +84,7 @@ GtkIntegration::GtkIntegration() { } GtkIntegration *GtkIntegration::Instance() { - static const auto useGtkIntegration = !qEnvironmentVariableIsSet( - kDisableGtkIntegration.utf8()); - - if (!useGtkIntegration) { + if (!BaseGtkIntegration::Instance()) { return nullptr; } @@ -311,131 +93,107 @@ GtkIntegration *GtkIntegration::Instance() { } void GtkIntegration::load() { - Expects(!GtkLoaded); - DEBUG_LOG(("Loading GTK")); + Expects(!loaded()); - QLibrary lib_gtk; - lib_gtk.setLoadHints(QLibrary::DeepBindHint); - - if (LoadLibrary(lib_gtk, "gtk-3", 0)) { - GtkLoaded = SetupGtkBase(lib_gtk); - } - if (!GtkLoaded - && !GtkTriedToInit - && LoadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { - GtkLoaded = SetupGtkBase(lib_gtk); + if (!BaseGtkIntegration::Instance()->loaded()) { + return; } - if (GtkLoaded) { - LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_default", gdk_display_get_default); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_monitor", gdk_display_get_monitor); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_show", gtk_widget_show); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide", gtk_widget_hide); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_get_window", gtk_widget_get_window); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_realize", gtk_widget_realize); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete); + LOAD_GTK_SYMBOL(Library(), "gtk_widget_destroy", gtk_widget_destroy); + LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_get", gtk_clipboard_get); + LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_store", gtk_clipboard_store); + LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents); + LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image); + LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image); + LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_free", gtk_selection_data_free); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_type", gtk_file_chooser_get_type); + LOAD_GTK_SYMBOL(Library(), "gtk_image_get_type", gtk_image_get_type); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter); + LOAD_GTK_SYMBOL(Library(), "gtk_window_get_type", gtk_window_get_type); + LOAD_GTK_SYMBOL(Library(), "gtk_window_set_title", gtk_window_set_title); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_action", gtk_file_chooser_set_action); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter); + LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_set_name", gtk_file_filter_set_name); + LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename); + LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active); + LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_new", gtk_file_filter_new); + LOAD_GTK_SYMBOL(Library(), "gtk_image_new", gtk_image_new); + LOAD_GTK_SYMBOL(Library(), "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height); - LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); + LOAD_GTK_SYMBOL(Library(), "gdk_window_set_modal_hint", gdk_window_set_modal_hint); + LOAD_GTK_SYMBOL(Library(), "gdk_window_focus", gdk_window_focus); + LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_type", gtk_dialog_get_type); + LOAD_GTK_SYMBOL(Library(), "gtk_dialog_run", gtk_dialog_run); - GdkHelperLoad(lib_gtk); + LOAD_GTK_SYMBOL(Library(), "gdk_atom_intern", gdk_atom_intern); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type); + LOAD_GTK_SYMBOL(Library(), "gdk_display_get_default", gdk_display_get_default); + LOAD_GTK_SYMBOL(Library(), "gdk_display_get_monitor", gdk_display_get_monitor); + LOAD_GTK_SYMBOL(Library(), "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); + LOAD_GTK_SYMBOL(Library(), "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); - LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_width", gdk_pixbuf_get_width); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_height", gdk_pixbuf_get_height); + LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); - SetScaleFactor(); - SetIconTheme(); - SetCursorSize(); + GdkHelperLoad(Library()); - const auto settings = gtk_settings_get_default(); + LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); + LOAD_GTK_SYMBOL(Library(), "gtk_button_set_label", gtk_button_set_label); + LOAD_GTK_SYMBOL(Library(), "gtk_button_get_type", gtk_button_get_type); - g_signal_connect( - settings, - "notify::gtk-icon-theme-name", - G_CALLBACK(SetIconTheme), - nullptr); + LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); + LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); + LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_type", gtk_app_chooser_get_type); - g_signal_connect( - settings, - "notify::gtk-theme-name", - G_CALLBACK(DarkModeChanged), - nullptr); + Loaded = true; - g_signal_connect( - settings, - "notify::gtk-cursor-theme-size", - G_CALLBACK(SetCursorSize), - nullptr); + SetScaleFactor(); - if (checkVersion(3, 0, 0)) { - g_signal_connect( - settings, - "notify::gtk-application-prefer-dark-theme", - G_CALLBACK(DarkModeChanged), - nullptr); - } + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-theme-name", + DarkModeChanged); - if (checkVersion(3, 12, 0)) { - g_signal_connect( - settings, - "notify::gtk-decoration-layout", - G_CALLBACK(DecorationLayoutChanged), - nullptr); - } - } else { - LOG(("Could not load gtk-3 or gtk-x11-2.0!")); + if (BaseGtkIntegration::Instance()->checkVersion(3, 0, 0)) { + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-application-prefer-dark-theme", + DarkModeChanged); + } + + if (BaseGtkIntegration::Instance()->checkVersion(3, 12, 0)) { + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-decoration-layout", + DecorationLayoutChanged); + + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-decoration-layout", + Ui::Platform::NotifyTitleControlsLayoutChanged); } } bool GtkIntegration::loaded() const { - return GtkLoaded; -} - -bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const { - return (loaded() && gtk_check_version != nullptr) - ? !gtk_check_version(major, minor, micro) - : false; -} - -std::optional<bool> GtkIntegration::getBoolSetting( - const QString &propertyName) const { - const auto value = GtkSetting<gboolean>(propertyName); - if (!value.has_value()) { - return std::nullopt; - } - DEBUG_LOG(("Getting GTK setting, %1: %2") - .arg(propertyName) - .arg(Logs::b(*value))); - return *value; -} - -std::optional<int> GtkIntegration::getIntSetting( - const QString &propertyName) const { - const auto value = GtkSetting<gint>(propertyName); - if (value.has_value()) { - DEBUG_LOG(("Getting GTK setting, %1: %2") - .arg(propertyName) - .arg(*value)); - } - return value; -} - -std::optional<QString> GtkIntegration::getStringSetting( - const QString &propertyName) const { - auto value = GtkSetting<gchararray>(propertyName); - if (!value.has_value()) { - return std::nullopt; - } - const auto str = QString::fromUtf8(*value); - g_free(*value); - DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str)); - return str; + return Loaded; } std::optional<int> GtkIntegration::scaleFactor() const { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h index 25774161b..4a24bd388 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h @@ -12,27 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace internal { -inline constexpr auto kDisableGtkIntegration = "TDESKTOP_DISABLE_GTK_INTEGRATION"_cs; - class GtkIntegration { public: static GtkIntegration *Instance(); void load(); [[nodiscard]] bool loaded() const; - [[nodiscard]] bool checkVersion( - uint major, - uint minor, - uint micro) const; - - [[nodiscard]] std::optional<bool> getBoolSetting( - const QString &propertyName) const; - - [[nodiscard]] std::optional<int> getIntSetting( - const QString &propertyName) const; - - [[nodiscard]] std::optional<QString> getStringSetting( - const QString &propertyName) const; [[nodiscard]] std::optional<int> scaleFactor() const; diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp index 59225e947..e27184daa 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp @@ -24,25 +24,6 @@ bool GtkIntegration::loaded() const { return false; } -bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const { - return false; -} - -std::optional<bool> GtkIntegration::getBoolSetting( - const QString &propertyName) const { - return std::nullopt; -} - -std::optional<int> GtkIntegration::getIntSetting( - const QString &propertyName) const { - return std::nullopt; -} - -std::optional<QString> GtkIntegration::getStringSetting( - const QString &propertyName) const { - return std::nullopt; -} - std::optional<int> GtkIntegration::scaleFactor() const { return std::nullopt; } diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h index b0b60e6bc..d2c9e9207 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include <QtCore/QLibrary> - extern "C" { #undef signals #include <gtk/gtk.h> @@ -16,16 +14,6 @@ extern "C" { #define signals public } // extern "C" -#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY -#define LINK_TO_GTK -#endif // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY - -#ifdef LINK_TO_GTK -#define LOAD_GTK_SYMBOL(lib, name, func) (func = ::func) -#else // LINK_TO_GTK -#define LOAD_GTK_SYMBOL Platform::Gtk::LoadSymbol -#endif // !LINK_TO_GTK - // To be able to compile with gtk-2.0 headers as well typedef struct _GdkMonitor GdkMonitor; typedef struct _GtkAppChooser GtkAppChooser; @@ -33,24 +21,6 @@ typedef struct _GtkAppChooser GtkAppChooser; namespace Platform { namespace Gtk { -template <typename Function> -bool LoadSymbol(QLibrary &lib, const char *name, Function &func) { - func = nullptr; - if (!lib.isLoaded()) { - return false; - } - - func = reinterpret_cast<Function>(lib.resolve(name)); - if (func) { - return true; - } - LOG(("Error: failed to load '%1' function!").arg(name)); - return false; -} - -inline gboolean (*gtk_init_check)(int *argc, char ***argv) = nullptr; -inline const gchar* (*gtk_check_version)(guint required_major, guint required_minor, guint required_micro) = nullptr; -inline GtkSettings* (*gtk_settings_get_default)(void) = nullptr; inline void (*gtk_widget_show)(GtkWidget *widget) = nullptr; inline void (*gtk_widget_hide)(GtkWidget *widget) = nullptr; inline GdkWindow* (*gtk_widget_get_window)(GtkWidget *widget) = nullptr; @@ -90,7 +60,6 @@ inline GtkWidget* (*gtk_image_new)(void) = nullptr; inline void (*gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf) = nullptr; inline GtkWidget* (*gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file) = nullptr; inline GAppInfo* (*gtk_app_chooser_get_app_info)(GtkAppChooser *self) = nullptr; -inline void (*gdk_set_allowed_backends)(const gchar *backends) = nullptr; inline void (*gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal) = nullptr; inline void (*gdk_window_focus)(GdkWindow *window, guint32 timestamp) = nullptr; diff --git a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp deleted file mode 100644 index 270f0ae8f..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -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 "platform/linux/linux_xlib_helper.h" - -extern "C" { -#include <X11/Xlib.h> -} - -namespace Platform { -namespace internal { - -class XErrorHandlerRestorer::Private { -public: - Private() - : _oldErrorHandler(XSetErrorHandler(nullptr)) { - } - - ~Private() { - XSetErrorHandler(_oldErrorHandler); - } - -private: - int (*_oldErrorHandler)(Display *, XErrorEvent *); -}; - -XErrorHandlerRestorer::XErrorHandlerRestorer() -: _private(std::make_unique<Private>()) { -} - -XErrorHandlerRestorer::~XErrorHandlerRestorer() = default; - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h b/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h deleted file mode 100644 index abd172066..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_xlib_helper.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 - -namespace Platform { -namespace internal { - -class XErrorHandlerRestorer { -public: - XErrorHandlerRestorer(); - ~XErrorHandlerRestorer(); - -private: - class Private; - const std::unique_ptr<Private> _private; -}; - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 5f5a94bfb..c5349ed8f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/window_session_controller.h" #include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_xcb_utilities_linux.h" +#include "base/platform/linux/base_linux_xcb_utilities.h" #include "base/call_delayed.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/input_fields.h" diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 12e98b37f..6fab06580 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -8,7 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/specific_linux.h" #include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_xcb_utilities_linux.h" +#include "base/platform/linux/base_linux_xcb_utilities.h" +#include "base/platform/linux/base_linux_gtk_integration.h" #include "platform/linux/linux_desktop_environment.h" #include "platform/linux/linux_gtk_integration.h" #include "platform/linux/linux_wayland_integration.h" @@ -60,6 +61,7 @@ extern "C" { #include <iostream> using namespace Platform; +using BaseGtkIntegration = base::Platform::GtkIntegration; using Platform::internal::WaylandIntegration; using Platform::internal::GtkIntegration; @@ -68,8 +70,6 @@ Q_DECLARE_METATYPE(QMargins); namespace Platform { namespace { -constexpr auto kIgnoreGtkIncompatibility = "TDESKTOP_I_KNOW_ABOUT_GTK_INCOMPATIBILITY"_cs; - constexpr auto kDesktopFile = ":/misc/telegramdesktop.desktop"_cs; constexpr auto kIconName = "telegram"_cs; constexpr auto kHandlerTypeName = "x-scheme-handler/tg"_cs; @@ -80,8 +80,6 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; -QStringList PlatformThemes; - #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION std::unique_ptr<internal::NotificationServiceWatcher> NSWInstance; @@ -562,27 +560,6 @@ bool InSnap() { return Result; } -bool IsStaticBinary() { -#ifdef DESKTOP_APP_USE_PACKAGED - return false; -#else // DESKTOP_APP_USE_PACKAGED - return true; -#endif // !DESKTOP_APP_USE_PACKAGED -} - -bool IsGtkIntegrationForced() { - static const auto Result = [&] { - if (!GtkIntegration::Instance()) { - return false; - } - - return PlatformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive) - || PlatformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive); - }(); - - return Result; -} - bool AreQtPluginsBundled() { #if !defined DESKTOP_APP_USE_PACKAGED || defined DESKTOP_APP_USE_PACKAGED_LAZY return true; @@ -719,7 +696,7 @@ QImage GetImageFromClipboard() { } std::optional<bool> IsDarkMode() { - const auto integration = GtkIntegration::Instance(); + const auto integration = BaseGtkIntegration::Instance(); if (!integration) { return std::nullopt; } @@ -832,7 +809,7 @@ bool WindowsNeedShadow() { Window::ControlsLayout WindowControlsLayout() { const auto gtkResult = []() -> std::optional<Window::ControlsLayout> { - const auto integration = GtkIntegration::Instance(); + const auto integration = BaseGtkIntegration::Instance(); if (!integration || !integration->checkVersion(3, 12, 0)) { return std::nullopt; } @@ -962,53 +939,15 @@ int psFixPrevious() { namespace Platform { void start() { - PlatformThemes = QString::fromUtf8(qgetenv("QT_QPA_PLATFORMTHEME")) - .split(':', base::QStringSkipEmptyParts); - LOG(("Launcher filename: %1").arg(GetLauncherFilename())); qputenv("PULSE_PROP_application.name", AppName.utf8()); qputenv("PULSE_PROP_application.icon_name", GetIconName().toLatin1()); - // if gtk integration and qgtk3/qgtk2 platformtheme (or qgtk2 style) - // is used at the same time, the app will crash - if (GtkIntegration::Instance() - && !IsStaticBinary() - && !qEnvironmentVariableIsSet( - kIgnoreGtkIncompatibility.utf8())) { - g_warning( - "Unfortunately, GTK integration " - "conflicts with qgtk2 platformtheme and style. " - "Therefore, QT_QPA_PLATFORMTHEME " - "and QT_STYLE_OVERRIDE will be unset."); - - g_message( - "This can be ignored by setting %s environment variable " - "to any value, however, if qgtk2 theme or style is used, " - "this will lead to a crash.", - kIgnoreGtkIncompatibility.utf8().constData()); - - g_message( - "GTK integration can be disabled by setting %s to any value. " - "Keep in mind that this will lead to clipboard issues " - "and tdesktop will be unable to get settings from GTK " - "(such as decoration layout, dark mode & more).", - internal::kDisableGtkIntegration.utf8().constData()); - - qunsetenv("QT_QPA_PLATFORMTHEME"); - qunsetenv("QT_STYLE_OVERRIDE"); - - // Don't allow qgtk3 to init gtk earlier than us - if (DesktopEnvironment::IsGtkBased()) { - QApplication::setDesktopSettingsAware(false); - } - } - - if (!GtkIntegration::Instance()) { - g_warning( - "GTK integration was disabled on build or in runtime. " - "This will lead to clipboard issues and a lack of some features " - "(like Auto-Night Mode or system window controls layout)."); + if (const auto integration = BaseGtkIntegration::Instance()) { + integration->prepareEnvironment(); + } else { + g_warning("GTK integration is disabled, some feature unavailable. "); } #ifdef DESKTOP_APP_USE_PACKAGED_RLOTTIE @@ -1227,13 +1166,17 @@ void start() { DEBUG_LOG(("Icon theme: %1").arg(QIcon::themeName())); DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName())); + if (const auto integration = BaseGtkIntegration::Instance()) { + integration->load(); + } + if (const auto integration = GtkIntegration::Instance()) { integration->load(); } // wait for interface announce to know if native window frame is supported - if (const auto waylandIntegration = WaylandIntegration::Instance()) { - waylandIntegration->waitForInterfaceAnnounce(); + if (const auto integration = WaylandIntegration::Instance()) { + integration->waitForInterfaceAnnounce(); } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 8b87b8997..ea815e4cf 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -17,9 +17,7 @@ namespace Platform { bool InFlatpak(); bool InSnap(); -bool IsStaticBinary(); bool AreQtPluginsBundled(); -bool IsGtkIntegrationForced(); bool UseXDGDesktopPortal(); bool CanOpenDirectoryWithPortal(); bool IsNotificationServiceActivatable(); diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index a67da47f3..5fe70fb2a 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -4,7 +4,6 @@ # For license and copyright information please follow this link: # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF) option(TDESKTOP_API_TEST "Use test API credentials." OFF) set(TDESKTOP_API_ID "0" CACHE STRING "Provide 'api_id' for the Telegram API access.") set(TDESKTOP_API_HASH "" CACHE STRING "Provide 'api_hash' for the Telegram API access.") @@ -52,10 +51,6 @@ if (DESKTOP_APP_SPECIAL_TARGET) target_compile_definitions(Telegram PRIVATE TDESKTOP_ALLOW_CLOSED_ALPHA) endif() -if (TDESKTOP_DISABLE_GTK_INTEGRATION) - target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_GTK_INTEGRATION) -endif() - if (NOT TDESKTOP_LAUNCHER_BASENAME) set(TDESKTOP_LAUNCHER_BASENAME "telegramdesktop") endif() From 15d18077b812a0ef72e4192e3807f80f0b6fddf7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 08:26:46 +0400 Subject: [PATCH 316/396] Use new methods from lib_ui --- Telegram/CMakeLists.txt | 14 - Telegram/SourceFiles/core/application.cpp | 3 - Telegram/SourceFiles/core/core_settings.h | 14 - Telegram/SourceFiles/mainwindow.cpp | 4 +- .../SourceFiles/media/view/media_view_pip.cpp | 13 +- .../platform/linux/linux_gtk_integration.cpp | 11 - .../linux/linux_wayland_integration.cpp | 96 +----- .../linux/linux_wayland_integration.h | 3 - .../linux/linux_wayland_integration_dummy.cpp | 12 - .../platform/linux/specific_linux.cpp | 308 ------------------ .../platform/linux/window_title_linux.cpp | 39 --- .../platform/linux/window_title_linux.h | 12 +- .../SourceFiles/platform/mac/specific_mac.h | 24 -- .../SourceFiles/platform/mac/specific_mac.mm | 10 - .../SourceFiles/platform/platform_specific.h | 9 - .../SourceFiles/platform/win/specific_win.cpp | 22 -- .../SourceFiles/platform/win/specific_win.h | 20 -- .../SourceFiles/ui/widgets/separate_panel.cpp | 6 - Telegram/SourceFiles/window/main_window.cpp | 2 +- .../window/window_controls_layout.h | 24 -- .../SourceFiles/window/window_title_qt.cpp | 49 +-- Telegram/SourceFiles/window/window_title_qt.h | 6 +- 22 files changed, 31 insertions(+), 670 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/window_title_linux.cpp delete mode 100644 Telegram/SourceFiles/window/window_controls_layout.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 600e9e37b..b14b0aafa 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -92,18 +92,6 @@ if (LINUX) ) endif() - if (DESKTOP_APP_USE_PACKAGED - AND NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION - AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0) - find_package(PkgConfig REQUIRED) - pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client) - - target_include_directories(Telegram - PRIVATE - ${WAYLAND_CLIENT_INCLUDE_DIRS} - ) - endif() - if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION) find_package(PkgConfig REQUIRED) @@ -852,7 +840,6 @@ PRIVATE platform/linux/notifications_manager_linux.h platform/linux/specific_linux.cpp platform/linux/specific_linux.h - platform/linux/window_title_linux.cpp platform/linux/window_title_linux.h platform/mac/file_utilities_mac.mm platform/mac/file_utilities_mac.h @@ -1057,7 +1044,6 @@ PRIVATE window/window_connecting_widget.h window/window_controller.cpp window/window_controller.h - window/window_controls_layout.h window/window_filters_menu.cpp window/window_filters_menu.h window/window_history_hider.cpp diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 2990e596f..3b5d6fb98 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/local_url_handlers.h" #include "core/launcher.h" #include "core/ui_integration.h" -#include "core/core_settings.h" #include "chat_helpers/emoji_keywords.h" #include "chat_helpers/stickers_emoji_image_loader.h" #include "base/platform/base_platform_info.h" @@ -212,8 +211,6 @@ void Application::run() { return; } - Core::App().settings().setWindowControlsLayout(Platform::WindowControlsLayout()); - _translator = std::make_unique<Lang::Translator>(); QCoreApplication::instance()->installTranslator(_translator.get()); diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 76320ad9e..732f75b2f 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "window/themes/window_themes_embedded.h" -#include "window/window_controls_layout.h" #include "ui/chat/attach/attach_send_files_way.h" #include "platform/platform_notifications_manager.h" @@ -503,18 +502,6 @@ public: [[nodiscard]] rpl::producer<bool> systemDarkModeEnabledChanges() const { return _systemDarkModeEnabled.changes(); } - void setWindowControlsLayout(Window::ControlsLayout value) { - _windowControlsLayout = value; - } - [[nodiscard]] Window::ControlsLayout windowControlsLayout() const { - return _windowControlsLayout.current(); - } - [[nodiscard]] rpl::producer<Window::ControlsLayout> windowControlsLayoutValue() const { - return _windowControlsLayout.value(); - } - [[nodiscard]] rpl::producer<Window::ControlsLayout> windowControlsLayoutChanges() const { - return _windowControlsLayout.changes(); - } [[nodiscard]] const WindowPosition &windowPosition() const { return _windowPosition; } @@ -602,7 +589,6 @@ private: rpl::variable<bool> _nativeWindowFrame = false; rpl::variable<std::optional<bool>> _systemDarkMode = std::nullopt; rpl::variable<bool> _systemDarkModeEnabled = false; - rpl::variable<Window::ControlsLayout> _windowControlsLayout; WindowPosition _windowPosition; // per-window bool _tabbedReplacedWithInfo = false; // per-window diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index fdeaa0af8..39c2a4fd0 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -38,9 +38,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_updates.h" #include "settings/settings_intro.h" -#include "platform/platform_specific.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" +#include "ui/platform/ui_platform_utility.h" #include "base/call_delayed.h" #include "window/notifications_manager.h" #include "window/themes/window_theme.h" @@ -111,7 +111,7 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller) setAttribute(Qt::WA_NoSystemBackground); - if (Platform::WindowsNeedShadow()) { + if (Ui::Platform::WindowExtentsSupported()) { setAttribute(Qt::WA_TranslucentBackground); } else { setAttribute(Qt::WA_OpaquePaintEvent); diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 83e716456..695bc639b 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_session.h" #include "core/application.h" -#include "platform/platform_specific.h" #include "base/platform/base_platform_info.h" #include "ui/platform/ui_platform_utility.h" #include "ui/widgets/buttons.h" @@ -724,17 +723,9 @@ void PipPanel::startSystemDrag() { const auto stateEdges = RectPartToQtEdges(*_dragState); if (stateEdges) { - if (!Platform::StartSystemResize(windowHandle(), stateEdges)) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - windowHandle()->startSystemResize(stateEdges); -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - } + windowHandle()->startSystemResize(stateEdges); } else { - if (!Platform::StartSystemMove(windowHandle())) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - windowHandle()->startSystemMove(); -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - } + windowHandle()->startSystemMove(); } } diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 75b2cd18e..4aa024d56 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -71,13 +71,6 @@ void DarkModeChanged() { }); } -void DecorationLayoutChanged() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - Core::App().settings().setWindowControlsLayout( - WindowControlsLayout()); - }); -} - } // namespace GtkIntegration::GtkIntegration() { @@ -182,10 +175,6 @@ void GtkIntegration::load() { } if (BaseGtkIntegration::Instance()->checkVersion(3, 12, 0)) { - BaseGtkIntegration::Instance()->connectToSetting( - "gtk-decoration-layout", - DecorationLayoutChanged); - BaseGtkIntegration::Instance()->connectToSetting( "gtk-decoration-layout", Ui::Platform::NotifyTitleControlsLayoutChanged); diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp index baefa943b..a3ff42761 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp @@ -9,52 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" -#include <QtGui/QWindow> - -#include <private/qwaylanddisplay_p.h> -#include <private/qwaylandwindow_p.h> -#include <private/qwaylandshellsurface_p.h> - #include <connection_thread.h> #include <registry.h> -#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) && !defined DESKTOP_APP_QT_PATCHED -#include <wayland-client.h> -#endif // Qt < 5.13 && !DESKTOP_APP_QT_PATCHED - -using QtWaylandClient::QWaylandWindow; using namespace KWayland::Client; namespace Platform { namespace internal { -namespace { - -#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) && !defined DESKTOP_APP_QT_PATCHED -enum wl_shell_surface_resize WlResizeFromEdges(Qt::Edges edges) { - if (edges == (Qt::TopEdge | Qt::LeftEdge)) - return WL_SHELL_SURFACE_RESIZE_TOP_LEFT; - if (edges == Qt::TopEdge) - return WL_SHELL_SURFACE_RESIZE_TOP; - if (edges == (Qt::TopEdge | Qt::RightEdge)) - return WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; - if (edges == Qt::RightEdge) - return WL_SHELL_SURFACE_RESIZE_RIGHT; - if (edges == (Qt::RightEdge | Qt::BottomEdge)) - return WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; - if (edges == Qt::BottomEdge) - return WL_SHELL_SURFACE_RESIZE_BOTTOM; - if (edges == (Qt::BottomEdge | Qt::LeftEdge)) - return WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; - if (edges == Qt::LeftEdge) - return WL_SHELL_SURFACE_RESIZE_LEFT; - - return WL_SHELL_SURFACE_RESIZE_NONE; -} -#endif // Qt < 5.13 && !DESKTOP_APP_QT_PATCHED - -} // namespace - class WaylandIntegration::Private : public QObject { public: Private(); @@ -95,7 +57,9 @@ WaylandIntegration::Private::Private() { connect(&_registry, &Registry::interfacesAnnounced, [=] { _interfacesAnnounced = true; - _interfacesLoop.quit(); + if (_interfacesLoop.isRunning()) { + _interfacesLoop.quit(); + } }); _connection.initConnection(); @@ -114,6 +78,7 @@ WaylandIntegration *WaylandIntegration::Instance() { } void WaylandIntegration::waitForInterfaceAnnounce() { + Expects(!_private->interfacesLoop().isRunning()); if (!_private->interfacesAnnounced()) { _private->interfacesLoop().exec(); } @@ -124,58 +89,5 @@ bool WaylandIntegration::supportsXdgDecoration() { Registry::Interface::XdgDecorationUnstableV1); } -bool WaylandIntegration::startMove(QWindow *window) { - // There are startSystemMove on Qt 5.15 -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) && !defined DESKTOP_APP_QT_PATCHED - if (const auto waylandWindow = static_cast<QWaylandWindow*>( - window->handle())) { - if (const auto seat = waylandWindow->display()->lastInputDevice()) { - if (const auto shellSurface = waylandWindow->shellSurface()) { - return shellSurface->move(seat); - } - } - } -#endif // Qt < 5.15 && !DESKTOP_APP_QT_PATCHED - - return false; -} - -bool WaylandIntegration::startResize(QWindow *window, Qt::Edges edges) { - // There are startSystemResize on Qt 5.15 -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) && !defined DESKTOP_APP_QT_PATCHED - if (const auto waylandWindow = static_cast<QWaylandWindow*>( - window->handle())) { - if (const auto seat = waylandWindow->display()->lastInputDevice()) { - if (const auto shellSurface = waylandWindow->shellSurface()) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - shellSurface->resize(seat, edges); - return true; -#else // Qt >= 5.13 - shellSurface->resize(seat, WlResizeFromEdges(edges)); - return true; -#endif // Qt < 5.13 - } - } - } -#endif // Qt < 5.15 && !DESKTOP_APP_QT_PATCHED - - return false; -} - -bool WaylandIntegration::showWindowMenu(QWindow *window) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) || defined DESKTOP_APP_QT_PATCHED - if (const auto waylandWindow = static_cast<QWaylandWindow*>( - window->handle())) { - if (const auto seat = waylandWindow->display()->lastInputDevice()) { - if (const auto shellSurface = waylandWindow->shellSurface()) { - return shellSurface->showWindowMenu(seat); - } - } - } -#endif // Qt >= 5.13 || DESKTOP_APP_QT_PATCHED - - return false; -} - } // namespace internal } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h index a6e582042..3a8705c08 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h @@ -17,9 +17,6 @@ public: static WaylandIntegration *Instance(); void waitForInterfaceAnnounce(); bool supportsXdgDecoration(); - bool startMove(QWindow *window); - bool startResize(QWindow *window, Qt::Edges edges); - bool showWindowMenu(QWindow *window); private: WaylandIntegration(); diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp index 7a5b52e96..166e8b575 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp @@ -33,17 +33,5 @@ bool WaylandIntegration::supportsXdgDecoration() { return false; } -bool WaylandIntegration::startMove(QWindow *window) { - return false; -} - -bool WaylandIntegration::startResize(QWindow *window, Qt::Edges edges) { - return false; -} - -bool WaylandIntegration::showWindowMenu(QWindow *window) { - return false; -} - } // namespace internal } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 6fab06580..566bb1d40 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -41,8 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <QtDBus/QDBusError> #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include <xcb/xcb.h> - #include <glib.h> extern "C" { @@ -78,8 +76,6 @@ constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs; constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs; constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; -constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; - #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION std::unique_ptr<internal::NotificationServiceWatcher> NSWInstance; @@ -351,187 +347,6 @@ bool GenerateDesktopFile( } } -uint XCBMoveResizeFromEdges(Qt::Edges edges) { - if (edges == (Qt::TopEdge | Qt::LeftEdge)) - return 0; - if (edges == Qt::TopEdge) - return 1; - if (edges == (Qt::TopEdge | Qt::RightEdge)) - return 2; - if (edges == Qt::RightEdge) - return 3; - if (edges == (Qt::RightEdge | Qt::BottomEdge)) - return 4; - if (edges == Qt::BottomEdge) - return 5; - if (edges == (Qt::BottomEdge | Qt::LeftEdge)) - return 6; - if (edges == Qt::LeftEdge) - return 7; - - return 0; -} - -bool StartXCBMoveResize(QWindow *window, int edges) { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto root = base::Platform::XCB::GetRootWindowFromQt(); - if (!root.has_value()) { - return false; - } - - const auto moveResizeAtom = base::Platform::XCB::GetAtom( - connection, - "_NET_WM_MOVERESIZE"); - - if (!moveResizeAtom.has_value()) { - return false; - } - - const auto globalPos = QCursor::pos(); - - xcb_client_message_event_t xev; - xev.response_type = XCB_CLIENT_MESSAGE; - xev.type = *moveResizeAtom; - xev.sequence = 0; - xev.window = window->winId(); - xev.format = 32; - xev.data.data32[0] = globalPos.x(); - xev.data.data32[1] = globalPos.y(); - xev.data.data32[2] = (edges == 16) - ? 8 // move - : XCBMoveResizeFromEdges(Qt::Edges(edges)); - xev.data.data32[3] = XCB_BUTTON_INDEX_1; - xev.data.data32[4] = 0; - - xcb_ungrab_pointer(connection, XCB_CURRENT_TIME); - xcb_send_event( - connection, - false, - *root, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT - | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, - reinterpret_cast<const char*>(&xev)); - - return true; -} - -bool ShowXCBWindowMenu(QWindow *window) { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto root = base::Platform::XCB::GetRootWindowFromQt(); - if (!root.has_value()) { - return false; - } - - const auto showWindowMenuAtom = base::Platform::XCB::GetAtom( - connection, - "_GTK_SHOW_WINDOW_MENU"); - - if (!showWindowMenuAtom.has_value()) { - return false; - } - - const auto globalPos = QCursor::pos(); - - xcb_client_message_event_t xev; - xev.response_type = XCB_CLIENT_MESSAGE; - xev.type = *showWindowMenuAtom; - xev.sequence = 0; - xev.window = window->winId(); - xev.format = 32; - xev.data.data32[0] = 0; - xev.data.data32[1] = globalPos.x(); - xev.data.data32[2] = globalPos.y(); - xev.data.data32[3] = 0; - xev.data.data32[4] = 0; - - xcb_ungrab_pointer(connection, XCB_CURRENT_TIME); - xcb_send_event( - connection, - false, - *root, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT - | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, - reinterpret_cast<const char*>(&xev)); - - return true; -} - -bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto frameExtentsAtom = base::Platform::XCB::GetAtom( - connection, - kXCBFrameExtentsAtomName.utf16()); - - if (!frameExtentsAtom.has_value()) { - return false; - } - - const auto extentsVector = std::vector<uint>{ - uint(extents.left()), - uint(extents.right()), - uint(extents.top()), - uint(extents.bottom()), - }; - - xcb_change_property( - connection, - XCB_PROP_MODE_REPLACE, - window->winId(), - *frameExtentsAtom, - XCB_ATOM_CARDINAL, - 32, - extentsVector.size(), - extentsVector.data()); - - return true; -} - -bool UnsetXCBFrameExtents(QWindow *window) { - const auto connection = base::Platform::XCB::GetConnectionFromQt(); - if (!connection) { - return false; - } - - const auto frameExtentsAtom = base::Platform::XCB::GetAtom( - connection, - kXCBFrameExtentsAtomName.utf16()); - - if (!frameExtentsAtom.has_value()) { - return false; - } - - xcb_delete_property( - connection, - window->winId(), - *frameExtentsAtom); - - return true; -} - -Window::Control GtkKeywordToWindowControl(const QString &keyword) { - if (keyword == qstr("minimize")) { - return Window::Control::Minimize; - } else if (keyword == qstr("maximize")) { - return Window::Control::Maximize; - } else if (keyword == qstr("close")) { - return Window::Control::Close; - } - - return Window::Control::Unknown; -} - } // namespace void SetWatchingMediaKeys(bool watching) { @@ -741,129 +556,6 @@ bool SkipTaskbarSupported() { && base::Platform::XCB::IsSupportedByWM("_NET_WM_STATE_SKIP_TASKBAR"); } -bool StartSystemMove(QWindow *window) { - if (const auto integration = WaylandIntegration::Instance()) { - return integration->startMove(window); - } else { - return StartXCBMoveResize(window, 16); - } -} - -bool StartSystemResize(QWindow *window, Qt::Edges edges) { - if (const auto integration = WaylandIntegration::Instance()) { - return integration->startResize(window, edges); - } else { - return StartXCBMoveResize(window, edges); - } -} - -bool ShowWindowMenu(QWindow *window) { - if (const auto integration = WaylandIntegration::Instance()) { - return integration->showWindowMenu(window); - } else { - return ShowXCBWindowMenu(window); - } -} - -bool SetWindowExtents(QWindow *window, const QMargins &extents) { - if (IsWayland()) { -#ifdef DESKTOP_APP_QT_PATCHED - window->setProperty("WaylandCustomMargins", QVariant::fromValue<QMargins>(extents)); - return true; -#else // DESKTOP_APP_QT_PATCHED - return false; -#endif // !DESKTOP_APP_QT_PATCHED - } else { - return SetXCBFrameExtents(window, extents); - } -} - -bool UnsetWindowExtents(QWindow *window) { - if (IsWayland()) { -#ifdef DESKTOP_APP_QT_PATCHED - window->setProperty("WaylandCustomMargins", QVariant()); - return true; -#else // DESKTOP_APP_QT_PATCHED - return false; -#endif // !DESKTOP_APP_QT_PATCHED - } else { - return UnsetXCBFrameExtents(window); - } -} - -bool WindowsNeedShadow() { -#ifdef DESKTOP_APP_QT_PATCHED - if (IsWayland()) { - return true; - } -#endif // DESKTOP_APP_QT_PATCHED - - namespace XCB = base::Platform::XCB; - if (!IsWayland() - && XCB::IsSupportedByWM(kXCBFrameExtentsAtomName.utf16())) { - return true; - } - - return false; -} - -Window::ControlsLayout WindowControlsLayout() { - const auto gtkResult = []() -> std::optional<Window::ControlsLayout> { - const auto integration = BaseGtkIntegration::Instance(); - if (!integration || !integration->checkVersion(3, 12, 0)) { - return std::nullopt; - } - - const auto decorationLayoutSetting = integration->getStringSetting( - qsl("gtk-decoration-layout")); - - if (!decorationLayoutSetting.has_value()) { - return std::nullopt; - } - - const auto decorationLayout = decorationLayoutSetting->split(':'); - - std::vector<Window::Control> controlsLeft; - ranges::transform( - decorationLayout[0].split(','), - ranges::back_inserter(controlsLeft), - GtkKeywordToWindowControl); - - std::vector<Window::Control> controlsRight; - if (decorationLayout.size() > 1) { - ranges::transform( - decorationLayout[1].split(','), - ranges::back_inserter(controlsRight), - GtkKeywordToWindowControl); - } - - return Window::ControlsLayout{ - .left = controlsLeft, - .right = controlsRight - }; - }(); - - if (gtkResult.has_value()) { - return *gtkResult; - } else if (DesktopEnvironment::IsUnity()) { - return Window::ControlsLayout{ - .left = { - Window::Control::Close, - Window::Control::Minimize, - Window::Control::Maximize, - } - }; - } else { - return Window::ControlsLayout{ - .right = { - Window::Control::Minimize, - Window::Control::Maximize, - Window::Control::Close, - } - }; - } -} - } // namespace Platform QRect psDesktopRect() { diff --git a/Telegram/SourceFiles/platform/linux/window_title_linux.cpp b/Telegram/SourceFiles/platform/linux/window_title_linux.cpp deleted file mode 100644 index bd990755c..000000000 --- a/Telegram/SourceFiles/platform/linux/window_title_linux.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -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 "platform/linux/window_title_linux.h" - -#include "platform/linux/linux_wayland_integration.h" -#include "base/platform/base_platform_info.h" - -namespace Platform { -namespace { - -bool SystemMoveResizeSupported() { -#if !defined DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION || QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - return true; -#else // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION || Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - return !IsWayland(); -#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION || Qt >= 5.15 || DESKTOP_APP_QT_PATCHED -} - -} // namespace - -bool AllowNativeWindowFrameToggle() { - const auto waylandIntegration = internal::WaylandIntegration::Instance(); - return SystemMoveResizeSupported() - && (!waylandIntegration - || waylandIntegration->supportsXdgDecoration()); -} - -object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) { - return SystemMoveResizeSupported() - ? object_ptr<Window::TitleWidgetQt>(parent) - : object_ptr<Window::TitleWidgetQt>{ nullptr }; -} - -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/window_title_linux.h b/Telegram/SourceFiles/platform/linux/window_title_linux.h index c493976c2..3bfebc236 100644 --- a/Telegram/SourceFiles/platform/linux/window_title_linux.h +++ b/Telegram/SourceFiles/platform/linux/window_title_linux.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "platform/platform_window_title.h" +#include "platform/linux/linux_wayland_integration.h" #include "base/object_ptr.h" namespace Window { @@ -21,8 +22,15 @@ void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palet namespace Platform { -bool AllowNativeWindowFrameToggle(); -object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent); +inline bool AllowNativeWindowFrameToggle() { + const auto waylandIntegration = internal::WaylandIntegration::Instance(); + return !waylandIntegration + || waylandIntegration->supportsXdgDecoration(); +} + +inline object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) { + return object_ptr<Window::TitleWidgetQt>(parent); +} inline bool NativeTitleRequiresShadow() { return false; diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index 00b0bb44c..9c91e2c32 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -22,18 +22,6 @@ inline QImage GetImageFromClipboard() { return {}; } -inline bool StartSystemMove(QWindow *window) { - return false; -} - -inline bool StartSystemResize(QWindow *window, Qt::Edges edges) { - return false; -} - -inline bool ShowWindowMenu(QWindow *window) { - return false; -} - inline bool AutostartSupported() { return false; } @@ -46,18 +34,6 @@ inline bool SkipTaskbarSupported() { return false; } -inline bool SetWindowExtents(QWindow *window, const QMargins &extents) { - return false; -} - -inline bool UnsetWindowExtents(QWindow *window) { - return false; -} - -inline bool WindowsNeedShadow() { - return false; -} - namespace ThirdParty { inline void start() { diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index 162629e89..a08342d87 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -203,16 +203,6 @@ void IgnoreApplicationActivationRightNow() { objc_ignoreApplicationActivationRightNow(); } -Window::ControlsLayout WindowControlsLayout() { - return Window::ControlsLayout{ - .left = { - Window::Control::Close, - Window::Control::Minimize, - Window::Control::Maximize, - } - }; -} - } // namespace Platform void psNewVersion() { diff --git a/Telegram/SourceFiles/platform/platform_specific.h b/Telegram/SourceFiles/platform/platform_specific.h index 45fafaefa..d0bfb84dc 100644 --- a/Telegram/SourceFiles/platform/platform_specific.h +++ b/Telegram/SourceFiles/platform/platform_specific.h @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "window/window_controls_layout.h" - namespace Platform { void start(); @@ -42,13 +40,6 @@ bool AutostartSupported(); bool TrayIconSupported(); bool SkipTaskbarSupported(); QImage GetImageFromClipboard(); -bool StartSystemMove(QWindow *window); -bool StartSystemResize(QWindow *window, Qt::Edges edges); -bool ShowWindowMenu(QWindow *window); -bool WindowsNeedShadow(); -bool SetWindowExtents(QWindow *window, const QMargins &extents); -bool UnsetWindowExtents(QWindow *window); -Window::ControlsLayout WindowControlsLayout(); [[nodiscard]] std::optional<bool> IsDarkMode(); [[nodiscard]] inline bool IsDarkModeSupported() { diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index cec77bfa6..6e015b378 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -312,28 +312,6 @@ bool AutostartSupported() { return !IsWindowsStoreBuild(); } -bool ShowWindowMenu(QWindow *window) { - const auto pos = QCursor::pos(); - - SendMessage( - HWND(window->winId()), - WM_SYSCOMMAND, - SC_MOUSEMENU, - MAKELPARAM(pos.x(), pos.y())); - - return true; -} - -Window::ControlsLayout WindowControlsLayout() { - return Window::ControlsLayout{ - .right = { - Window::Control::Minimize, - Window::Control::Maximize, - Window::Control::Close, - } - }; -} - } // namespace Platform namespace { diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index c5fa006a5..4ef32a72c 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -26,14 +26,6 @@ inline QImage GetImageFromClipboard() { return {}; } -inline bool StartSystemMove(QWindow *window) { - return false; -} - -inline bool StartSystemResize(QWindow *window, Qt::Edges edges) { - return false; -} - inline bool TrayIconSupported() { return true; } @@ -42,18 +34,6 @@ inline bool SkipTaskbarSupported() { return true; } -inline bool SetWindowExtents(QWindow *window, const QMargins &extents) { - return false; -} - -inline bool UnsetWindowExtents(QWindow *window) { - return false; -} - -inline bool WindowsNeedShadow() { - return false; -} - namespace ThirdParty { void start(); diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp index d2caed510..799405a79 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/separate_panel.h" #include "window/main_window.h" -#include "platform/platform_specific.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -554,14 +553,9 @@ void SeparatePanel::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { if (dragArea.contains(e->pos())) { const auto dragViaSystem = [&] { - if (::Platform::StartSystemMove(windowHandle())) { - return true; - } -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED if (windowHandle()->startSystemMove()) { return true; } -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED return false; }(); if (!dragViaSystem) { diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 6ed2698c3..ccd0d047a 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -346,7 +346,7 @@ HitTestResult MainWindow::hitTest(const QPoint &p) const { bool MainWindow::hasShadow() const { const auto center = geometry().center(); - return Platform::WindowsNeedShadow() + return Ui::Platform::WindowExtentsSupported() && Ui::Platform::TranslucentWindowsSupported(center) && _title; } diff --git a/Telegram/SourceFiles/window/window_controls_layout.h b/Telegram/SourceFiles/window/window_controls_layout.h deleted file mode 100644 index cb8c47bd7..000000000 --- a/Telegram/SourceFiles/window/window_controls_layout.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 - -namespace Window { - -enum class Control { - Unknown, - Minimize, - Maximize, - Close, -}; - -struct ControlsLayout { - std::vector<Control> left; - std::vector<Control> right; -}; - -} // namespace Window diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 558e0d14e..4815dc082 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -7,12 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_title_qt.h" -#include "platform/platform_specific.h" #include "ui/platform/ui_platform_utility.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" -#include "core/core_settings.h" -#include "core/application.h" #include "styles/style_widgets.h" #include "styles/style_window.h" @@ -66,7 +63,7 @@ TitleWidgetQt::TitleWidgetQt(QWidget *parent) }); _close->setPointerCursor(false); - Core::App().settings().windowControlsLayoutChanges( + Ui::Platform::TitleControlsLayoutChanged( ) | rpl::start_with_next([=] { updateControlsPosition(); }, lifetime()); @@ -94,7 +91,7 @@ TitleWidgetQt::~TitleWidgetQt() { } if (_extentsSet) { - Platform::UnsetWindowExtents(window()->windowHandle()); + Ui::Platform::UnsetWindowExtents(window()->windowHandle()); } } @@ -116,7 +113,7 @@ void TitleWidgetQt::init() { bool TitleWidgetQt::hasShadow() const { const auto center = window()->geometry().center(); - return Platform::WindowsNeedShadow() + return Ui::Platform::WindowExtentsSupported() && Ui::Platform::TranslucentWindowsSupported(center); } @@ -145,19 +142,19 @@ void TitleWidgetQt::toggleFramelessWindow(bool enabled) { void TitleWidgetQt::updateWindowExtents() { if (hasShadow()) { - Platform::SetWindowExtents( + Ui::Platform::SetWindowExtents( window()->windowHandle(), resizeArea() * cIntRetinaFactor()); _extentsSet = true; } else if (_extentsSet) { - Platform::UnsetWindowExtents(window()->windowHandle()); + Ui::Platform::UnsetWindowExtents(window()->windowHandle()); _extentsSet = false; } } void TitleWidgetQt::updateControlsPosition() { - const auto controlsLayout = Core::App().settings().windowControlsLayout(); + const auto controlsLayout = Ui::Platform::TitleControlsLayout(); const auto controlsLeft = controlsLayout.left; const auto controlsRight = controlsLayout.right; @@ -223,7 +220,7 @@ void TitleWidgetQt::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { _mousePressed = true; } else if (e->button() == Qt::RightButton) { - Platform::ShowWindowMenu(window()->windowHandle()); + Ui::Platform::ShowWindowMenu(window()->windowHandle()); } } @@ -235,7 +232,7 @@ void TitleWidgetQt::mouseReleaseEvent(QMouseEvent *e) { void TitleWidgetQt::mouseMoveEvent(QMouseEvent *e) { if (_mousePressed) { - startMove(); + window()->windowHandle()->startSystemMove(); } } @@ -264,7 +261,7 @@ bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { if (e->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton && edges) { - return startResize(edges); + return window()->windowHandle()->startSystemResize(edges); } } } else if (e->type() == QEvent::Leave) { @@ -413,32 +410,4 @@ void TitleWidgetQt::restoreCursor() { } } -bool TitleWidgetQt::startMove() { - if (Platform::StartSystemMove(window()->windowHandle())) { - return true; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - if (window()->windowHandle()->startSystemMove()) { - return true; - } -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - - return false; -} - -bool TitleWidgetQt::startResize(Qt::Edges edges) { - if (Platform::StartSystemResize(window()->windowHandle(), edges)) { - return true; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED - if (window()->windowHandle()->startSystemResize(edges)) { - return true; - } -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - - return false; -} - } // namespace Window diff --git a/Telegram/SourceFiles/window/window_title_qt.h b/Telegram/SourceFiles/window/window_title_qt.h index 9bed67470..4767f045b 100644 --- a/Telegram/SourceFiles/window/window_title_qt.h +++ b/Telegram/SourceFiles/window/window_title_qt.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "window/window_title.h" -#include "window/window_controls_layout.h" +#include "ui/platform/ui_platform_window_title.h" #include "base/object_ptr.h" namespace style { @@ -24,6 +24,8 @@ namespace Window { class TitleWidgetQt : public TitleWidget { public: + using Control = Ui::Platform::TitleControls::Control; + TitleWidgetQt(QWidget *parent); ~TitleWidgetQt(); @@ -56,8 +58,6 @@ private: Qt::Edges edgesFromPos(const QPoint &pos) const; void updateCursor(Qt::Edges edges); void restoreCursor(); - bool startMove(); - bool startResize(Qt::Edges edges); const style::WindowTitle &_st; object_ptr<Ui::IconButton> _minimize; From 6932d859c90c4fe3506526770d4b7deb2e4d64cc Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 08:27:58 +0400 Subject: [PATCH 317/396] Get rid of unneeded QT_VERSION_CHECK in linux platform code --- Telegram/SourceFiles/platform/linux/launcher_linux.cpp | 3 --- .../platform/linux/notifications_manager_linux.cpp | 4 ---- Telegram/SourceFiles/platform/platform_window_title.h | 8 +------- .../ThirdParty/statusnotifieritem/statusnotifieritem.cpp | 7 +------ 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp index 18840ea96..b2fe79b6b 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp @@ -49,10 +49,7 @@ Launcher::Launcher(int argc, char *argv[]) } void Launcher::initHook() { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QApplication::setAttribute(Qt::AA_DisableSessionManager, true); -#endif // Qt >= 5.14 - QApplication::setDesktopFileName(GetLauncherFilename()); } diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index eb7ca59e5..5d90b2e4f 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -590,11 +590,7 @@ void NotificationData::setImage(const QString &imagePath) { g_variant_new_from_data( G_VARIANT_TYPE("ay"), _image.constBits(), -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - _image.byteCount(), -#else // Qt < 5.10.0 _image.sizeInBytes(), -#endif // Qt >= 5.10.0 true, nullptr, nullptr))); diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index cd6b3a631..8c70e8fa1 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -36,17 +36,11 @@ void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRe namespace Platform { inline bool AllowNativeWindowFrameToggle() { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED return true; -#else // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED - return false; -#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED } inline object_ptr<Window::TitleWidget> CreateTitleWidget(QWidget *parent) { - return AllowNativeWindowFrameToggle() - ? object_ptr<Window::TitleWidgetQt>(parent) - : object_ptr<Window::TitleWidgetQt>{ nullptr }; + return object_ptr<Window::TitleWidgetQt>(parent); } inline bool NativeTitleRequiresShadow() { diff --git a/Telegram/ThirdParty/statusnotifieritem/statusnotifieritem.cpp b/Telegram/ThirdParty/statusnotifieritem/statusnotifieritem.cpp index 38f0eea1c..34ceb3c74 100644 --- a/Telegram/ThirdParty/statusnotifieritem/statusnotifieritem.cpp +++ b/Telegram/ThirdParty/statusnotifieritem/statusnotifieritem.cpp @@ -333,12 +333,7 @@ IconPixmapList StatusNotifierItem::iconToPixmapList(const QIcon& icon) if (image.format() != QImage::Format_ARGB32) image = image.convertToFormat(QImage::Format_ARGB32); - pix.bytes = QByteArray((char *) image.bits(), -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - image.byteCount()); -#else - image.sizeInBytes()); -#endif + pix.bytes = QByteArray((char *) image.bits(), image.sizeInBytes()); // swap to network byte order if we are little endian if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) From 513054a30770809e299afc24656afb444fee0476 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 08:57:53 +0400 Subject: [PATCH 318/396] Get rid of unneeded material wayland decorations --- .gitmodules | 3 --- .../platform/linux/main_window_linux.cpp | 26 ------------------- .../platform/linux/main_window_linux.h | 1 - .../platform/linux/specific_linux.cpp | 4 --- Telegram/ThirdParty/materialdecoration | 1 - 5 files changed, 35 deletions(-) delete mode 160000 Telegram/ThirdParty/materialdecoration diff --git a/.gitmodules b/.gitmodules index d4624361b..17a1be422 100644 --- a/.gitmodules +++ b/.gitmodules @@ -64,9 +64,6 @@ [submodule "Telegram/ThirdParty/hunspell"] path = Telegram/ThirdParty/hunspell url = https://github.com/hunspell/hunspell -[submodule "Telegram/ThirdParty/materialdecoration"] - path = Telegram/ThirdParty/materialdecoration - url = https://github.com/desktop-app/materialdecoration.git [submodule "Telegram/ThirdParty/range-v3"] path = Telegram/ThirdParty/range-v3 url = https://github.com/ericniebler/range-v3.git diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index c5349ed8f..4cfc4b0d0 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -601,12 +601,6 @@ void MainWindow::initHook() { #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION LOG(("System tray available: %1").arg(Logs::b(trayAvailable()))); - - updateWaylandDecorationColors(); - style::PaletteChanged( - ) | rpl::start_with_next([=] { - updateWaylandDecorationColors(); - }, lifetime()); } bool MainWindow::hasTrayIcon() const { @@ -896,26 +890,6 @@ void MainWindow::updateIconCounters() { } } -void MainWindow::updateWaylandDecorationColors() { - windowHandle()->setProperty( - "__material_decoration_backgroundColor", - st::titleBgActive->c); - - windowHandle()->setProperty( - "__material_decoration_foregroundColor", - st::titleFgActive->c); - - windowHandle()->setProperty( - "__material_decoration_backgroundInactiveColor", - st::titleBg->c); - windowHandle()->setProperty( - "__material_decoration_foregroundInactiveColor", - st::titleFg->c); - - // Trigger a QtWayland client-side decoration update - windowHandle()->resize(windowHandle()->size()); -} - void MainWindow::initTrayMenuHook() { _trayIconMenuXEmbed.emplace(nullptr, trayIconMenu); _trayIconMenuXEmbed->deleteOnHide(false); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index ee7ecbee5..cc2ca4f16 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -79,7 +79,6 @@ private: base::unique_qptr<Ui::PopupMenu> _trayIconMenuXEmbed; void updateIconCounters(); - void updateWaylandDecorationColors(); #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION StatusNotifierItem *_sniTrayIcon = nullptr; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 566bb1d40..b5e4d41a6 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -654,10 +654,6 @@ void start() { "this may lead to font issues."); #endif // DESKTOP_APP_USE_PACKAGED_FONTS - if (AreQtPluginsBundled()) { - qputenv("QT_WAYLAND_DECORATION", "material"); - } - #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION // Tell the user when XDP file dialog is used DEBUG_LOG(("Checking for XDG Desktop Portal...")); diff --git a/Telegram/ThirdParty/materialdecoration b/Telegram/ThirdParty/materialdecoration deleted file mode 160000 index 90b26e827..000000000 --- a/Telegram/ThirdParty/materialdecoration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 90b26e827e27b829b208a3d906c38ca91cb2c50d From 4fde7852b164b6554a8366df8b5cc8f73fe1ba97 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 09:16:17 +0400 Subject: [PATCH 319/396] Use qEnvironmentVariable where appropriate --- Telegram/SourceFiles/core/sandbox.cpp | 8 ++++---- .../platform/linux/linux_desktop_environment.cpp | 3 +-- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index bbf608b4f..0ac205e2e 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -203,10 +203,10 @@ void Sandbox::setupScreenScale() { if (ratio > 1.) { if (!Platform::IsMac() || (ratio != 2.)) { LOG(("Found non-trivial Device Pixel Ratio: %1").arg(ratio)); - LOG(("Environmental variables: QT_DEVICE_PIXEL_RATIO='%1'").arg(QString::fromLatin1(qgetenv("QT_DEVICE_PIXEL_RATIO")))); - LOG(("Environmental variables: QT_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_SCALE_FACTOR")))); - LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR")))); - LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(QString::fromLatin1(qgetenv("QT_SCREEN_SCALE_FACTORS")))); + LOG(("Environmental variables: QT_DEVICE_PIXEL_RATIO='%1'").arg(qEnvironmentVariable("QT_DEVICE_PIXEL_RATIO"))); + LOG(("Environmental variables: QT_SCALE_FACTOR='%1'").arg(qEnvironmentVariable("QT_SCALE_FACTOR"))); + LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(qEnvironmentVariable("QT_AUTO_SCREEN_SCALE_FACTOR"))); + LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(qEnvironmentVariable("QT_SCREEN_SCALE_FACTORS"))); } style::SetDevicePixelRatio(int(ratio)); cSetScreenScale(style::kScaleDefault); diff --git a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp index bc9ce7293..1915224c5 100644 --- a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp @@ -14,8 +14,7 @@ namespace DesktopEnvironment { namespace { QString GetEnv(const char *name) { - auto result = getenv(name); - auto value = result ? QString::fromLatin1(result) : QString(); + auto value = qEnvironmentVariable(name); LOG(("Getting DE, %1: '%2'").arg(name).arg(value)); return value; } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index b5e4d41a6..bb26a1bf0 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -253,7 +253,7 @@ QString EscapeShellInLauncher(const QString &content) { QString FlatpakID() { static const auto Result = [] { if (!qEnvironmentVariableIsEmpty("FLATPAK_ID")) { - return QString::fromLatin1(qgetenv("FLATPAK_ID")); + return qEnvironmentVariable("FLATPAK_ID"); } else { return GetLauncherBasename(); } From 53869ed9942e2ac4af1c6d351881498431c5a4ad Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 10:03:34 +0400 Subject: [PATCH 320/396] Use base::QScreenNearestTo in PipPanel --- Telegram/SourceFiles/media/view/media_view_pip.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 695bc639b..d00596889 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_widgets.h" #include "styles/style_window.h" #include "styles/style_media_view.h" +#include "base/qt_adapters.h" #include <QtGui/QWindow> #include <QtGui/QScreen> @@ -48,14 +49,7 @@ constexpr auto kMsInSecond = 1000; } [[nodiscard]] QRect ScreenFromPosition(QPoint point) { - const auto screen = [&]() -> QScreen* { - for (const auto screen : QGuiApplication::screens()) { - if (screen->geometry().contains(point)) { - return screen; - } - } - return nullptr; - }(); + const auto screen = base::QScreenNearestTo(point); const auto use = screen ? screen : QGuiApplication::primaryScreen(); return use ? use->availableGeometry() From 22cbf32a1459094bc54f7091142d1a646231f918 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 12:53:37 +0400 Subject: [PATCH 321/396] Update submodules --- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index f1e416808..39776fab6 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit f1e4168081428fa451d2f50eee7b1c448268c43a +Subproject commit 39776fab6cad042ef9d6382ad6b2f8a60fd872d6 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index e14bc4681..492121950 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit e14bc4681d69c1b538b8c5af51501077ae5a8a86 +Subproject commit 492121950da14615bfab6e6c9b4415a50899ddde diff --git a/cmake b/cmake index 695fabda6..199bd8d0b 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 695fabda6830b58bdc02d09db70531d5dececcd0 +Subproject commit 199bd8d0b21d4f5c291a0d8b613bd0b4eda05c7f From bbf49b024acfe0204d85f6ad3f6d79dd67f6f0a7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 14:09:01 +0400 Subject: [PATCH 322/396] Fix implicit conversions from QByteArray --- .../export/data/export_data_types.cpp | 5 ++- .../passport/passport_panel_edit_scans.cpp | 2 +- .../platform/linux/file_utilities_linux.cpp | 7 ++-- .../platform/linux/linux_gsd_media_keys.cpp | 18 +++++------ .../platform/linux/linux_gtk_file_dialog.cpp | 30 ++++++++--------- .../platform/linux/linux_open_with_dialog.cpp | 2 +- .../platform/linux/main_window_linux.cpp | 6 ++-- .../linux/notifications_manager_linux.cpp | 32 +++++++++---------- .../platform/linux/specific_linux.cpp | 10 +++--- .../SourceFiles/storage/localimageloader.cpp | 2 +- Telegram/lib_spellcheck | 2 +- 11 files changed, 60 insertions(+), 56 deletions(-) diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index c4c30d8cc..52958126c 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -642,7 +642,10 @@ std::pair<QString, QSize> WriteImageThumb( ? largePath.mid(0, firstDot) + postfix + largePath.mid(firstDot) : largePath + postfix; const auto result = Output::File::PrepareRelativePath(basePath, thumb); - if (!image.save(basePath + result, finalFormat, finalQuality)) { + if (!image.save( + basePath + result, + finalFormat.constData(), + finalQuality)) { return {}; } return { result, finalSize }; diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp index 85b77c712..9ef957d15 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp @@ -55,7 +55,7 @@ std::variant<ReadScanError, QByteArray> ProcessImage(QByteArray &&bytes) { auto result = QByteArray(); { QBuffer buffer(&result); - if (!image.save(&buffer, QByteArray("JPG"), kJpegQuality)) { + if (!image.save(&buffer, "JPG", kJpegQuality)) { return ReadScanError::Unknown; } base::take(image); diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index b9ebef8c0..11d264843 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -29,7 +29,7 @@ namespace File { void UnsafeOpenUrl(const QString &url) { if (!g_app_info_launch_default_for_uri( - url.toUtf8(), + url.toUtf8().constData(), nullptr, nullptr)) { QDesktopServices::openUrl(url); @@ -57,7 +57,10 @@ void UnsafeLaunch(const QString &filepath) { const auto absolutePath = QFileInfo(filepath).absoluteFilePath(); if (!g_app_info_launch_default_for_uri( - g_filename_to_uri(absolutePath.toUtf8(), nullptr, nullptr), + g_filename_to_uri( + absolutePath.toUtf8().constData(), + nullptr, + nullptr), nullptr, nullptr)) { if (!UnsafeShowOpenWith(filepath)) { diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp index e6bda1eb0..6ea0c8d14 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp @@ -103,9 +103,9 @@ GSDMediaKeys::GSDMediaKeys() { auto reply = g_dbus_connection_call_sync( _dbusConnection, - _service.toUtf8(), - _objectPath.toUtf8(), - _interface.toUtf8(), + _service.toUtf8().constData(), + _objectPath.toUtf8().constData(), + _interface.toUtf8().constData(), "GrabMediaPlayerKeys", g_variant_new( "(su)", @@ -127,10 +127,10 @@ GSDMediaKeys::GSDMediaKeys() { _signalId = g_dbus_connection_signal_subscribe( _dbusConnection, - _service.toUtf8(), - _interface.toUtf8(), + _service.toUtf8().constData(), + _interface.toUtf8().constData(), "MediaPlayerKeyPressed", - _objectPath.toUtf8(), + _objectPath.toUtf8().constData(), nullptr, G_DBUS_SIGNAL_FLAGS_NONE, KeyPressed, @@ -150,9 +150,9 @@ GSDMediaKeys::~GSDMediaKeys() { if (_grabbed) { auto reply = g_dbus_connection_call_sync( _dbusConnection, - _service.toUtf8(), - _objectPath.toUtf8(), - _interface.toUtf8(), + _service.toUtf8().constData(), + _objectPath.toUtf8().constData(), + _interface.toUtf8().constData(), "ReleaseMediaPlayerKeys", g_variant_new( "(s)", diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index 96c725c7b..eaede478f 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -310,9 +310,7 @@ GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QStr d.reset(new QGtkDialog(gtk_file_chooser_dialog_new("", nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, - // https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new - // first_button_text doesn't need explicit conversion to char*, while all others are vardict - tr::lng_cancel(tr::now).toUtf8(), GTK_RESPONSE_CANCEL, + tr::lng_cancel(tr::now).toUtf8().constData(), GTK_RESPONSE_CANCEL, tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr))); d.data()->accept( @@ -400,7 +398,7 @@ bool GtkFileDialog::defaultNameFilterDisables() const { void GtkFileDialog::setDirectory(const QString &directory) { GtkDialog *gtkDialog = d->gtkDialog(); - gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), directory.toUtf8()); + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), directory.toUtf8().constData()); } QDir GtkFileDialog::directory() const { @@ -511,7 +509,7 @@ GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileD void GtkFileDialog::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); - gtk_window_set_title(gtk_window_cast(gtkDialog), _windowTitle.toUtf8()); + gtk_window_set_title(gtk_window_cast(gtkDialog), _windowTitle.toUtf8().constData()); gtk_file_chooser_set_local_only(gtk_file_chooser_cast(gtkDialog), true); const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode); @@ -532,12 +530,12 @@ void GtkFileDialog::applyOptions() { for_const (const auto &filename, _initialFiles) { if (_acceptMode == QFileDialog::AcceptSave) { QFileInfo fi(filename); - gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8()); - gtk_file_chooser_set_current_name(gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8()); + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8().constData()); + gtk_file_chooser_set_current_name(gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8().constData()); } else if (filename.endsWith('/')) { - gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); + gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), filename.toUtf8().constData()); } else { - gtk_file_chooser_select_filename(gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); + gtk_file_chooser_select_filename(gtk_file_chooser_cast(gtkDialog), filename.toUtf8().constData()); } } @@ -549,19 +547,19 @@ void GtkFileDialog::applyOptions() { GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); if (acceptButton) { /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) - gtk_button_set_label(gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); + gtk_button_set_label(gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8().constData()); else*/ if (_acceptMode == QFileDialog::AcceptOpen) - gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8()); + gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8().constData()); else - gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8()); + gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8().constData()); } GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); if (rejectButton) { /*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) - gtk_button_set_label(gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); + gtk_button_set_label(gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8().constData()); else*/ - gtk_button_set_label(gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8()); + gtk_button_set_label(gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8().constData()); } } } @@ -579,7 +577,7 @@ void GtkFileDialog::setNameFilters(const QStringList &filters) { auto name = filter;//.left(filter.indexOf(QLatin1Char('('))); auto extensions = cleanFilterList(filter); - gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); + gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8().constData() : name.toUtf8().constData()); for_const (auto &ext, extensions) { auto caseInsensitiveExt = QString(); caseInsensitiveExt.reserve(4 * ext.size()); @@ -593,7 +591,7 @@ void GtkFileDialog::setNameFilters(const QStringList &filters) { } } - gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8()); + gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8().constData()); } gtk_file_chooser_add_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter); diff --git a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp index 63ffba923..5e87f10e8 100644 --- a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp @@ -49,7 +49,7 @@ private: }; OpenWithDialog::OpenWithDialog(const QString &filepath) -: _gfileInstance(g_file_new_for_path(filepath.toUtf8())) +: _gfileInstance(g_file_new_for_path(filepath.toUtf8().constData())) , _gtkWidget(gtk_app_chooser_dialog_new( nullptr, GTK_DIALOG_MODAL, diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 4cfc4b0d0..367ec0a3e 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -537,9 +537,9 @@ void MainWindow::initHook() { G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, - kSNIWatcherService.utf8(), - kSNIWatcherObjectPath.utf8(), - kSNIWatcherInterface.utf8(), + kSNIWatcherService.utf8().constData(), + kSNIWatcherObjectPath.utf8().constData(), + kSNIWatcherInterface.utf8().constData(), nullptr, nullptr); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 5d90b2e4f..fc856bfe2 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -363,10 +363,10 @@ NotificationData::NotificationData( _notificationRepliedSignalId = g_dbus_connection_signal_subscribe( _dbusConnection, - kService.utf8(), - kInterface.utf8(), + kService.utf8().constData(), + kInterface.utf8().constData(), "NotificationReplied", - kObjectPath.utf8(), + kObjectPath.utf8().constData(), nullptr, G_DBUS_SIGNAL_FLAGS_NONE, signalEmitted, @@ -380,10 +380,10 @@ NotificationData::NotificationData( _actionInvokedSignalId = g_dbus_connection_signal_subscribe( _dbusConnection, - kService.utf8(), - kInterface.utf8(), + kService.utf8().constData(), + kInterface.utf8().constData(), "ActionInvoked", - kObjectPath.utf8(), + kObjectPath.utf8().constData(), nullptr, G_DBUS_SIGNAL_FLAGS_NONE, signalEmitted, @@ -420,14 +420,14 @@ NotificationData::NotificationData( _hints.emplace( qsl("desktop-entry"), - g_variant_new_string(GetLauncherBasename().toUtf8())); + g_variant_new_string(GetLauncherBasename().toUtf8().constData())); _notificationClosedSignalId = g_dbus_connection_signal_subscribe( _dbusConnection, - kService.utf8(), - kInterface.utf8(), + kService.utf8().constData(), + kInterface.utf8().constData(), "NotificationClosed", - kObjectPath.utf8(), + kObjectPath.utf8().constData(), nullptr, G_DBUS_SIGNAL_FLAGS_NONE, signalEmitted, @@ -497,9 +497,9 @@ void NotificationData::show() { g_dbus_connection_call( _dbusConnection, - kService.utf8(), - kObjectPath.utf8(), - kInterface.utf8(), + kService.utf8().constData(), + kObjectPath.utf8().constData(), + kInterface.utf8().constData(), "Notify", g_variant_new( "(susssasa{sv}i)", @@ -559,9 +559,9 @@ void NotificationData::notificationShown( void NotificationData::close() { g_dbus_connection_call( _dbusConnection, - kService.utf8(), - kObjectPath.utf8(), - kInterface.utf8(), + kService.utf8().constData(), + kObjectPath.utf8().constData(), + kInterface.utf8().constData(), "CloseNotification", g_variant_new("(u)", _notificationId), nullptr, diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index bb26a1bf0..a7c343ff0 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -743,7 +743,7 @@ void RegisterCustomScheme(bool force) { .arg(neededCommandlineBuilder); auto currentAppInfo = g_app_info_get_default_for_type( - kHandlerTypeName.utf8(), + kHandlerTypeName.utf8().constData(), true); if (currentAppInfo) { @@ -758,7 +758,7 @@ void RegisterCustomScheme(bool force) { } auto registeredAppInfoList = g_app_info_get_recommended_for_type( - kHandlerTypeName.utf8()); + kHandlerTypeName.utf8().constData()); for (auto l = registeredAppInfoList; l != nullptr; l = l->next) { const auto currentRegisteredAppInfo = reinterpret_cast<GAppInfo*>( @@ -781,15 +781,15 @@ void RegisterCustomScheme(bool force) { } auto newAppInfo = g_app_info_create_from_commandline( - neededCommandlineBuilder.toUtf8(), - AppName.utf8(), + neededCommandlineBuilder.toUtf8().constData(), + AppName.utf8().constData(), G_APP_INFO_CREATE_SUPPORTS_URIS, &error); if (newAppInfo) { g_app_info_set_as_default_for_type( newAppInfo, - kHandlerTypeName.utf8(), + kHandlerTypeName.utf8().constData(), &error); g_object_unref(newAppInfo); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 81038b936..26d871b09 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -107,7 +107,7 @@ PreparedFileThumbnail FinalizeFileThumbnail( bool isSticker) { prepared.name = isSticker ? qsl("thumb.webp") : qsl("thumb.jpg"); if (FileThumbnailUploadRequired(filemime, filesize)) { - const auto format = QByteArray(isSticker ? "WEBP" : "JPG"); + const auto format = isSticker ? "WEBP" : "JPG"; auto buffer = QBuffer(&prepared.bytes); prepared.image.save(&buffer, format, kThumbnailQuality); } diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 1b540b38e..a92ba8f1e 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 1b540b38ed78e9a3cba93e9ba4ce4525ab692277 +Subproject commit a92ba8f1ee41e8c56b3cfced8498b33ec270228e From 5b0ad9ec4df1b80fa5f77fea5657f2e351364145 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sun, 21 Feb 2021 08:07:43 +0400 Subject: [PATCH 323/396] Revert "Take in account device pixel ratio when setting window extents" This reverts commit d44f076f0baeb7696d19ddb6a8fce17c860c0a9f. --- Telegram/SourceFiles/window/window_title_qt.cpp | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp index 4815dc082..7063d3f29 100644 --- a/Telegram/SourceFiles/window/window_title_qt.cpp +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -144,7 +144,7 @@ void TitleWidgetQt::updateWindowExtents() { if (hasShadow()) { Ui::Platform::SetWindowExtents( window()->windowHandle(), - resizeArea() * cIntRetinaFactor()); + resizeArea()); _extentsSet = true; } else if (_extentsSet) { diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 492121950..cf3975366 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 492121950da14615bfab6e6c9b4415a50899ddde +Subproject commit cf3975366630de43a9a5b25d1a68c0fb6404291a From 19b6f6e9a193ba38ae0ad265d51f2b3398d2d476 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sun, 21 Feb 2021 08:08:21 +0400 Subject: [PATCH 324/396] Add script for uploading to Mac App Store. --- Telegram/build/mac_store_upload.sh | 73 ++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 Telegram/build/mac_store_upload.sh diff --git a/Telegram/build/mac_store_upload.sh b/Telegram/build/mac_store_upload.sh new file mode 100755 index 000000000..1c6e362b8 --- /dev/null +++ b/Telegram/build/mac_store_upload.sh @@ -0,0 +1,73 @@ +set -e +FullExecPath=$PWD +pushd `dirname $0` > /dev/null +FullScriptPath=`pwd` +popd > /dev/null + +if [ ! -d "$FullScriptPath/../../../DesktopPrivate" ]; then + echo "" + echo "This script is for building the production version of Telegram Desktop." + echo "" + echo "For building custom versions please visit the build instructions page at:" + echo "https://github.com/telegramdesktop/tdesktop/#build-instructions" + exit +fi + +Error () { + cd $FullExecPath + echo "$1" + exit 1 +} + +if [ ! -f "$FullScriptPath/target" ]; then + Error "Build target not found!" +fi + +while IFS='' read -r line || [[ -n "$line" ]]; do + BuildTarget="$line" +done < "$FullScriptPath/target" + +while IFS='' read -r line || [[ -n "$line" ]]; do + set $line + eval $1="$2" +done < "$FullScriptPath/version" + +VersionForPacker="$AppVersion" +if [ "$AlphaVersion" != "0" ]; then + AppVersion="$AlphaVersion" + AppVersionStrFull="${AppVersionStr}_${AlphaVersion}" + AlphaBetaParam="-alpha $AlphaVersion" + AlphaKeyFile="talpha_${AppVersion}_key" +elif [ "$BetaChannel" == "0" ]; then + AppVersionStrFull="$AppVersionStr" + AlphaBetaParam='' +else + AppVersionStrFull="$AppVersionStr.beta" + AlphaBetaParam='-beta' +fi + +echo "" +HomePath="$FullScriptPath/.." +if [ "$BuildTarget" != "macstore" ]; then + Error "Invalid target!" +fi +if [ "$AlphaVersion" != "0" ]; then + Error "Can't upload macstore alpha version!" +fi + +echo "Uploading version $AppVersionStrFull to Mac App Store.." +ProjectPath="$HomePath/../out" +ReleasePath="$ProjectPath/Release" +BinaryName="Telegram Lite" +DeployPath="$ReleasePath/deploy/$AppVersionStrMajor/$AppVersionStrFull" +PackageFile="$DeployPath/$BinaryName.pkg" + +set +e +xcrun altool --upload-app --username "$AC_USERNAME" --password "@keychain:AC_PASSWORD" -t macOS -f "$PackageFile" +set -e + +echo -en "\007"; +sleep 1; +echo -en "\007"; +sleep 1; +echo -en "\007"; From 53fd3fce84c722ac52ff9e6fe0032cc637dee3d1 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 22 Feb 2021 06:41:26 +0400 Subject: [PATCH 325/396] Allow initializing gtk integration before QApplication --- .../platform/linux/linux_gtk_integration.cpp | 47 ------------------ .../platform/linux/specific_linux.cpp | 48 +++++++++++++++++-- 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 4aa024d56..ae178a81b 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -13,11 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_gtk_file_dialog.h" #include "platform/linux/linux_open_with_dialog.h" -#include "platform/linux/specific_linux.h" -#include "ui/platform/ui_platform_utility.h" -#include "core/sandbox.h" -#include "core/core_settings.h" -#include "core/application.h" namespace Platform { namespace internal { @@ -47,30 +42,6 @@ bool GetImageFromClipboardSupported() { && (gdk_atom_intern != nullptr); } -void SetScaleFactor() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - const auto integration = GtkIntegration::Instance(); - const auto ratio = Core::Sandbox::Instance().devicePixelRatio(); - if (!integration || ratio > 1.) { - return; - } - - const auto scaleFactor = integration->scaleFactor().value_or(1); - if (scaleFactor == 1) { - return; - } - - LOG(("GTK scale factor: %1").arg(scaleFactor)); - cSetScreenScale(style::CheckScale(scaleFactor * 100)); - }); -} - -void DarkModeChanged() { - Core::Sandbox::Instance().customEnterFromEventLoop([] { - Core::App().settings().setSystemDarkMode(IsDarkMode()); - }); -} - } // namespace GtkIntegration::GtkIntegration() { @@ -161,24 +132,6 @@ void GtkIntegration::load() { LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_type", gtk_app_chooser_get_type); Loaded = true; - - SetScaleFactor(); - - BaseGtkIntegration::Instance()->connectToSetting( - "gtk-theme-name", - DarkModeChanged); - - if (BaseGtkIntegration::Instance()->checkVersion(3, 0, 0)) { - BaseGtkIntegration::Instance()->connectToSetting( - "gtk-application-prefer-dark-theme", - DarkModeChanged); - } - - if (BaseGtkIntegration::Instance()->checkVersion(3, 12, 0)) { - BaseGtkIntegration::Instance()->connectToSetting( - "gtk-decoration-layout", - Ui::Platform::NotifyTitleControlsLayoutChanged); - } } bool GtkIntegration::loaded() const { diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index a7c343ff0..73d1f94b5 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_linux_xcb_utilities.h" #include "base/platform/linux/base_linux_gtk_integration.h" +#include "ui/platform/ui_platform_utility.h" #include "platform/linux/linux_desktop_environment.h" #include "platform/linux/linux_gtk_integration.h" #include "platform/linux/linux_wayland_integration.h" @@ -17,9 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mainwindow.h" #include "storage/localstorage.h" +#include "core/sandbox.h" +#include "core/application.h" +#include "core/core_settings.h" #include "core/update_checker.h" #include "window/window_controller.h" -#include "core/application.h" #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "platform/linux/linux_notification_service_watcher.h" @@ -347,6 +350,28 @@ bool GenerateDesktopFile( } } +void SetGtkScaleFactor() { + const auto integration = GtkIntegration::Instance(); + const auto ratio = Core::Sandbox::Instance().devicePixelRatio(); + if (!integration || ratio > 1.) { + return; + } + + const auto scaleFactor = integration->scaleFactor().value_or(1); + if (scaleFactor == 1) { + return; + } + + LOG(("GTK scale factor: %1").arg(scaleFactor)); + cSetScreenScale(style::CheckScale(scaleFactor * 100)); +} + +void DarkModeChanged() { + Core::Sandbox::Instance().customEnterFromEventLoop([] { + Core::App().settings().setSystemDarkMode(IsDarkMode()); + }); +} + } // namespace void SetWatchingMediaKeys(bool watching) { @@ -851,9 +876,6 @@ bool OpenSystemSettings(SystemSettingsType type) { namespace ThirdParty { void start() { - DEBUG_LOG(("Icon theme: %1").arg(QIcon::themeName())); - DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName())); - if (const auto integration = BaseGtkIntegration::Instance()) { integration->load(); } @@ -862,6 +884,24 @@ void start() { integration->load(); } + SetGtkScaleFactor(); + + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-theme-name", + DarkModeChanged); + + if (BaseGtkIntegration::Instance()->checkVersion(3, 0, 0)) { + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-application-prefer-dark-theme", + DarkModeChanged); + } + + if (BaseGtkIntegration::Instance()->checkVersion(3, 12, 0)) { + BaseGtkIntegration::Instance()->connectToSetting( + "gtk-decoration-layout", + Ui::Platform::NotifyTitleControlsLayoutChanged); + } + // wait for interface announce to know if native window frame is supported if (const auto integration = WaylandIntegration::Instance()) { integration->waitForInterfaceAnnounce(); From 23c8d7f38f682d8ce30f616649d7605fe96979f5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 22 Feb 2021 08:10:58 +0400 Subject: [PATCH 326/396] Remove unneeded checks for GtkIntegration::loaded() Due to rearranged loading of gtk methods --- .../platform/linux/linux_gtk_integration.cpp | 12 +++--------- .../platform/linux/linux_gtk_integration.h | 1 - .../platform/linux/linux_gtk_integration_dummy.cpp | 4 ---- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index ae178a81b..394a2fbb0 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -22,8 +22,6 @@ using BaseGtkIntegration = base::Platform::GtkIntegration; namespace { -bool Loaded = false; - QLibrary &Library() { return BaseGtkIntegration::Instance()->library(); } @@ -57,7 +55,8 @@ GtkIntegration *GtkIntegration::Instance() { } void GtkIntegration::load() { - Expects(!loaded()); + static bool Loaded = false; + Expects(!Loaded); if (!BaseGtkIntegration::Instance()->loaded()) { return; @@ -134,13 +133,8 @@ void GtkIntegration::load() { Loaded = true; } -bool GtkIntegration::loaded() const { - return Loaded; -} - std::optional<int> GtkIntegration::scaleFactor() const { - if (!loaded() - || (gdk_display_get_default == nullptr) + if ((gdk_display_get_default == nullptr) || (gdk_display_get_monitor == nullptr) || (gdk_display_get_primary_monitor == nullptr) || (gdk_monitor_get_scale_factor == nullptr)) { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h index 4a24bd388..ffd242c31 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.h @@ -17,7 +17,6 @@ public: static GtkIntegration *Instance(); void load(); - [[nodiscard]] bool loaded() const; [[nodiscard]] std::optional<int> scaleFactor() const; diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp index e27184daa..ac83bf3be 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration_dummy.cpp @@ -20,10 +20,6 @@ GtkIntegration *GtkIntegration::Instance() { void GtkIntegration::load() { } -bool GtkIntegration::loaded() const { - return false; -} - std::optional<int> GtkIntegration::scaleFactor() const { return std::nullopt; } From a40a8ac7eeeaab8e686304b18d9ac0bdc2abee49 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 22 Feb 2021 08:21:52 +0400 Subject: [PATCH 327/396] Use local variable for QLibrary from BaseGtkIntegration --- .../platform/linux/linux_gtk_integration.cpp | 126 +++++++++--------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index 394a2fbb0..b9f36c5fb 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -22,10 +22,6 @@ using BaseGtkIntegration = base::Platform::GtkIntegration; namespace { -QLibrary &Library() { - return BaseGtkIntegration::Instance()->library(); -} - bool GetImageFromClipboardSupported() { return (gtk_clipboard_get != nullptr) && (gtk_clipboard_wait_for_contents != nullptr) @@ -62,73 +58,75 @@ void GtkIntegration::load() { return; } - LOAD_GTK_SYMBOL(Library(), "gtk_widget_show", gtk_widget_show); - LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide", gtk_widget_hide); - LOAD_GTK_SYMBOL(Library(), "gtk_widget_get_window", gtk_widget_get_window); - LOAD_GTK_SYMBOL(Library(), "gtk_widget_realize", gtk_widget_realize); - LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete); - LOAD_GTK_SYMBOL(Library(), "gtk_widget_destroy", gtk_widget_destroy); - LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_get", gtk_clipboard_get); - LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_store", gtk_clipboard_store); - LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents); - LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image); - LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image); - LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_free", gtk_selection_data_free); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_type", gtk_file_chooser_get_type); - LOAD_GTK_SYMBOL(Library(), "gtk_image_get_type", gtk_image_get_type); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter); - LOAD_GTK_SYMBOL(Library(), "gtk_window_get_type", gtk_window_get_type); - LOAD_GTK_SYMBOL(Library(), "gtk_window_set_title", gtk_window_set_title); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_action", gtk_file_chooser_set_action); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter); - LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_set_name", gtk_file_filter_set_name); - LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename); - LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active); - LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_new", gtk_file_filter_new); - LOAD_GTK_SYMBOL(Library(), "gtk_image_new", gtk_image_new); - LOAD_GTK_SYMBOL(Library(), "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf); + auto &lib = BaseGtkIntegration::Instance()->library(); - LOAD_GTK_SYMBOL(Library(), "gdk_window_set_modal_hint", gdk_window_set_modal_hint); - LOAD_GTK_SYMBOL(Library(), "gdk_window_focus", gdk_window_focus); - LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_type", gtk_dialog_get_type); - LOAD_GTK_SYMBOL(Library(), "gtk_dialog_run", gtk_dialog_run); + LOAD_GTK_SYMBOL(lib, "gtk_widget_show", gtk_widget_show); + LOAD_GTK_SYMBOL(lib, "gtk_widget_hide", gtk_widget_hide); + LOAD_GTK_SYMBOL(lib, "gtk_widget_get_window", gtk_widget_get_window); + LOAD_GTK_SYMBOL(lib, "gtk_widget_realize", gtk_widget_realize); + LOAD_GTK_SYMBOL(lib, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete); + LOAD_GTK_SYMBOL(lib, "gtk_widget_destroy", gtk_widget_destroy); + LOAD_GTK_SYMBOL(lib, "gtk_clipboard_get", gtk_clipboard_get); + LOAD_GTK_SYMBOL(lib, "gtk_clipboard_store", gtk_clipboard_store); + LOAD_GTK_SYMBOL(lib, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents); + LOAD_GTK_SYMBOL(lib, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image); + LOAD_GTK_SYMBOL(lib, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image); + LOAD_GTK_SYMBOL(lib, "gtk_selection_data_free", gtk_selection_data_free); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_type", gtk_file_chooser_get_type); + LOAD_GTK_SYMBOL(lib, "gtk_image_get_type", gtk_image_get_type); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter); + LOAD_GTK_SYMBOL(lib, "gtk_window_get_type", gtk_window_get_type); + LOAD_GTK_SYMBOL(lib, "gtk_window_set_title", gtk_window_set_title); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_action", gtk_file_chooser_set_action); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter); + LOAD_GTK_SYMBOL(lib, "gtk_file_filter_set_name", gtk_file_filter_set_name); + LOAD_GTK_SYMBOL(lib, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename); + LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active); + LOAD_GTK_SYMBOL(lib, "gtk_file_filter_new", gtk_file_filter_new); + LOAD_GTK_SYMBOL(lib, "gtk_image_new", gtk_image_new); + LOAD_GTK_SYMBOL(lib, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf); - LOAD_GTK_SYMBOL(Library(), "gdk_atom_intern", gdk_atom_intern); + LOAD_GTK_SYMBOL(lib, "gdk_window_set_modal_hint", gdk_window_set_modal_hint); + LOAD_GTK_SYMBOL(lib, "gdk_window_focus", gdk_window_focus); + LOAD_GTK_SYMBOL(lib, "gtk_dialog_get_type", gtk_dialog_get_type); + LOAD_GTK_SYMBOL(lib, "gtk_dialog_run", gtk_dialog_run); - LOAD_GTK_SYMBOL(Library(), "gdk_display_get_default", gdk_display_get_default); - LOAD_GTK_SYMBOL(Library(), "gdk_display_get_monitor", gdk_display_get_monitor); - LOAD_GTK_SYMBOL(Library(), "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); - LOAD_GTK_SYMBOL(Library(), "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); + LOAD_GTK_SYMBOL(lib, "gdk_atom_intern", gdk_atom_intern); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_width", gdk_pixbuf_get_width); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_height", gdk_pixbuf_get_height); - LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); + LOAD_GTK_SYMBOL(lib, "gdk_display_get_default", gdk_display_get_default); + LOAD_GTK_SYMBOL(lib, "gdk_display_get_monitor", gdk_display_get_monitor); + LOAD_GTK_SYMBOL(lib, "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); + LOAD_GTK_SYMBOL(lib, "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); - GdkHelperLoad(Library()); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_width", gdk_pixbuf_get_width); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_height", gdk_pixbuf_get_height); + LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); - LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); - LOAD_GTK_SYMBOL(Library(), "gtk_button_set_label", gtk_button_set_label); - LOAD_GTK_SYMBOL(Library(), "gtk_button_get_type", gtk_button_get_type); + GdkHelperLoad(lib); - LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); - LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); - LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_type", gtk_app_chooser_get_type); + LOAD_GTK_SYMBOL(lib, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); + LOAD_GTK_SYMBOL(lib, "gtk_button_set_label", gtk_button_set_label); + LOAD_GTK_SYMBOL(lib, "gtk_button_get_type", gtk_button_get_type); + + LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); + LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); + LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_get_type", gtk_app_chooser_get_type); Loaded = true; } From 14b09e88d68a54038d5f1322c66d6c328c459624 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 22 Feb 2021 17:41:25 +0400 Subject: [PATCH 328/396] Add DESKTOP_APP_DISABLE_X11_INTEGRATION --- Telegram/CMakeLists.txt | 24 ++++++++++++------- .../platform/linux/linux_gdk_helper.cpp | 18 ++++++++++++++ .../platform/linux/main_window_linux.cpp | 11 +++++++-- .../platform/linux/specific_linux.cpp | 9 ++++++- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index b14b0aafa..aef5cce01 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -73,7 +73,6 @@ PRIVATE if (LINUX) target_link_libraries(Telegram PRIVATE - desktop-app::external_xcb desktop-app::external_glib ) @@ -85,6 +84,13 @@ if (LINUX) ) endif() + if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) + target_link_libraries(Telegram + PRIVATE + desktop-app::external_xcb + ) + endif() + if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) target_link_libraries(Telegram PRIVATE @@ -97,17 +103,19 @@ if (LINUX) if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY) pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0) - pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) + target_link_libraries(Telegram PRIVATE PkgConfig::GTK3) - target_link_libraries(Telegram - PRIVATE - PkgConfig::GTK3 - PkgConfig::X11 - ) + if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) + pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) + target_link_libraries(Telegram PRIVATE PkgConfig::X11) + endif() else() pkg_search_module(GTK REQUIRED gtk+-3.0 gtk+-2.0) target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS}) - target_link_libraries(Telegram PRIVATE X11) + + if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) + target_link_libraries(Telegram PRIVATE X11) + endif() endif() endif() endif() diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp index e7440da76..9a067ec80 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp @@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/linux/base_linux_gtk_integration_p.h" #include "platform/linux/linux_gtk_integration_p.h" +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION extern "C" { #undef signals #include <gdk/gdkx.h> #define signals public } // extern "C" +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION namespace Platform { namespace internal { @@ -29,6 +31,7 @@ enum class GtkLoaded { GtkLoaded gdk_helper_loaded = GtkLoaded::GtkNone; +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION // To be able to compile with gtk-3.0 headers as well #define GdkDrawable GdkWindow @@ -57,8 +60,10 @@ f_gdk_x11_display_get_xdisplay gdk_x11_display_get_xdisplay = nullptr; using f_gdk_x11_window_get_xid = Window(*)(GdkWindow *window); f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr; +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION bool GdkHelperLoadGtk2(QLibrary &lib) { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION #ifdef LINK_TO_GTK return false; #else // LINK_TO_GTK @@ -66,14 +71,21 @@ bool GdkHelperLoadGtk2(QLibrary &lib) { if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false; return true; #endif // !LINK_TO_GTK +#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION + return false; +#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } bool GdkHelperLoadGtk3(QLibrary &lib) { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false; if (!LOAD_GTK_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false; if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false; if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false; return true; +#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION + return false; +#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } void GdkHelperLoad(QLibrary &lib) { @@ -86,10 +98,15 @@ void GdkHelperLoad(QLibrary &lib) { } bool GdkHelperLoaded() { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION return gdk_helper_loaded != GtkLoaded::GtkNone; +#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION + return true; +#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION } void XSetTransientForHint(GdkWindow *window, quintptr winId) { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION if (gdk_helper_loaded == GtkLoaded::Gtk2) { ::XSetTransientForHint(gdk_x11_drawable_get_xdisplay(window), gdk_x11_drawable_get_xid(window), @@ -101,6 +118,7 @@ void XSetTransientForHint(GdkWindow *window, quintptr winId) { winId); } } +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION } } // namespace internal diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 367ec0a3e..23b036f35 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -24,18 +24,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/window_session_controller.h" #include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_linux_xcb_utilities.h" #include "base/call_delayed.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/input_fields.h" #include "facades.h" #include "app.h" +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION +#include "base/platform/linux/base_linux_xcb_utilities.h" +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION + #include <QtCore/QSize> +#include <QtCore/QTemporaryFile> #include <QtGui/QWindow> #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include <QtCore/QTemporaryFile> #include <QtDBus/QDBusInterface> #include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnectionInterface> @@ -82,6 +85,7 @@ base::flat_map<int, QImage> TrayIconImageBack; QIcon TrayIcon; QString TrayIconThemeName, TrayIconName; +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION bool XCBSkipTaskbar(QWindow *window, bool set) { const auto connection = base::Platform::XCB::GetConnectionFromQt(); if (!connection) { @@ -131,11 +135,14 @@ bool XCBSkipTaskbar(QWindow *window, bool set) { return true; } +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION bool SkipTaskbar(QWindow *window, bool set) { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION if (!IsWayland()) { return XCBSkipTaskbar(window, set); } +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION return false; } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 73d1f94b5..d358cb0e8 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/specific_linux.h" #include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_linux_xcb_utilities.h" #include "base/platform/linux/base_linux_gtk_integration.h" #include "ui/platform/ui_platform_utility.h" #include "platform/linux/linux_desktop_environment.h" @@ -24,6 +23,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/update_checker.h" #include "window/window_controller.h" +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION +#include "base/platform/linux/base_linux_xcb_utilities.h" +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION + #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "platform/linux/linux_notification_service_watcher.h" #include "platform/linux/linux_gsd_media_keys.h" @@ -577,8 +580,12 @@ bool TrayIconSupported() { } bool SkipTaskbarSupported() { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION return !IsWayland() && base::Platform::XCB::IsSupportedByWM("_NET_WM_STATE_SKIP_TASKBAR"); +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION + + return false; } } // namespace Platform From 4becfe409a5f8daacf8168c312f4d8ba58b85eb2 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Mon, 22 Feb 2021 17:42:28 +0400 Subject: [PATCH 329/396] Add Haiku autostart & system settings support --- .../platform/linux/specific_linux.cpp | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index d358cb0e8..b7c14dc6e 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -110,14 +110,14 @@ QStringList ListDBusActivatableNames() { return Result; } -void PortalAutostart(bool autostart, bool silent = false) { +void PortalAutostart(bool start, bool silent = false) { if (cExeName().isEmpty()) { return; } QVariantMap options; options["reason"] = tr::lng_settings_auto_start(tr::now); - options["autostart"] = autostart; + options["autostart"] = start; options["commandline"] = QStringList{ cExeName(), qsl("-workdir"), @@ -610,7 +610,7 @@ void psActivateProcess(uint64 pid) { namespace { -QString getHomeDir() { +QString GetHomeDir() { const auto home = QString(g_get_home_dir()); if (!home.isEmpty() && !home.endsWith('/')) { @@ -620,12 +620,39 @@ QString getHomeDir() { return home; } +#ifdef __HAIKU__ +void HaikuAutostart(bool start) { + const auto home = GetHomeDir(); + if (home.isEmpty()) { + return; + } + + QFile file(home + "config/settings/boot/launch/telegram-desktop"); + if (start) { + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&file); + out + << "#!/bin/bash" << Qt::endl + << "cd /system/apps" << Qt::endl + << "./Telegram -autostart" << " &" << Qt::endl; + file.close(); + file.setPermissions(file.permissions() + | QFileDevice::ExeOwner + | QFileDevice::ExeGroup + | QFileDevice::ExeOther); + } + } else { + file.remove(); + } +} +#endif // __HAIKU__ + } // namespace QString psAppDataPath() { // Previously we used ~/.TelegramDesktop, so look there first. // If we find data there, we should still use it. - auto home = getHomeDir(); + auto home = GetHomeDir(); if (!home.isEmpty()) { auto oldPath = home + qsl(".TelegramDesktop/"); auto oldSettingsBase = oldPath + qsl("tdata/settings"); @@ -870,6 +897,9 @@ bool OpenSystemSettings(SystemSettingsType type) { } else if (DesktopEnvironment::IsMATE()) { add("mate-volume-control"); } +#ifdef __HAIKU__ + add("Media"); +#endif // __ HAIKU__ add("pavucontrol-qt"); add("pavucontrol"); add("alsamixergui"); @@ -930,11 +960,18 @@ void finish() { } // namespace Platform void psNewVersion() { +#ifndef __HAIKU__ Platform::InstallLauncher(); +#endif // __HAIKU__ Platform::RegisterCustomScheme(); } void psAutoStart(bool start, bool silent) { +#ifdef __HAIKU__ + HaikuAutostart(start); + return; +#endif // __HAIKU__ + if (InFlatpak()) { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION PortalAutostart(start, silent); From fd2e41024cfc9c9c33a4244e4a19b6e0fa889c05 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 19:58:01 +0400 Subject: [PATCH 330/396] Rearrange includes in XDP file dialog like in Qt dev branch currently --- .../SourceFiles/platform/linux/linux_xdp_file_dialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp index e3988783f..4c0eb6d65 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp @@ -12,21 +12,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "base/qt_adapters.h" -#include <QtCore/qeventloop.h> - -#include <QtDBus/QtDBus> #include <QDBusConnection> #include <QDBusMessage> #include <QDBusPendingCall> #include <QDBusPendingCallWatcher> #include <QDBusPendingReply> +#include <QDBusMetaType> +#include <QEventLoop> #include <QFile> #include <QMetaType> #include <QMimeType> #include <QMimeDatabase> #include <QRandomGenerator> #include <QWindow> +#include <QRegularExpression> namespace Platform { namespace FileDialog { From 7ada85aa5ae76ab89bb13afad951d02e762b25a2 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 20:04:08 +0400 Subject: [PATCH 331/396] Remove unneeded forward-declaration for wayland integration --- Telegram/SourceFiles/platform/linux/linux_wayland_integration.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h index 3a8705c08..b2f716184 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -class QWindow; - namespace Platform { namespace internal { From 40b776befc51bed97604088bd1d3385f2294cc0a Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Fri, 19 Feb 2021 22:41:03 +0400 Subject: [PATCH 332/396] Remove unneeded Q_DECLARE_METATYPE from specific_linux --- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index b7c14dc6e..b150b2d23 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -69,8 +69,6 @@ using BaseGtkIntegration = base::Platform::GtkIntegration; using Platform::internal::WaylandIntegration; using Platform::internal::GtkIntegration; -Q_DECLARE_METATYPE(QMargins); - namespace Platform { namespace { From 6d07d49659cfc3a119ab38f40c977d80c5679dde Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 20 Feb 2021 22:06:00 +0400 Subject: [PATCH 333/396] Check for AllowNativeWindowFrameToggle in Controller::verticalShadowTop --- Telegram/SourceFiles/window/window_controller.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index f76b5d697..b4a168f83 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -258,6 +258,7 @@ void Controller::showSettings() { int Controller::verticalShadowTop() const { return (Platform::NativeTitleRequiresShadow() + && Platform::AllowNativeWindowFrameToggle() && Core::App().settings().nativeWindowFrame()) ? st::lineWidth : 0; From ca0be2c3ffae64f7b6426e93f70cfcab75bba4d4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Feb 2021 23:42:19 +0400 Subject: [PATCH 334/396] Update submodules. --- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index 39776fab6..51709fe69 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 39776fab6cad042ef9d6382ad6b2f8a60fd872d6 +Subproject commit 51709fe6946b9a0dfc742fca5fd55e99e5ba35b0 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index cf3975366..7d18c8687 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit cf3975366630de43a9a5b25d1a68c0fb6404291a +Subproject commit 7d18c8687b536500c562c226c1224f3d07ed1e13 diff --git a/cmake b/cmake index 199bd8d0b..ac193a597 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 199bd8d0b21d4f5c291a0d8b613bd0b4eda05c7f +Subproject commit ac193a597d6b953f9869a240e21e275ce6e388cb From fe7cdd7c0ba41084a79aeec4ba89ade14eccaeab Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Feb 2021 12:31:35 +0400 Subject: [PATCH 335/396] Correctly update participant muted state locally. --- .../SourceFiles/calls/calls_group_call.cpp | 36 +++++++++++++++---- Telegram/SourceFiles/calls/calls_group_call.h | 4 +++ .../SourceFiles/calls/calls_group_members.cpp | 27 ++------------ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 5f03074c1..14bbe001d 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -74,6 +74,28 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000); } // namespace +[[nodiscard]] bool IsGroupCallAdmin( + not_null<PeerData*> peer, + not_null<UserData*> user) { + if (const auto chat = peer->asChat()) { + return chat->admins.contains(user) + || (chat->creator == user->bareId()); + } else if (const auto group = peer->asMegagroup()) { + if (const auto mgInfo = group->mgInfo.get()) { + if (mgInfo->creator == user) { + return true; + } + const auto i = mgInfo->lastAdmins.find(user); + if (i == mgInfo->lastAdmins.end()) { + return false; + } + const auto &rights = i->second.rights; + return rights.c_chatAdminRights().is_manage_call(); + } + } + return false; +} + GroupCall::GroupCall( not_null<Delegate*> delegate, not_null<PeerData*> peer, @@ -410,7 +432,12 @@ void GroupCall::applyParticipantLocally( if (!participant || !participant->ssrc) { return; } - const auto canSelfUnmute = participant->canSelfUnmute; + const auto canManageCall = _peer->canManageGroupCall(); + const auto isMuted = participant->muted || (mute && canManageCall); + const auto canSelfUnmute = !canManageCall + ? participant->canSelfUnmute + : (!mute || IsGroupCallAdmin(_peer, user)); + const auto isMutedByYou = mute && !canManageCall; const auto mutedCount = 0/*participant->mutedCount*/; using Flag = MTPDgroupCallParticipant::Flag; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) @@ -419,11 +446,8 @@ void GroupCall::applyParticipantLocally( ? Flag::f_volume_by_admin : Flag(0)) | (participant->lastActive ? Flag::f_active_date : Flag(0)) - | (!mute - ? Flag(0) - : _peer->canManageGroupCall() - ? Flag::f_muted - : Flag::f_muted_by_you); + | (isMuted ? Flag::f_muted : Flag(0)) + | (isMutedByYou ? Flag::f_muted_by_you : Flag(0)); _peer->groupCall()->applyUpdateChecked( MTP_updateGroupCallParticipants( inputCall(), diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index c038fd6c2..90d18839d 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -54,6 +54,10 @@ enum class MuteState { }); } +[[nodiscard]] bool IsGroupCallAdmin( + not_null<PeerData*> peer, + not_null<UserData*> user); + struct LevelUpdate { uint32 ssrc = 0; float value = 0.; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 70939e9b0..7229dad6a 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -366,11 +366,7 @@ void Row::updateState(const Data::GroupCall::Participant *participant) { setSpeaking(false); } else if (!participant->muted || (participant->sounding && participant->ssrc != 0)) { - setState(participant->mutedByMe - ? State::MutedByMe - : (participant->sounding || participant->speaking) - ? State::Active - : State::Inactive); + setState(participant->mutedByMe ? State::MutedByMe : State::Active); setSounding(participant->sounding && participant->ssrc != 0); setSpeaking(participant->speaking && participant->ssrc != 0); } else if (participant->canSelfUnmute) { @@ -1287,26 +1283,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu( st::groupCallPopupMenu); const auto muteState = real->state(); - const auto admin = [&] { - if (const auto chat = _peer->asChat()) { - return chat->admins.contains(user) - || (chat->creator == user->bareId()); - } else if (const auto group = _peer->asMegagroup()) { - if (const auto mgInfo = group->mgInfo.get()) { - if (mgInfo->creator == user) { - return true; - } - const auto i = mgInfo->lastAdmins.find(user); - if (i == mgInfo->lastAdmins.end()) { - return false; - } - const auto &rights = i->second.rights; - return rights.c_chatAdminRights().is_manage_call(); - } - } - return false; - }(); - + const auto admin = IsGroupCallAdmin(_peer, user); const auto session = &user->session(); const auto getCurrentWindow = [=]() -> Window::SessionController* { if (const auto window = Core::App().activeWindow()) { From 502a3ca70f8527f503b5f8482bdb17b47d26e1bc Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 2 Feb 2021 14:10:38 +0400 Subject: [PATCH 336/396] Update API scheme to layer 124. --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/Resources/tl/api.tl | 50 +++++++++++++++---- Telegram/SourceFiles/api/api_invite_links.cpp | 21 ++++---- Telegram/SourceFiles/api/api_sending.cpp | 12 +++-- Telegram/SourceFiles/api/api_updates.cpp | 11 +++- Telegram/SourceFiles/apiwrap.cpp | 6 ++- .../data/data_scheduled_messages.cpp | 9 +++- Telegram/SourceFiles/data/data_session.cpp | 3 +- .../export/data/export_data_types.cpp | 6 +-- .../export/output/export_output_html.cpp | 2 +- .../admin_log/history_admin_log_item.cpp | 15 +++++- .../inline_bots/inline_bot_send_data.cpp | 3 +- .../settings/settings_privacy_controllers.cpp | 3 +- .../support/support_autocomplete.cpp | 3 +- 14 files changed, 103 insertions(+), 43 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index dd78fbb46..00814986b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1170,7 +1170,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_members#other" = "{count} members, among them:"; "lng_channel_invite_private" = "This channel is private.\nPlease join it to continue viewing its content."; -"lng_group_invite_create" = "Create an invite link"; // #TODO links legacy +"lng_group_invite_create" = "Create an invite link"; "lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you."; "lng_group_invite_copied" = "Invite link copied to clipboard."; "lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already."; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 741268feb..45c0aa92c 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -127,8 +127,8 @@ chatForbidden#7328bdb id:int title:string = Chat; channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#f3474af6 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall = ChatFull; -channelFull#7a7de4f7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall = ChatFull; +chatFull#e22542a0 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl:flags.14?PeerHistoryTTL = ChatFull; +channelFull#fc048a06 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl:flags.24?PeerHistoryTTL = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -141,7 +141,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; -message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message; +message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message; messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message; messageMediaEmpty#3ded6320 = MessageMedia; @@ -231,7 +231,7 @@ inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; inputReportReasonFake#f5ddd6e7 = ReportReason; -userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; +userFull#a54475a7 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl:flags.14?PeerHistoryTTL = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -369,6 +369,7 @@ updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int updateChat#1330a196 chat_id:int = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update; updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update; +updatePeerHistoryTTL#1265be8 flags:# peer:Peer ttl:flags.0?PeerHistoryTTL = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -378,12 +379,12 @@ updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_mess updates.differenceTooLong#4afe8f6d pts:int = updates.Difference; updatesTooLong#e317af7e = Updates; -updateShortMessage#2296d2c8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates; -updateShortChatMessage#402d5dbb flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates; +updateShortMessage#faeff833 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates; +updateShortChatMessage#1157b858 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates; updateShort#78d4dec1 update:Update date:int = Updates; updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates; updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates; -updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector<MessageEntity> = Updates; +updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates; photos.photos#8dca6aa5 photos:Vector<Photo> users:Vector<User> = photos.Photos; photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> = photos.Photos; @@ -886,12 +887,18 @@ channelAdminLogEventActionDiscardGroupCall#db9f9140 call:InputGroupCall = Channe channelAdminLogEventActionParticipantMute#f92424d2 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantUnmute#e64429c0 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionToggleGroupCallSetting#56d6a247 join_muted:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantJoinByInvite#5cdada77 invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteDelete#5a50fca4 invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; @@ -1214,6 +1221,22 @@ messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector<int> = messages.AffectedFoundMessages; +peerHistoryTTLPM#cf622d96 flags:# my_oneside:flags.0?true my_ttl_period:flags.1?int peer_ttl_period:flags.2?int = PeerHistoryTTL; +peerHistoryTTL#3e11cee9 ttl_period:int = PeerHistoryTTL; + +chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; + +messages.exportedChatInvites#bdc62dcc count:int invites:Vector<ExportedChatInvite> users:Vector<User> = messages.ExportedChatInvites; + +messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector<User> = messages.ExportedChatInvite; +messages.exportedChatInviteReplaced#222600ef invite:ExportedChatInvite new_invite:ExportedChatInvite users:Vector<User> = messages.ExportedChatInvite; + +messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImporter> users:Vector<User> = messages.ChatInviteImporters; + +chatAdminWithInvites#deaa220c admin_id:int invites_count:int = ChatAdminWithInvites; + +messages.chatAdminsWithInvites#b69b72d7 admins:Vector<ChatAdminWithInvites> users:Vector<User> = messages.ChatAdminsWithInvites; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1373,7 +1396,7 @@ messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; -messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1469,6 +1492,13 @@ messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImport messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; +messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool; +messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; +messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; +messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.setHistoryTTL#cccb4721 flags:# pm_oneside:flags.0?true peer:InputPeer period:int = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1600,4 +1630,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; -// LAYER 123 +// LAYER 124 diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 34a79cc44..986ccaa08 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -92,19 +92,16 @@ void InviteLinks::performCreate( callbacks.push_back(std::move(done)); } - // #TODO links - //using Flag = MTPmessages_ExportChatInvite::Flag; - //_api->request(MTPmessages_ExportChatInvite( - // MTP_flags((revokeLegacyPermanent - // ? Flag::f_legacy_revoke_permanent - // : Flag(0)) - // | (expireDate ? Flag::f_expire_date : Flag(0)) - // | (usageLimit ? Flag::f_usage_limit : Flag(0))), - // peer->input, - // MTP_int(expireDate), - // MTP_int(usageLimit) + using Flag = MTPmessages_ExportChatInvite::Flag; _api->request(MTPmessages_ExportChatInvite( - peer->input + MTP_flags((revokeLegacyPermanent + ? Flag::f_legacy_revoke_permanent + : Flag(0)) + | (expireDate ? Flag::f_expire_date : Flag(0)) + | (usageLimit ? Flag::f_usage_limit : Flag(0))), + peer->input, + MTP_int(expireDate), + MTP_int(usageLimit) )).done([=](const MTPExportedChatInvite &result) { const auto callbacks = _createCallbacks.take(peer); const auto link = prepend(peer, result); diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index fea236ed2..076c6e43a 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -304,7 +304,8 @@ bool SendDice(Api::MessageToSend &message) { MTP_string(messagePostAuthor), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); @@ -453,7 +454,8 @@ void SendConfirmedFile( MTP_string(messagePostAuthor), MTP_long(groupId), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()); + MTPVector<MTPRestrictionReason>(), + MTPint()); // ttl_period if (itemToEdit) { itemToEdit->savePreviousMedia(); @@ -491,7 +493,8 @@ void SendConfirmedFile( MTP_string(messagePostAuthor), MTP_long(groupId), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()); + MTPVector<MTPRestrictionReason>(), + MTPint()); // ttl_period if (itemToEdit) { itemToEdit->savePreviousMedia(); @@ -533,7 +536,8 @@ void SendConfirmedFile( MTP_string(messagePostAuthor), MTP_long(groupId), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); // Voices can't be edited. diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 188e06a22..f7012632e 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -997,7 +997,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPstring(), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTP_int(d.vttl_period().value_or_empty())), MTPDmessage_ClientFlags(), NewMessageType::Unread); } break; @@ -1027,7 +1028,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPstring(), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTP_int(d.vttl_period().value_or_empty())), MTPDmessage_ClientFlags(), NewMessageType::Unread); } break; @@ -1825,6 +1827,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { } } break; + case mtpc_updatePeerHistoryTTL: { + const auto &d = update.c_updatePeerHistoryTTL(); + // #TODO ttl + } break; + case mtpc_updateNewEncryptedMessage: { auto &d = update.c_updateNewEncryptedMessage(); } break; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 6222b28bb..b5c9a7bc7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4129,7 +4129,8 @@ void ApiWrap::sendSharedContact( MTP_string(messagePostAuthor), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); @@ -4407,7 +4408,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) { MTP_string(messagePostAuthor), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); histories.sendRequest(history, requestType, [=](Fn<void()> finish) { diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index f6bb51527..75ebf0027 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -72,7 +72,8 @@ MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) { MTP_bytes(data.vpost_author().value_or_empty()), MTP_long(data.vgrouped_id().value_or_empty()), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()); + MTPVector<MTPRestrictionReason>(), + MTP_int(data.vttl_period().value_or_empty())); }); } @@ -178,6 +179,9 @@ void ScheduledMessages::sendNowSimpleMessage( | MTPDmessage::Flag::f_from_id | (local->replyToId() ? MTPDmessage::Flag::f_reply_to + : MTPDmessage::Flag(0)) + | (update.vttl_period() + ? MTPDmessage::Flag::f_ttl_period : MTPDmessage::Flag(0)); auto clientFlags = NewMessageClientFlags() | MTPDmessage_ClientFlag::f_local_history_entry; @@ -206,7 +210,8 @@ void ScheduledMessages::sendNowSimpleMessage( MTP_string(), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTP_int(update.vttl_period().value_or_empty())), clientFlags, NewMessageType::Unread); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index bcc4392b1..aa0a0e98e 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -4000,7 +4000,8 @@ void Session::insertCheckedServiceNotification( MTPstring(), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); } diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 52958126c..af4b0f6cf 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -643,9 +643,9 @@ std::pair<QString, QSize> WriteImageThumb( : largePath + postfix; const auto result = Output::File::PrepareRelativePath(basePath, thumb); if (!image.save( - basePath + result, - finalFormat.constData(), - finalQuality)) { + basePath + result, + finalFormat.constData(), + finalQuality)) { return {}; } return { result, finalSize }; diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 3935bbdba..fe87118ba 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -966,7 +966,7 @@ auto HtmlWriter::Wrap::pushMessage( + SerializeString(data.title) + "»") : (serviceFrom - + " fchanged group title to «" + + " changed group title to «" + SerializeString(data.title) + "»"); }, [&](const ActionChatEditPhoto &data) { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index cfac099bf..6c6f57517 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -86,7 +86,8 @@ MTPMessage PrepareLogMessage( | MTPDmessage::Flag::f_views | MTPDmessage::Flag::f_forwards //| MTPDmessage::Flag::f_reactions - | MTPDmessage::Flag::f_restriction_reason; + | MTPDmessage::Flag::f_restriction_reason + | MTPDmessage::Flag::f_ttl_period; return MTP_message( MTP_flags(data.vflags().v & ~removeFlags), MTP_int(newId), @@ -109,7 +110,8 @@ MTPMessage PrepareLogMessage( MTP_string(), MTP_long(0), // grouped_id //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()); + MTPVector<MTPRestrictionReason>(), + MTPint()); // ttl_period }); } @@ -968,6 +970,15 @@ void GenerateItems( createParticipantUnmute(data); }, [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) { createToggleGroupCallSetting(data); + }, [&](const MTPDchannelAdminLogEventActionParticipantJoinByInvite &data) { + }, [&](const MTPDchannelAdminLogEventActionExportedInviteDelete &data) { + }, [&](const MTPDchannelAdminLogEventActionExportedInviteRevoke &data) { + }, [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) { + // #TODO links + }, [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) { + // #TODO calls + }, [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) { + // #TODO ttl }); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index fff7c4f13..2088615ee 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -73,7 +73,8 @@ void SendDataCommon::addToHistory( MTP_string(postAuthor), MTPlong(), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()), + MTPVector<MTPRestrictionReason>(), + MTPint()), // ttl_period clientFlags, NewMessageType::Unread); } diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index efb774bcf..f31e59a60 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -167,7 +167,8 @@ AdminLog::OwnedItem GenerateForwardedItem( MTPstring(), // post_author MTPlong(), // grouped_id //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>() + MTPVector<MTPRestrictionReason>(), + MTPint() // ttl_period ).match([&](const MTPDmessage &data) { return history->makeMessage( data, diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp index d7ceabdd9..d601e5611 100644 --- a/Telegram/SourceFiles/support/support_autocomplete.cpp +++ b/Telegram/SourceFiles/support/support_autocomplete.cpp @@ -322,7 +322,8 @@ AdminLog::OwnedItem GenerateContactItem( MTP_string(), MTP_long(0), //MTPMessageReactions(), - MTPVector<MTPRestrictionReason>()); + MTPVector<MTPRestrictionReason>(), + MTPint()); // ttl_period const auto item = history->makeMessage( message.c_message(), MTPDmessage_ClientFlag::f_fake_history_item); From 9d3edb785c45b3af96841ce707b9a98d826de2ce Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 2 Feb 2021 14:43:38 +0400 Subject: [PATCH 337/396] Return rich invite links section. --- Telegram/SourceFiles/api/api_invite_links.cpp | 389 +++++++++--------- Telegram/SourceFiles/api/api_invite_links.h | 17 +- .../boxes/peers/edit_peer_info_box.cpp | 80 ++-- .../boxes/peers/edit_peer_invite_link.cpp | 33 +- .../boxes/peers/edit_peer_invite_links.cpp | 8 +- .../boxes/peers/edit_peer_type_box.cpp | 41 +- 6 files changed, 282 insertions(+), 286 deletions(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 986ccaa08..e43ad32a7 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -41,27 +41,26 @@ void RemovePermanent(PeerInviteLinks &links) { } // namespace -// #TODO links -//JoinedByLinkSlice ParseJoinedByLinkSlice( -// not_null<PeerData*> peer, -// const MTPmessages_ChatInviteImporters &slice) { -// auto result = JoinedByLinkSlice(); -// slice.match([&](const MTPDmessages_chatInviteImporters &data) { -// auto &owner = peer->session().data(); -// owner.processUsers(data.vusers()); -// result.count = data.vcount().v; -// result.users.reserve(data.vimporters().v.size()); -// for (const auto importer : data.vimporters().v) { -// importer.match([&](const MTPDchatInviteImporter &data) { -// result.users.push_back({ -// .user = owner.user(data.vuser_id().v), -// .date = data.vdate().v, -// }); -// }); -// } -// }); -// return result; -//} +JoinedByLinkSlice ParseJoinedByLinkSlice( + not_null<PeerData*> peer, + const MTPmessages_ChatInviteImporters &slice) { + auto result = JoinedByLinkSlice(); + slice.match([&](const MTPDmessages_chatInviteImporters &data) { + auto &owner = peer->session().data(); + owner.processUsers(data.vusers()); + result.count = data.vcount().v; + result.users.reserve(data.vimporters().v.size()); + for (const auto importer : data.vimporters().v) { + importer.match([&](const MTPDchatInviteImporter &data) { + result.users.push_back({ + .user = owner.user(data.vuser_id().v), + .date = data.vdate().v, + }); + }); + } + }); + return result; +} InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) { } @@ -216,51 +215,55 @@ void InviteLinks::performEdit( if (done) { callbacks.push_back(std::move(done)); } - // #TODO links - //using Flag = MTPmessages_EditExportedChatInvite::Flag; - //_api->request(MTPmessages_EditExportedChatInvite( - // MTP_flags((revoke ? Flag::f_revoked : Flag(0)) - // | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) - // | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), - // peer->input, - // MTP_string(link), - // MTP_int(expireDate), - // MTP_int(usageLimit) - //)).done([=](const MTPmessages_ExportedChatInvite &result) { - // const auto callbacks = _editCallbacks.take(key); - // const auto peer = key.peer; - // result.match([&](const MTPDmessages_exportedChatInvite &data) { - // _api->session().data().processUsers(data.vusers()); - // const auto link = parse(peer, data.vinvite()); - // auto i = _firstSlices.find(peer); - // if (i != end(_firstSlices)) { - // const auto j = ranges::find( - // i->second.links, - // key.link, - // &Link::link); - // if (j != end(i->second.links)) { - // if (link.revoked && !j->revoked) { - // i->second.links.erase(j); - // if (i->second.count > 0) { - // --i->second.count; - // } - // } else { - // *j = link; - // } - // } - // } - // for (const auto &callback : *callbacks) { - // callback(link); - // } - // _updates.fire(Update{ - // .peer = peer, - // .was = key.link, - // .now = link, - // }); - // }); - //}).fail([=](const RPCError &error) { - // _editCallbacks.erase(key); - //}).send(); + using Flag = MTPmessages_EditExportedChatInvite::Flag; + _api->request(MTPmessages_EditExportedChatInvite( + MTP_flags((revoke ? Flag::f_revoked : Flag(0)) + | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) + | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), + peer->input, + MTP_string(link), + MTP_int(expireDate), + MTP_int(usageLimit) + )).done([=](const MTPmessages_ExportedChatInvite &result) { + const auto callbacks = _editCallbacks.take(key); + const auto peer = key.peer; + result.match([&](const auto &data) { + _api->session().data().processUsers(data.vusers()); + const auto link = parse(peer, data.vinvite()); + auto i = _firstSlices.find(peer); + if (i != end(_firstSlices)) { + const auto j = ranges::find( + i->second.links, + key.link, + &Link::link); + if (j != end(i->second.links)) { + if (link.revoked && !j->revoked) { + i->second.links.erase(j); + if (i->second.count > 0) { + --i->second.count; + } + } else { + *j = link; + } + } + } + for (const auto &callback : *callbacks) { + callback(link); + } + _updates.fire(Update{ + .peer = peer, + .was = key.link, + .now = link, + }); + + using Replaced = MTPDmessages_exportedChatInviteReplaced; + if constexpr (Replaced::Is<decltype(data)>()) { + prepend(peer, data.vnew_invite()); + } + }); + }).fail([=](const RPCError &error) { + _editCallbacks.erase(key); + }).send(); } void InviteLinks::revoke( @@ -272,8 +275,13 @@ void InviteLinks::revoke( void InviteLinks::revokePermanent( not_null<PeerData*> peer, - Fn<void(Link)> done) { - performCreate(peer, std::move(done), true); + Fn<void()> done) { + const auto callback = [=](auto&&) { done(); }; + if (const auto permanent = lookupPermanent(peer)) { + performEdit(peer, permanent->link, callback, true); + } else { + performCreate(peer, callback, true); + } } void InviteLinks::destroy( @@ -294,24 +302,23 @@ void InviteLinks::destroy( if (done) { callbacks.push_back(std::move(done)); } - // #TODO links - //_api->request(MTPmessages_DeleteExportedChatInvite( - // peer->input, - // MTP_string(link) - //)).done([=](const MTPBool &result) { - // const auto callbacks = _deleteCallbacks.take(key); - // if (callbacks) { - // for (const auto &callback : *callbacks) { - // callback(); - // } - // } - // _updates.fire(Update{ - // .peer = peer, - // .was = key.link, - // }); - //}).fail([=](const RPCError &error) { - // _deleteCallbacks.erase(key); - //}).send(); + _api->request(MTPmessages_DeleteExportedChatInvite( + peer->input, + MTP_string(link) + )).done([=](const MTPBool &result) { + const auto callbacks = _deleteCallbacks.take(key); + if (callbacks) { + for (const auto &callback : *callbacks) { + callback(); + } + } + _updates.fire(Update{ + .peer = peer, + .was = key.link, + }); + }).fail([=](const RPCError &error) { + _deleteCallbacks.erase(key); + }).send(); } void InviteLinks::destroyAllRevoked( @@ -328,62 +335,61 @@ void InviteLinks::destroyAllRevoked( if (done) { callbacks.push_back(std::move(done)); } - // #TODO links - //_api->request(MTPmessages_DeleteRevokedExportedChatInvites( - // peer->input - //)).done([=](const MTPBool &result) { - // if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { - // for (const auto &callback : *callbacks) { - // callback(); - // } - // } - // _allRevokedDestroyed.fire_copy(peer); - //}).fail([=](const RPCError &error) { - //}).send(); + _api->request(MTPmessages_DeleteRevokedExportedChatInvites( + peer->input, + MTP_inputUserSelf() + )).done([=](const MTPBool &result) { + if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { + for (const auto &callback : *callbacks) { + callback(); + } + } + _allRevokedDestroyed.fire_copy(peer); + }).fail([=](const RPCError &error) { + }).send(); } void InviteLinks::requestLinks(not_null<PeerData*> peer) { if (_firstSliceRequests.contains(peer)) { return; } - // #TODO links - //const auto requestId = _api->request(MTPmessages_GetExportedChatInvites( - // MTP_flags(0), - // peer->input, - // MTPInputUser(), // admin_id - // MTPint(), // offset_date - // MTPstring(), // offset_link - // MTP_int(kFirstPage) - //)).done([=](const MTPmessages_ExportedChatInvites &result) { - // _firstSliceRequests.remove(peer); - // auto slice = parseSlice(peer, result); - // auto i = _firstSlices.find(peer); - // const auto permanent = (i != end(_firstSlices)) - // ? lookupPermanent(i->second) - // : nullptr; - // if (!permanent) { - // BringPermanentToFront(slice); - // const auto j = _firstSlices.emplace_or_assign( - // peer, - // std::move(slice)).first; - // if (const auto permanent = lookupPermanent(j->second)) { - // editPermanentLink(peer, permanent->link); - // } - // } else { - // RemovePermanent(slice); - // auto &existing = i->second.links; - // existing.erase(begin(existing) + 1, end(existing)); - // existing.insert( - // end(existing), - // begin(slice.links), - // end(slice.links)); - // i->second.count = std::max(slice.count, int(existing.size())); - // } - // notify(peer); - //}).fail([=](const RPCError &error) { - // _firstSliceRequests.remove(peer); - //}).send(); - //_firstSliceRequests.emplace(peer, requestId); + const auto requestId = _api->request(MTPmessages_GetExportedChatInvites( + MTP_flags(0), + peer->input, + MTP_inputUserSelf(), + MTPint(), // offset_date + MTPstring(), // offset_link + MTP_int(kFirstPage) + )).done([=](const MTPmessages_ExportedChatInvites &result) { + _firstSliceRequests.remove(peer); + auto slice = parseSlice(peer, result); + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; + if (!permanent) { + BringPermanentToFront(slice); + const auto j = _firstSlices.emplace_or_assign( + peer, + std::move(slice)).first; + if (const auto permanent = lookupPermanent(j->second)) { + editPermanentLink(peer, permanent->link); + } + } else { + RemovePermanent(slice); + auto &existing = i->second.links; + existing.erase(begin(existing) + 1, end(existing)); + existing.insert( + end(existing), + begin(slice.links), + end(slice.links)); + i->second.count = std::max(slice.count, int(existing.size())); + } + notify(peer); + }).fail([=](const RPCError &error) { + _firstSliceRequests.remove(peer); + }).send(); + _firstSliceRequests.emplace(peer, requestId); } std::optional<JoinedByLinkSlice> InviteLinks::lookupJoinedFirstSlice( @@ -444,23 +450,23 @@ rpl::producer<> InviteLinks::allRevokedDestroyed( } void InviteLinks::requestJoinedFirstSlice(LinkKey key) { - //if (_firstJoinedRequests.contains(key)) { // #TODO links - // return; - //} - //const auto requestId = _api->request(MTPmessages_GetChatInviteImporters( - // key.peer->input, - // MTP_string(key.link), - // MTP_int(0), // offset_date - // MTP_inputUserEmpty(), // offset_user - // MTP_int(kJoinedFirstPage) - //)).done([=](const MTPmessages_ChatInviteImporters &result) { - // _firstJoinedRequests.remove(key); - // _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); - // _joinedFirstSliceLoaded.fire_copy(key); - //}).fail([=](const RPCError &error) { - // _firstJoinedRequests.remove(key); - //}).send(); - //_firstJoinedRequests.emplace(key, requestId); + if (_firstJoinedRequests.contains(key)) { + return; + } + const auto requestId = _api->request(MTPmessages_GetChatInviteImporters( + key.peer->input, + MTP_string(key.link), + MTP_int(0), // offset_date + MTP_inputUserEmpty(), // offset_user + MTP_int(kJoinedFirstPage) + )).done([=](const MTPmessages_ChatInviteImporters &result) { + _firstJoinedRequests.remove(key); + _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); + _joinedFirstSliceLoaded.fire_copy(key); + }).fail([=](const RPCError &error) { + _firstJoinedRequests.remove(key); + }).send(); + _firstJoinedRequests.emplace(key, requestId); } void InviteLinks::setPermanent( @@ -548,27 +554,27 @@ auto InviteLinks::links(not_null<PeerData*> peer) const -> const Links & { const auto i = _firstSlices.find(peer); return (i != end(_firstSlices)) ? i->second : kEmpty; } -// #TODO links -//auto InviteLinks::parseSlice( -// not_null<PeerData*> peer, -// const MTPmessages_ExportedChatInvites &slice) const -> Links { -// auto i = _firstSlices.find(peer); -// const auto permanent = (i != end(_firstSlices)) -// ? lookupPermanent(i->second) -// : nullptr; -// auto result = Links(); -// slice.match([&](const MTPDmessages_exportedChatInvites &data) { -// peer->session().data().processUsers(data.vusers()); -// result.count = data.vcount().v; -// for (const auto &invite : data.vinvites().v) { -// const auto link = parse(peer, invite); -// if (!permanent || link.link != permanent->link) { -// result.links.push_back(link); -// } -// } -// }); -// return result; -//} + +auto InviteLinks::parseSlice( + not_null<PeerData*> peer, + const MTPmessages_ExportedChatInvites &slice) const -> Links { + auto i = _firstSlices.find(peer); + const auto permanent = (i != end(_firstSlices)) + ? lookupPermanent(i->second) + : nullptr; + auto result = Links(); + slice.match([&](const MTPDmessages_exportedChatInvites &data) { + peer->session().data().processUsers(data.vusers()); + result.count = data.vcount().v; + for (const auto &invite : data.vinvites().v) { + const auto link = parse(peer, invite); + if (!permanent || link.link != permanent->link) { + result.links.push_back(link); + } + } + }); + return result; +} auto InviteLinks::parse( not_null<PeerData*> peer, @@ -582,7 +588,7 @@ auto InviteLinks::parse( .expireDate = data.vexpire_date().value_or_empty(), .usageLimit = data.vusage_limit().value_or_empty(), .usage = data.vusage().value_or_empty(), - .permanent = true,//data.is_permanent(), // #TODO links + .permanent = data.is_permanent(), .revoked = data.is_revoked(), }; }); @@ -594,23 +600,22 @@ void InviteLinks::requestMoreLinks( const QString &lastLink, bool revoked, Fn<void(Links)> done) { - // #TODO links - //using Flag = MTPmessages_GetExportedChatInvites::Flag; - //_api->request(MTPmessages_GetExportedChatInvites( - // MTP_flags(Flag::f_offset_link - // | (revoked ? Flag::f_revoked : Flag(0))), - // peer->input, - // MTPInputUser(), // admin_id, - // MTP_int(lastDate), - // MTP_string(lastLink), - // MTP_int(kPerPage) - //)).done([=](const MTPmessages_ExportedChatInvites &result) { - // auto slice = parseSlice(peer, result); - // RemovePermanent(slice); - // done(std::move(slice)); - //}).fail([=](const RPCError &error) { - // done(Links()); - //}).send(); + using Flag = MTPmessages_GetExportedChatInvites::Flag; + _api->request(MTPmessages_GetExportedChatInvites( + MTP_flags(Flag::f_offset_link + | (revoked ? Flag::f_revoked : Flag(0))), + peer->input, + MTP_inputUserSelf(), + MTP_int(lastDate), + MTP_string(lastLink), + MTP_int(kPerPage) + )).done([=](const MTPmessages_ExportedChatInvites &result) { + auto slice = parseSlice(peer, result); + RemovePermanent(slice); + done(std::move(slice)); + }).fail([=](const RPCError &error) { + done(Links()); + }).send(); } void InviteLinks::editPermanentLink( diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index e7089dbcf..e84447a42 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -43,10 +43,10 @@ struct InviteLinkUpdate { QString was; std::optional<InviteLink> now; }; -// #TODO links -//[[nodiscard]] JoinedByLinkSlice ParseJoinedByLinkSlice( -// not_null<PeerData*> peer, -// const MTPmessages_ChatInviteImporters &slice); + +[[nodiscard]] JoinedByLinkSlice ParseJoinedByLinkSlice( + not_null<PeerData*> peer, + const MTPmessages_ChatInviteImporters &slice); class InviteLinks final { public: @@ -73,7 +73,7 @@ public: Fn<void(Link)> done = nullptr); void revokePermanent( not_null<PeerData*> peer, - Fn<void(Link)> done = nullptr); + Fn<void()> done = nullptr); void destroy( not_null<PeerData*> peer, const QString &link, @@ -124,10 +124,9 @@ private: } }; - // #TODO links - //[[nodiscard]] Links parseSlice( - // not_null<PeerData*> peer, - // const MTPmessages_ExportedChatInvites &slice) const; + [[nodiscard]] Links parseSlice( + not_null<PeerData*> peer, + const MTPmessages_ExportedChatInvites &slice) const; [[nodiscard]] Link parse( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) const; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 54dfa5334..bf6f6596c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -332,7 +332,7 @@ private: void showEditLinkedChatBox(); void fillPrivacyTypeButton(); void fillLinkedChatButton(); - void fillInviteLinkButton(); + //void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); void fillManageSection(); @@ -798,20 +798,20 @@ void Controller::fillLinkedChatButton() { [=] { showEditLinkedChatBox(); }); _linkedChatUpdates.fire_copy(*_linkedChatSavedValue); } - -void Controller::fillInviteLinkButton() { - Expects(_controls.buttonsLayout != nullptr); - - const auto buttonCallback = [=] { - Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther); - }; - - AddButtonWithText( - _controls.buttonsLayout, - tr::lng_profile_invite_link_section(), - rpl::single(QString()), //Empty text. - buttonCallback); -} +// +//void Controller::fillInviteLinkButton() { +// Expects(_controls.buttonsLayout != nullptr); +// +// const auto buttonCallback = [=] { +// Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther); +// }; +// +// AddButtonWithText( +// _controls.buttonsLayout, +// tr::lng_profile_invite_link_section(), +// rpl::single(QString()), //Empty text. +// buttonCallback); +//} void Controller::fillSignaturesButton() { Expects(_controls.buttonsLayout != nullptr); @@ -959,8 +959,8 @@ void Controller::fillManageSection() { if (canEditUsername) { fillPrivacyTypeButton(); - } else if (canEditInviteLinks) { - fillInviteLinkButton(); + //} else if (canEditInviteLinks) { + // fillInviteLinkButton(); } if (canViewOrEditLinkedChat) { fillLinkedChatButton(); @@ -995,29 +995,29 @@ void Controller::fillManageSection() { [=] { ShowEditPermissions(_navigation, _peer); }, st::infoIconPermissions); } - //if (canEditInviteLinks) { // #TODO links - // AddButtonWithCount( - // _controls.buttonsLayout, - // tr::lng_manage_peer_invite_links(), - // Info::Profile::MigratedOrMeValue( - // _peer - // ) | rpl::map([=](not_null<PeerData*> peer) { - // peer->session().api().inviteLinks().requestLinks(peer); - // return peer->session().changes().peerUpdates( - // peer, - // Data::PeerUpdate::Flag::InviteLinks - // ) | rpl::map([=] { - // return peer->session().api().inviteLinks().links( - // peer).count; - // }); - // }) | rpl::flatten_latest( - // ) | ToPositiveNumberString(), - // [=] { Ui::show( - // Box(ManageInviteLinksBox, _peer), - // Ui::LayerOption::KeepOther); - // }, - // st::infoIconInviteLinks); - //} + if (canEditInviteLinks) { + AddButtonWithCount( + _controls.buttonsLayout, + tr::lng_manage_peer_invite_links(), + Info::Profile::MigratedOrMeValue( + _peer + ) | rpl::map([=](not_null<PeerData*> peer) { + peer->session().api().inviteLinks().requestLinks(peer); + return peer->session().changes().peerUpdates( + peer, + Data::PeerUpdate::Flag::InviteLinks + ) | rpl::map([=] { + return peer->session().api().inviteLinks().links( + peer).count; + }); + }) | rpl::flatten_latest( + ) | ToPositiveNumberString(), + [=] { Ui::show( + Box(ManageInviteLinksBox, _peer), + Ui::LayerOption::KeepOther); + }, + st::infoIconInviteLinks); + } if (canViewAdmins) { AddButtonWithCount( _controls.buttonsLayout, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index df593d0d7..12af5097a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -222,22 +222,21 @@ void Controller::loadMoreRows() { if (_requestId || _allLoaded) { return; } - _allLoaded = true; // #TODO links - //_requestId = _api.request(MTPmessages_GetChatInviteImporters( - // _peer->input, - // MTP_string(_data.link), - // MTP_int(_lastUser ? _lastUser->date : 0), - // _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), - // MTP_int(_lastUser ? kPerPage : kFirstPage) - //)).done([=](const MTPmessages_ChatInviteImporters &result) { - // _requestId = 0; - // auto slice = Api::ParseJoinedByLinkSlice(_peer, result); - // _allLoaded = slice.users.empty(); - // appendSlice(slice); - //}).fail([=](const RPCError &error) { - // _requestId = 0; - // _allLoaded = true; - //}).send(); + _requestId = _api.request(MTPmessages_GetChatInviteImporters( + _peer->input, + MTP_string(_data.link), + MTP_int(_lastUser ? _lastUser->date : 0), + _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), + MTP_int(_lastUser ? kPerPage : kFirstPage) + )).done([=](const MTPmessages_ChatInviteImporters &result) { + _requestId = 0; + auto slice = Api::ParseJoinedByLinkSlice(_peer, result); + _allLoaded = slice.users.empty(); + appendSlice(slice); + }).fail([=](const RPCError &error) { + _requestId = 0; + _allLoaded = true; + }).send(); } void Controller::appendSlice(const Api::JoinedByLinkSlice &slice) { @@ -318,7 +317,7 @@ void AddPermanentLinkBlock( const auto revokeLink = crl::guard(weak, [=] { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto done = crl::guard(weak, [=] { - const auto close = [=](auto&&) { + const auto close = [=] { if (*box) { (*box)->closeBox(); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 4f8f08f5a..a28ea6fa1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -441,8 +441,10 @@ Controller::Controller(not_null<PeerData*> peer, bool revoked) delegate()->peerListRefreshRows(); } } else if (update.was.isEmpty()) { - prependRow(*update.now, now); - delegate()->peerListRefreshRows(); + if (!update.now->permanent || update.now->revoked) { + prependRow(*update.now, now); + delegate()->peerListRefreshRows(); + } } else { updateRow(*update.now, now); } @@ -581,6 +583,8 @@ void Controller::updateRow(const InviteLinkData &data, TimeId now) { real->update(data, now); checkExpiringTimer(real); delegate()->peerListUpdateRow(row); + } else if (_revoked) { + prependRow(data, now); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index d587ecbca..dcc40a5ee 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -186,20 +186,20 @@ void Controller::createContent() { _wrap->add(createUsernameEdit()); } - //using namespace Settings; // #TODO links - //AddSkip(_wrap.get()); - //_wrap->add(EditPeerInfoBox::CreateButton( - // _wrap.get(), - // tr::lng_group_invite_manage(), - // rpl::single(QString()), - // [=] { Ui::show( - // Box(ManageInviteLinksBox, _peer), - // Ui::LayerOption::KeepOther); - // }, - // st::manageGroupButton, - // &st::infoIconInviteLinks)); - //AddSkip(_wrap.get()); - //AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); + using namespace Settings; + AddSkip(_wrap.get()); + _wrap->add(EditPeerInfoBox::CreateButton( + _wrap.get(), + tr::lng_group_invite_manage(), + rpl::single(QString()), + [=] { Ui::show( + Box(ManageInviteLinksBox, _peer), + Ui::LayerOption::KeepOther); + }, + st::manageGroupButton, + &st::infoIconInviteLinks)); + AddSkip(_wrap.get()); + AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); if (_linkOnly) { _controls.inviteLinkWrap->show(anim::type::instant); @@ -564,9 +564,8 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { if (_privacySavedValue) { AddSkip(container); - AddSubsectionTitle(container, tr::lng_create_invite_link_title()); + AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); } - // tr::lng_create_permanent_link_title()); // #TODO links AddPermanentLinkBlock(container, _peer); AddSkip(container); @@ -577,16 +576,6 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { ? tr::lng_group_invite_about_permanent_group() : tr::lng_group_invite_about_permanent_channel())); - if (_peer->wasFullUpdated()) { - const auto link = _peer->isChat() - ? _peer->asChat()->inviteLink() - : _peer->asChannel()->inviteLink(); - if (link.isEmpty()) { - // #TODO links remove this auto-export link. - _peer->session().api().inviteLinks().revokePermanent(_peer); - } - } - return result; } From 4153603d0938e698e6252425b71dd006502dc7e4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 5 Feb 2021 17:22:02 +0400 Subject: [PATCH 338/396] Add ability to see invite links of other admins. --- Telegram/Resources/langs/lang.strings | 5 + Telegram/SourceFiles/api/api_invite_links.cpp | 86 ++++-- Telegram/SourceFiles/api/api_invite_links.h | 18 +- .../boxes/peers/edit_peer_info_box.cpp | 6 +- .../boxes/peers/edit_peer_invite_link.cpp | 86 +++--- .../boxes/peers/edit_peer_invite_link.h | 9 +- .../boxes/peers/edit_peer_invite_links.cpp | 284 +++++++++++++++--- .../boxes/peers/edit_peer_invite_links.h | 3 +- .../boxes/peers/edit_peer_type_box.cpp | 8 +- Telegram/SourceFiles/data/data_channel.cpp | 4 +- Telegram/SourceFiles/data/data_chat.cpp | 4 +- 11 files changed, 385 insertions(+), 128 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 00814986b..8c4c9892c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1216,6 +1216,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_custom_limit" = "Enter custom limit"; "lng_group_invite_usage_any" = "No limit"; "lng_group_invite_usage_custom" = "Custom"; +"lng_group_invite_other_title" = "Invite links created by other admins"; +"lng_group_invite_other_count#one" = "{count} invite link"; +"lng_group_invite_other_count#other" = "{count} invite links"; +"lng_group_invite_permanent_other" = "Permanent link of this admin"; +"lng_group_invite_other_list" = "Invite links created by this admin"; "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index e43ad32a7..84719efb7 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_invite_links.h" #include "data/data_peer.h" +#include "data/data_user.h" #include "data/data_chat.h" #include "data/data_channel.h" #include "data/data_session.h" @@ -103,7 +104,7 @@ void InviteLinks::performCreate( MTP_int(usageLimit) )).done([=](const MTPExportedChatInvite &result) { const auto callbacks = _createCallbacks.take(peer); - const auto link = prepend(peer, result); + const auto link = prepend(peer, peer->session().user(), result); if (callbacks) { for (const auto &callback : *callbacks) { callback(link); @@ -135,6 +136,7 @@ auto InviteLinks::lookupPermanent(const Links &links) const -> const Link* { auto InviteLinks::prepend( not_null<PeerData*> peer, + not_null<UserData*> admin, const MTPExportedChatInvite &invite) -> Link { const auto link = parse(peer, invite); auto i = _firstSlices.find(peer); @@ -144,7 +146,10 @@ auto InviteLinks::prepend( auto &links = i->second; const auto permanent = lookupPermanent(links); const auto hadPermanent = (permanent != nullptr); - auto updateOldPermanent = Update{ .peer = peer }; + auto updateOldPermanent = Update{ + .peer = peer, + .admin = admin, + }; if (link.permanent && hadPermanent) { updateOldPermanent.was = permanent->link; updateOldPermanent.now = *permanent; @@ -171,21 +176,34 @@ auto InviteLinks::prepend( if (updateOldPermanent.now) { _updates.fire(std::move(updateOldPermanent)); } - _updates.fire(Update{ .peer = peer, .now = link }); + _updates.fire(Update{ + .peer = peer, + .admin = admin, + .now = link + }); return link; } void InviteLinks::edit( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, TimeId expireDate, int usageLimit, Fn<void(Link)> done) { - performEdit(peer, link, std::move(done), false, expireDate, usageLimit); + performEdit( + peer, + admin, + link, + std::move(done), + false, + expireDate, + usageLimit); } void InviteLinks::performEdit( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void(Link)> done, bool revoke, @@ -202,15 +220,6 @@ void InviteLinks::performEdit( return; } - if (const auto permanent = revoke ? lookupPermanent(peer) : nullptr) { - if (permanent->link == link) { - // In case of revoking a permanent link - // we should just create a new one instead. - performCreate(peer, std::move(done), true); - return; - } - } - auto &callbacks = _editCallbacks[key]; if (done) { callbacks.push_back(std::move(done)); @@ -252,13 +261,14 @@ void InviteLinks::performEdit( } _updates.fire(Update{ .peer = peer, + .admin = admin, .was = key.link, .now = link, }); using Replaced = MTPDmessages_exportedChatInviteReplaced; if constexpr (Replaced::Is<decltype(data)>()) { - prepend(peer, data.vnew_invite()); + prepend(peer, admin, data.vnew_invite()); } }); }).fail([=](const RPCError &error) { @@ -268,17 +278,22 @@ void InviteLinks::performEdit( void InviteLinks::revoke( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void(Link)> done) { - performEdit(peer, link, std::move(done), true); + performEdit(peer, admin, link, std::move(done), true); } void InviteLinks::revokePermanent( not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link, Fn<void()> done) { const auto callback = [=](auto&&) { done(); }; - if (const auto permanent = lookupPermanent(peer)) { - performEdit(peer, permanent->link, callback, true); + if (!link.isEmpty()) { + performEdit(peer, admin, link, callback, true); + } else if (!admin->isSelf()) { + crl::on_main(&peer->session(), done); } else { performCreate(peer, callback, true); } @@ -286,6 +301,7 @@ void InviteLinks::revokePermanent( void InviteLinks::destroy( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void()> done) { const auto key = LinkKey{ peer, link }; @@ -314,6 +330,7 @@ void InviteLinks::destroy( } _updates.fire(Update{ .peer = peer, + .admin = admin, .was = key.link, }); }).fail([=](const RPCError &error) { @@ -323,6 +340,7 @@ void InviteLinks::destroy( void InviteLinks::destroyAllRevoked( not_null<PeerData*> peer, + not_null<UserData*> admin, Fn<void()> done) { if (const auto i = _deleteRevokedCallbacks.find(peer) ; i != end(_deleteRevokedCallbacks)) { @@ -337,7 +355,7 @@ void InviteLinks::destroyAllRevoked( } _api->request(MTPmessages_DeleteRevokedExportedChatInvites( peer->input, - MTP_inputUserSelf() + admin->inputUser )).done([=](const MTPBool &result) { if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { for (const auto &callback : *callbacks) { @@ -349,7 +367,7 @@ void InviteLinks::destroyAllRevoked( }).send(); } -void InviteLinks::requestLinks(not_null<PeerData*> peer) { +void InviteLinks::requestMyLinks(not_null<PeerData*> peer) { if (_firstSliceRequests.contains(peer)) { return; } @@ -469,7 +487,7 @@ void InviteLinks::requestJoinedFirstSlice(LinkKey key) { _firstJoinedRequests.emplace(key, requestId); } -void InviteLinks::setPermanent( +void InviteLinks::setMyPermanent( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) { auto link = parse(peer, invite); @@ -483,13 +501,17 @@ void InviteLinks::setPermanent( i = _firstSlices.emplace(peer).first; } auto &links = i->second; - auto updateOldPermanent = Update{ .peer = peer }; + auto updateOldPermanent = Update{ + .peer = peer, + .admin = peer->session().user(), + }; if (const auto permanent = lookupPermanent(links)) { if (permanent->link == link.link) { if (permanent->usage != link.usage) { permanent->usage = link.usage; _updates.fire(Update{ .peer = peer, + .admin = peer->session().user(), .was = link.link, .now = *permanent }); @@ -512,10 +534,14 @@ void InviteLinks::setPermanent( if (updateOldPermanent.now) { _updates.fire(std::move(updateOldPermanent)); } - _updates.fire(Update{ .peer = peer, .now = link }); + _updates.fire(Update{ + .peer = peer, + .admin = peer->session().user(), + .now = link + }); } -void InviteLinks::clearPermanent(not_null<PeerData*> peer) { +void InviteLinks::clearMyPermanent(not_null<PeerData*> peer) { auto i = _firstSlices.find(peer); if (i == end(_firstSlices)) { return; @@ -526,7 +552,10 @@ void InviteLinks::clearPermanent(not_null<PeerData*> peer) { return; } - auto updateOldPermanent = Update{ .peer = peer }; + auto updateOldPermanent = Update{ + .peer = peer, + .admin = peer->session().user() + }; updateOldPermanent.was = permanent->link; updateOldPermanent.now = *permanent; updateOldPermanent.now->revoked = true; @@ -549,7 +578,7 @@ void InviteLinks::notify(not_null<PeerData*> peer) { Data::PeerUpdate::Flag::InviteLinks); } -auto InviteLinks::links(not_null<PeerData*> peer) const -> const Links & { +auto InviteLinks::myLinks(not_null<PeerData*> peer) const -> const Links & { static const auto kEmpty = Links(); const auto i = _firstSlices.find(peer); return (i != end(_firstSlices)) ? i->second : kEmpty; @@ -596,6 +625,7 @@ auto InviteLinks::parse( void InviteLinks::requestMoreLinks( not_null<PeerData*> peer, + not_null<UserData*> admin, TimeId lastDate, const QString &lastLink, bool revoked, @@ -605,14 +635,12 @@ void InviteLinks::requestMoreLinks( MTP_flags(Flag::f_offset_link | (revoked ? Flag::f_revoked : Flag(0))), peer->input, - MTP_inputUserSelf(), + admin->inputUser, MTP_int(lastDate), MTP_string(lastLink), MTP_int(kPerPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { - auto slice = parseSlice(peer, result); - RemovePermanent(slice); - done(std::move(slice)); + done(parseSlice(peer, result)); }).fail([=](const RPCError &error) { done(Links()); }).send(); diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index e84447a42..024f7510c 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -40,6 +40,7 @@ struct JoinedByLinkSlice { struct InviteLinkUpdate { not_null<PeerData*> peer; + not_null<UserData*> admin; QString was; std::optional<InviteLink> now; }; @@ -63,32 +64,38 @@ public: int usageLimit = 0); void edit( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, TimeId expireDate, int usageLimit, Fn<void(Link)> done = nullptr); void revoke( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void(Link)> done = nullptr); void revokePermanent( not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link, Fn<void()> done = nullptr); void destroy( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void()> done = nullptr); void destroyAllRevoked( not_null<PeerData*> peer, + not_null<UserData*> admin, Fn<void()> done = nullptr); - void setPermanent( + void setMyPermanent( not_null<PeerData*> peer, const MTPExportedChatInvite &invite); - void clearPermanent(not_null<PeerData*> peer); + void clearMyPermanent(not_null<PeerData*> peer); - void requestLinks(not_null<PeerData*> peer); - [[nodiscard]] const Links &links(not_null<PeerData*> peer) const; + void requestMyLinks(not_null<PeerData*> peer); + [[nodiscard]] const Links &myLinks(not_null<PeerData*> peer) const; [[nodiscard]] rpl::producer<JoinedByLinkSlice> joinedFirstSliceValue( not_null<PeerData*> peer, @@ -104,6 +111,7 @@ public: void requestMoreLinks( not_null<PeerData*> peer, + not_null<UserData*> admin, TimeId lastDate, const QString &lastLink, bool revoked, @@ -135,6 +143,7 @@ private: [[nodiscard]] const Link *lookupPermanent(const Links &links) const; Link prepend( not_null<PeerData*> peer, + not_null<UserData*> admin, const MTPExportedChatInvite &invite); void notify(not_null<PeerData*> peer); @@ -144,6 +153,7 @@ private: void performEdit( not_null<PeerData*> peer, + not_null<UserData*> admin, const QString &link, Fn<void(Link)> done, bool revoke, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index bf6f6596c..de3b9e09d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1002,18 +1002,18 @@ void Controller::fillManageSection() { Info::Profile::MigratedOrMeValue( _peer ) | rpl::map([=](not_null<PeerData*> peer) { - peer->session().api().inviteLinks().requestLinks(peer); + peer->session().api().inviteLinks().requestMyLinks(peer); return peer->session().changes().peerUpdates( peer, Data::PeerUpdate::Flag::InviteLinks ) | rpl::map([=] { - return peer->session().api().inviteLinks().links( + return peer->session().api().inviteLinks().myLinks( peer).count; }); }) | rpl::flatten_latest( ) | ToPositiveNumberString(), [=] { Ui::show( - Box(ManageInviteLinksBox, _peer), + Box(ManageInviteLinksBox, _peer, _peer->session().user()), Ui::LayerOption::KeepOther); }, st::infoIconInviteLinks); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 12af5097a..fe6fc00af 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -102,7 +102,7 @@ void AddHeaderBlock( ShareInviteLinkBox(peer, link); }); const auto revokeLink = crl::guard(weak, [=] { - RevokeLink(peer, link); + RevokeLink(peer, data.admin, data.link); }); const auto createMenu = [=] { @@ -285,33 +285,44 @@ Main::Session &SingleRowController::session() const { void AddPermanentLinkBlock( not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer) { - const auto computePermanentLink = [=] { - const auto &links = peer->session().api().inviteLinks().links( - peer).links; - const auto link = links.empty() ? nullptr : &links.front(); - return (link && link->permanent && !link->revoked) ? link : nullptr; + not_null<PeerData*> peer, + not_null<UserData*> admin, + rpl::producer<Api::InviteLink> fromList) { + struct LinkData { + QString link; + int usage = 0; }; - auto value = peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::InviteLinks - ) | rpl::map([=] { - const auto link = computePermanentLink(); - return link - ? std::make_tuple(link->link, link->usage) - : std::make_tuple(QString(), 0); - }) | rpl::distinct_until_changed( - ) | rpl::start_spawning(container->lifetime()); - + const auto value = container->lifetime().make_state< + rpl::variable<LinkData> + >(); + if (admin->isSelf()) { + *value = peer->session().changes().peerFlagsValue( + peer, + Data::PeerUpdate::Flag::InviteLinks + ) | rpl::map([=] { + const auto &links = peer->session().api().inviteLinks().myLinks( + peer).links; + const auto link = links.empty() ? nullptr : &links.front(); + return (link && link->permanent && !link->revoked) + ? LinkData{ link->link, link->usage } + : LinkData(); + }); + } else { + *value = std::move( + fromList + ) | rpl::map([](const Api::InviteLink &link) { + return LinkData{ link.link, link.usage }; + }); + } const auto weak = Ui::MakeWeak(container); const auto copyLink = crl::guard(weak, [=] { - if (const auto link = computePermanentLink()) { - CopyInviteLink(link->link); + if (const auto current = value->current(); !current.link.isEmpty()) { + CopyInviteLink(current.link); } }); const auto shareLink = crl::guard(weak, [=] { - if (const auto link = computePermanentLink()) { - ShareInviteLinkBox(peer, link->link); + if (const auto current = value->current(); !current.link.isEmpty()) { + ShareInviteLinkBox(peer, current.link); } }); const auto revokeLink = crl::guard(weak, [=] { @@ -322,18 +333,23 @@ void AddPermanentLinkBlock( (*box)->closeBox(); } }; - peer->session().api().inviteLinks().revokePermanent(peer, close); + peer->session().api().inviteLinks().revokePermanent( + peer, + admin, + value->current().link, + close); }); *box = Ui::show( Box<ConfirmBox>(tr::lng_group_invite_about_new(tr::now), done), Ui::LayerOption::KeepOther); }); - auto link = rpl::duplicate( - value - ) | rpl::map([=](QString link, int usage) { + auto link = value->value( + ) | rpl::map([=](const LinkData &data) { const auto prefix = qstr("https://"); - return link.startsWith(prefix) ? link.mid(prefix.size()) : link; + return data.link.startsWith(prefix) + ? data.link.mid(prefix.size()) + : data.link; }); const auto createMenu = [=] { auto result = base::make_unique_q<Ui::PopupMenu>(container); @@ -389,13 +405,12 @@ void AddPermanentLinkBlock( .userpics = state->cachedUserpics }; }; - std::move( - value - ) | rpl::map([=](QString link, int usage) { + value->value( + ) | rpl::map([=](const LinkData &data) { return peer->session().api().inviteLinks().joinedFirstSliceValue( peer, - link, - usage); + data.link, + data.usage); }) | rpl::flatten_latest( ) | rpl::start_with_next([=](const Api::JoinedByLinkSlice &slice) { auto list = std::vector<HistoryView::UserpicInRow>(); @@ -540,7 +555,10 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) { Ui::LayerOption::KeepOther); } -void RevokeLink(not_null<PeerData*> peer, const QString &link) { +void RevokeLink( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link) { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto revoke = [=] { const auto done = [=](const LinkData &data) { @@ -548,7 +566,7 @@ void RevokeLink(not_null<PeerData*> peer, const QString &link) { (*box)->closeBox(); } }; - peer->session().api().inviteLinks().revoke(peer, link, done); + peer->session().api().inviteLinks().revoke(peer, admin, link, done); }; *box = Ui::show( Box<ConfirmBox>( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index abd353043..3cab90224 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -21,11 +21,16 @@ class VerticalLayout; void AddPermanentLinkBlock( not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer); + not_null<PeerData*> peer, + not_null<UserData*> admin, + rpl::producer<Api::InviteLink> fromList); void CopyInviteLink(const QString &link); void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link); -void RevokeLink(not_null<PeerData*> peer, const QString &link); +void RevokeLink( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link); void ShowInviteLinkBox( not_null<PeerData*> peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index a28ea6fa1..a1c2ab3f2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -8,6 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_invite_links.h" #include "data/data_peer.h" +#include "data/data_user.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_session.h" #include "main/main_session.h" #include "api/api_invite_links.h" #include "ui/boxes/edit_invite_link.h" @@ -167,7 +171,9 @@ private: return result; } -void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { +void EditLink( + not_null<PeerData*> peer, + const InviteLinkData &data) { const auto creating = data.link.isEmpty(); const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); using Fields = Ui::InviteLinkFields; @@ -181,6 +187,7 @@ void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { } }; if (creating) { + Assert(data.admin->isSelf()); peer->session().api().inviteLinks().create( peer, finish, @@ -189,6 +196,7 @@ void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { } else { peer->session().api().inviteLinks().edit( peer, + data.admin, result.link, result.expireDate, result.usageLimit, @@ -209,7 +217,10 @@ void EditLink(not_null<PeerData*> peer, const InviteLinkData &data) { Ui::LayerOption::KeepOther); } -void DeleteLink(not_null<PeerData*> peer, const QString &link) { +void DeleteLink( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link) { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto sure = [=] { const auto finish = [=] { @@ -217,14 +228,20 @@ void DeleteLink(not_null<PeerData*> peer, const QString &link) { (*box)->closeBox(); } }; - peer->session().api().inviteLinks().destroy(peer, link, finish); + peer->session().api().inviteLinks().destroy( + peer, + admin, + link, + finish); }; *box = Ui::show( Box<ConfirmBox>(tr::lng_group_invite_delete_sure(tr::now), sure), Ui::LayerOption::KeepOther); } -void DeleteAllRevoked(not_null<PeerData*> peer) { +void DeleteAllRevoked( + not_null<PeerData*> peer, + not_null<UserData*> admin) { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto sure = [=] { const auto finish = [=] { @@ -232,7 +249,10 @@ void DeleteAllRevoked(not_null<PeerData*> peer) { (*box)->closeBox(); } }; - peer->session().api().inviteLinks().destroyAllRevoked(peer, finish); + peer->session().api().inviteLinks().destroyAllRevoked( + peer, + admin, + finish); }; *box = Ui::show( Box<ConfirmBox>(tr::lng_group_invite_delete_all_sure(tr::now), sure), @@ -365,12 +385,15 @@ void Row::paintAction( : st::inviteLinkThreeDotsIcon).paint(p, x, y, outerWidth); } -class Controller final +class LinksController final : public PeerListController , public RowDelegate , public base::has_weak_ptr { public: - Controller(not_null<PeerData*> peer, bool revoked); + LinksController( + not_null<PeerData*> peer, + not_null<UserData*> admin, + bool revoked); void prepare() override; void loadMoreRows() override; @@ -390,6 +413,10 @@ public: float64 progress, Color color) override; + [[nodiscard]] rpl::producer<InviteLinkData> permanentFound() const { + return _permanentFound.events(); + } + private: void appendRow(const InviteLinkData &data, TimeId now); void prependRow(const InviteLinkData &data, TimeId now); @@ -405,6 +432,7 @@ private: not_null<PeerListRow*> row); const not_null<PeerData*> _peer; + const not_null<UserData*> _admin; const bool _revoked = false; base::unique_qptr<Ui::PopupMenu> _menu; @@ -413,6 +441,7 @@ private: bool _requesting = false; bool _allLoaded = false; + rpl::event_stream<InviteLinkData> _permanentFound; base::flat_set<not_null<Row*>> _expiringRows; base::Timer _updateExpiringTimer; @@ -421,8 +450,12 @@ private: }; -Controller::Controller(not_null<PeerData*> peer, bool revoked) +LinksController::LinksController( + not_null<PeerData*> peer, + not_null<UserData*> admin, + bool revoked) : _peer(peer) +, _admin(admin) , _revoked(revoked) , _updateExpiringTimer([=] { expiringProgressTimer(); }) { style::PaletteChanged( @@ -464,16 +497,16 @@ Controller::Controller(not_null<PeerData*> peer, bool revoked) } } -void Controller::prepare() { - if (!_revoked) { - appendSlice(_peer->session().api().inviteLinks().links(_peer)); +void LinksController::prepare() { + if (!_revoked && _admin->isSelf()) { + appendSlice(_peer->session().api().inviteLinks().myLinks(_peer)); } if (!delegate()->peerListFullRowsCount()) { loadMoreRows(); } } -void Controller::loadMoreRows() { +void LinksController::loadMoreRows() { if (_requesting || _allLoaded) { return; } @@ -491,16 +524,19 @@ void Controller::loadMoreRows() { }; _peer->session().api().inviteLinks().requestMoreLinks( _peer, + _admin, _offsetDate, _offsetLink, _revoked, crl::guard(this, done)); } -void Controller::appendSlice(const InviteLinksSlice &slice) { +void LinksController::appendSlice(const InviteLinksSlice &slice) { const auto now = base::unixtime::now(); for (const auto &link : slice.links) { - if (!link.permanent || link.revoked) { + if (link.permanent && !link.revoked) { + _permanentFound.fire_copy(link); + } else { appendRow(link, now); } _offsetLink = link.link; @@ -512,15 +548,15 @@ void Controller::appendSlice(const InviteLinksSlice &slice) { delegate()->peerListRefreshRows(); } -void Controller::rowClicked(not_null<PeerListRow*> row) { +void LinksController::rowClicked(not_null<PeerListRow*> row) { ShowInviteLinkBox(_peer, static_cast<Row*>(row.get())->data()); } -void Controller::rowActionClicked(not_null<PeerListRow*> row) { +void LinksController::rowActionClicked(not_null<PeerListRow*> row) { delegate()->peerListShowRowMenu(row, nullptr); } -base::unique_qptr<Ui::PopupMenu> Controller::rowContextMenu( +base::unique_qptr<Ui::PopupMenu> LinksController::rowContextMenu( QWidget *parent, not_null<PeerListRow*> row) { auto result = createRowContextMenu(parent, row); @@ -537,7 +573,7 @@ base::unique_qptr<Ui::PopupMenu> Controller::rowContextMenu( return result; } -base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( +base::unique_qptr<Ui::PopupMenu> LinksController::createRowContextMenu( QWidget *parent, not_null<PeerListRow*> row) { const auto real = static_cast<Row*>(row.get()); @@ -546,7 +582,7 @@ base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( auto result = base::make_unique_q<Ui::PopupMenu>(parent); if (data.revoked) { result->addAction(tr::lng_group_invite_context_delete(tr::now), [=] { - DeleteLink(_peer, link); + DeleteLink(_peer, _admin, link); }); } else { result->addAction(tr::lng_group_invite_context_copy(tr::now), [=] { @@ -559,25 +595,25 @@ base::unique_qptr<Ui::PopupMenu> Controller::createRowContextMenu( EditLink(_peer, data); }); result->addAction(tr::lng_group_invite_context_revoke(tr::now), [=] { - RevokeLink(_peer, link); + RevokeLink(_peer, _admin, link); }); } return result; } -Main::Session &Controller::session() const { +Main::Session &LinksController::session() const { return _peer->session(); } -void Controller::appendRow(const InviteLinkData &data, TimeId now) { +void LinksController::appendRow(const InviteLinkData &data, TimeId now) { delegate()->peerListAppendRow(std::make_unique<Row>(this, data, now)); } -void Controller::prependRow(const InviteLinkData &data, TimeId now) { +void LinksController::prependRow(const InviteLinkData &data, TimeId now) { delegate()->peerListPrependRow(std::make_unique<Row>(this, data, now)); } -void Controller::updateRow(const InviteLinkData &data, TimeId now) { +void LinksController::updateRow(const InviteLinkData &data, TimeId now) { if (const auto row = delegate()->peerListFindRow(ComputeRowId(data))) { const auto real = static_cast<Row*>(row); real->update(data, now); @@ -588,7 +624,7 @@ void Controller::updateRow(const InviteLinkData &data, TimeId now) { } } -bool Controller::removeRow(const QString &link) { +bool LinksController::removeRow(const QString &link) { if (const auto row = delegate()->peerListFindRow(ComputeRowId(link))) { delegate()->peerListRemoveRow(row); return true; @@ -596,7 +632,7 @@ bool Controller::removeRow(const QString &link) { return false; } -void Controller::checkExpiringTimer(not_null<Row*> row) { +void LinksController::checkExpiringTimer(not_null<Row*> row) { const auto updateIn = row->updateExpireIn(); if (updateIn > 0) { _expiringRows.emplace(row); @@ -609,7 +645,7 @@ void Controller::checkExpiringTimer(not_null<Row*> row) { } } -void Controller::expiringProgressTimer() { +void LinksController::expiringProgressTimer() { const auto now = base::unixtime::now(); auto minimalIn = 0; for (auto i = begin(_expiringRows); i != end(_expiringRows);) { @@ -629,11 +665,11 @@ void Controller::expiringProgressTimer() { } } -void Controller::rowUpdateRow(not_null<Row*> row) { +void LinksController::rowUpdateRow(not_null<Row*> row) { delegate()->peerListUpdateRow(row); } -void Controller::rowPaintIcon( +void LinksController::rowPaintIcon( QPainter &p, int x, int y, @@ -650,7 +686,7 @@ void Controller::rowPaintIcon( case Color::Expired: return &st::msgFile3Bg; case Color::Revoked: return &st::windowSubTextFg; } - Unexpected("Color in Controller::rowPaintIcon."); + Unexpected("Color in LinksController::rowPaintIcon."); }(); auto &icon = _icons[int(color)]; if (icon.isNull()) { @@ -687,17 +723,111 @@ void Controller::rowPaintIcon( } } +class AdminsController final + : public PeerListController + , public base::has_weak_ptr { +public: + AdminsController(not_null<PeerData*> peer, not_null<UserData*> admin); + ~AdminsController(); + + void prepare() override; + void loadMoreRows() override; + void rowClicked(not_null<PeerListRow*> row) override; + Main::Session &session() const override; + +private: + void appendRow(not_null<UserData*> user, int count); + + const not_null<PeerData*> _peer; + const not_null<UserData*> _admin; + mtpRequestId _requestId = 0; + +}; + +AdminsController::AdminsController( + not_null<PeerData*> peer, + not_null<UserData*> admin) +: _peer(peer) +, _admin(admin) { +} + +AdminsController::~AdminsController() { + session().api().request(base::take(_requestId)).cancel(); +} + +void AdminsController::prepare() { + if (const auto chat = _peer->asChat()) { + if (!chat->amCreator()) { + return; + } + } else if (const auto channel = _peer->asChannel()) { + if (!channel->amCreator()) { + return; + } + } + if (!_admin->isSelf()) { + return; + } + _requestId = session().api().request(MTPmessages_GetAdminsWithInvites( + _peer->input + )).done([=](const MTPmessages_ChatAdminsWithInvites &result) { + result.match([&](const MTPDmessages_chatAdminsWithInvites &data) { + auto &owner = _peer->owner(); + owner.processUsers(data.vusers()); + for (const auto &admin : data.vadmins().v) { + admin.match([&](const MTPDchatAdminWithInvites &data) { + const auto adminId = data.vadmin_id().v; + if (const auto user = owner.userLoaded(adminId)) { + if (!user->isSelf()) { + appendRow(user, data.vinvites_count().v); + } + } + }); + } + delegate()->peerListRefreshRows(); + }); + }).send(); +} + +void AdminsController::loadMoreRows() { +} + +void AdminsController::rowClicked(not_null<PeerListRow*> row) { + Ui::show( + Box(ManageInviteLinksBox, _peer, row->peer()->asUser()), + Ui::LayerOption::KeepOther); +} + +Main::Session &AdminsController::session() const { + return _peer->session(); +} + +void AdminsController::appendRow(not_null<UserData*> user, int count) { + auto row = std::make_unique<PeerListRow>(user); + row->setCustomStatus( + tr::lng_group_invite_other_count(tr::now, lt_count, count)); + delegate()->peerListAppendRow(std::move(row)); +} + } // namespace -not_null<Ui::RpWidget*> AddLinksList( +struct LinksList { + not_null<Ui::RpWidget*> widget; + rpl::producer<InviteLinkData> permanentFound; +}; + +LinksList AddLinksList( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, + not_null<UserData*> admin, bool revoked) { - const auto delegate = container->lifetime().make_state< + auto &lifetime = container->lifetime(); + const auto delegate = lifetime.make_state< PeerListContentDelegateSimple >(); - const auto controller = container->lifetime().make_state<Controller>( + const auto controller = lifetime.make_state<LinksController>( peer, + admin, revoked); controller->setStyleOverrides(&st::inviteLinkList); const auto content = container->add(object_ptr<PeerListContent>( @@ -706,27 +836,67 @@ not_null<Ui::RpWidget*> AddLinksList( delegate->setContent(content); controller->setDelegate(delegate); + return { content, controller->permanentFound() }; +} + +not_null<Ui::RpWidget*> AddAdminsList( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer, + not_null<UserData*> admin) { + auto &lifetime = container->lifetime(); + const auto delegate = lifetime.make_state< + PeerListContentDelegateSimple + >(); + const auto controller = lifetime.make_state<AdminsController>( + peer, + admin); + controller->setStyleOverrides(&st::inviteLinkList); + const auto content = container->add(object_ptr<PeerListContent>( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); + return content; } void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, - not_null<PeerData*> peer) { + not_null<PeerData*> peer, + not_null<UserData*> admin) { using namespace Settings; box->setTitle(tr::lng_group_invite_title()); const auto container = box->verticalLayout(); + const auto permanentFromList = box->lifetime().make_state< + rpl::event_stream<InviteLinkData> + >(); AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); - AddPermanentLinkBlock(container, peer); + AddPermanentLinkBlock( + container, + peer, + admin, + permanentFromList->events()); AddDivider(container); - const auto add = AddCreateLinkButton(container); - add->setClickedCallback([=] { - EditLink(peer, InviteLinkData{ .admin = peer->session().user() }); - }); + if (admin->isSelf()) { + const auto add = AddCreateLinkButton(container); + add->setClickedCallback([=] { + EditLink(peer, InviteLinkData{ .admin = admin }); + }); + } else { + AddSubsectionTitle(container, tr::lng_group_invite_other_list()); + } + + auto [list, newPermanent] = AddLinksList(container, peer, admin, false); + + std::move( + newPermanent + ) | rpl::start_with_next([=](InviteLinkData &&data) { + permanentFromList->fire(std::move(data)); + }, container->lifetime()); - const auto list = AddLinksList(container, peer, false); const auto dividerAbout = container->add(object_ptr<Ui::SlideWrap<>>( container, object_ptr<Ui::DividerLabel>( @@ -737,24 +907,37 @@ void ManageInviteLinksBox( st::boxDividerLabel), st::settingsDividerLabelPadding)), style::margins(0, st::inviteLinkCreateSkip, 0, 0)); - const auto divider = container->add(object_ptr<Ui::SlideWrap<>>( + + const auto adminsDivider = container->add(object_ptr<Ui::SlideWrap<>>( container, object_ptr<Ui::BoxContentDivider>(container))); - const auto header = container->add(object_ptr<Ui::SlideWrap<>>( + const auto adminsHeader = container->add(object_ptr<Ui::SlideWrap<>>( + container, + object_ptr<Ui::FlatLabel>( + container, + tr::lng_group_invite_other_title(), + st::settingsSubsectionTitle), + st::inviteLinkRevokedTitlePadding)); + const auto admins = AddAdminsList(container, peer, admin); + + const auto revokedDivider = container->add(object_ptr<Ui::SlideWrap<>>( + container, + object_ptr<Ui::BoxContentDivider>(container))); + const auto revokedHeader = container->add(object_ptr<Ui::SlideWrap<>>( container, object_ptr<Ui::FlatLabel>( container, tr::lng_group_invite_revoked_title(), st::settingsSubsectionTitle), st::inviteLinkRevokedTitlePadding)); - const auto revoked = AddLinksList(container, peer, true); + const auto revoked = AddLinksList(container, peer, admin, true).widget; const auto deleteAll = Ui::CreateChild<Ui::LinkButton>( container.get(), tr::lng_group_invite_context_delete_all(tr::now), st::defaultLinkButton); rpl::combine( - header->topValue(), + revokedHeader->topValue(), container->widthValue() ) | rpl::start_with_next([=](int top, int outerWidth) { deleteAll->moveToRight( @@ -763,18 +946,21 @@ void ManageInviteLinksBox( outerWidth); }, deleteAll->lifetime()); deleteAll->setClickedCallback([=] { - DeleteAllRevoked(peer); + DeleteAllRevoked(peer, admin); }); rpl::combine( list->heightValue(), + admins->heightValue(), revoked->heightValue() - ) | rpl::start_with_next([=](int list, int revoked) { + ) | rpl::start_with_next([=](int list, int admins, int revoked) { dividerAbout->toggle(!list, anim::type::instant); - divider->toggle(list > 0 && revoked > 0, anim::type::instant); - header->toggle(revoked > 0, anim::type::instant); + adminsDivider->toggle(admins > 0 && list > 0, anim::type::instant); + adminsHeader->toggle(admins > 0, anim::type::instant); + revokedDivider->toggle(revoked > 0 && (list > 0 || admins > 0), anim::type::instant); + revokedHeader->toggle(revoked > 0, anim::type::instant); deleteAll->setVisible(revoked > 0); - }, header->lifetime()); + }, revokedHeader->lifetime()); box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h index 09b2880c3..2f1050317 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h @@ -13,4 +13,5 @@ class PeerData; void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, - not_null<PeerData*> peer); + not_null<PeerData*> peer, + not_null<UserData*> admin); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index dcc40a5ee..52e11b80c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -193,7 +193,7 @@ void Controller::createContent() { tr::lng_group_invite_manage(), rpl::single(QString()), [=] { Ui::show( - Box(ManageInviteLinksBox, _peer), + Box(ManageInviteLinksBox, _peer, _peer->session().user()), Ui::LayerOption::KeepOther); }, st::manageGroupButton, @@ -566,7 +566,11 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() { AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); } - AddPermanentLinkBlock(container, _peer); + AddPermanentLinkBlock( + container, + _peer, + _peer->session().user(), + nullptr); AddSkip(container); diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 6bc11d7d5..72a9e7788 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -776,11 +776,11 @@ void ApplyChannelUpdate( next->v - channel->slowmodeSeconds()); } if (const auto invite = update.vexported_invite()) { - channel->session().api().inviteLinks().setPermanent( + channel->session().api().inviteLinks().setMyPermanent( channel, *invite); } else { - channel->session().api().inviteLinks().clearPermanent(channel); + channel->session().api().inviteLinks().clearMyPermanent(channel); } if (const auto location = update.vlocation()) { channel->setLocation(*location); diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index b0ec13d23..d554877ae 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -390,9 +390,9 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) { chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0))); } if (const auto invite = update.vexported_invite()) { - chat->session().api().inviteLinks().setPermanent(chat, *invite); + chat->session().api().inviteLinks().setMyPermanent(chat, *invite); } else { - chat->session().api().inviteLinks().clearPermanent(chat); + chat->session().api().inviteLinks().clearMyPermanent(chat); } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(chat, pinned->v); From 9262b773cbed4206dd71f191c2134a50e2d4ae51 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 5 Feb 2021 18:36:37 +0400 Subject: [PATCH 339/396] Fix invite link updates handling. --- Telegram/SourceFiles/api/api_invite_links.cpp | 17 +++++++++-------- Telegram/SourceFiles/api/api_invite_links.h | 13 ++++++++++--- .../boxes/peers/edit_peer_invite_link.cpp | 3 ++- .../boxes/peers/edit_peer_invite_links.cpp | 7 +++++-- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 84719efb7..a4d007f2a 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -362,7 +362,7 @@ void InviteLinks::destroyAllRevoked( callback(); } } - _allRevokedDestroyed.fire_copy(peer); + _allRevokedDestroyed.fire({ peer, admin }); }).fail([=](const RPCError &error) { }).send(); } @@ -452,19 +452,20 @@ rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue( } auto InviteLinks::updates( - not_null<PeerData*> peer) const -> rpl::producer<Update> { + not_null<PeerData*> peer, + not_null<UserData*> admin) const -> rpl::producer<Update> { return _updates.events() | rpl::filter([=](const Update &update) { - return update.peer == peer; + return update.peer == peer && update.admin == admin; }); } rpl::producer<> InviteLinks::allRevokedDestroyed( - not_null<PeerData*> peer) const { - using namespace rpl::mappers; + not_null<PeerData*> peer, + not_null<UserData*> admin) const { return _allRevokedDestroyed.events( - ) | rpl::filter( - _1 == peer - ) | rpl::to_empty; + ) | rpl::filter([=](const AllRevokedDestroyed &which) { + return which.peer == peer && which.admin == admin; + }) | rpl::to_empty; } void InviteLinks::requestJoinedFirstSlice(LinkKey key) { diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 024f7510c..df29731e4 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -105,9 +105,11 @@ public: not_null<PeerData*> peer, const QString &link) const; [[nodiscard]] rpl::producer<Update> updates( - not_null<PeerData*> peer) const; + not_null<PeerData*> peer, + not_null<UserData*> admin) const; [[nodiscard]] rpl::producer<> allRevokedDestroyed( - not_null<PeerData*> peer) const; + not_null<PeerData*> peer, + not_null<UserData*> admin) const; void requestMoreLinks( not_null<PeerData*> peer, @@ -189,7 +191,12 @@ private: std::vector<Fn<void()>>> _deleteRevokedCallbacks; rpl::event_stream<Update> _updates; - rpl::event_stream<not_null<PeerData*>> _allRevokedDestroyed; + + struct AllRevokedDestroyed { + not_null<PeerData*> peer; + not_null<UserData*> admin; + }; + rpl::event_stream<AllRevokedDestroyed> _allRevokedDestroyed; }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index fe6fc00af..035eaaf7b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -583,7 +583,8 @@ void ShowInviteLinkBox( ? tr::lng_manage_peer_link_permanent() : tr::lng_manage_peer_link_invite()); peer->session().api().inviteLinks().updates( - peer + peer, + link.admin ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { if (update.was == link.link && (!update.now || (!link.revoked && update.now->revoked))) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index a1c2ab3f2..8dec6db45 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -466,7 +466,8 @@ LinksController::LinksController( }, _lifetime); peer->session().api().inviteLinks().updates( - peer + peer, + admin ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { const auto now = base::unixtime::now(); if (!update.now || update.now->revoked != _revoked) { @@ -485,7 +486,8 @@ LinksController::LinksController( if (_revoked) { peer->session().api().inviteLinks().allRevokedDestroyed( - peer + peer, + admin ) | rpl::start_with_next([=] { _requesting = false; _allLoaded = true; @@ -621,6 +623,7 @@ void LinksController::updateRow(const InviteLinkData &data, TimeId now) { delegate()->peerListUpdateRow(row); } else if (_revoked) { prependRow(data, now); + delegate()->peerListRefreshRows(); } } From 046a3906c485c4c77122f5ec2a313aaee0cc273a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 2 Feb 2021 18:42:26 +0400 Subject: [PATCH 340/396] Destroy messages by ttl_period dates. --- Telegram/Resources/langs/lang.strings | 7 +++ Telegram/SourceFiles/data/data_session.cpp | 49 +++++++++++++++++++ Telegram/SourceFiles/data/data_session.h | 8 +++ Telegram/SourceFiles/history/history_item.h | 3 ++ .../SourceFiles/history/history_message.cpp | 35 +++++++++++++ .../SourceFiles/history/history_message.h | 8 +++ 6 files changed, 110 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 8c4c9892c..9c764943b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -975,6 +975,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_history_visibility_hidden_about" = "New members won't see earlier messages."; "lng_manage_history_visibility_hidden_legacy" = "New members won't see more than 100 previous messages."; +"lng_manage_messages_ttl_title" = "Auto-delete messages"; +"lng_manage_messages_ttl_never" = "Never"; +"lng_manage_messages_ttl_after1" = "After 24 hours"; +"lng_manage_messages_ttl_after2" = "After 7 days"; +"lng_manage_messages_ttl_about" = "Turning on this setting will make auto-delete messages from this group after the selected period."; +"lng_manage_messages_ttl_about_channel" = "Turning on this setting will make auto-delete messages from this channel after the selected period."; + "lng_report_title" = "Report channel"; "lng_report_group_title" = "Report group"; "lng_report_bot_title" = "Report bot"; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index aa0a0e98e..e7d83e8a7 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -220,6 +220,7 @@ Session::Session(not_null<Main::Session*> session) session->serverConfig().pinnedDialogsCountMax.value()) , _contactsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name) +, _ttlCheckTimer([=] { checkTTLs(); }) , _selfDestructTimer([=] { checkSelfDestructItems(); }) , _sendActionsAnimation([=](crl::time now) { return sendActionsAnimationCallback(now); @@ -1959,6 +1960,54 @@ void Session::registerMessage(not_null<HistoryItem*> item) { list->emplace(itemId, item); } +void Session::registerMessageTTL(TimeId when, not_null<HistoryItem*> item) { + Expects(when > 0); + + auto &list = _ttlMessages[when]; + list.emplace(item); + + const auto nearest = _ttlMessages.begin()->first; + if (nearest < when && _ttlCheckTimer.isActive()) { + return; + } + scheduleNextTTLs(); +} + +void Session::scheduleNextTTLs() { + if (_ttlMessages.empty()) { + return; + } + const auto nearest = _ttlMessages.begin()->first; + const auto now = base::unixtime::now(); + const auto timeout = (std::max(now, nearest) - now) * crl::time(1000); + _ttlCheckTimer.callOnce(timeout); +} + +void Session::unregisterMessageTTL( + TimeId when, + not_null<HistoryItem*> item) { + Expects(when > 0); + + const auto i = _ttlMessages.find(when); + if (i == end(_ttlMessages)) { + return; + } + auto &list = i->second; + list.erase(item); + if (list.empty()) { + _ttlMessages.erase(i); + } +} + +void Session::checkTTLs() { + _ttlCheckTimer.cancel(); + const auto now = base::unixtime::now(); + while (!_ttlMessages.empty() && _ttlMessages.begin()->first <= now) { + _ttlMessages.begin()->second.front()->destroy(); + } + scheduleNextTTLs(); +} + void Session::processMessagesDeleted( ChannelId channelId, const QVector<MTPint> &data) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 4192aff98..6914c33d0 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -328,6 +328,9 @@ public: void registerMessage(not_null<HistoryItem*> item); void unregisterMessage(not_null<HistoryItem*> item); + void registerMessageTTL(TimeId when, not_null<HistoryItem*> item); + void unregisterMessageTTL(TimeId when, not_null<HistoryItem*> item); + // Returns true if item found and it is not detached. bool checkEntitiesAndViewsUpdate(const MTPDmessage &data); void updateEditedMessage(const MTPMessage &data); @@ -686,6 +689,9 @@ private: void checkSelfDestructItems(); + void scheduleNextTTLs(); + void checkTTLs(); + int computeUnreadBadge(const Dialogs::UnreadState &state) const; bool computeUnreadBadgeMuted(const Dialogs::UnreadState &state) const; @@ -857,6 +863,8 @@ private: std::map< not_null<HistoryItem*>, base::flat_set<not_null<HistoryItem*>>> _dependentMessages; + std::map<TimeId, base::flat_set<not_null<HistoryItem*>>> _ttlMessages; + base::Timer _ttlCheckTimer; base::flat_map<uint64, FullMsgId> _messageByRandomId; base::flat_map<uint64, SentData> _sentMessagesData; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 008cb72cd..de50cd2f2 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -79,6 +79,9 @@ public: [[nodiscard]] virtual bool notificationReady() const { return true; } + [[nodiscard]] virtual TimeId ttlDestroyAt() const { + return 0; + } [[nodiscard]] PeerData *specialNotificationPeer() const; [[nodiscard]] UserData *viaBot() const; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 605483c67..36f557505 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -50,6 +50,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_window.h" +#include "base/call_delayed.h" // #TODO ttl +#include "base/unixtime.h" + #include <QtGui/QGuiApplication> #include <QtGui/QClipboard> @@ -523,6 +526,12 @@ HistoryMessage::HistoryMessage( setGroupId( MessageGroupId::FromRaw(history->peer->id, groupedId->v)); } + + if (const auto period = data.vttl_period()) { + if (period->v > 0) { + applyTTL(data.vdate().v + period->v); + } + } } HistoryMessage::HistoryMessage( @@ -1352,6 +1361,12 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) { clearReplies(); } + if (const auto period = message.vttl_period(); period && period->v > 0) { + applyTTL(message.vdate().v + period->v); + } else { + applyTTL(0); + } + finishEdition(keyboardTop); } @@ -1367,6 +1382,25 @@ void HistoryMessage::applyEdition(const MTPDmessageService &message) { } } +void HistoryMessage::applyTTL(TimeId destroyAt) { + const auto previousDestroyAt = std::exchange(_ttlDestroyAt, destroyAt); + if (previousDestroyAt) { + history()->owner().unregisterMessageTTL(previousDestroyAt, this); + } + if (!_ttlDestroyAt) { + return; + } else if (base::unixtime::now() >= _ttlDestroyAt) { + const auto session = &history()->session(); + crl::on_main(session, [session, id = fullId()]{ + if (const auto item = session->data().message(id)) { + item->destroy(); + } + }); + } else { + history()->owner().registerMessageTTL(_ttlDestroyAt, this); + } +} + void HistoryMessage::updateSentContent( const TextWithEntities &textWithEntities, const MTPMessageMedia *media) { @@ -1872,6 +1906,7 @@ std::unique_ptr<HistoryView::Element> HistoryMessage::createView( } HistoryMessage::~HistoryMessage() { + applyTTL(0); _media.reset(); clearSavedMedia(); if (auto reply = Get<HistoryMessageReply>()) { diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 5bfc295d7..3f8ce8d41 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -188,6 +188,10 @@ public: return replyToId(); } + [[nodiscard]] TimeId ttlDestroyAt() const override { + return _ttlDestroyAt; + } + // dynamic_cast optimization. [[nodiscard]] HistoryMessage *toHistoryMessage() override { return this; @@ -240,6 +244,8 @@ private: const TextWithEntities &textWithEntities) const; void reapplyText(); + void applyTTL(TimeId destroyAt); + [[nodiscard]] bool checkRepliesPts(const MTPMessageReplies &data) const; QString _timeText; @@ -247,6 +253,8 @@ private: mutable int32 _fromNameVersion = 0; + TimeId _ttlDestroyAt = 0; + friend class HistoryView::Element; friend class HistoryView::Message; From 6a6e355af4a4f6ebcc7fbfdeb8a4ee51ba4fe1ea Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 2 Feb 2021 20:38:30 +0400 Subject: [PATCH 341/396] Handle ttl_period locally. --- Telegram/SourceFiles/api/api_updates.cpp | 18 +-- .../boxes/peers/edit_peer_info_box.cpp | 117 +++++++++++++++++- Telegram/SourceFiles/data/data_channel.cpp | 3 + Telegram/SourceFiles/data/data_chat.cpp | 8 ++ Telegram/SourceFiles/data/data_chat.h | 21 ++-- Telegram/SourceFiles/data/data_peer.cpp | 29 +++++ Telegram/SourceFiles/data/data_peer.h | 34 +++-- Telegram/SourceFiles/data/data_session.cpp | 28 +---- Telegram/SourceFiles/data/data_user.cpp | 3 + Telegram/SourceFiles/history/history_item.cpp | 51 +++++++- Telegram/SourceFiles/history/history_item.h | 6 + .../SourceFiles/history/history_message.cpp | 27 +++- .../SourceFiles/history/history_message.h | 6 + 13 files changed, 282 insertions(+), 69 deletions(-) diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index f7012632e..d4a950d4b 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1331,14 +1331,7 @@ void Updates::applyUpdates( item->id, ApiWrap::RequestMessageDataCallback()); } - item->updateSentContent({ - sent.text, - Api::EntitiesFromMTP(&session(), list.value_or_empty()) - }, d.vmedia()); - item->contributeToSlowmode(d.vdate().v); - if (!wasAlready) { - item->indexAsNewItem(); - } + item->applySentMessage(sent.text, d, wasAlready); } } @@ -1829,7 +1822,14 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updatePeerHistoryTTL: { const auto &d = update.c_updatePeerHistoryTTL(); - // #TODO ttl + const auto peerId = peerFromMTP(d.vpeer()); + if (const auto peer = session().data().peerLoaded(peerId)) { + if (const auto ttl = d.vttl()) { + peer->applyMessagesTTL(*ttl); + } else { + peer->setMessagesTTL(0, 0, false); + } + } } break; case mtpc_updateNewEncryptedMessage: { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index de3b9e09d..44c8ab463 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" +#include "boxes/single_choice_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_type_box.h" @@ -307,13 +308,14 @@ private: Ui::UserpicButton *photo = nullptr; rpl::lifetime initialPhotoImageWaiting; Ui::VerticalLayout *buttonsLayout = nullptr; - Ui::SlideWrap<Ui::RpWidget> *historyVisibilityWrap = nullptr; + Ui::SlideWrap<> *historyVisibilityWrap = nullptr; }; struct Saving { std::optional<QString> username; std::optional<QString> title; std::optional<QString> description; std::optional<bool> hiddenPreHistory; + std::optional<TimeId> messagesTTL; std::optional<bool> signatures; std::optional<ChannelData*> linkedChat; }; @@ -335,6 +337,7 @@ private: //void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); + void fillSetMessagesTTLButton(); void fillManageSection(); void submitTitle(); @@ -348,6 +351,7 @@ private: bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; + bool validateMessagesTTL(Saving &to) const; bool validateSignatures(Saving &to) const; void save(); @@ -356,6 +360,7 @@ private: void saveTitle(); void saveDescription(); void saveHistoryVisibility(); + void saveMessagesTTL(); void saveSignatures(); void savePhoto(); void pushSaveStage(FnMut<void()> &&lambda); @@ -372,6 +377,7 @@ private: void migrate(not_null<ChannelData*> channel); std::optional<Privacy> _privacySavedValue; + std::optional<TimeId> _ttlSavedValue; std::optional<ChannelData*> _linkedChatSavedValue; ChannelData *_linkedChatOriginalValue = nullptr; bool _channelHasLocationOriginalValue = false; @@ -637,7 +643,7 @@ void Controller::refreshHistoryVisibility(anim::type animated) { && !_channelHasLocationOriginalValue && (!_linkedChatSavedValue || !*_linkedChatSavedValue)), animated); -}; +} void Controller::showEditPeerTypeBox( std::optional<rpl::producer<QString>> error) { @@ -880,6 +886,68 @@ void Controller::fillHistoryVisibilityButton() { refreshHistoryVisibility(anim::type::instant); } +void Controller::fillSetMessagesTTLButton() { + Expects(_controls.buttonsLayout != nullptr); + + _ttlSavedValue = _peer->messagesTTL(); + + const auto updateMessagesTTL = + std::make_shared<rpl::event_stream<TimeId>>(); + + const auto boxCallback = crl::guard(this, [=](TimeId value) { + updateMessagesTTL->fire_copy(value); + _ttlSavedValue = value; + }); + const auto buttonCallback = [=] { + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + const auto options = { + tr::lng_manage_messages_ttl_never(tr::now), + tr::lng_manage_messages_ttl_after1(tr::now), + tr::lng_manage_messages_ttl_after2(tr::now), + u"5 seconds"_q, AssertIsDebug() + }; + const auto initial = !*_ttlSavedValue + ? 0 + : (*_ttlSavedValue == 5) AssertIsDebug() + ? 3 AssertIsDebug() + : (*_ttlSavedValue < 3 * 86400) + ? 1 + : 2; + const auto callback = [=](int option) { + boxCallback(!option + ? 0 + : (option == 1) + ? 86400 + : (option == 3) AssertIsDebug() + ? 5 AssertIsDebug() + : 7 * 86400); + }; + SingleChoiceBox(box, { + .title = tr::lng_manage_messages_ttl_title(), + .options = options, + .initialSelection = initial, + .callback = callback, + }); + }), Ui::LayerOption::KeepOther); + }; + AddButtonWithText( + _controls.buttonsLayout, + tr::lng_manage_messages_ttl_title(), + updateMessagesTTL->events( + ) | rpl::map([](TimeId value) { + return !value + ? tr::lng_manage_messages_ttl_never() + : (value == 5) AssertIsDebug() + ? rpl::single<QString>("5 seconds") AssertIsDebug() + : (value < 3 * 86400) + ? tr::lng_manage_messages_ttl_after1() + : tr::lng_manage_messages_ttl_after2(); + }) | rpl::flatten_latest(), + buttonCallback); + + updateMessagesTTL->fire_copy(*_ttlSavedValue); +} + void Controller::fillManageSection() { Expects(_controls.buttonsLayout != nullptr); @@ -903,6 +971,11 @@ void Controller::fillManageSection() { ? channel->canEditPreHistoryHidden() : chat->canEditPreHistoryHidden(); }(); + const auto canSetMessagesTTL = [&] { + return isChannel + ? channel->canDeleteMessages() + : chat->canDeleteMessages(); + }(); const auto canEditPermissions = [&] { return isChannel @@ -965,17 +1038,21 @@ void Controller::fillManageSection() { if (canViewOrEditLinkedChat) { fillLinkedChatButton(); } - if (canEditSignatures) { - fillSignaturesButton(); - } if (canEditPreHistoryHidden) { fillHistoryVisibilityButton(); } + if (canSetMessagesTTL) { + fillSetMessagesTTLButton(); + } + if (canEditSignatures) { + fillSignaturesButton(); + } if (canEditPreHistoryHidden || canEditSignatures || canEditInviteLinks || canViewOrEditLinkedChat - || canEditUsername) { + || canEditUsername + || canSetMessagesTTL) { AddSkip( _controls.buttonsLayout, st::editPeerTopButtonsLayoutSkip, @@ -1132,6 +1209,7 @@ std::optional<Controller::Saving> Controller::validate() const { && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) + && validateMessagesTTL(result) && validateSignatures(result)) { return result; } @@ -1199,6 +1277,14 @@ bool Controller::validateHistoryVisibility(Saving &to) const { return true; } +bool Controller::validateMessagesTTL(Saving &to) const { + if (!_ttlSavedValue) { + return true; + } + to.messagesTTL = _ttlSavedValue; + return true; +} + bool Controller::validateSignatures(Saving &to) const { if (!_signaturesSavedValue.has_value()) { return true; @@ -1220,6 +1306,7 @@ void Controller::save() { pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); + pushSaveStage([=] { saveMessagesTTL(); }); pushSaveStage([=] { saveSignatures(); }); pushSaveStage([=] { savePhoto(); }); continueSave(); @@ -1427,6 +1514,24 @@ void Controller::saveHistoryVisibility() { [=] { cancelSave(); }); } +void Controller::saveMessagesTTL() { + if (!_savingData.messagesTTL + || *_savingData.messagesTTL == _peer->messagesTTL()) { + return continueSave(); + } + using Flag = MTPmessages_SetHistoryTTL::Flag; + _api.request(MTPmessages_SetHistoryTTL( + MTP_flags(_peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), + _peer->input, + MTP_int(*_savingData.messagesTTL) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + continueSave(); + }).fail([=](const RPCError &error) { + cancelSave(); + }).send(); +} + void Controller::togglePreHistoryHidden( not_null<ChannelData*> channel, bool hidden, diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 72a9e7788..051e78c9c 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -749,6 +749,9 @@ void ApplyChannelUpdate( channel->clearGroupCall(); } + if (const auto ttl = update.vttl()) { + channel->applyMessagesTTL(*ttl); + } channel->setFullFlags(update.vflags().v); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index d554877ae..bb1385b62 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -84,6 +84,11 @@ bool ChatData::canEditPreHistoryHidden() const { return amCreator(); } +bool ChatData::canDeleteMessages() const { + return amCreator() + || (adminRights() & AdminRight::f_delete_messages); +} + bool ChatData::canAddMembers() const { return amIn() && !amRestricted(Restriction::f_invite_users); } @@ -372,6 +377,9 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) { chat->clearGroupCall(); } + if (const auto ttl = update.vttl()) { + chat->applyMessagesTTL(*ttl); + } if (const auto info = update.vbot_info()) { for (const auto &item : info->v) { item.match([&](const MTPDbotInfo &data) { diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index d1c5d4343..b6ef3e5a0 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -128,16 +128,17 @@ public: [[nodiscard]] AdminRights defaultAdminRights(not_null<UserData*> user); // Like in ChannelData. - bool canWrite() const; - bool canEditInformation() const; - bool canEditPermissions() const; - bool canEditUsername() const; - bool canEditPreHistoryHidden() const; - bool canAddMembers() const; - bool canAddAdmins() const; - bool canBanMembers() const; - bool canSendPolls() const; - bool anyoneCanAddMembers() const; + [[nodiscard]] bool canWrite() const; + [[nodiscard]] bool canEditInformation() const; + [[nodiscard]] bool canEditPermissions() const; + [[nodiscard]] bool canEditUsername() const; + [[nodiscard]] bool canEditPreHistoryHidden() const; + [[nodiscard]] bool canDeleteMessages() const; + [[nodiscard]] bool canAddMembers() const; + [[nodiscard]] bool canAddAdmins() const; + [[nodiscard]] bool canBanMembers() const; + [[nodiscard]] bool canSendPolls() const; + [[nodiscard]] bool anyoneCanAddMembers() const; void applyEditAdmin(not_null<UserData*> user, bool isAdmin); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 5d36a346c..52c2c750f 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -945,6 +945,35 @@ void PeerData::setLoadedStatus(LoadedStatus status) { _loadedStatus = status; } +TimeId PeerData::messagesTTL() const { + return (_ttlMyPeriod && _ttlPeerPeriod) + ? std::min(_ttlMyPeriod, _ttlPeerPeriod) + : std::max(_ttlMyPeriod, _ttlPeerPeriod); +} + +void PeerData::setMessagesTTL( + TimeId myPeriod, + TimeId peerPeriod, + bool oneSide) { + _ttlMyPeriod = myPeriod; + _ttlPeerPeriod = peerPeriod; + _ttlOneSide = oneSide; +} + +void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { + ttl.match([&](const MTPDpeerHistoryTTL &data) { + setMessagesTTL( + data.vttl_period().v, + data.vttl_period().v, + false); + }, [&](const MTPDpeerHistoryTTLPM &data) { + setMessagesTTL( + data.vmy_ttl_period().value_or_empty(), + data.vpeer_ttl_period().value_or_empty(), + data.is_my_oneside()); + }); +} + namespace Data { std::vector<ChatRestrictions> ListOfRestrictions() { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 72b4593ee..26025bc11 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -17,6 +17,11 @@ class UserData; class ChatData; class ChannelData; +using ChatAdminRight = MTPDchatAdminRights::Flag; +using ChatRestriction = MTPDchatBannedRights::Flag; +using ChatAdminRights = MTPDchatAdminRights::Flags; +using ChatRestrictions = MTPDchatBannedRights::Flags; + namespace Ui { class EmptyUserpic; } // namespace Ui @@ -30,23 +35,13 @@ namespace Data { class Session; class GroupCall; +class CloudImageView; int PeerColorIndex(PeerId peerId); int PeerColorIndex(int32 bareId); style::color PeerUserpicColor(PeerId peerId); PeerId FakePeerIdForJustName(const QString &name); -} // namespace Data - -using ChatAdminRight = MTPDchatAdminRights::Flag; -using ChatRestriction = MTPDchatBannedRights::Flag; -using ChatAdminRights = MTPDchatAdminRights::Flags; -using ChatRestrictions = MTPDchatBannedRights::Flags; - -namespace Data { - -class CloudImageView; - class RestrictionCheckResult { public: [[nodiscard]] static RestrictionCheckResult Allowed() { @@ -386,6 +381,19 @@ public: } void setLoadedStatus(LoadedStatus status); + [[nodiscard]] TimeId myMessagesTTL() const { + return _ttlMyPeriod; + } + [[nodiscard]] TimeId peerMessagesTTL() const { + return _ttlPeerPeriod; + } + [[nodiscard]] bool oneSideTTL() const { + return _ttlOneSide; + } + [[nodiscard]] TimeId messagesTTL() const; + void setMessagesTTL(TimeId myPeriod, TimeId peerPeriod, bool oneSide); + void applyMessagesTTL(const MTPPeerHistoryTTL &ttl); + [[nodiscard]] Data::GroupCall *groupCall() const; const PeerId id; @@ -429,6 +437,10 @@ private: base::flat_set<QChar> _nameFirstLetters; crl::time _lastFullUpdate = 0; + + TimeId _ttlMyPeriod = 0; + TimeId _ttlPeerPeriod = 0; + bool _ttlOneSide = false; bool _hasPinnedMessages = false; Settings _settings = { kSettingsUnknown }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index e7d83e8a7..e3843b692 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1846,33 +1846,7 @@ bool Session::checkEntitiesAndViewsUpdate(const MTPDmessage &data) { if (!existing) { return false; } - existing->updateSentContent({ - qs(data.vmessage()), - Api::EntitiesFromMTP( - &session(), - data.ventities().value_or_empty()) - }, data.vmedia()); - existing->updateReplyMarkup(data.vreply_markup()); - existing->updateForwardedInfo(data.vfwd_from()); - existing->setViewsCount(data.vviews().value_or(-1)); - if (const auto replies = data.vreplies()) { - existing->setReplies(*replies); - } else { - existing->clearReplies(); - } - existing->setForwardsCount(data.vforwards().value_or(-1)); - if (const auto reply = data.vreply_to()) { - reply->match([&](const MTPDmessageReplyHeader &data) { - existing->setReplyToTop( - data.vreply_to_top_id().value_or( - data.vreply_to_msg_id().v)); - }); - } - existing->setPostAuthor(data.vpost_author().value_or_empty()); - existing->indexAsNewItem(); - existing->contributeToSlowmode(data.vdate().v); - requestItemTextRefresh(existing); - updateDependentMessages(existing); + existing->applySentMessage(data); const auto result = (existing->mainView() != nullptr); if (result) { stickers().checkSavedGif(existing); diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 5a865fde1..86114b894 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -256,6 +256,9 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { MTP_inputNotifyPeer(user->input), update.vnotify_settings()); + if (const auto ttl = update.vttl()) { + user->applyMessagesTTL(*ttl); + } if (const auto info = update.vbot_info()) { user->setBotInfo(*info); } else { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index f5857ca09..5d887c9dc 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "core/crash_reports.h" #include "base/unixtime.h" +#include "api/api_text_entities.h" #include "data/data_scheduled_messages.h" // kScheduledUntilOnlineTimestamp #include "data/data_changes.h" #include "data/data_session.h" @@ -490,6 +491,52 @@ void HistoryItem::applyEditionToHistoryCleared() { ).c_messageService()); } +void HistoryItem::applySentMessage(const MTPDmessage &data) { + updateSentContent({ + qs(data.vmessage()), + Api::EntitiesFromMTP( + &history()->session(), + data.ventities().value_or_empty()) + }, data.vmedia()); + updateReplyMarkup(data.vreply_markup()); + updateForwardedInfo(data.vfwd_from()); + setViewsCount(data.vviews().value_or(-1)); + if (const auto replies = data.vreplies()) { + setReplies(*replies); + } else { + clearReplies(); + } + setForwardsCount(data.vforwards().value_or(-1)); + if (const auto reply = data.vreply_to()) { + reply->match([&](const MTPDmessageReplyHeader &data) { + setReplyToTop( + data.vreply_to_top_id().value_or( + data.vreply_to_msg_id().v)); + }); + } + setPostAuthor(data.vpost_author().value_or_empty()); + contributeToSlowmode(data.vdate().v); + indexAsNewItem(); + history()->owner().requestItemTextRefresh(this); + history()->owner().updateDependentMessages(this); +} + +void HistoryItem::applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) { + updateSentContent({ + text, + Api::EntitiesFromMTP( + &history()->session(), + data.ventities().value_or_empty()) + }, data.vmedia()); + contributeToSlowmode(data.vdate().v); + if (!wasAlready) { + indexAsNewItem(); + } +} + void HistoryItem::indexAsNewItem() { if (IsServerMsgId(id)) { addToUnreadMentions(UnreadMentionType::New); @@ -629,9 +676,7 @@ bool HistoryItem::canDeleteForEveryone(TimeId now) const { } if (!out()) { if (const auto chat = peer->asChat()) { - if (!chat->amCreator() - && !(chat->adminRights() - & ChatAdminRight::f_delete_messages)) { + if (!chat->canDeleteMessages()) { return false; } } else if (peer->isUser()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index de50cd2f2..778d08165 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -262,6 +262,12 @@ public: } [[nodiscard]] virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0; + virtual void applySentMessage(const MTPDmessage &data); + virtual void applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready); + void indexAsNewItem(); [[nodiscard]] virtual QString notificationHeader() const { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 36f557505..1a0b7e555 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "base/openssl_help.h" +#include "base/unixtime.h" #include "lang/lang_keys.h" #include "mainwidget.h" #include "mainwindow.h" @@ -50,9 +51,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_window.h" -#include "base/call_delayed.h" // #TODO ttl -#include "base/unixtime.h" - #include <QtGui/QGuiApplication> #include <QtGui/QClipboard> @@ -976,6 +974,29 @@ bool HistoryMessage::updateDependencyItem() { return true; } +void HistoryMessage::applySentMessage(const MTPDmessage &data) { + HistoryItem::applySentMessage(data); + + if (const auto period = data.vttl_period(); period && period->v > 0) { + applyTTL(data.vdate().v + period->v); + } else { + applyTTL(0); + } +} + +void HistoryMessage::applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) { + HistoryItem::applySentMessage(text, data, wasAlready); + + if (const auto period = data.vttl_period(); period && period->v > 0) { + applyTTL(data.vdate().v + period->v); + } else { + applyTTL(0); + } +} + bool HistoryMessage::allowsForward() const { if (id < 0 || !isHistoryEntry()) { return false; diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 3f8ce8d41..2c832c797 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -188,6 +188,12 @@ public: return replyToId(); } + void applySentMessage(const MTPDmessage &data) override; + void applySentMessage( + const QString &text, + const MTPDupdateShortSentMessage &data, + bool wasAlready) override; + [[nodiscard]] TimeId ttlDestroyAt() const override { return _ttlDestroyAt; } From ac33a8bd966178018259d562d5f16c6e89c851bb Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 5 Feb 2021 19:47:00 +0400 Subject: [PATCH 342/396] Fix other admin links box layout. --- .../boxes/peers/edit_peer_invite_links.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 8dec6db45..b5872a8fb 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -883,13 +883,20 @@ void ManageInviteLinksBox( permanentFromList->events()); AddDivider(container); + auto otherHeader = (Ui::SlideWrap<>*)nullptr; if (admin->isSelf()) { const auto add = AddCreateLinkButton(container); add->setClickedCallback([=] { EditLink(peer, InviteLinkData{ .admin = admin }); }); } else { - AddSubsectionTitle(container, tr::lng_group_invite_other_list()); + otherHeader = container->add(object_ptr<Ui::SlideWrap<>>( + container, + object_ptr<Ui::FlatLabel>( + container, + tr::lng_group_invite_other_list(), + st::settingsSubsectionTitle), + st::inviteLinkRevokedTitlePadding)); } auto [list, newPermanent] = AddLinksList(container, peer, admin, false); @@ -957,7 +964,10 @@ void ManageInviteLinksBox( admins->heightValue(), revoked->heightValue() ) | rpl::start_with_next([=](int list, int admins, int revoked) { - dividerAbout->toggle(!list, anim::type::instant); + if (otherHeader) { + otherHeader->toggle(list > 0, anim::type::instant); + } + dividerAbout->toggle(!list && !otherHeader, anim::type::instant); adminsDivider->toggle(admins > 0 && list > 0, anim::type::instant); adminsHeader->toggle(admins > 0, anim::type::instant); revokedDivider->toggle(revoked > 0 && (list > 0 || admins > 0), anim::type::instant); From d188ab671932ec9efc8712437b800302ee271126 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 5 Feb 2021 21:35:29 +0400 Subject: [PATCH 343/396] Fix other admins links management. --- Telegram/SourceFiles/api/api_invite_links.cpp | 43 ++++++----- Telegram/SourceFiles/api/api_invite_links.h | 10 ++- Telegram/SourceFiles/boxes/boxes.style | 3 + .../boxes/peers/edit_peer_info_box.cpp | 30 ++++---- .../boxes/peers/edit_peer_invite_link.cpp | 53 +++++++++----- .../boxes/peers/edit_peer_invite_link.h | 5 ++ .../boxes/peers/edit_peer_invite_links.cpp | 72 ++++++++++++++++--- .../boxes/peers/edit_peer_invite_links.h | 4 +- .../boxes/peers/edit_peer_type_box.cpp | 2 +- 9 files changed, 160 insertions(+), 62 deletions(-) diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index a4d007f2a..2b945fd55 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -115,19 +115,19 @@ void InviteLinks::performCreate( }).send(); } -auto InviteLinks::lookupPermanent(not_null<PeerData*> peer) -> Link* { +auto InviteLinks::lookupMyPermanent(not_null<PeerData*> peer) -> Link* { auto i = _firstSlices.find(peer); - return (i != end(_firstSlices)) ? lookupPermanent(i->second) : nullptr; + return (i != end(_firstSlices)) ? lookupMyPermanent(i->second) : nullptr; } -auto InviteLinks::lookupPermanent(Links &links) -> Link* { +auto InviteLinks::lookupMyPermanent(Links &links) -> Link* { const auto first = links.links.begin(); return (first != end(links.links) && first->permanent && !first->revoked) ? &*first : nullptr; } -auto InviteLinks::lookupPermanent(const Links &links) const -> const Link* { +auto InviteLinks::lookupMyPermanent(const Links &links) const -> const Link* { const auto first = links.links.begin(); return (first != end(links.links) && first->permanent && !first->revoked) ? &*first @@ -139,12 +139,29 @@ auto InviteLinks::prepend( not_null<UserData*> admin, const MTPExportedChatInvite &invite) -> Link { const auto link = parse(peer, invite); + if (admin->isSelf()) { + prependMyToFirstSlice(peer, admin, link); + } + _updates.fire(Update{ + .peer = peer, + .admin = admin, + .now = link + }); + return link; +} + +void InviteLinks::prependMyToFirstSlice( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const Link &link) { + Expects(admin->isSelf()); + auto i = _firstSlices.find(peer); if (i == end(_firstSlices)) { i = _firstSlices.emplace(peer).first; } auto &links = i->second; - const auto permanent = lookupPermanent(links); + const auto permanent = lookupMyPermanent(links); const auto hadPermanent = (permanent != nullptr); auto updateOldPermanent = Update{ .peer = peer, @@ -176,12 +193,6 @@ auto InviteLinks::prepend( if (updateOldPermanent.now) { _updates.fire(std::move(updateOldPermanent)); } - _updates.fire(Update{ - .peer = peer, - .admin = admin, - .now = link - }); - return link; } void InviteLinks::edit( @@ -383,14 +394,14 @@ void InviteLinks::requestMyLinks(not_null<PeerData*> peer) { auto slice = parseSlice(peer, result); auto i = _firstSlices.find(peer); const auto permanent = (i != end(_firstSlices)) - ? lookupPermanent(i->second) + ? lookupMyPermanent(i->second) : nullptr; if (!permanent) { BringPermanentToFront(slice); const auto j = _firstSlices.emplace_or_assign( peer, std::move(slice)).first; - if (const auto permanent = lookupPermanent(j->second)) { + if (const auto permanent = lookupMyPermanent(j->second)) { editPermanentLink(peer, permanent->link); } } else { @@ -506,7 +517,7 @@ void InviteLinks::setMyPermanent( .peer = peer, .admin = peer->session().user(), }; - if (const auto permanent = lookupPermanent(links)) { + if (const auto permanent = lookupMyPermanent(links)) { if (permanent->link == link.link) { if (permanent->usage != link.usage) { permanent->usage = link.usage; @@ -548,7 +559,7 @@ void InviteLinks::clearMyPermanent(not_null<PeerData*> peer) { return; } auto &links = i->second; - const auto permanent = lookupPermanent(links); + const auto permanent = lookupMyPermanent(links); if (!permanent) { return; } @@ -590,7 +601,7 @@ auto InviteLinks::parseSlice( const MTPmessages_ExportedChatInvites &slice) const -> Links { auto i = _firstSlices.find(peer); const auto permanent = (i != end(_firstSlices)) - ? lookupPermanent(i->second) + ? lookupMyPermanent(i->second) : nullptr; auto result = Links(); slice.match([&](const MTPDmessages_exportedChatInvites &data) { diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index df29731e4..38f673f8a 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -140,13 +140,17 @@ private: [[nodiscard]] Link parse( not_null<PeerData*> peer, const MTPExportedChatInvite &invite) const; - [[nodiscard]] Link *lookupPermanent(not_null<PeerData*> peer); - [[nodiscard]] Link *lookupPermanent(Links &links); - [[nodiscard]] const Link *lookupPermanent(const Links &links) const; + [[nodiscard]] Link *lookupMyPermanent(not_null<PeerData*> peer); + [[nodiscard]] Link *lookupMyPermanent(Links &links); + [[nodiscard]] const Link *lookupMyPermanent(const Links &links) const; Link prepend( not_null<PeerData*> peer, not_null<UserData*> admin, const MTPExportedChatInvite &invite); + void prependMyToFirstSlice( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const Link &link); void notify(not_null<PeerData*> peer); void editPermanentLink( diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 1c76ebaeb..46807672e 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -928,6 +928,9 @@ peerListWithInviteViaLink: PeerList(peerListBox) { 0px, membersMarginBottom); } +peerListSingleRow: PeerList(peerListBox) { + padding: margins(0px, 0px, 0px, 0px); +} scheduleHeight: 95px; scheduleDateTop: 38px; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 44c8ab463..739477483 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1073,24 +1073,26 @@ void Controller::fillManageSection() { st::infoIconPermissions); } if (canEditInviteLinks) { + auto count = Info::Profile::MigratedOrMeValue( + _peer + ) | rpl::map([=](not_null<PeerData*> peer) { + peer->session().api().inviteLinks().requestMyLinks(peer); + return peer->session().changes().peerUpdates( + peer, + Data::PeerUpdate::Flag::InviteLinks + ) | rpl::map([=] { + return peer->session().api().inviteLinks().myLinks( + peer).count; + }); + }) | rpl::flatten_latest( + ) | rpl::start_spawning(_controls.buttonsLayout->lifetime()); + AddButtonWithCount( _controls.buttonsLayout, tr::lng_manage_peer_invite_links(), - Info::Profile::MigratedOrMeValue( - _peer - ) | rpl::map([=](not_null<PeerData*> peer) { - peer->session().api().inviteLinks().requestMyLinks(peer); - return peer->session().changes().peerUpdates( - peer, - Data::PeerUpdate::Flag::InviteLinks - ) | rpl::map([=] { - return peer->session().api().inviteLinks().myLinks( - peer).count; - }); - }) | rpl::flatten_latest( - ) | ToPositiveNumberString(), + rpl::duplicate(count) | ToPositiveNumberString(), [=] { Ui::show( - Box(ManageInviteLinksBox, _peer, _peer->session().user()), + Box(ManageInviteLinksBox, _peer, _peer->session().user(), 0, 0), Ui::LayerOption::KeepOther); }, st::infoIconInviteLinks); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 035eaaf7b..69dca4252 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "settings/settings_common.h" #include "mtproto/sender.h" +#include "styles/style_boxes.h" #include "styles/style_info.h" #include <QtGui/QGuiApplication> @@ -75,7 +76,9 @@ private: class SingleRowController final : public PeerListController { public: - SingleRowController(not_null<PeerData*> peer, TimeId date); + SingleRowController( + not_null<PeerData*> peer, + rpl::producer<QString> status); void prepare() override; void loadMoreRows() override; @@ -84,7 +87,8 @@ public: private: const not_null<PeerData*> _peer; - TimeId _date = 0; + rpl::producer<QString> _status; + rpl::lifetime _lifetime; }; @@ -164,23 +168,15 @@ void AddHeader( } else { AddDivider(container); } + AddSkip(container); } - AddSkip(container); AddSubsectionTitle( container, tr::lng_group_invite_created_by()); - - const auto delegate = container->lifetime().make_state< - PeerListContentDelegateSimple - >(); - const auto controller = container->lifetime().make_state< - SingleRowController - >(data.admin, data.date); - const auto content = container->add(object_ptr<PeerListContent>( + AddSinglePeerRow( container, - controller)); - delegate->setContent(content); - controller->setDelegate(delegate); + data.admin, + rpl::single(langDateTime(base::unixtime::parse(data.date)))); } Controller::Controller(not_null<PeerData*> peer, const LinkData &data) @@ -258,14 +254,19 @@ Main::Session &Controller::session() const { SingleRowController::SingleRowController( not_null<PeerData*> peer, - TimeId date) + rpl::producer<QString> status) : _peer(peer) -, _date(date) { +, _status(std::move(status)) { } void SingleRowController::prepare() { auto row = std::make_unique<PeerListRow>(_peer); - row->setCustomStatus(langDateTime(base::unixtime::parse(_date))); + const auto raw = row.get(); + std::move( + _status + ) | rpl::start_with_next([=](const QString &status) { + raw->setCustomStatus(status); + }, _lifetime); delegate()->peerListAppendRow(std::move(row)); delegate()->peerListRefreshRows(); } @@ -283,6 +284,24 @@ Main::Session &SingleRowController::session() const { } // namespace +void AddSinglePeerRow( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer, + rpl::producer<QString> status) { + const auto delegate = container->lifetime().make_state< + PeerListContentDelegateSimple + >(); + const auto controller = container->lifetime().make_state< + SingleRowController + >(peer, std::move(status)); + controller->setStyleOverrides(&st::peerListSingleRow); + const auto content = container->add(object_ptr<PeerListContent>( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); +} + void AddPermanentLinkBlock( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index 3cab90224..50e4da743 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -19,6 +19,11 @@ namespace Ui { class VerticalLayout; } // namespace Ui +void AddSinglePeerRow( + not_null<Ui::VerticalLayout*> container, + not_null<PeerData*> peer, + rpl::producer<QString> status); + void AddPermanentLinkBlock( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index b5872a8fb..8cff8a49e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -341,7 +341,16 @@ crl::time Row::updateExpireIn() const { QString Row::generateName() { auto result = _data.link; - return result.replace(qstr("https://"), QString()); + return result.replace( + qstr("https://"), + QString() + ).replace( + qstr("t.me/+"), + QString() + ).replace( + qstr("t.me/joinchat/"), + QString() + ); } QString Row::generateShortName() { @@ -393,8 +402,13 @@ public: LinksController( not_null<PeerData*> peer, not_null<UserData*> admin, + int count, bool revoked); + [[nodiscard]] rpl::producer<int> fullCountValue() const { + return _count.value(); + } + void prepare() override; void loadMoreRows() override; void rowClicked(not_null<PeerListRow*> row) override; @@ -434,6 +448,7 @@ private: const not_null<PeerData*> _peer; const not_null<UserData*> _admin; const bool _revoked = false; + rpl::variable<int> _count; base::unique_qptr<Ui::PopupMenu> _menu; QString _offsetLink; @@ -453,10 +468,12 @@ private: LinksController::LinksController( not_null<PeerData*> peer, not_null<UserData*> admin, + int count, bool revoked) : _peer(peer) , _admin(admin) , _revoked(revoked) +, _count(count) , _updateExpiringTimer([=] { expiringProgressTimer(); }) { style::PaletteChanged( ) | rpl::start_with_next([=] { @@ -475,7 +492,9 @@ LinksController::LinksController( delegate()->peerListRefreshRows(); } } else if (update.was.isEmpty()) { - if (!update.now->permanent || update.now->revoked) { + if (update.now->permanent && !update.now->revoked) { + _permanentFound.fire_copy(*update.now); + } else { prependRow(*update.now, now); delegate()->peerListRefreshRows(); } @@ -547,6 +566,9 @@ void LinksController::appendSlice(const InviteLinksSlice &slice) { if (slice.links.size() >= slice.count) { _allLoaded = true; } + const auto rowsCount = delegate()->peerListFullRowsCount(); + const auto minimalCount = _revoked ? rowsCount : (rowsCount + 1); + _count = _allLoaded ? minimalCount : std::max(slice.count, minimalCount); delegate()->peerListRefreshRows(); } @@ -797,7 +819,7 @@ void AdminsController::loadMoreRows() { void AdminsController::rowClicked(not_null<PeerListRow*> row) { Ui::show( - Box(ManageInviteLinksBox, _peer, row->peer()->asUser()), + Box(ManageInviteLinksBox, _peer, row->peer()->asUser(), 0, 0), Ui::LayerOption::KeepOther); } @@ -816,13 +838,14 @@ void AdminsController::appendRow(not_null<UserData*> user, int count) { struct LinksList { not_null<Ui::RpWidget*> widget; - rpl::producer<InviteLinkData> permanentFound; + not_null<LinksController*> controller; }; LinksList AddLinksList( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, not_null<UserData*> admin, + int count, bool revoked) { auto &lifetime = container->lifetime(); const auto delegate = lifetime.make_state< @@ -831,6 +854,7 @@ LinksList AddLinksList( const auto controller = lifetime.make_state<LinksController>( peer, admin, + count, revoked); controller->setStyleOverrides(&st::inviteLinkList); const auto content = container->add(object_ptr<PeerListContent>( @@ -839,7 +863,7 @@ LinksList AddLinksList( delegate->setContent(content); controller->setDelegate(delegate); - return { content, controller->permanentFound() }; + return { content, controller }; } not_null<Ui::RpWidget*> AddAdminsList( @@ -866,7 +890,9 @@ not_null<Ui::RpWidget*> AddAdminsList( void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, not_null<PeerData*> peer, - not_null<UserData*> admin) { + not_null<UserData*> admin, + int count, + int revokedCount) { using namespace Settings; box->setTitle(tr::lng_group_invite_title()); @@ -875,6 +901,22 @@ void ManageInviteLinksBox( const auto permanentFromList = box->lifetime().make_state< rpl::event_stream<InviteLinkData> >(); + const auto countValue = box->lifetime().make_state<rpl::variable<int>>( + count); + + if (!admin->isSelf()) { + auto status = countValue->value() | rpl::map([](int count) { + // #TODO links + return (count == 1) + ? "1 link" + : QString::number(count) + " links"; + }); + AddSinglePeerRow( + container, + admin, + std::move(status)); + } + AddSubsectionTitle(container, tr::lng_create_permanent_link_title()); AddPermanentLinkBlock( container, @@ -899,10 +941,15 @@ void ManageInviteLinksBox( st::inviteLinkRevokedTitlePadding)); } - auto [list, newPermanent] = AddLinksList(container, peer, admin, false); + auto [list, controller] = AddLinksList( + container, + peer, + admin, + count, + false); + *countValue = controller->fullCountValue(); - std::move( - newPermanent + controller->permanentFound( ) | rpl::start_with_next([=](InviteLinkData &&data) { permanentFromList->fire(std::move(data)); }, container->lifetime()); @@ -940,7 +987,12 @@ void ManageInviteLinksBox( tr::lng_group_invite_revoked_title(), st::settingsSubsectionTitle), st::inviteLinkRevokedTitlePadding)); - const auto revoked = AddLinksList(container, peer, admin, true).widget; + const auto revoked = AddLinksList( + container, + peer, + admin, + revokedCount, + true).widget; const auto deleteAll = Ui::CreateChild<Ui::LinkButton>( container.get(), diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h index 2f1050317..c16db91b4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.h @@ -14,4 +14,6 @@ class PeerData; void ManageInviteLinksBox( not_null<Ui::GenericBox*> box, not_null<PeerData*> peer, - not_null<UserData*> admin); + not_null<UserData*> admin, + int count, + int revokedCount); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 52e11b80c..876882bcd 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -193,7 +193,7 @@ void Controller::createContent() { tr::lng_group_invite_manage(), rpl::single(QString()), [=] { Ui::show( - Box(ManageInviteLinksBox, _peer, _peer->session().user()), + Box(ManageInviteLinksBox, _peer, _peer->session().user(), 0, 0), Ui::LayerOption::KeepOther); }, st::manageGroupButton, From 3399a05f1fd6a3794b910eab122c6a4b4396d1a4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 8 Feb 2021 19:04:21 +0400 Subject: [PATCH 344/396] Improve phrases for invite links. --- Telegram/Resources/langs/lang.strings | 18 ++- Telegram/SourceFiles/boxes/boxes.style | 4 + Telegram/SourceFiles/boxes/peer_list_box.cpp | 10 +- Telegram/SourceFiles/boxes/peer_list_box.h | 1 + .../boxes/peers/edit_peer_invite_link.cpp | 139 +++++++++++++++--- .../boxes/peers/edit_peer_invite_link.h | 5 + .../boxes/peers/edit_peer_invite_links.cpp | 86 ++++------- Telegram/SourceFiles/info/info.style | 1 + .../ui/controls/invite_link_buttons.cpp | 14 ++ .../ui/controls/invite_link_buttons.h | 4 + 10 files changed, 199 insertions(+), 83 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9c764943b..1b79c72d9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -943,6 +943,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_link_type" = "Link type"; "lng_manage_peer_link_permanent" = "Permanent link"; "lng_manage_peer_link_invite" = "Invite link"; +"lng_manage_peer_link_expired" = "Expired link"; "lng_manage_private_group_title" = "Private"; "lng_manage_public_group_title" = "Public"; "lng_manage_private_peer_title" = "Private"; @@ -1186,9 +1187,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_copy" = "Copy Link"; "lng_group_invite_share" = "Share Link"; "lng_group_invite_revoke" = "Revoke Link"; +"lng_group_invite_reactivate" = "Reactivate Link"; "lng_group_invite_no_joined" = "No one joined yet"; -"lng_group_invite_joined#one" = "{count} person joined"; -"lng_group_invite_joined#other" = "{count} people joined"; +"lng_group_invite_joined#one" = "{count} joined"; +"lng_group_invite_joined#other" = "{count} joined"; +"lng_group_invite_remaining#one" = "{count} remaining"; +"lng_group_invite_remaining#other" = "{count} remaining"; +"lng_group_invite_can_join#one" = "{count} can join"; +"lng_group_invite_can_join#other" = "{count} can join"; +"lng_group_invite_days_left#one" = "{count} day left"; +"lng_group_invite_days_left#other" = "{count} days left"; "lng_group_invite_about_permanent_group" = "Anyone who has Telegram installed will be able to join your group by following this link."; "lng_group_invite_about_permanent_channel" = "Anyone who has Telegram installed will be able to join your channel by following this link."; "lng_group_invite_manage" = "Manage Invite Links"; @@ -1197,7 +1205,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_add" = "Create a New Link"; "lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used."; "lng_group_invite_expires_at" = "This link expires {when}."; -"lng_group_invite_expired_already" = "This link has expired."; "lng_group_invite_created_by" = "Link created by"; "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; @@ -1210,7 +1217,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_revoked_title" = "Revoked links"; "lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?"; "lng_group_invite_link_expired" = "Expired"; -"lng_group_invite_link_revoked" = "Revoked"; "lng_group_invite_edit_title" = "Edit Link"; "lng_group_invite_new_title" = "New Link"; "lng_group_invite_expire_title" = "Limit by time period"; @@ -1228,6 +1234,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_other_count#other" = "{count} invite links"; "lng_group_invite_permanent_other" = "Permanent link of this admin"; "lng_group_invite_other_list" = "Invite links created by this admin"; +"lng_group_invite_expired_about" = "The time limit for this link has expired."; +"lng_group_invite_used_about" = "This link reached its usage limit."; +"lng_group_invite_can_join_via_link#one" = "{count} person can join via this link."; +"lng_group_invite_can_join_via_link#other" = "{count} people can join via this link."; "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 46807672e..5905da120 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -960,3 +960,7 @@ scheduleTimeSeparator: FlatLabel(defaultFlatLabel) { } } scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px); + +boxAttentionDividerLabel: FlatLabel(boxDividerLabel) { + textFg: boxTextFgError; +} diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 08fd4104c..e8939f11a 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -125,7 +125,11 @@ void PeerListBox::prepare() { _controller->setDelegate(this); - setDimensions(_controller->contentWidth(), st::boxMaxListHeight); + _controller->boxHeightValue( + ) | rpl::start_with_next([=](int height) { + setDimensions(_controller->contentWidth(), height); + }, lifetime()); + if (_select) { _select->finishAnimating(); Ui::SendPendingMoveResizeEvents(_select); @@ -332,6 +336,10 @@ int PeerListController::contentWidth() const { return st::boxWideWidth; } +rpl::producer<int> PeerListController::boxHeightValue() const { + return rpl::single(st::boxMaxListHeight); +} + void PeerListBox::addSelectItem( not_null<PeerData*> peer, anim::type animated) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 81069bca3..aafbdd5df 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -430,6 +430,7 @@ public: std::unique_ptr<PeerListState> state); virtual int contentWidth() const; + virtual rpl::producer<int> boxHeightValue() const; bool isRowSelected(not_null<PeerListRow*> row) { return delegate()->peerListIsRowChecked(row); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 69dca4252..036f39ab0 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/abstract_button.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" +#include "ui/boxes/edit_invite_link.h" #include "boxes/share_box.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... #include "history/history_message.h" // GetErrorTextForSending. @@ -38,7 +39,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "mtproto/sender.h" #include "styles/style_boxes.h" +#include "styles/style_layers.h" // st::boxDividerLabel. #include "styles/style_info.h" +#include "styles/style_settings.h" // st::settingsDividerLabelPadding. #include <QtGui/QGuiApplication> @@ -58,6 +61,8 @@ public: void rowClicked(not_null<PeerListRow*> row) override; Main::Session &session() const override; + //rpl::producer<int> boxHeightValue() const override; + private: void appendSlice(const Api::JoinedByLinkSlice &slice); [[nodiscard]] object_ptr<Ui::RpWidget> prepareHeader(); @@ -69,6 +74,8 @@ private: std::optional<Api::JoinedByLinkUser> _lastUser; bool _allLoaded = false; + Ui::RpWidget *_headerWidget = nullptr; + MTP::Sender _api; rpl::lifetime _lifetime; @@ -108,6 +115,9 @@ void AddHeaderBlock( const auto revokeLink = crl::guard(weak, [=] { RevokeLink(peer, data.admin, data.link); }); + const auto editLink = crl::guard(weak, [=] { + EditLink(peer, data); + }); const auto createMenu = [=] { auto result = base::make_unique_q<Ui::PopupMenu>(container); @@ -117,6 +127,9 @@ void AddHeaderBlock( result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); + result->addAction( + tr::lng_group_invite_context_edit(tr::now), + editLink); result->addAction( tr::lng_group_invite_context_revoke(tr::now), revokeLink); @@ -137,34 +150,43 @@ void AddHeaderBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - if ((data.expireDate <= 0 || now < data.expireDate) - && (data.usageLimit <= 0 || data.usage < data.usageLimit)) { - AddCopyShareLinkButtons( - container, - copyLink, - shareLink); + if (IsExpiredLink(data, now)) { + AddReactivateLinkButton(container, editLink); + } else { + AddCopyShareLinkButtons(container, copyLink, shareLink); } } void AddHeader( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, - const LinkData &data) { + const LinkData &data, + TimeId now) { using namespace Settings; - if (!data.revoked && !data.permanent) { - const auto now = base::unixtime::now(); AddHeaderBlock(container, peer, data, now); AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); - if (data.expireDate > 0) { - AddDividerText( + const auto timeExpired = (data.expireDate > 0) + && (data.expireDate <= now); + const auto usageExpired = (data.usageLimit > 0) + && (data.usageLimit <= data.usage); + if (data.expireDate > 0 || usageExpired) { + container->add(object_ptr<Ui::DividerLabel>( container, - (data.expireDate > now - ? tr::lng_group_invite_expires_at( - lt_when, - rpl::single(langDateTime( - base::unixtime::parse(data.expireDate)))) - : tr::lng_group_invite_expired_already())); + object_ptr<Ui::FlatLabel>( + container, + (timeExpired + ? tr::lng_group_invite_expired_about() + : usageExpired + ? tr::lng_group_invite_used_about() + : tr::lng_group_invite_expires_at( + lt_when, + rpl::single(langDateTime( + base::unixtime::parse(data.expireDate))))), + (timeExpired + ? st::boxAttentionDividerLabel + : st::boxDividerLabel)), + st::settingsDividerLabelPadding)); } else { AddDivider(container); } @@ -177,6 +199,7 @@ void AddHeader( container, data.admin, rpl::single(langDateTime(base::unixtime::parse(data.date)))); + AddSkip(container, st::membersMarginBottom); } Controller::Controller(not_null<PeerData*> peer, const LinkData &data) @@ -188,9 +211,11 @@ Controller::Controller(not_null<PeerData*> peer, const LinkData &data) object_ptr<Ui::RpWidget> Controller::prepareHeader() { using namespace Settings; + const auto now = base::unixtime::now(); + auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr); const auto container = result.data(); - AddHeader(container, _peer, _data); + AddHeader(container, _peer, _data, now); AddDivider(container); AddSkip(container); AddSubsectionTitle( @@ -200,11 +225,20 @@ object_ptr<Ui::RpWidget> Controller::prepareHeader() { lt_count, rpl::single(float64(_data.usage))) : tr::lng_group_invite_no_joined())); + + _headerWidget = result.data(); return result; } void Controller::prepare() { delegate()->peerListSetAboveWidget(prepareHeader()); + if (!_data.usage && _data.usageLimit > 0) { + setDescriptionText( + tr::lng_group_invite_can_join_via_link( + tr::now, + lt_count, + _data.usageLimit)); + } _allLoaded = (_data.usage == 0); const auto &inviteLinks = _peer->session().api().inviteLinks(); const auto slice = inviteLinks.joinedFirstSliceLoaded(_peer, _data.link); @@ -252,6 +286,12 @@ Main::Session &Controller::session() const { return _peer->session(); } +//rpl::producer<int> Controller::boxHeightValue() const { +// Expects(_headerWidget != nullptr); +// +// return _headerWidget->heightValue(); +//} + SingleRowController::SingleRowController( not_null<PeerData*> peer, rpl::producer<QString> status) @@ -284,6 +324,11 @@ Main::Session &SingleRowController::session() const { } // namespace +bool IsExpiredLink(const Api::InviteLink &data, TimeId now) { + return (data.expireDate > 0 && data.expireDate <= now) + || (data.usageLimit > 0 && data.usageLimit <= data.usage); +} + void AddSinglePeerRow( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, @@ -394,10 +439,7 @@ void AddPermanentLinkBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - AddCopyShareLinkButtons( - container, - copyLink, - shareLink); + AddCopyShareLinkButtons(container, copyLink, shareLink); struct JoinedState { QImage cachedUserpics; @@ -574,6 +616,52 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) { Ui::LayerOption::KeepOther); } +void EditLink( + not_null<PeerData*> peer, + const Api::InviteLink &data) { + const auto creating = data.link.isEmpty(); + const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); + using Fields = Ui::InviteLinkFields; + const auto done = [=](Fields result) { + const auto finish = [=](Api::InviteLink finished) { + if (creating) { + ShowInviteLinkBox(peer, finished); + } + if (*box) { + (*box)->closeBox(); + } + }; + if (creating) { + Assert(data.admin->isSelf()); + peer->session().api().inviteLinks().create( + peer, + finish, + result.expireDate, + result.usageLimit); + } else { + peer->session().api().inviteLinks().edit( + peer, + data.admin, + result.link, + result.expireDate, + result.usageLimit, + finish); + } + }; + *box = Ui::show( + (creating + ? Box(Ui::CreateInviteLinkBox, done) + : Box( + Ui::EditInviteLinkBox, + Fields{ + .link = data.link, + .expireDate = data.expireDate, + .usageLimit = data.usageLimit + }, + done)), + Ui::LayerOption::KeepOther); +} + void RevokeLink( not_null<PeerData*> peer, not_null<UserData*> admin, @@ -597,8 +685,11 @@ void RevokeLink( void ShowInviteLinkBox( not_null<PeerData*> peer, const Api::InviteLink &link) { + const auto now = base::unixtime::now(); auto initBox = [=](not_null<Ui::BoxContent*> box) { - box->setTitle((link.permanent && !link.revoked) + box->setTitle(IsExpiredLink(link, now) + ? tr::lng_manage_peer_link_expired() + : (link.permanent && !link.revoked) ? tr::lng_manage_peer_link_permanent() : tr::lng_manage_peer_link_invite()); peer->session().api().inviteLinks().updates( @@ -622,7 +713,7 @@ void ShowInviteLinkBox( Ui::show(Box([=](not_null<Ui::GenericBox*> box) { initBox(box); const auto container = box->verticalLayout(); - AddHeader(container, peer, link); + AddHeader(container, peer, link, now); }), Ui::LayerOption::KeepOther); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index 50e4da743..1543504f6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -19,6 +19,8 @@ namespace Ui { class VerticalLayout; } // namespace Ui +[[nodiscard]] bool IsExpiredLink(const Api::InviteLink &data, TimeId now); + void AddSinglePeerRow( not_null<Ui::VerticalLayout*> container, not_null<PeerData*> peer, @@ -36,6 +38,9 @@ void RevokeLink( not_null<PeerData*> peer, not_null<UserData*> admin, const QString &link); +void EditLink( + not_null<PeerData*> peer, + const Api::InviteLink &data); void ShowInviteLinkBox( not_null<PeerData*> peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 8cff8a49e..6c5f04362 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "api/api_invite_links.h" -#include "ui/boxes/edit_invite_link.h" #include "ui/wrap/slide_wrap.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" @@ -156,71 +155,50 @@ private: } [[nodiscard]] QString ComputeStatus(const InviteLinkData &link, TimeId now) { + const auto expired = IsExpiredLink(link, now); + const auto revoked = link.revoked; auto result = link.usage ? tr::lng_group_invite_joined(tr::now, lt_count_decimal, link.usage) + : (!expired && !revoked && link.usageLimit > 0) + ? tr::lng_group_invite_can_join( + tr::now, + lt_count_decimal, + link.usageLimit) : tr::lng_group_invite_no_joined(tr::now); const auto add = [&](const QString &text) { result += QString::fromUtf8(" \xE2\xB8\xB1 ") + text; }; - if (link.revoked) { - add(tr::lng_group_invite_link_revoked(tr::now)); - } else if ((link.usageLimit > 0 && link.usage >= link.usageLimit) - || (link.expireDate > 0 && now >= link.expireDate)) { + if (revoked) { + return result; + } else if (expired) { add(tr::lng_group_invite_link_expired(tr::now)); + return result; + } + if (link.usage > 0 && link.usageLimit > link.usage) { + result += ", " + tr::lng_group_invite_remaining( + tr::now, + lt_count_decimal, + link.usageLimit - link.usage); + } + if (link.expireDate > now) { + const auto left = (link.expireDate - now); + if (left >= 86400) { + add(tr::lng_group_invite_days_left( + tr::now, + lt_count, + left / 86400)); + } else { + const auto time = base::unixtime::parse(link.expireDate).time(); + add(time.toString(Qt::SystemLocaleLongDate)); + } } return result; } -void EditLink( - not_null<PeerData*> peer, - const InviteLinkData &data) { - const auto creating = data.link.isEmpty(); - const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); - using Fields = Ui::InviteLinkFields; - const auto done = [=](Fields result) { - const auto finish = [=](Api::InviteLink finished) { - if (creating) { - ShowInviteLinkBox(peer, finished); - } - if (*box) { - (*box)->closeBox(); - } - }; - if (creating) { - Assert(data.admin->isSelf()); - peer->session().api().inviteLinks().create( - peer, - finish, - result.expireDate, - result.usageLimit); - } else { - peer->session().api().inviteLinks().edit( - peer, - data.admin, - result.link, - result.expireDate, - result.usageLimit, - finish); - } - }; - *box = Ui::show( - (creating - ? Box(Ui::CreateInviteLinkBox, done) - : Box( - Ui::EditInviteLinkBox, - Fields{ - .link = data.link, - .expireDate = data.expireDate, - .usageLimit = data.usageLimit - }, - done)), - Ui::LayerOption::KeepOther); -} - void DeleteLink( - not_null<PeerData*> peer, - not_null<UserData*> admin, - const QString &link) { + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link) { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto sure = [=] { const auto finish = [=] { diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index f919fd15f..91a6d8ee7 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -868,6 +868,7 @@ inviteLinkShare: RoundButton(inviteLinkCopy) { icon: icon {{ "info/edit/links_share", activeButtonFg }}; iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }}; } +inviteLinkReactivate: inviteLinkShare; inviteLinkUserpics: GroupCallUserpics { size: 28px; shift: 6px; diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp index 11a904057..d050524ae 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -61,6 +61,20 @@ void AddCopyShareLinkButtons( }, wrap->lifetime()); } + +void AddReactivateLinkButton( + not_null<VerticalLayout*> container, + Fn<void()> editLink) { + const auto button = container->add( + object_ptr<RoundButton>( + container, + tr::lng_group_invite_reactivate(), + st::inviteLinkReactivate), + st::inviteLinkButtonsPadding); + button->setTextTransform(RoundButton::TextTransform::NoTransform); + button->setClickedCallback(editLink); +} + not_null<AbstractButton*> AddJoinedCountButton( not_null<VerticalLayout*> container, rpl::producer<JoinedCountContent> content, diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h index c21195b76..5b4af472c 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h @@ -17,6 +17,10 @@ void AddCopyShareLinkButtons( Fn<void()> copyLink, Fn<void()> shareLink); +void AddReactivateLinkButton( + not_null<VerticalLayout*> container, + Fn<void()> editLink); + struct JoinedCountContent { int count = 0; QImage userpics; From 047bf467b57efbe4975a86b7aa7b162375ee297e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 8 Feb 2021 22:14:51 +0400 Subject: [PATCH 345/396] Allow editing links from ShowInviteLinkBox. --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 11 +- Telegram/SourceFiles/boxes/peer_list_box.h | 15 +- .../boxes/peers/edit_peer_invite_link.cpp | 399 ++++++++++++------ Telegram/SourceFiles/settings/settings.style | 3 + 4 files changed, 301 insertions(+), 127 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index e8939f11a..4ca467980 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -296,7 +296,6 @@ void PeerListController::setDescriptionText(const QString &text) { if (text.isEmpty()) { setDescription(nullptr); } else { - const auto &st = _listSt ? *_listSt : st::peerListBox; setDescription(object_ptr<Ui::FlatLabel>(nullptr, text, computeListSt().about)); } } @@ -340,6 +339,10 @@ rpl::producer<int> PeerListController::boxHeightValue() const { return rpl::single(st::boxMaxListHeight); } +int PeerListController::descriptionTopSkipMin() const { + return computeListSt().item.height; +} + void PeerListBox::addSelectItem( not_null<PeerData*> peer, anim::type animated) { @@ -959,6 +962,7 @@ int PeerListContent::fullRowsCount() const { not_null<PeerListRow*> PeerListContent::rowAt(int index) const { Expects(index >= 0 && index < _rows.size()); + return _rows[index].get(); } @@ -1128,7 +1132,10 @@ int PeerListContent::resizeGetHeight(int newWidth) { _aboveHeight = _aboveSearchWidget->height(); } } - const auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight; + const auto labelTop = rowsTop() + + std::max( + shownRowsCount() * _rowHeight, + _controller->descriptionTopSkipMin()); const auto labelWidth = newWidth - 2 * st::contactsPadding.left(); if (_description) { _description->resizeToWidth(labelWidth); diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index aafbdd5df..2fca8547e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -429,29 +429,30 @@ public: virtual void restoreState( std::unique_ptr<PeerListState> state); - virtual int contentWidth() const; - virtual rpl::producer<int> boxHeightValue() const; + [[nodiscard]] virtual int contentWidth() const; + [[nodiscard]] virtual rpl::producer<int> boxHeightValue() const; + [[nodiscard]] virtual int descriptionTopSkipMin() const; - bool isRowSelected(not_null<PeerListRow*> row) { + [[nodiscard]] bool isRowSelected(not_null<PeerListRow*> row) { return delegate()->peerListIsRowChecked(row); } virtual bool searchInLocal() { return true; } - bool hasComplexSearch() const; + [[nodiscard]] bool hasComplexSearch() const; void search(const QString &query); void peerListSearchAddRow(not_null<PeerData*> peer) override; void peerListSearchRefreshRows() override; - virtual bool respectSavedMessagesChat() const { + [[nodiscard]] virtual bool respectSavedMessagesChat() const { return false; } - virtual rpl::producer<int> onlineCountValue() const; + [[nodiscard]] virtual rpl::producer<int> onlineCountValue() const; - rpl::lifetime &lifetime() { + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 036f39ab0..9688f6d16 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -41,7 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_layers.h" // st::boxDividerLabel. #include "styles/style_info.h" -#include "styles/style_settings.h" // st::settingsDividerLabelPadding. +#include "styles/style_settings.h" #include <QtGui/QGuiApplication> @@ -54,27 +54,37 @@ using LinkData = Api::InviteLink; class Controller final : public PeerListController { public: - Controller(not_null<PeerData*> peer, const LinkData &data); + Controller( + not_null<PeerData*> peer, + not_null<UserData*> admin, + rpl::producer<LinkData> data); void prepare() override; void loadMoreRows() override; void rowClicked(not_null<PeerListRow*> row) override; Main::Session &session() const override; - //rpl::producer<int> boxHeightValue() const override; + rpl::producer<int> boxHeightValue() const override; + int descriptionTopSkipMin() const override; private: void appendSlice(const Api::JoinedByLinkSlice &slice); - [[nodiscard]] object_ptr<Ui::RpWidget> prepareHeader(); + void addHeaderBlock(not_null<Ui::VerticalLayout*> container); + + [[nodiscard]] rpl::producer<LinkData> dataValue() const; const not_null<PeerData*> _peer; - LinkData _data; + rpl::variable<LinkData> _data; + + QString _link; + bool _revoked = false; mtpRequestId _requestId = 0; std::optional<Api::JoinedByLinkUser> _lastUser; bool _allLoaded = false; Ui::RpWidget *_headerWidget = nullptr; + rpl::variable<int> _addedHeight; MTP::Sender _api; rpl::lifetime _lifetime; @@ -99,24 +109,48 @@ private: }; -void AddHeaderBlock( - not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer, - const LinkData &data, - TimeId now) { - const auto link = data.link; +[[nodiscard]] bool ClosingLinkBox(const LinkData &updated, bool revoked) { + return updated.link.isEmpty() || (!revoked && updated.revoked); +} + +Controller::Controller( + not_null<PeerData*> peer, + not_null<UserData*> admin, + rpl::producer<LinkData> data) +: _peer(peer) +, _data(LinkData{ .admin = admin }) +, _api(&_peer->session().api().instance()) { + _data = std::move(data); + const auto current = _data.current(); + _link = current.link; + _revoked = current.revoked; +} + +rpl::producer<LinkData> Controller::dataValue() const { + return _data.value( + ) | rpl::filter([=](const LinkData &data) { + return !ClosingLinkBox(data, _revoked); + }); +} + +void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { + using namespace Settings; + + const auto current = _data.current(); + const auto link = current.link; + const auto admin = current.admin; const auto weak = Ui::MakeWeak(container); const auto copyLink = crl::guard(weak, [=] { CopyInviteLink(link); }); const auto shareLink = crl::guard(weak, [=] { - ShareInviteLinkBox(peer, link); + ShareInviteLinkBox(_peer, link); }); const auto revokeLink = crl::guard(weak, [=] { - RevokeLink(peer, data.admin, data.link); + RevokeLink(_peer, admin, link); }); const auto editLink = crl::guard(weak, [=] { - EditLink(peer, data); + EditLink(_peer, _data.current()); }); const auto createMenu = [=] { @@ -150,98 +184,198 @@ void AddHeaderBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - if (IsExpiredLink(data, now)) { - AddReactivateLinkButton(container, editLink); - } else { - AddCopyShareLinkButtons(container, copyLink, shareLink); - } -} + const auto reactivateWrap = container->add( + object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( + container, + object_ptr<Ui::VerticalLayout>( + container))); + const auto copyShareWrap = container->add( + object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( + container, + object_ptr<Ui::VerticalLayout>( + container))); + + AddReactivateLinkButton(reactivateWrap->entity(), editLink); + AddCopyShareLinkButtons(copyShareWrap->entity(), copyLink, shareLink); + + AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); + + auto grayLabelText = dataValue( + ) | rpl::map([=](const LinkData &data) { + const auto usageExpired = (data.usageLimit > 0) + && (data.usageLimit <= data.usage); + return usageExpired + ? tr::lng_group_invite_used_about() + : tr::lng_group_invite_expires_at( + lt_when, + rpl::single(langDateTime( + base::unixtime::parse(data.expireDate)))); + }) | rpl::flatten_latest(); + + const auto redLabelWrap = container->add( + object_ptr<Ui::SlideWrap<Ui::DividerLabel>>( + container, + object_ptr<Ui::DividerLabel>( + container, + object_ptr<Ui::FlatLabel>( + container, + tr::lng_group_invite_expired_about(), + st::boxAttentionDividerLabel), + st::settingsDividerLabelPadding))); + const auto grayLabelWrap = container->add( + object_ptr<Ui::SlideWrap<Ui::DividerLabel>>( + container, + object_ptr<Ui::DividerLabel>( + container, + object_ptr<Ui::FlatLabel>( + container, + std::move(grayLabelText), + st::boxDividerLabel), + st::settingsDividerLabelPadding))); + const auto justDividerWrap = container->add( + object_ptr<Ui::SlideWrap<>>( + container, + object_ptr<Ui::BoxContentDivider>(container))); + AddSkip(container); + + dataValue( + ) | rpl::start_with_next([=](const LinkData &data) { + const auto now = base::unixtime::now(); + const auto expired = IsExpiredLink(data, now); + reactivateWrap->toggle(expired, anim::type::instant); + copyShareWrap->toggle(!expired, anim::type::instant); -void AddHeader( - not_null<Ui::VerticalLayout*> container, - not_null<PeerData*> peer, - const LinkData &data, - TimeId now) { - using namespace Settings; - if (!data.revoked && !data.permanent) { - AddHeaderBlock(container, peer, data, now); - AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); const auto timeExpired = (data.expireDate > 0) && (data.expireDate <= now); const auto usageExpired = (data.usageLimit > 0) && (data.usageLimit <= data.usage); - if (data.expireDate > 0 || usageExpired) { - container->add(object_ptr<Ui::DividerLabel>( - container, - object_ptr<Ui::FlatLabel>( - container, - (timeExpired - ? tr::lng_group_invite_expired_about() - : usageExpired - ? tr::lng_group_invite_used_about() - : tr::lng_group_invite_expires_at( - lt_when, - rpl::single(langDateTime( - base::unixtime::parse(data.expireDate))))), - (timeExpired - ? st::boxAttentionDividerLabel - : st::boxDividerLabel)), - st::settingsDividerLabelPadding)); - } else { - AddDivider(container); - } - AddSkip(container); + redLabelWrap->toggle(timeExpired, anim::type::instant); + grayLabelWrap->toggle( + !timeExpired && (data.expireDate > 0 || usageExpired), + anim::type::instant); + justDividerWrap->toggle( + !data.expireDate && !expired, + anim::type::instant); + }, lifetime()); +} + +void Controller::prepare() { + using namespace Settings; + + auto header = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr); + const auto container = header.data(); + + const auto current = _data.current(); + if (!current.revoked && !current.permanent) { + addHeaderBlock(container); } AddSubsectionTitle( container, tr::lng_group_invite_created_by()); AddSinglePeerRow( container, - data.admin, - rpl::single(langDateTime(base::unixtime::parse(data.date)))); + current.admin, + rpl::single(langDateTime(base::unixtime::parse(current.date)))); AddSkip(container, st::membersMarginBottom); -} -Controller::Controller(not_null<PeerData*> peer, const LinkData &data) -: _peer(peer) -, _data(data) -, _api(&_peer->session().api().instance()) { -} + const auto listHeaderWrap = container->add( + object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( + container, + object_ptr<Ui::VerticalLayout>( + container))); + const auto listHeader = listHeaderWrap->entity(); -object_ptr<Ui::RpWidget> Controller::prepareHeader() { - using namespace Settings; + // Make this container occupy full width. + listHeader->add(object_ptr<Ui::RpWidget>(listHeader)); - const auto now = base::unixtime::now(); + AddDivider(listHeader); + AddSkip(listHeader); - auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr); - const auto container = result.data(); - AddHeader(container, _peer, _data, now); - AddDivider(container); - AddSkip(container); - AddSubsectionTitle( - container, - (_data.usage + auto listHeaderText = dataValue( + ) | rpl::map([=](const LinkData &data) { + const auto now = base::unixtime::now(); + const auto timeExpired = (data.expireDate > 0) + && (data.expireDate <= now); + if (!data.usage && data.usageLimit > 0 && !timeExpired) { + auto description = object_ptr<Ui::FlatLabel>( + nullptr, + tr::lng_group_invite_can_join_via_link( + tr::now, + lt_count, + data.usageLimit), + computeListSt().about); + if (!delegate()->peerListFullRowsCount()) { + using namespace rpl::mappers; + _addedHeight = description->heightValue( + ) | rpl::map(_1 + + st::membersAboutLimitPadding.top() + + st::membersAboutLimitPadding.bottom()); + } + delegate()->peerListSetDescription(std::move(description)); + } else { + _addedHeight = std::max( + data.usage, + delegate()->peerListFullRowsCount() + ) * computeListSt().item.height; + delegate()->peerListSetDescription(nullptr); + } + listHeaderWrap->toggle( + data.usage || (data.usageLimit > 0 && !timeExpired), + anim::type::instant); + delegate()->peerListRefreshRows(); + return data.usage ? tr::lng_group_invite_joined( lt_count, - rpl::single(float64(_data.usage))) - : tr::lng_group_invite_no_joined())); - - _headerWidget = result.data(); - return result; -} - -void Controller::prepare() { - delegate()->peerListSetAboveWidget(prepareHeader()); - if (!_data.usage && _data.usageLimit > 0) { - setDescriptionText( - tr::lng_group_invite_can_join_via_link( + rpl::single(float64(data.usage))) + : tr::lng_group_invite_no_joined(); + }) | rpl::flatten_latest(); + const auto listTitle = AddSubsectionTitle( + listHeader, + std::move(listHeaderText)); + auto remainingText = dataValue( + ) | rpl::map([=](const LinkData &data) { + return !data.usageLimit + ? QString() + : tr::lng_group_invite_remaining( tr::now, - lt_count, - _data.usageLimit)); - } - _allLoaded = (_data.usage == 0); + lt_count_decimal, + std::max(data.usageLimit - data.usage, 0)); + }); + const auto remaining = Ui::CreateChild<Ui::FlatLabel>( + listHeader, + std::move(remainingText), + st::settingsSubsectionTitleRight); + dataValue( + ) | rpl::start_with_next([=](const LinkData &data) { + remaining->setTextColorOverride( + (data.usageLimit && (data.usageLimit <= data.usage) + ? std::make_optional(st::boxTextFgError->c) + : std::nullopt)); + if (!data.usage && data.usageLimit > 0) { + remaining->hide(); + } else { + remaining->show(); + } + }, remaining->lifetime()); + + rpl::combine( + listTitle->positionValue(), + remaining->widthValue(), + listHeader->widthValue() + ) | rpl::start_with_next([=]( + QPoint position, + int width, + int outerWidth) { + remaining->moveToRight(position.x(), position.y(), outerWidth); + }, remaining->lifetime()); + + _headerWidget = header.data(); + + delegate()->peerListSetAboveWidget(std::move(header)); + _allLoaded = (current.usage == 0); + const auto &inviteLinks = _peer->session().api().inviteLinks(); - const auto slice = inviteLinks.joinedFirstSliceLoaded(_peer, _data.link); + const auto slice = inviteLinks.joinedFirstSliceLoaded(_peer, _link); if (slice) { appendSlice(*slice); } @@ -254,7 +388,7 @@ void Controller::loadMoreRows() { } _requestId = _api.request(MTPmessages_GetChatInviteImporters( _peer->input, - MTP_string(_data.link), + MTP_string(_link), MTP_int(_lastUser ? _lastUser->date : 0), _lastUser ? _lastUser->user->inputUser : MTP_inputUserEmpty(), MTP_int(_lastUser ? kPerPage : kFirstPage) @@ -276,6 +410,12 @@ void Controller::appendSlice(const Api::JoinedByLinkSlice &slice) { std::make_unique<PeerListRow>(user.user)); } delegate()->peerListRefreshRows(); + if (delegate()->peerListFullRowsCount() > 0) { + _addedHeight = std::max( + _data.current().usage, + delegate()->peerListFullRowsCount() + ) * computeListSt().item.height; + } } void Controller::rowClicked(not_null<PeerListRow*> row) { @@ -286,11 +426,25 @@ Main::Session &Controller::session() const { return _peer->session(); } -//rpl::producer<int> Controller::boxHeightValue() const { -// Expects(_headerWidget != nullptr); -// -// return _headerWidget->heightValue(); -//} +rpl::producer<int> Controller::boxHeightValue() const { + Expects(_headerWidget != nullptr); + + return rpl::combine( + _headerWidget->heightValue(), + _addedHeight.value() + ) | rpl::map([=](int header, int description) { + const auto wrapped = description + ? (computeListSt().padding.top() + + description + + computeListSt().padding.bottom()) + : 0; + return std::min(header + wrapped, st::boxMaxListHeight); + }); +} + +int Controller::descriptionTopSkipMin() const { + return 0; +} SingleRowController::SingleRowController( not_null<PeerData*> peer, @@ -685,35 +839,44 @@ void RevokeLink( void ShowInviteLinkBox( not_null<PeerData*> peer, const Api::InviteLink &link) { - const auto now = base::unixtime::now(); - auto initBox = [=](not_null<Ui::BoxContent*> box) { - box->setTitle(IsExpiredLink(link, now) - ? tr::lng_manage_peer_link_expired() - : (link.permanent && !link.revoked) - ? tr::lng_manage_peer_link_permanent() - : tr::lng_manage_peer_link_invite()); - peer->session().api().inviteLinks().updates( - peer, - link.admin - ) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) { - if (update.was == link.link - && (!update.now || (!link.revoked && update.now->revoked))) { + const auto admin = link.admin; + const auto linkText = link.link; + const auto revoked = link.revoked; + + auto updates = peer->session().api().inviteLinks().updates( + peer, + admin + ) | rpl::filter([=](const Api::InviteLinkUpdate &update) { + return (update.was == linkText); + }) | rpl::map([=](const Api::InviteLinkUpdate &update) { + return update.now ? *update.now : LinkData{ .admin = admin }; + }); + auto data = revoked + ? rpl::single(link) | rpl::type_erased() + : rpl::single(link) | rpl::then(std::move(updates)); + + auto initBox = [=, data = rpl::duplicate(data)]( + not_null<Ui::BoxContent*> box) { + rpl::duplicate( + data + ) | rpl::start_with_next([=](const LinkData &link) { + if (ClosingLinkBox(link, revoked)) { box->closeBox(); + return; } + const auto now = base::unixtime::now(); + box->setTitle(IsExpiredLink(link, now) + ? tr::lng_manage_peer_link_expired() + : (link.permanent && !link.revoked) + ? tr::lng_manage_peer_link_permanent() + : tr::lng_manage_peer_link_invite()); }, box->lifetime()); + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); }; - if (link.usage > 0) { - Ui::show( - Box<PeerListBox>( - std::make_unique<Controller>(peer, link), - std::move(initBox)), - Ui::LayerOption::KeepOther); - } else { - Ui::show(Box([=](not_null<Ui::GenericBox*> box) { - initBox(box); - const auto container = box->verticalLayout(); - AddHeader(container, peer, link, now); - }), Ui::LayerOption::KeepOther); - } + Ui::show( + Box<PeerListBox>( + std::make_unique<Controller>(peer, link.admin, std::move(data)), + std::move(initBox)), + Ui::LayerOption::KeepOther); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index d2582c0c3..eca3f69bf 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -83,6 +83,9 @@ settingsSubsectionTitle: FlatLabel(defaultFlatLabel) { textFg: windowActiveTextFg; minWidth: 240px; } +settingsSubsectionTitleRight: FlatLabel(settingsSubsectionTitle) { + minWidth: 0px; +} settingsSubsectionTitlePadding: margins(22px, 7px, 10px, 9px); settingsBackgroundPadding: margins(22px, 11px, 10px, 12px); settingsTileSkip: 15px; From d81c8f002d7d4b4a4cab7bbff21af537ece19f9a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 12:21:28 +0400 Subject: [PATCH 346/396] Improve other admins list style in Invite Links. --- Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp | 2 +- Telegram/SourceFiles/info/info.style | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 6c5f04362..64e71c489 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -855,7 +855,7 @@ not_null<Ui::RpWidget*> AddAdminsList( const auto controller = lifetime.make_state<AdminsController>( peer, admin); - controller->setStyleOverrides(&st::inviteLinkList); + controller->setStyleOverrides(&st::inviteLinkAdminsList); const auto content = container->add(object_ptr<PeerListContent>( container, controller)); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 91a6d8ee7..6e5e7dd24 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -914,6 +914,9 @@ inviteLinkList: PeerList(defaultPeerList) { item: inviteLinkListItem; padding: margins(0px, 4px, 0px, 4px); } +inviteLinkAdminsList: PeerList(peerListBox) { + padding: margins(0px, 4px, 0px, membersMarginBottom); +} inviteLinkIconSkip: 7px; inviteLinkIconStroke: 2px; inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }}; From 756fa702b2ce8a9cb7975f15db9551ac4a458929 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 19:17:27 +0400 Subject: [PATCH 347/396] Allow to edit chat ttl_period from Clear History. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/confirm_box.cpp | 46 ++++++++++++- Telegram/SourceFiles/boxes/confirm_box.h | 2 + .../boxes/peers/edit_peer_info_box.cpp | 69 +++++++++++-------- .../boxes/peers/edit_peer_info_box.h | 5 ++ 5 files changed, 91 insertions(+), 32 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1b79c72d9..262e1e2e4 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1696,6 +1696,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_delete_for_me_chat_hint#other" = "This will delete them just for you, not for other participants of the chat."; "lng_delete_for_me_hint#one" = "This will delete it just for you."; "lng_delete_for_me_hint#other" = "This will delete them just for you."; +"lng_edit_auto_delete_settings" = "Edit Auto-Delete Settings"; "lng_selected_unsend_about_user_one" = "You can also delete the message you sent from {user}'s inbox by checking \"Unsend my messages\"."; "lng_selected_unsend_about_user#one" = "You can also delete the {count} message you sent from {user}'s inbox by checking \"Unsend my messages\"."; "lng_selected_unsend_about_user#other" = "You can also delete the {count} messages you sent from {user}'s inbox by checking \"Unsend my messages\"."; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index d7f68a63c..e560fe0b2 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_invite_links.h" #include "history/history.h" #include "history/history_item.h" +#include "ui/layers/generic_box.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -36,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "data/data_changes.h" #include "base/unixtime.h" +#include "boxes/peers/edit_peer_info_box.h" #include "main/main_session.h" #include "mtproto/mtproto_config.h" #include "facades.h" // Ui::showChatsList @@ -650,6 +652,37 @@ void DeleteMessagesBox::prepare() { } _text.create(this, rpl::single(std::move(details)), st::boxLabel); + if (_wipeHistoryJustClear + && _wipeHistoryPeer + && (_wipeHistoryPeer->isUser() + || _wipeHistoryPeer->isMegagroup() + || _wipeHistoryPeer->isChat())) { + _autoDeleteSettings.create( + this, + tr::lng_edit_auto_delete_settings(tr::now), + st::boxLinkButton); + const auto peer = _wipeHistoryPeer; + const auto callback = crl::guard(&peer->session(), [=](TimeId period) { + using Flag = MTPmessages_SetHistoryTTL::Flag; + peer->session().api().request(MTPmessages_SetHistoryTTL( + MTP_flags(peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), + peer->input, + MTP_int(period) + )).done([=](const MTPUpdates &result) { + peer->session().api().applyUpdates(result); + }).fail([=](const RPCError &error) { + }).send(); + }); + _autoDeleteSettings->setClickedCallback([=] { + getDelegate()->show( + Box( + AutoDeleteSettingsBox, + _wipeHistoryPeer->myMessagesTTL(), + callback), + Ui::LayerOption(0)); + }); + } + addButton( deleteText->value(), [=] { deleteAndClear(); }, @@ -669,6 +702,9 @@ void DeleteMessagesBox::prepare() { } else if (_revoke) { fullHeight += st::boxMediumSkip + _revoke->heightNoMargins(); } + if (_autoDeleteSettings) { + fullHeight += st::boxMediumSkip + _autoDeleteSettings->height(); + } setDimensions(st::boxWidth, fullHeight); } @@ -791,8 +827,8 @@ void DeleteMessagesBox::resizeEvent(QResizeEvent *e) { BoxContent::resizeEvent(e); _text->moveToLeft(st::boxPadding.left(), st::boxPadding.top()); + auto top = _text->bottomNoMargins() + st::boxMediumSkip; if (_moderateFrom) { - auto top = _text->bottomNoMargins() + st::boxMediumSkip; if (_banUser) { _banUser->moveToLeft(st::boxPadding.left(), top); top += _banUser->heightNoMargins() + st::boxLittleSkip; @@ -801,11 +837,17 @@ void DeleteMessagesBox::resizeEvent(QResizeEvent *e) { top += _reportSpam->heightNoMargins() + st::boxLittleSkip; if (_deleteAll) { _deleteAll->moveToLeft(st::boxPadding.left(), top); + top += _deleteAll->heightNoMargins() + st::boxLittleSkip; } } else if (_revoke) { const auto availableWidth = width() - 2 * st::boxPadding.left(); _revoke->resizeToNaturalWidth(availableWidth); - _revoke->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip); + _revoke->moveToLeft(st::boxPadding.left(), top); + top += _revoke->heightNoMargins() + st::boxLittleSkip; + } + if (_autoDeleteSettings) { + top += st::boxMediumSkip - st::boxLittleSkip; + _autoDeleteSettings->moveToLeft(st::boxPadding.left(), top); } } diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index dea112536..22ebb6b60 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -23,6 +23,7 @@ namespace Ui { class Checkbox; class FlatLabel; class EmptyUserpic; +class LinkButton; } // namespace Ui class InformBox; @@ -243,6 +244,7 @@ private: object_ptr<Ui::Checkbox> _banUser = { nullptr }; object_ptr<Ui::Checkbox> _reportSpam = { nullptr }; object_ptr<Ui::Checkbox> _deleteAll = { nullptr }; + object_ptr<Ui::LinkButton> _autoDeleteSettings = { nullptr }; Fn<void()> _deleteConfirmedCallback; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 739477483..c0ae442d4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -899,36 +899,9 @@ void Controller::fillSetMessagesTTLButton() { _ttlSavedValue = value; }); const auto buttonCallback = [=] { - Ui::show(Box([=](not_null<Ui::GenericBox*> box) { - const auto options = { - tr::lng_manage_messages_ttl_never(tr::now), - tr::lng_manage_messages_ttl_after1(tr::now), - tr::lng_manage_messages_ttl_after2(tr::now), - u"5 seconds"_q, AssertIsDebug() - }; - const auto initial = !*_ttlSavedValue - ? 0 - : (*_ttlSavedValue == 5) AssertIsDebug() - ? 3 AssertIsDebug() - : (*_ttlSavedValue < 3 * 86400) - ? 1 - : 2; - const auto callback = [=](int option) { - boxCallback(!option - ? 0 - : (option == 1) - ? 86400 - : (option == 3) AssertIsDebug() - ? 5 AssertIsDebug() - : 7 * 86400); - }; - SingleChoiceBox(box, { - .title = tr::lng_manage_messages_ttl_title(), - .options = options, - .initialSelection = initial, - .callback = callback, - }); - }), Ui::LayerOption::KeepOther); + Ui::show( + Box(AutoDeleteSettingsBox, *_ttlSavedValue, boxCallback), + Ui::LayerOption::KeepOther); }; AddButtonWithText( _controls.buttonsLayout, @@ -1641,6 +1614,42 @@ void Controller::deleteChannel() { } // namespace + +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + TimeId ttlPeriod, + Fn<void(TimeId)> callback) { + const auto options = { + tr::lng_manage_messages_ttl_never(tr::now), + tr::lng_manage_messages_ttl_after1(tr::now), + tr::lng_manage_messages_ttl_after2(tr::now), + u"5 seconds"_q, AssertIsDebug() + }; + const auto initial = !ttlPeriod + ? 0 + : (ttlPeriod == 5) AssertIsDebug() + ? 3 AssertIsDebug() + : (ttlPeriod < 3 * 86400) + ? 1 + : 2; + const auto callbackFromOption = [=](int option) { + const auto period = !option + ? 0 + : (option == 1) + ? 86400 + : (option == 3) AssertIsDebug() + ? 5 AssertIsDebug() + : 7 * 86400; + callback(period); + }; + SingleChoiceBox(box, { + .title = tr::lng_manage_messages_ttl_title(), + .options = options, + .initialSelection = initial, + .callback = callbackFromOption, + }); +} + EditPeerInfoBox::EditPeerInfoBox( QWidget*, not_null<Window::SessionNavigation*> navigation, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 11ffd36ec..3a81e6fa1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -23,6 +23,11 @@ class VerticalLayout; class SettingsButton; } // namespace Ui +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + TimeId ttlPeriod, + Fn<void(TimeId)> callback); + class EditPeerInfoBox : public Ui::BoxContent { public: EditPeerInfoBox( From 7a5dc72f86b1d5e464d6aec2c56fe3297246dedf Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 21:03:05 +0400 Subject: [PATCH 348/396] Improve invite links UI. --- .../icons/info/edit/links_reactivate.png | Bin 0 -> 347 bytes .../icons/info/edit/links_reactivate@2x.png | Bin 0 -> 584 bytes .../icons/info/edit/links_reactivate@3x.png | Bin 0 -> 863 bytes .../icons/info/edit/links_revoked.png | Bin 0 -> 737 bytes .../icons/info/edit/links_revoked@2x.png | Bin 0 -> 1433 bytes .../icons/info/edit/links_revoked@3x.png | Bin 0 -> 2242 bytes .../icons/info/edit/roundbtn_plus.png | Bin 289 -> 287 bytes .../icons/info/edit/roundbtn_plus@2x.png | Bin 460 -> 465 bytes .../icons/info/edit/roundbtn_plus@3x.png | Bin 752 -> 787 bytes Telegram/Resources/langs/lang.strings | 4 +-- .../boxes/peers/edit_peer_invite_link.cpp | 6 ++-- .../boxes/peers/edit_peer_invite_links.cpp | 14 +++++--- Telegram/SourceFiles/info/info.style | 34 +++++++++++------- 13 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/links_reactivate.png create mode 100644 Telegram/Resources/icons/info/edit/links_reactivate@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_reactivate@3x.png create mode 100644 Telegram/Resources/icons/info/edit/links_revoked.png create mode 100644 Telegram/Resources/icons/info/edit/links_revoked@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_revoked@3x.png diff --git a/Telegram/Resources/icons/info/edit/links_reactivate.png b/Telegram/Resources/icons/info/edit/links_reactivate.png new file mode 100644 index 0000000000000000000000000000000000000000..f8780df551dc4386fb0fefd06afc9aed60be776a GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp6?j(WN{hD30_ zo$AQNtjNQ#b%Fhd%(@``xSC$|gOl?GE{WdS^yN}U&>ocsi3hKq%$ao6OLNDyjTJJx zf0k}!=wLM5kmHbj_SQ~s22ItgQNj1B!#XTE1G)1Aofw|WyxJ@O{ITBr_8Ry1vU<`X z9kmVS0vFk26Pg5aJ}>|=lh1@8E{WuTtsAej-MF{;+A2e~GVAq?5|6X0KQYMY9!k#j zIPoiPLG}EG8DHfb&Pisim3jP~+2H@$&krO1r1GsvQD#VT;E^%^z;bkMWn&A&&bc6u NdAj<!taD0e0syfSakl^f literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_reactivate@2x.png b/Telegram/Resources/icons/info/edit/links_reactivate@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9d7e6484d75894b39dee6b2aca0100a1ec2b2e12 GIT binary patch literal 584 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blLz^w#n1QF{Fa= z?F`3NEe1Reix-#|ypR0CRi$U!e8!9aF=NcmpgTLnTIyK2>t^i#VUig<tEps$t51BK zcQOmZtEwj1!VTK>VtNcm8V+!t5aWnp5MYKgEm=~zfnswSau~fB7ilz^_-e){%FI5~ zut*~zQtZZe*<I_Jbx!w8UdmNaFXUXmEHmi+#@yMfR&o8c<=FXH|DWHh^%uYI-Ieoz z<&a*3G>@_L8|DjT1{)8`GN_sDU_EevO^#s;Z)xmpJB6PN1u4w-4AXKSa2{C1yN5Ai zL!&)IuJdJ+7wjot?kru;Ssvs0n5|~f(@MV2Hj9s{y=J>;q{8<^v|(Fr;Og}^7rkGt zz2V+Ajn8*&1mh-D$TuA}2-tq!hEX8=Z?3I9gV?Hb!VO1PeP&MB`KpG&VwDv?1K;X* zVhqizb~7J%;;doYP$*M>muY6`TseoxX<4!1rSq2ms<FGWedqV4e_v;>k=-ZVYtg6Z zDP+;-{9X1<S`fpVu-~eR>u){O|0(@nm7zzlO3s1j!=8p?FS{9!aoF-JuvNr4JpOW& uA-QE9y8yF|{({0^NezZh=b2gZ5Af$&F8XwGqc12mFnGH9xvX<aXaWGLh}5wF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_reactivate@3x.png b/Telegram/Resources/icons/info/edit/links_reactivate@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..39a00a246f7491fcb91fc3f70b019585a2956205 GIT binary patch literal 863 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9Ph`#z;x2n#WAFU z@$GEGJf=V%m;X&Jj=a30;R~K~Oeqjo@ycVqzHo`N6O+_}r8oYl-c;%8nWFUZ#=qtk zk2y&<zS~G2ahe*m>hoN4fkr-!=^N!)G#K`9i8wGOFg>U+YhXISa^PbQ18W0k!{eI_ zTnxet$2T*IFsL!~r88?V_%O(vXA5AM!C*0ucL9R|V}Xq{4vjyQF1#$clwj~fO60@j zPPUTWceli?zx=k$d-`eBx8Hx8%s<b4;D24m*>vZH-=7vmdT&3Q7R=x7{GvEGp}F<G zOv1g2&i}<_cJp7~X*(hGfhq3N|8MOFaz7|8C=}enTgS4cmE#&)JyX-xgUcHnazE%Q zI7t8DF1Xib!8B)^;xvZ#+z!nHfzpq-3NE$%V7g_dBgWnRUURDAfdc7MLJ1pqpK=;p zYb#<kQSw_JY&+8@;lK2%er>y-jOC|g3LaYitgu=3v0{!~fBUb`28F&6e^=DA^tmlI znd?{YQ(Cu_LH3+}a0S<?j0&!*o$~+xx7W-xKAUEI;^%%_s~g%4e^}<-e0$pI?eVu| z*$<QTpYG+!)q5vh6}?D@(QVtm-;v9C7>{k+r^Cp$eIE~F+jhHs43DGb!y6{A?bkeT z@!D~r2L-Pma}@0Q`j6G(e${=ZA0NNi|MR<<f4_x$%h!KW0y(d9IW1)P^t}a2fUJnM zVT&3LMCW@ugoc*>TU1uN|NeZXvfZ&?)-S8$0=j+2UX$yuzXt8U?|=RER@v5i6IBJ9 z+=mQ5H;OQJJ?mgH$>fd{QE<2@6WE}ZtJuxNqA^RE>%tP<l)wfC*J2mOSzB6lxC9nt z^l3Ou-ZsINkxAsNAZx}Yu}vBd4T~%nG5Fqc5@Y2EFmV@I;2C{l5d&$4-eZ_}xMDd= SQPnF@;`Vg)b6Mw<&;$TPVowqP literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_revoked.png b/Telegram/Resources/icons/info/edit/links_revoked.png new file mode 100644 index 0000000000000000000000000000000000000000..89b361901dbc89773a54688f73d9d2a7ae9f66e4 GIT binary patch literal 737 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZv>fz~t@e;usRa z`8LF|+sIKMH{XWIxcR8HVZf0CA?AX`^#{_D6jsd0$VpIfdnAA1hzqaFK@WqdDX~7R zkpTjNA_tjmUcc6q5iCqk`zvO)apCf1yH~~St*h<eP;~e`-}K}BP&@HDp$e@JArIWS zR3>`{=}qr+QTloH_GufJrmDSh?8_?m#`TDI9~IzWS#UAq!13ekx3+IL7wB|p`f#pv zXSes#OWYAELXJ!~bHtv1`f1abZ@)B%^GVT9)&BIx`@#XMuNuttTX;9G`CtOW;)@+y z*_s)f)P*=#gspy1wtM4)*RQo}YHJgHY~=peGa1d~xp)7*V1eEI=94K+=g<4gSj+V7 zW}V}w4n#A2)XM*E&nl2R^taC6MvnjF)1s8^w*^;mtPIf-X%?7%y7k+)%dfwB?7y#W zHMcLrWLEU|yBigjnFv1)(Gq28b_~#(TK;(kvxQuLqYzsf_e~qV-+v`{_$B#On@Bxn z<!N==;d(Z0viY~3^ZpN>K4lF*vVDDzQTt)WX{S|hy#01xd4ku{g3yf-J;GZqzdVq% zkzxM%<C5iT*G%E7n0+(n*o?5%M<ri>|9#OvpC|p!*2$m6HZ;EeDpj9rCT(51F(fNU zN31h$y}HlC`6B6#6;BswAJ&!VbxYV9rT=k?08hK~7sGx5)|w^U-dZAD5gEJhI;{;m zyzgdCf!^ekHbFDa3c5Kg3~;cT+jcdp)ph3i9JA&Ei!)5NDX!@^Z@hi5HEQj&2put} z`I(pAmYw`q@#O47t{;YsdK=tpn(_`RA8;z3$1;!0M)U`}K%$lIgu{QdLCMS0)z4*} HQ$iB}6MQFr literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_revoked@2x.png b/Telegram/Resources/icons/info/edit/links_revoked@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0f6d6bb903d7ebba0dffda1bb4d9f6ae2a3a2a GIT binary patch literal 1433 zcmV;K1!nq*P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuB14%?dRCodHSxYE=K^ULwO<pUK zykA*ZAVnS}Hk3y!h!tCVVnY`4T9CCYu%M8#!NwyLC5iG}AW|rlS03~KzSIAVbKK`S zGxzU*yJzY)GiPRg^L@YhX1;Icng}5jer}sQ+*b3yiW>wN7U1R#7+b*24S*OHU|4{g zGhl21H#Y#|5Hz-cu?5_m0jF&NUteF+*Vjkp=H^IIQPF=*Gd_q2|FyQZQaL?5JkX$^ zAmMX}klV{yOA{Fx$)JOSgGotA37MRnWZ#s9kdP2kQBgs1b8|^-Y%KZu`XblY*JOQt zoy^S4klWi^Wq09ar>snMb+v2&Qa(ODZZ=OqKmhIN=%7zePpZDk%FD|O9U2;<fq{W$ z<ADhg--Lje34#Kn@N&Apzo*{b-mG6(SQy>g+moB^=BKBpG%+!ejTfXm;_(v@9UZMw zPR4`-P^DOLaWPY%r>95N=1ArI{G3KbMKKI{9-f@b3wU{Xk>%xOa&~q`j*gB<ettg4 z@||X9XTK{@s;sPJbvPJV`SbIWjE#+vjEoEt8X8JqYml<CGRcFmPEY{l$?)*7@{^FV zZbreqsTS_z<Ab)fwNaDFBw>b!hf`3F^}!VciKU;vzdr>xNhc;IXjWDh`!2U*jyUb@ z?QFbUsH&>6*k3tTLs|b8h>MG(Z*On1Tr8iTpJ#3lPIC4g3z?ajnuQ-38Bt*@E-q@u zutA7^0uZ#cw3Oc6-Khk`%HZIjBxG}Qv#PDLvs1J1F)=YJqS4a){QNZISRsLr00hND z@aIa8j*dzKrlh2(2-w`*)Fn8sx-zAwr|ZJ8KmtDj2nq)x?ds~H&(F{F`1qKnrlv9h z_xJai?0N}+Ly}=&AulhF;agBjOPd%1Yza|NP(aa-9~>OeoSYmj!m+*(MXCtUbCy_< z(fI^enV2bOuN?y@45Z}r{{BwUi`X0INIO>?025eDa3&BiQR3kt2EMVek>GtnPHSsx z1fop2%AE^g5~PrD&?~&YzDkQVJw45h4HqPp2SDJOni_6gSzcsM7#Kux6mD*^rAoOn z{?|HJd3iZ~e0)^lLFCB-Ev<M~`imo=zrSB4?83r=ZgT7D>L^@`Rd|<|mlT69D~jm- zE`|VfgEu!fOkfOF5)u;h3Po2|Ru~o*;J!hVl9F`e*&s~}0omEvDuLxd$i~Drl~G#L z>gp=R>$6@gTjPYi2qh;c!#v*!Pimgj($Ye9c6JD`D!n9u>*Im5I#w6~;FndCfZtZt zwN~USvet20`-^eZFst%@k3yuSr8z7McnZ?c&_L?z>j}Ow{OcrSZ{Y!@OoxYu6apc; z*w_aiPT0`i-d?+7fFO~5#DU=@NSW{;vnOnKceio~U0PbQJH{TS#&&T8G&MD;l)(z# z|1HJsK0ZDap0~{U*Vfj$E&&(|Y;A3sDRp&qB|Vj42#cXIuKG`F!9fE91OHlioKKtu zAQlEc7Z(?1B-r~fH8n*&Jw08Y03;zkKA!IG?%LCtwd0!~&z*sD5ye`7EDS1yFU0T{ z7gO|SVS9U<;)O>Z^BcM8@1&49C!vooEG#7OhDRbIA_xXJCnqOlV`D>#^qe!R<ETz2 zz;RK2r>C$;@OQ-LpVc6MpF`J1Ff72e*N<O%!vgp@bZrE~0$h9j_@y^2fS*IxMldYE nwbzeddcy+vIdp9Vx3_>l&%W#a76v~}00000NkvXXu0mjfU8{T~ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_revoked@3x.png b/Telegram/Resources/icons/info/edit/links_revoked@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..31bde4c44c80a8186512c519c4945de23d1f171a GIT binary patch literal 2242 zcmbVOc{tQv8~)7@V{9WIlbuuw38N?xl0gz<$&hA*EYT=C^O{l3$o}HBuZb+#w-g#7 zON>1-Q<_G}zBCDA`=)=s@9*#X<2m<p&biNduIoI{xvwYM$`UCEm4pHSAZTW4jN%~n zk3o1iBQqmdhXWvg6w(N&8kC;rOpM(f%<f#f1}JlU2mp%m0=WNBI3~$40N^SB0bCpe z{aGsj|7RCl!1Z7M53vk^vE*E#YG!O`8v<H(Libu49?3h{q?@JnCZl>$O%;4ekL!-V zPnLR*Itpi!yGTqUg`{4`qe+EsEd@)*;YPo6eWoTSFP0og`wwRN&pHhb;N=h-8`Myb z*}uMo<{@@+pxsB-^ZC<Yr44SAnUyB(BG_#^KW-hcsv>r(^)4s@=!ZC*TXiHVwrhgZ zs4e0QNKG8komF+{|Dw2q%T?*Z1Cy0b>C{X_RN%sRbgpi2!ZNKfR%B1^Mw->t7Zk@v z?~!fRkMq-nPO_j>1=|fK+QN8NIMb7O5Q`67nyki*H~G>Dd5@Fl9y&I7iUeGVD5|SV zj+fF-sKijF;inGnPF7LL0uszX*Jb)kkGTo`h~3^>lnAQY&F1zH6<0ZZyw!q6?pwCg z1ch+mz)xg+>q|MmJyGv*omT70+eSFJRo(;N-`!3TlD%m9h)3|f*%_q{V|_&vlnTn) z+HfdOk98lqPuSfu<OH=;rMHIq(_ROvJZ*WeEqy5>ht}YgQc8zy)!_1ht(s8Qom1qo zlI!UYwXp3#*gBKcU{Tfa68B~FW36j3Taby8<c^cobBv+y@9nq(>l%+jC~DOF>b0HC z)vlhbjCeu}@%goERQ0X4(uh5FwAgp9lj|T&vYeZ#C@53I)kGCzOI%rG{TTYiIpK*~ z+NQt6X$8y;|1CI@H`_cdUgOqx7<>d0a@T{JmFi&Kx?D$`P<W{B(!q^Bhm%G(`A7sV z(^AXJ%Ud<&Wf=FqyvYRuP$6p!&{?04>Tbw}GL5Q4;&fa1)x{Q%Az3f?kuUw=Ze9_9 z7{0Oa!OC8sJ6`fE^t))kywL3`=N55gC4HYy%^Hn(Ta3vN8{$xdjdY+`?aorO$J@d; ztw<U(>rSy55{P31|8zmMJ%@?nYBx>qPE<T~=_HdzyYM4AKE{14UzaRzmpes?MC)fJ z+kq+jgzcZCt^MneR#7EOQ`YTD*rEF2ANG}wBiY)xqM)=hn7qtf>Af)|fT4~?a`6_4 ziIHN(PeYaU2>W=AQ%^XLccC}M^&N^elye?2eA;B?)2kFmHGaB?3>!jxDtXp>_<gF1 z4g6||)SjW}&ZRoPneKRt$Vj~@rf^fXmRE9FC4^!9IgD>DURX{)C+`wLt#cyfLj@Y) zr&^XcyQ6C;i@|9eWS{d*X1hKx_g*p}Z!T9L6J?ji%WS0U{dB^9ot$}ulfQ`H@`J$@ zWiAB3<YE(85=pkm#fi$|2hJT9UzAte%Jt*;W8r6>r=P)uA@VLq35ke%-#%{vcjqq+ zTBebHM!Q3!uV<gTMgu>$%*`s3A>rnUr=alfCwomt6X%PM*jG3tWoVm4nd^uD_8BpT z<!gDNW}8s>TBJdYjV!>$D^&E#|HonRfbv#Sky1QpWW!A1HS46!YwmB>(XD(Ps&joQ zN^hN3ku`*GL%jjC%|I!s$FJTdL-F)S>xa+-CG{Rdns1IK^aVnHMuaybGXI$$D-m9Y zj&bVSYjfrEcm=xN;#c`@r@4-oZaA|m+5`{75AawRx>$<MO;k#)vm7bk!w>KmvTUaC zzR}xGO+IE0<9!{^kcn|Ymp(*fDgiHM7ALEF)Z54FZ?%QdOxve4nsQUX_dK4sNyKC; zVW(R|Q39M|M0(kb-3H!{|KvnQwZK+Bw@mVE)p(^5F9a?OzqnCdWbh>W0h)a_WDVP4 z?~iKNahq4~z6dfaL3<45jQr-|<R8Hojyp`OC#mucA=B5r<~Snoim=`;jAM4h9owbF z*p9vVYv<z=5yjSir%?g&W0NV4T0|`Qw&J(z?B&?Mm!&QUck2}vUrqOJ?0eO3M;Mgw zx68;7PWN+9FP-2hoWqI4n4*Qb$!a-YdJ|PQrzf|t;+Ob~p1IFf`n(?aw9dbTqN0T5 z$cH5J{Ivf?LGj<$(hBRXCYd&}(iBn;gpHnYGoII}2CRHSpBQxNUIjGXr1Y&}EZ=$( zPSPXsyIZxvK?8;=AK}tfSS3-=&c%`boA2f(ZZ@r%!<k3&+3)Tc!Ynd1i?M6lwwg`D z9q*h3*rYAb<~TIi@2s`B-(`g`txSH{ZcYZ!q`J0Bl?GYEYNyf~#Nsv#Xq$kw^X^&> zM-EPRPSaNym|h)lVZqnX59M~KVfn7qD+5#R-;E`HG7j6J+B4*5&wOp3QiB*2h<)NH zA)hX;mb@p0H$V1!=83CoJfTB!k#)tLqsyn^YhO!3!B|mT&C<~-$T8*?k;zG3!0L3{ zaFCH!{sDgJV{M^_Em@=XGN2rtYYpVswI_Jfkn-e6YNHrU5g^7%AWmdZ;Fq~6_irRH z=!*}#UWx?kU(eTfhV5f05gYetUo&jR(I)p>PPg+y<**ETHRn&w8gmQ%dg(J|B%>Ro zdV`@yQ7H#82*sj)(ZSz(lcwFL)tq0e`%J8Jg?Cri)~np8s=1o3R;!3iP~Ue6n%8kI zP&#gA5E*!?|F%i+<^ID8R7+j37FPvo6=J1ObpEqQfdn=+3`vA4Q0Ef>D)3@l7i<gh zvJB?-<Z&pT7AY_?^?$8sfM{3VNC3o)vc_aUSs?qeVD3`bXwC{`SfIwdP0@h#_iO-? nc!y}WAOli<tX1+qhhWPsSu@mjM$sLA`UkTsmc~^^*oXfHdjc<g literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/roundbtn_plus.png b/Telegram/Resources/icons/info/edit/roundbtn_plus.png index 86138484edf39e09a0ee7501abacd087d998f214..1f35ddbdb586da0c645708321e926c8264188cc3 100644 GIT binary patch delta 166 zcmV;X09pT`0-pkqfq#ifL_t(o3GJ1O3cw%;MRn}||E7Mg<u;+g7+f1ja0TgsuZ}`0 zBCTthY-ySA#4LaWNPq;^CQwQd(;3n39R3ShyIfT*Oib2pLF~;>FNlEk+pxQOC_meZ z&ID9|C;VEi2t+u9zqbIvZ?-$?FxuBz&w(88(EZd5pvm6s(;r*F0!V-aNFbfS0kgd< U!H{npkN^Mx07*qoM6N<$f(jQ*RR910 delta 168 zcmV;Z09XH?0-*wsfq#ohL_t(o3GI|&3cw%;g>~%yx2fy3-nL*12AT~2JV9Ea^YLk^ zh_vo$wxw0R3sV3IkN^pU2$WI;9x?67%TIA@SE?$0vt}UzYbD=V6ksNRVMEWKK43o2 z@a-8)vw%^|vO<K_cnf}MffYaE(go}uwR3E(^}H4Hxo005LLpC~uh{e!Pyh*#00|s# WeKNb<zF_VE0000<MNUMnLSTaZkx7F9 diff --git a/Telegram/Resources/icons/info/edit/roundbtn_plus@2x.png b/Telegram/Resources/icons/info/edit/roundbtn_plus@2x.png index f2772af06d54919c3a6f59ad68860629bc761ec7..7d2e67a9d62f472ffd830215afab729d5a91c8b0 100644 GIT binary patch delta 345 zcmV-f0jB=U1JMJJfqy$mL_t(|0qvM!4#FS|Mwz$U{g1HSW42~nbRqSJwk-J`2@%0i z`t_9(r`FnLUA8@BJ>MNi00<CQfD{l1pg{lxh$}z}hy%bSND7bw;tG%g;s8(uNC8qn zT!CRJFwb*amgUubJhJ)U;^p0(-;U<v5od6l({9*4YMVL#VSjL%{3!KpSPIm;+P8=R zeO+{BAwVbAeNWQ}IO@FL_Pu^RPVYB9=Wm7VcS1@Hey{W9$WJZvosdSrHNtoPo<e|O zyZwU#-^2GZ0aH3J!8@Z_aW?ro00Am6yR!`l7!{p$T~E{R<8!ibkWydm{NcXGJ0ZKy ze>=ypcR*o)LsC=R0iiqsix40X<j_S32;~u2gaCmchb}@uD38D*1PBB<bP)nVc?1?A rKp@DWix3dXBd`bo0znR4lqcW`RP@CRxDd}z00000NkvXXu0mjfvB8yl delta 340 zcmV-a0jvJe1Iz=EfqynhL_t(|0qvO44uc>JMPYyE|9^u1hiex#j)!g!P)zP2A;V#8 zdwYuAYAI#6DLY=WkspC000f9DKnaKgP#^#T#1)_f!~x(Cqy#7daRn#=aR7J=Py(|` zU|E*3uIp#|dgkyyMboDRzn{a`GkkEH^)PH7xx<40GFYp-l7Bxq1M)S0mPH77RzC8* zfq;C?pJfpOdgtTp@cca=&DL&oukU%+II3gp+J)UaJafG37|}7xmw=Pll>ozZA3=dX z)1TejUw#X;hmqQ~{271%YRI?oLO`pC+BF2s%J{Zzm+AZZ`Z96Ry}L5se?If<7)Nz@ z|M=kdOzQ*_6HOeN;tUAo5!i$Pfh30^LO>{wz$OF;BsmNb0z!ENHX%SD$zg~P5XvL4 m2>}904nu^1P#%Fzc>>;{Ea>Qos;f-^0000<MNUMnLSTaCZkPc8 diff --git a/Telegram/Resources/icons/info/edit/roundbtn_plus@3x.png b/Telegram/Resources/icons/info/edit/roundbtn_plus@3x.png index 30bd16f135059994f6bda084d25c053ca1a7b699..2b9621f36f0e322953a9a07a00a28a12b28c149b 100644 GIT binary patch literal 787 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9Ph`#z*Ot$;uuoF z_;z++-XQ}4m&tVn>=uU49~2buWGQPYnizJ4oWJoRvg=l!#^XKHRbP3<PEXxAy;)T0 zgYYTUZ=MW{52G0wIXHwlj$fuZ<$sg0{cxh?{PU;3R@KUU{T(&C^y>c!Ghb;Oc>jL& zwY1%R&+DEy#hb^z`&0g}|5gvvHGA%-uT558m|h)wy~?t9t>%2jV#U1oVvM<Vch4&= zuzpm=z;vr-*L+up@*{5<ShoBuvF~np*KwPXBjfYSx-t&N^_%DZa=*4bx%<@e`>UV7 z{*$rgW?)U*qBLvPu<NF$-ehzHy6n~NKmPby@7<+!yYEiB{PN1vz2En|6p(kUHv5yi zUe@Wr)1r;VzA1UJDSspb<PLrRHFM3eGynf8tdKNaohGL6F}WlCz_qy>L-m86Xdb&L z#C=q(QE?vQTHd<fmMks2$_$K&Ob;up;M8x<D~`8*2W|a+z30d0DY6TmS*$WsD%iUG zhs~0ltm|@d>F$fqmVJvmJ!P{(srcEb*`Jg%!fs^U=LiVtF=dfRT&=;Pp><4(sb@o| z2$P6tGB2as4J|IluC9%349Yp8tqiV5Bb*xqw{<x+EJ|9Vcz`3?RnZ~P@R~qE%i2W( k3sy*Ea~K>73#6WL`|pa2Y%}uHpAJfpp00i_>zopr0P-Ft1poj5 literal 752 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9Ph`#z!c@_;uuoF z_;yxc7L%g@>-CxncH525KNM8)7|V*d%xe<dRkndmY;xa8>D9|(Gd4#~dupU7?(%^B zl<7Aw2gVP90S!zEOa*&n7ck6VkjZD)U{GT?ew&f2f%Cw}GRmYHzH{bmzx}kpLTCBq zm+x1tUv2h!RlWcGS5gP``&Vae-rM)O?s-%EY&rG+6aUBOx-rh4-TKkutkjpsaptqD zEQ@vX4{gwACFGuD*#i5ZbvMshUpG#B|EISquXpoxgHvwDc4RKGS)kA@lbU?_f>rJA zyYFr$=B=H7{(0*D``gp*9on#c!ELsEe+7jLY~;cZ&%4K6#4lm~(0lLqW&YcD<*`hS ztzNOW=W;{L4ciBkAKSAWjsGsZvE%3?Z6=_=)n)r$rgJsqYCA9*Fk0N>3t;eJ=-bXL z!XVu6_$>qL0hR|pG_4mr``)j8Rc`*~WAB%XOgMIY<u<nmnf@mv%5t9E-}!y<8`GXC z_LaK9eTy$^&f04BH2T7t4VU+cPFP`bP2>WHwX3E=VCEXl1%hQ=K@N*9MFcx2zY$s5 z;Iee%vIe&snqG{ZUdg_Uy&Hn3FbPdPHj7Crak&PoidL^FtH~k1KrW9Escf!{mbr^W cC@^qEyz4EmhS<s;e^6@kboFyt=akR{01H+hp8x;= diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 262e1e2e4..115a19773 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1229,11 +1229,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_custom_limit" = "Enter custom limit"; "lng_group_invite_usage_any" = "No limit"; "lng_group_invite_usage_custom" = "Custom"; -"lng_group_invite_other_title" = "Invite links created by other admins"; +"lng_group_invite_other_title" = "Links created by other admins"; "lng_group_invite_other_count#one" = "{count} invite link"; "lng_group_invite_other_count#other" = "{count} invite links"; "lng_group_invite_permanent_other" = "Permanent link of this admin"; -"lng_group_invite_other_list" = "Invite links created by this admin"; +"lng_group_invite_other_list" = "Links created by this admin"; "lng_group_invite_expired_about" = "The time limit for this link has expired."; "lng_group_invite_used_about" = "This link reached its usage limit."; "lng_group_invite_can_join_via_link#one" = "{count} person can join via this link."; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 9688f6d16..0868a58ea 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -865,9 +865,11 @@ void ShowInviteLinkBox( return; } const auto now = base::unixtime::now(); - box->setTitle(IsExpiredLink(link, now) + box->setTitle(link.revoked + ? tr::lng_manage_peer_link_invite() + : IsExpiredLink(link, now) ? tr::lng_manage_peer_link_expired() - : (link.permanent && !link.revoked) + : link.permanent ? tr::lng_manage_peer_link_permanent() : tr::lng_manage_peer_link_invite()); }, box->lifetime()); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 64e71c489..9b31ab49b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -691,6 +691,7 @@ void LinksController::rowPaintIcon( } Unexpected("Color in LinksController::rowPaintIcon."); }(); + const auto stroke = st::inviteLinkIconStroke; auto &icon = _icons[int(color)]; if (icon.isNull()) { icon = QImage( @@ -703,12 +704,17 @@ void LinksController::rowPaintIcon( p.setPen(Qt::NoPen); p.setBrush(*bg); auto hq = PainterHighQualityEnabler(p); - p.drawEllipse(0, 0, inner, inner); - st::inviteLinkIcon.paintInCenter(p, { 0, 0, inner, inner }); + auto rect = QRect(0, 0, inner, inner); + if (color == Color::Expiring || color == Color::ExpireSoon) { + rect = rect.marginsRemoved({ stroke, stroke, stroke, stroke }); + } + p.drawEllipse(rect); + (color == Color::Revoked + ? st::inviteLinkRevokedIcon + : st::inviteLinkIcon).paintInCenter(p, { 0, 0, inner, inner }); } p.drawImage(x + skip, y + skip, icon); if (progress >= 0. && progress < 1.) { - const auto stroke = st::inviteLinkIconStroke; auto hq = PainterHighQualityEnabler(p); auto pen = QPen((*bg)->c); pen.setWidth(stroke); @@ -716,7 +722,7 @@ void LinksController::rowPaintIcon( p.setPen(pen); p.setBrush(Qt::NoBrush); - const auto margins = 1.5 * stroke; + const auto margins = .5 * stroke; p.drawArc(QRectF(x + skip, y + skip, inner, inner).marginsAdded({ margins, margins, diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 6e5e7dd24..720bdf1b5 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -868,7 +868,10 @@ inviteLinkShare: RoundButton(inviteLinkCopy) { icon: icon {{ "info/edit/links_share", activeButtonFg }}; iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }}; } -inviteLinkReactivate: inviteLinkShare; +inviteLinkReactivate: RoundButton(inviteLinkCopy) { + icon: icon {{ "info/edit/links_reactivate", activeButtonFg }}; + iconOver: icon {{ "info/edit/links_reactivate", activeButtonFgOver }}; +} inviteLinkUserpics: GroupCallUserpics { size: 28px; shift: 6px; @@ -879,7 +882,7 @@ inviteLinkUserpicsSkip: 8px; inviteLinkJoinedFont: font(14px); inviteLinkJoinedRowPadding: margins(0px, 18px, 0px, 8px); -inviteLinkCreateSkip: 8px; +inviteLinkCreateSkip: 10px; inviteLinkCreate: SettingsButton(defaultSettingsButton) { textFg: lightButtonFg; textFgOver: lightButtonFgOver; @@ -889,7 +892,7 @@ inviteLinkCreate: SettingsButton(defaultSettingsButton) { font: semiboldFont; height: 20px; - padding: margins(58px, 7px, 12px, 5px); + padding: margins(60px, 7px, 12px, 5px); toggle: infoProfileToggle; toggleOver: infoProfileToggleOver; @@ -898,28 +901,35 @@ inviteLinkCreate: SettingsButton(defaultSettingsButton) { ripple: defaultRippleAnimation; } inviteLinkCreateIcon: icon {{ "info/edit/roundbtn_plus", windowFgActive }}; -inviteLinkCreateIconSize: 20px; +inviteLinkCreateIconSize: 18px; inviteLinkListItem: PeerListItem(defaultPeerListItem) { button: OutlineButton(defaultPeerListButton) { font: normalFont; padding: margins(11px, 5px, 11px, 5px); } height: 52px; - photoPosition: point(8px, 6px); - namePosition: point(58px, 6px); - statusPosition: point(58px, 25px); - photoSize: 40px; + photoPosition: point(9px, 4px); + namePosition: point(60px, 6px); + statusPosition: point(60px, 26px); + photoSize: 44px; } inviteLinkList: PeerList(defaultPeerList) { item: inviteLinkListItem; - padding: margins(0px, 4px, 0px, 4px); + padding: margins(0px, 4px, 0px, 0px); } -inviteLinkAdminsList: PeerList(peerListBox) { - padding: margins(0px, 4px, 0px, membersMarginBottom); +inviteLinkAdminsList: PeerList(inviteLinkList) { + item: PeerListItem(inviteLinkListItem) { + photoPosition: point(16px, 9px); + namePosition: point(62px, 6px); + statusPosition: point(62px, 26px); + photoSize: 34px; + } + padding: margins(0px, 5px, 0px, 6px); } inviteLinkIconSkip: 7px; inviteLinkIconStroke: 2px; inviteLinkIcon: icon {{ "info/edit/links_link", mediaviewFileExtFg }}; +inviteLinkRevokedIcon: icon {{ "info/edit/links_revoked", mediaviewFileExtFg }}; inviteLinkThreeDotsSkip: 12px; -inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 9px); +inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 4px); inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px); From c1fde61e03ff2d52f30470be222cbfad2aeee93a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 9 Feb 2021 21:52:20 +0400 Subject: [PATCH 349/396] Add admin log events about links, volumes, ttl. --- Telegram/Resources/langs/lang.strings | 11 + Telegram/SourceFiles/api/api_invite_links.cpp | 4 +- .../SourceFiles/core/local_url_handlers.cpp | 24 ++ .../admin_log/history_admin_log_item.cpp | 279 +++++++++++++++--- 4 files changed, 271 insertions(+), 47 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 115a19773..61cd43f59 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2134,6 +2134,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_deleted_message" = "{from} deleted message:"; "lng_admin_log_participant_joined" = "{from} joined the group"; "lng_admin_log_participant_joined_channel" = "{from} joined the channel"; +"lng_admin_log_participant_joined_by_link" = "{from} joined the group via {link}"; +"lng_admin_log_participant_joined_by_link_channel" = "{from} joined the channel via {link}"; +"lng_admin_log_revoke_invite_link" = "{from} revoked invite link {link}"; +"lng_admin_log_delete_invite_link" = "{from} deleted invite link {link}"; "lng_admin_log_participant_left" = "{from} left the group"; "lng_admin_log_participant_left_channel" = "{from} left the channel"; "lng_admin_log_stopped_poll" = "{from} stopped poll:"; @@ -2160,7 +2164,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_unmuted_participant" = "{from} unmuted {user} in a voice chat"; "lng_admin_log_allowed_unmute_self" = "{from} allowed new voice chat members to speak"; "lng_admin_log_disallowed_unmute_self" = "{from} started muting new voice chat members"; +"lng_admin_log_participant_volume" = "{from} changed voice chat volume for {user} to {percent}"; "lng_admin_log_user_with_username" = "{name} ({mention})"; +"lng_admin_log_messages_ttl_set" = "{from} enabled messages auto-delete after {duration}"; +"lng_admin_log_messages_ttl_changed" = "{from} changed messages auto-delete period from {previous} to {duration}"; +"lng_admin_log_messages_ttl_removed" = "{from} disabled messages auto-deletion after {duration}"; +"lng_admin_log_edited_invite_link" = "edited invite link {link}"; +"lng_admin_log_invite_link_expire_date" = "Expire date: {previous} -> {limit}"; +"lng_admin_log_invite_link_usage_limit" = "Usage limit: {previous} -> {limit}"; "lng_admin_log_restricted_forever" = "indefinitely"; "lng_admin_log_restricted_until" = "until {date}"; "lng_admin_log_banned_view_messages" = "Read messages"; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 2b945fd55..5bc25514b 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -238,8 +238,8 @@ void InviteLinks::performEdit( using Flag = MTPmessages_EditExportedChatInvite::Flag; _api->request(MTPmessages_EditExportedChatInvite( MTP_flags((revoke ? Flag::f_revoked : Flag(0)) - | ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0)) - | ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))), + | (!revoke ? Flag::f_expire_date : Flag(0)) + | (!revoke ? Flag::f_usage_limit : Flag(0))), peer->input, MTP_string(link), MTP_int(expireDate), diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 9d47fa80d..5a3fa6701 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/language_box.h" #include "passport/passport_form_controller.h" #include "window/window_session_controller.h" +#include "ui/toast/toast.h" #include "data/data_session.h" #include "data/data_document.h" #include "data/data_cloud_themes.h" @@ -40,6 +41,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "app.h" +#include <QtGui/QGuiApplication> + namespace Core { namespace { @@ -433,6 +436,23 @@ bool OpenMediaTimestamp( return false; } +bool ShowInviteLink( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + if (!controller) { + return false; + } + const auto base64link = match->captured(1).toLatin1(); + const auto link = QString::fromUtf8(QByteArray::fromBase64(base64link)); + if (link.isEmpty()) { + return false; + } + QGuiApplication::clipboard()->setText(link); + Ui::Toast::Show(tr::lng_group_invite_copied(tr::now)); + return true; +} + } // namespace const std::vector<LocalUrlHandler> &LocalUrlHandlers() { @@ -507,6 +527,10 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() { qsl("^media_timestamp/?\\?base=([a-zA-Z0-9\\.\\_\\-]+)&t=(\\d+)(&|$)"), OpenMediaTimestamp }, + { + qsl("^show_invite_link/?\\?link=([a-zA-Z0-9_\\+\\/\\=\\-]+)(&|$)"), + ShowInviteLink + }, }; return Result; } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 6c6f57517..02b820bdb 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "lang/lang_keys.h" #include "ui/text/text_utilities.h" +#include "ui/basic_click_handlers.h" #include "boxes/sticker_set_box.h" #include "base/unixtime.h" #include "core/application.h" @@ -261,6 +262,86 @@ TextWithEntities GenerateBannedChangeText( return result; } +QString ExtractInviteLink(const MTPExportedChatInvite &data) { + return data.match([&](const MTPDchatInviteExported &data) { + return qs(data.vlink()); + }); +} + +QString InternalInviteLinkUrl(const MTPExportedChatInvite &data) { + const auto base64 = ExtractInviteLink(data).toUtf8().toBase64(); + return "internal:show_invite_link/?link=" + QString::fromLatin1(base64); +} + +QString GenerateInviteLinkText(const MTPExportedChatInvite &data) { + return ExtractInviteLink(data).replace( + qstr("https://"), + QString() + ).replace( + qstr("t.me/+"), + QString() + ).replace( + qstr("t.me/joinchat/"), + QString() + ); +} + +QString GenerateInviteLinkLink(const MTPExportedChatInvite &data) { + const auto text = GenerateInviteLinkText(data); + return text.endsWith("...") + ? text + : textcmdLink(InternalInviteLinkUrl(data), text); +} + +TextWithEntities GenerateInviteLinkChangeText( + const MTPExportedChatInvite &newLink, + const MTPExportedChatInvite &prevLink) { + auto link = TextWithEntities{ GenerateInviteLinkText(newLink) }; + if (!link.text.endsWith("...")) { + link.entities.push_back({ + EntityType::CustomUrl, + 0, + link.text.size(), + InternalInviteLinkUrl(newLink) }); + } + auto result = tr::lng_admin_log_edited_invite_link(tr::now, lt_link, link, Ui::Text::WithEntities); + result.text.append('\n'); + + const auto expireDate = [](const MTPExportedChatInvite &link) { + return link.match([](const MTPDchatInviteExported &data) { + return data.vexpire_date().value_or_empty(); + }); + }; + const auto usageLimit = [](const MTPExportedChatInvite &link) { + return link.match([](const MTPDchatInviteExported &data) { + return data.vusage_limit().value_or_empty(); + }); + }; + const auto wrapDate = [](TimeId date) { + return date + ? langDateTime(base::unixtime::parse(date)) + : tr::lng_group_invite_expire_never(tr::now); + }; + const auto wrapUsage = [](int count) { + return count + ? QString::number(count) + : tr::lng_group_invite_usage_any(tr::now); + }; + const auto wasExpireDate = expireDate(prevLink); + const auto nowExpireDate = expireDate(newLink); + const auto wasUsageLimit = usageLimit(prevLink); + const auto nowUsageLimit = usageLimit(newLink); + if (wasExpireDate != nowExpireDate) { + result.text.append('\n').append(tr::lng_admin_log_invite_link_expire_date(tr::now, lt_previous, wrapDate(wasExpireDate), lt_limit, wrapDate(nowExpireDate))); + } + if (wasUsageLimit != nowUsageLimit) { + result.text.append('\n').append(tr::lng_admin_log_invite_link_usage_limit(tr::now, lt_previous, wrapUsage(wasUsageLimit), lt_limit, wrapUsage(nowUsageLimit))); + } + + result.entities.push_front(EntityInText(EntityType::Italic, 0, result.text.size())); + return result; +}; + auto GenerateUserString( not_null<Main::Session*> session, MTPint userId) { @@ -863,52 +944,49 @@ void GenerateItems( addSimpleServiceMessage(text); }; - auto createParticipantMute = [&](const MTPDchannelAdminLogEventActionParticipantMute &data) { - data.vparticipant().match([&](const MTPDgroupCallParticipant &data) { - const auto user = history->owner().user(data.vuser_id().v); - const auto userLink = user->createOpenLink(); - const auto userLinkText = textcmdLink(2, user->name); - auto text = tr::lng_admin_log_muted_participant( - tr::now, - lt_from, - fromLinkText, - lt_user, - userLinkText); - auto message = HistoryService::PreparedText{ text }; - message.links.push_back(fromLink); - message.links.push_back(userLink); - addPart(history->makeServiceMessage( - history->nextNonHistoryEntryId(), - MTPDmessage_ClientFlag::f_admin_log_entry, - date, - message, - MTPDmessage::Flags(0), - peerToUser(from->id))); + auto groupCallParticipantUser = [&](const MTPGroupCallParticipant &data) { + return data.match([&](const MTPDgroupCallParticipant &data) { + return history->owner().user(data.vuser_id().v); }); }; + auto addServiceMessageWithLink = [&](const QString &text, const ClickHandlerPtr &link) { + auto message = HistoryService::PreparedText{ text }; + message.links.push_back(fromLink); + message.links.push_back(link); + addPart(history->makeServiceMessage( + history->nextNonHistoryEntryId(), + MTPDmessage_ClientFlag::f_admin_log_entry, + date, + message, + MTPDmessage::Flags(0), + peerToUser(from->id))); + }; + + auto createParticipantMute = [&](const MTPDchannelAdminLogEventActionParticipantMute &data) { + const auto user = groupCallParticipantUser(data.vparticipant()); + const auto userLink = user->createOpenLink(); + const auto userLinkText = textcmdLink(2, user->name); + auto text = tr::lng_admin_log_muted_participant( + tr::now, + lt_from, + fromLinkText, + lt_user, + userLinkText); + addServiceMessageWithLink(text, userLink); + }; + auto createParticipantUnmute = [&](const MTPDchannelAdminLogEventActionParticipantUnmute &data) { - data.vparticipant().match([&](const MTPDgroupCallParticipant &data) { - const auto user = history->owner().user(data.vuser_id().v); - const auto userLink = user->createOpenLink(); - const auto userLinkText = textcmdLink(2, user->name); - auto text = tr::lng_admin_log_unmuted_participant( - tr::now, - lt_from, - fromLinkText, - lt_user, - userLinkText); - auto message = HistoryService::PreparedText{ text }; - message.links.push_back(fromLink); - message.links.push_back(userLink); - addPart(history->makeServiceMessage( - history->nextNonHistoryEntryId(), - MTPDmessage_ClientFlag::f_admin_log_entry, - date, - message, - MTPDmessage::Flags(0), - peerToUser(from->id))); - }); + const auto user = groupCallParticipantUser(data.vparticipant()); + const auto userLink = user->createOpenLink(); + const auto userLinkText = textcmdLink(2, user->name); + auto text = tr::lng_admin_log_unmuted_participant( + tr::now, + lt_from, + fromLinkText, + lt_user, + userLinkText); + addServiceMessageWithLink(text, userLink); }; auto createToggleGroupCallSetting = [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) { @@ -918,6 +996,114 @@ void GenerateItems( addSimpleServiceMessage(text); }; + auto addInviteLinkServiceMessage = [&](const QString &text, const MTPExportedChatInvite &data) { + auto message = HistoryService::PreparedText{ text }; + message.links.push_back(fromLink); + if (!ExtractInviteLink(data).endsWith("...")) { + message.links.push_back(std::make_shared<UrlClickHandler>(InternalInviteLinkUrl(data))); + } + addPart(history->makeServiceMessage( + history->nextNonHistoryEntryId(), + MTPDmessage_ClientFlag::f_admin_log_entry, + date, + message, + MTPDmessage::Flags(0), + peerToUser(from->id), + nullptr)); + }; + + auto createParticipantJoinByInvite = [&](const MTPDchannelAdminLogEventActionParticipantJoinByInvite &data) { + auto text = (channel->isMegagroup() + ? tr::lng_admin_log_participant_joined_by_link + : tr::lng_admin_log_participant_joined_by_link_channel); + addInviteLinkServiceMessage( + text( + tr::now, + lt_from, + fromLinkText, + lt_link, + GenerateInviteLinkLink(data.vinvite())), + data.vinvite()); + }; + + auto createExportedInviteDelete = [&](const MTPDchannelAdminLogEventActionExportedInviteDelete &data) { + addInviteLinkServiceMessage( + tr::lng_admin_log_delete_invite_link( + tr::now, + lt_from, + fromLinkText, + lt_link, + GenerateInviteLinkLink(data.vinvite())), + data.vinvite()); + }; + + auto createExportedInviteRevoke = [&](const MTPDchannelAdminLogEventActionExportedInviteRevoke &data) { + addInviteLinkServiceMessage( + tr::lng_admin_log_revoke_invite_link( + tr::now, + lt_from, + fromLinkText, + lt_link, + GenerateInviteLinkLink(data.vinvite())), + data.vinvite()); + }; + + auto createExportedInviteEdit = [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) { + auto bodyFlags = Flag::f_entities | Flag::f_from_id; + auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry; + auto bodyReplyTo = 0; + auto bodyViaBotId = 0; + auto bodyText = GenerateInviteLinkChangeText(data.vnew_invite(), data.vprev_invite()); + addPart(history->makeMessage( + history->nextNonHistoryEntryId(), + bodyFlags, + bodyClientFlags, + bodyReplyTo, + bodyViaBotId, + date, + peerToUser(from->id), + QString(), + bodyText)); + }; + + auto createParticipantVolume = [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) { + const auto user = groupCallParticipantUser(data.vparticipant()); + const auto userLink = user->createOpenLink(); + const auto userLinkText = textcmdLink(2, user->name); + const auto volume = data.vparticipant().match([&]( + const MTPDgroupCallParticipant &data) { + return data.vvolume().value_or(10000); + }); + const auto volumeText = QString::number(volume / 100) + '%'; + auto text = tr::lng_admin_log_participant_volume( + tr::now, + lt_from, + fromLinkText, + lt_user, + userLinkText, + lt_percent, + volumeText); + addServiceMessageWithLink(text, userLink); + }; + + auto createChangeHistoryTTL = [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) { + const auto was = data.vprev_value().v; + const auto now = data.vnew_value().v; + const auto wrap = [](int duration) { + return (duration == 5) + ? u"5 seconds"_q + : (duration < 3 * 86400) + ? tr::lng_manage_messages_ttl_after1(tr::now) + : tr::lng_manage_messages_ttl_after2(tr::now); + }; + auto text = !was + ? tr::lng_admin_log_messages_ttl_set(tr::now, lt_from, fromLinkText, lt_duration, wrap(now)) + : !now + ? tr::lng_admin_log_messages_ttl_removed(tr::now, lt_from, fromLinkText, lt_duration, wrap(was)) + : tr::lng_admin_log_messages_ttl_changed(tr::now, lt_from, fromLinkText, lt_previous, wrap(was), lt_duration, wrap(now)); + addSimpleServiceMessage(text); + }; + action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) { createChangeTitle(data); }, [&](const MTPDchannelAdminLogEventActionChangeAbout &data) { @@ -971,14 +1157,17 @@ void GenerateItems( }, [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) { createToggleGroupCallSetting(data); }, [&](const MTPDchannelAdminLogEventActionParticipantJoinByInvite &data) { + createParticipantJoinByInvite(data); }, [&](const MTPDchannelAdminLogEventActionExportedInviteDelete &data) { + createExportedInviteDelete(data); }, [&](const MTPDchannelAdminLogEventActionExportedInviteRevoke &data) { + createExportedInviteRevoke(data); }, [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) { - // #TODO links + createExportedInviteEdit(data); }, [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) { - // #TODO calls + createParticipantVolume(data); }, [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) { - // #TODO ttl + createChangeHistoryTTL(data); }); } From 1426a437439d9bf9f2e8f35677ba04ec8cafdffa Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 10 Feb 2021 15:47:01 +0400 Subject: [PATCH 350/396] Add admin log filter for invite links. --- Telegram/Resources/langs/lang.strings | 1 + .../SourceFiles/history/admin_log/history_admin_log_filter.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 61cd43f59..1ad35512b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2089,6 +2089,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_filter_messages_edited" = "Edited messages"; "lng_admin_log_filter_messages_pinned" = "Pinned messages"; "lng_admin_log_filter_voice_chats" = "Voice chat"; +"lng_admin_log_filter_invite_links" = "Invite links"; "lng_admin_log_filter_members_removed" = "Leaving members"; "lng_admin_log_filter_all_admins" = "All users and admins"; "lng_admin_log_about" = "What is this?"; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index da48d045b..e754e01f8 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -284,6 +284,7 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) { addFlag(Flag::f_pinned, tr::lng_admin_log_filter_messages_pinned(tr::now)); addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now)); } + addFlag(Flag::f_invites, tr::lng_admin_log_filter_invite_links(tr::now)); addFlag(Flag::f_leave, tr::lng_admin_log_filter_members_removed(tr::now)); } From c9fccc8ea781ba1f6316cf3b3b54480183df8d11 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 10 Feb 2021 16:14:53 +0400 Subject: [PATCH 351/396] Improve revoked link view box. --- .../icons/info/edit/links_delete.png | Bin 0 -> 432 bytes .../icons/info/edit/links_delete@2x.png | Bin 0 -> 791 bytes .../icons/info/edit/links_delete@3x.png | Bin 0 -> 1154 bytes Telegram/Resources/langs/lang.strings | 1 + .../boxes/peers/edit_peer_invite_link.cpp | 82 +++++++++++++----- .../boxes/peers/edit_peer_invite_link.h | 4 + .../boxes/peers/edit_peer_invite_links.cpp | 22 ----- Telegram/SourceFiles/info/info.style | 4 + .../ui/controls/invite_link_buttons.cpp | 13 +++ .../ui/controls/invite_link_buttons.h | 4 + 10 files changed, 84 insertions(+), 46 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/links_delete.png create mode 100644 Telegram/Resources/icons/info/edit/links_delete@2x.png create mode 100644 Telegram/Resources/icons/info/edit/links_delete@3x.png diff --git a/Telegram/Resources/icons/info/edit/links_delete.png b/Telegram/Resources/icons/info/edit/links_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..f20b3d13503caec0f56b235eb6f967f9377b753f GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{Cgthk7!twx zHZ+>6#ek=$zqsSaYt}FK<~e=fs{8R!^GcrEHtxC=!j7k$*pn8|RC{?OR#>nw>)-^o z)qiwlq&v9&%x6lC^txVZ)c5${2cC`U2mUkUMx9{1Y+{+4Wd4I8X<Ez!wfnrg3J#q7 zY%_h42A}j6&d6!5t5#Xrr041Fp7*@c{_bS%Z&}RK#r~+8^fyera%iDwfTink2gw~e z8LR<!np7NLRbSxx5t4hGi^t-#?exgATt6l|PBfNpt$nkOb2C#p=h}}q912*AUZ0t@ z>}Rg!=X#by3+q{5@A8^e)RcQ$EpnRRQ`a3mv$7=Ev=lwW^nOf_*Op)Z`qtOl_aU8^ z`IVC%G`#+PckLGrlZ)C5+&_4g`bq)4#S;F2*{D?cQsPZnZ%}A?y85}Sb4q9e00UN< AhyVZp literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_delete@2x.png b/Telegram/Resources/icons/info/edit/links_delete@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..94e5b4a0e0d8ba82d998cfb62589b0e7fbcbbff9 GIT binary patch literal 791 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^uz(rC1}St4blSkcz|`pJ;uuoF z_%__T?~nnH@5K()m}cV*4~6D5HWzSD5D?0?VAN(wIz6rXKojfSJy|hJMKdyz+TFG; zov`JK{<qJ|Gt+ubzJ0Ogi`DC&^B;RPq&Xayydd<Vf=S{m!`X&D;{~c0KC)a;yYW-# zM`tM0<da8ymIoV4_4=&;KI{AQ&zbA4ufCbL{aJy<5u>#~WR9tCyfNea^PQ!7|3g0L zl&ItuZFnkkT+QrL?;Q0>4ooGo&aT%UsWxmmzv;}cM4q5OHFmR)KMst!UjOUwKFR*$ zuAL6-y9~V?TOwb_H159tUUS9Ij}?E)^=+J)Vh>H66<@x%DuZd=-L~&~pY-<p?pgiX z?(kl|q{^*Pl~*lXZ<f3DAGkNIXF=m5mWJ&M)~!6Y*U?5)w=zF|<$R~E=?9j#)x6%B zz;x~PR!5a2?r%r#Efk7jTF6!tZEwN6@2$g&e{WcBoQsjEn6PLL^I5SQcMiodP0D8# zI8|$waGv30_#zfFYsNd8TD|Ii2a0x@9DcY#B4V=1UjHhg;tPS7q(UxkFO8abFd^Vy zzp3Zt-%G;p>wlTGmov&)+i}%(-M{z0*fn1J`eNUEoj9g_ZAUK!GbI_`y;f_&d`oI( zL|G==k)&N~e`m05F*VxwM(8|4^%LD%&dBKsOx>T><ZW!eUXr%Fa%033i|@$^zL&n( zy)3-(BB`UEyMTSo*ZWN0f6u-BT*#kk_FncFz3G<{-nD|9I{R$f?e{|dT-G~2MsjOQ z?7rJqC3c(V%r85i>!FWlI()vFGi|=#@;_y}D<?C=CNOlLUT4MkJNiK0gee=oH!Rt+ z{g@WxO4pv-ac3Fw+=35Z=JC)?y7MiIt-yGe)Vwr@EWyXy!i^hdU%c|6j&V|eWmY?% R_#9B8^mO%eS?83{1OV>ATZRAt literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/links_delete@3x.png b/Telegram/Resources/icons/info/edit/links_delete@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..28df4fad1ef412abec16cf29079e7a2f6b436ef6 GIT binary patch literal 1154 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;}uz(rC1}R)=9PbBI{>IbAF{Fa= zZH#rEmZ6Agzxomd$0#l_VXh!Hts9PI2l#IA$O`esZelfaaAbAmvJ(|L^VzrJ!Oy)% zPTjHWySLG#>-f&%^It9ZB}=P^^cYUu8{xq7hv}ivw?ho94E(HI4vYtQ8XnGM6k#~S zATdvp3~qh%NBh?LiVB0de#f^&=?1S2v)?M-eEDU{wbxrKD=Q7xuU{Wu#g?g3FDiKF zYTow6KWpS_w|DS=6it%XH*EF%cl-A3@}2wb^!G7|DcUf0N&F1|rZ4(YGDECIwW0R8 z`cCaPaW(B~P6d-U`0O}YWFvRB#LD#dU%Q{H&-`{<ocQbSzLi(AHs0@ke?jf(1C|L_ z{Wfv@Jp9n$=AZMwPo^ZXH9MYBJ6(Q;p+NYKn@;x1pI0Zo|1|xp&$RsSk}kFui~=eP z3`5nIJ%9c@WA|M#vx(E5dL&J|vDRW$_*13@W*j;0KAUfbd|v;uV*BlxzkgTzaxB(s zklnaS_Ci_QN)Fem*5U@a?eU-1=rt_)=&zf4WS!oD3=^Z&k|h}vRnt0Scdt0lcuV%j z%YRpmA1oJMB5TijA?=6oe*?xhYR6B=IZj)7!aHd~L;$N9d&?Hb$(-TUho`C=_%vKF z%eW=I&W15z{enFW|F)NLF@()w3TUuko_+S&UyF@#>!o)-$q2l4CumCVYvIW7zk6<% z>0I7Fx5fDM<;%gR_FaE{G^cx0;oN6OBb}<v9B&=Sdw%!V-Mg_~44K8=foALc-~M~{ zlRr;#)^?dKDL3tErYy?&_iOU}O1oI;UMuxP-Z0ymAO`+TCO(=CY^&w$G#l6>rIv{} zG>7uf7dg<Z!yC-C;NYruf363P)7msyFC<(&tj_vD!TazOri=}*9!_Sen6Na_i_s+F z>jzIpn-iA|c>NfDN7k>Je{$QCy*3kayziyoT++b6dyw<O+p=I28#}vWx4JfUTC(0- z;<q42=T+{z%qh{^?|&3HIdQS<!S~Z;+`VQ;cWo-Hop$lLPR|3sBLU14)sr-y9o(_O z@<D+`!|u9f_hU@L2|Hu%NibZWu<-!PjI(J8xAH%{h`17Nvu(8|&+q+*FJ^5m5?7kA z>xja}l%o+r(X-}EYEa%ium9Y0<CY3W(VWhOYyLfZ08Bp(EetMd9BB+sx_?A4vc$(R zKK^JCr6V@+?=<19Jp%We6eAv7KWF+nLE12I-cgAgS{+a74S<<OxsfS=!GJlTA`O_W gj2YU}*+}R9uvh;p9qPj<Y68mtp00i_>zopr08YEyJpcdz literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1ad35512b..ec29b886a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1188,6 +1188,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_share" = "Share Link"; "lng_group_invite_revoke" = "Revoke Link"; "lng_group_invite_reactivate" = "Reactivate Link"; +"lng_group_invite_delete" = "Delete Link"; "lng_group_invite_no_joined" = "No one joined yet"; "lng_group_invite_joined#one" = "{count} joined"; "lng_group_invite_joined#other" = "{count} joined"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 0868a58ea..fa624b79e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -137,6 +137,7 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { using namespace Settings; const auto current = _data.current(); + const auto revoked = current.revoked; const auto link = current.link; const auto admin = current.admin; const auto weak = Ui::MakeWeak(container); @@ -152,21 +153,30 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { const auto editLink = crl::guard(weak, [=] { EditLink(_peer, _data.current()); }); + const auto deleteLink = [=] { + DeleteLink(_peer, admin, link); + }; const auto createMenu = [=] { auto result = base::make_unique_q<Ui::PopupMenu>(container); - result->addAction( - tr::lng_group_invite_context_copy(tr::now), - copyLink); - result->addAction( - tr::lng_group_invite_context_share(tr::now), - shareLink); - result->addAction( - tr::lng_group_invite_context_edit(tr::now), - editLink); - result->addAction( - tr::lng_group_invite_context_revoke(tr::now), - revokeLink); + if (revoked) { + result->addAction( + tr::lng_group_invite_context_delete(tr::now), + deleteLink); + } else { + result->addAction( + tr::lng_group_invite_context_copy(tr::now), + copyLink); + result->addAction( + tr::lng_group_invite_context_share(tr::now), + shareLink); + result->addAction( + tr::lng_group_invite_context_edit(tr::now), + editLink); + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + } return result; }; @@ -197,6 +207,9 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { AddReactivateLinkButton(reactivateWrap->entity(), editLink); AddCopyShareLinkButtons(copyShareWrap->entity(), copyLink, shareLink); + if (revoked) { + AddDeleteLinkButton(container, deleteLink); + } AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); @@ -242,19 +255,19 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { ) | rpl::start_with_next([=](const LinkData &data) { const auto now = base::unixtime::now(); const auto expired = IsExpiredLink(data, now); - reactivateWrap->toggle(expired, anim::type::instant); - copyShareWrap->toggle(!expired, anim::type::instant); + reactivateWrap->toggle(!revoked && expired, anim::type::instant); + copyShareWrap->toggle(!revoked && !expired, anim::type::instant); const auto timeExpired = (data.expireDate > 0) && (data.expireDate <= now); const auto usageExpired = (data.usageLimit > 0) && (data.usageLimit <= data.usage); - redLabelWrap->toggle(timeExpired, anim::type::instant); + redLabelWrap->toggle(!revoked && timeExpired, anim::type::instant); grayLabelWrap->toggle( - !timeExpired && (data.expireDate > 0 || usageExpired), + !revoked && !timeExpired && (data.expireDate > 0 || usageExpired), anim::type::instant); justDividerWrap->toggle( - !data.expireDate && !expired, + revoked || (!data.expireDate && !expired), anim::type::instant); }, lifetime()); } @@ -266,7 +279,8 @@ void Controller::prepare() { const auto container = header.data(); const auto current = _data.current(); - if (!current.revoked && !current.permanent) { + const auto revoked = current.revoked; + if (revoked || !current.permanent) { addHeaderBlock(container); } AddSubsectionTitle( @@ -296,7 +310,7 @@ void Controller::prepare() { const auto now = base::unixtime::now(); const auto timeExpired = (data.expireDate > 0) && (data.expireDate <= now); - if (!data.usage && data.usageLimit > 0 && !timeExpired) { + if (!revoked && !data.usage && data.usageLimit > 0 && !timeExpired) { auto description = object_ptr<Ui::FlatLabel>( nullptr, tr::lng_group_invite_can_join_via_link( @@ -320,7 +334,7 @@ void Controller::prepare() { delegate()->peerListSetDescription(nullptr); } listHeaderWrap->toggle( - data.usage || (data.usageLimit > 0 && !timeExpired), + !revoked && (data.usage || (data.usageLimit > 0 && !timeExpired)), anim::type::instant); delegate()->peerListRefreshRows(); return data.usage @@ -351,7 +365,7 @@ void Controller::prepare() { (data.usageLimit && (data.usageLimit <= data.usage) ? std::make_optional(st::boxTextFgError->c) : std::nullopt)); - if (!data.usage && data.usageLimit > 0) { + if (revoked || (!data.usage && data.usageLimit > 0)) { remaining->hide(); } else { remaining->show(); @@ -836,6 +850,28 @@ void RevokeLink( Ui::LayerOption::KeepOther); } +void DeleteLink( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link) { + const auto box = std::make_shared<QPointer<ConfirmBox>>(); + const auto sure = [=] { + const auto finish = [=] { + if (*box) { + (*box)->closeBox(); + } + }; + peer->session().api().inviteLinks().destroy( + peer, + admin, + link, + finish); + }; + *box = Ui::show( + Box<ConfirmBox>(tr::lng_group_invite_delete_sure(tr::now), sure), + Ui::LayerOption::KeepOther); +} + void ShowInviteLinkBox( not_null<PeerData*> peer, const Api::InviteLink &link) { @@ -851,9 +887,7 @@ void ShowInviteLinkBox( }) | rpl::map([=](const Api::InviteLinkUpdate &update) { return update.now ? *update.now : LinkData{ .admin = admin }; }); - auto data = revoked - ? rpl::single(link) | rpl::type_erased() - : rpl::single(link) | rpl::then(std::move(updates)); + auto data = rpl::single(link) | rpl::then(std::move(updates)); auto initBox = [=, data = rpl::duplicate(data)]( not_null<Ui::BoxContent*> box) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index 1543504f6..c8825e10f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -41,6 +41,10 @@ void RevokeLink( void EditLink( not_null<PeerData*> peer, const Api::InviteLink &data); +void DeleteLink( + not_null<PeerData*> peer, + not_null<UserData*> admin, + const QString &link); void ShowInviteLinkBox( not_null<PeerData*> peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 9b31ab49b..49c82ddb5 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -195,28 +195,6 @@ private: return result; } -void DeleteLink( - not_null<PeerData*> peer, - not_null<UserData*> admin, - const QString &link) { - const auto box = std::make_shared<QPointer<ConfirmBox>>(); - const auto sure = [=] { - const auto finish = [=] { - if (*box) { - (*box)->closeBox(); - } - }; - peer->session().api().inviteLinks().destroy( - peer, - admin, - link, - finish); - }; - *box = Ui::show( - Box<ConfirmBox>(tr::lng_group_invite_delete_sure(tr::now), sure), - Ui::LayerOption::KeepOther); -} - void DeleteAllRevoked( not_null<PeerData*> peer, not_null<UserData*> admin) { diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 720bdf1b5..ec076f378 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -872,6 +872,10 @@ inviteLinkReactivate: RoundButton(inviteLinkCopy) { icon: icon {{ "info/edit/links_reactivate", activeButtonFg }}; iconOver: icon {{ "info/edit/links_reactivate", activeButtonFgOver }}; } +inviteLinkDelete: RoundButton(inviteLinkCopy) { + icon: icon {{ "info/edit/links_delete", activeButtonFg }}; + iconOver: icon {{ "info/edit/links_delete", activeButtonFgOver }}; +} inviteLinkUserpics: GroupCallUserpics { size: 28px; shift: 6px; diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp index d050524ae..ec0760378 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -75,6 +75,19 @@ void AddReactivateLinkButton( button->setClickedCallback(editLink); } +void AddDeleteLinkButton( + not_null<VerticalLayout*> container, + Fn<void()> deleteLink) { + const auto button = container->add( + object_ptr<RoundButton>( + container, + tr::lng_group_invite_delete(), + st::inviteLinkDelete), + st::inviteLinkButtonsPadding); + button->setTextTransform(RoundButton::TextTransform::NoTransform); + button->setClickedCallback(deleteLink); +} + not_null<AbstractButton*> AddJoinedCountButton( not_null<VerticalLayout*> container, rpl::producer<JoinedCountContent> content, diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h index 5b4af472c..7059054cd 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h @@ -21,6 +21,10 @@ void AddReactivateLinkButton( not_null<VerticalLayout*> container, Fn<void()> editLink); +void AddDeleteLinkButton( + not_null<VerticalLayout*> container, + Fn<void()> deleteLink); + struct JoinedCountContent { int count = 0; QImage userpics; From 107d4aea369d41835d708a7c35a391002a39df97 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 10 Feb 2021 17:43:09 +0400 Subject: [PATCH 352/396] Add admin link count phrase to langpack. --- Telegram/Resources/langs/lang.strings | 2 ++ .../SourceFiles/boxes/peers/edit_peer_invite_links.cpp | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ec29b886a..7fdf695e1 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1207,6 +1207,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used."; "lng_group_invite_expires_at" = "This link expires {when}."; "lng_group_invite_created_by" = "Link created by"; +"lng_group_invite_links_count#one" = "{count} link"; +"lng_group_invite_links_count#other" = "{count} links"; "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; "lng_group_invite_context_edit" = "Edit"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 49c82ddb5..8062305ba 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -867,12 +867,9 @@ void ManageInviteLinksBox( count); if (!admin->isSelf()) { - auto status = countValue->value() | rpl::map([](int count) { - // #TODO links - return (count == 1) - ? "1 link" - : QString::number(count) + " links"; - }); + auto status = tr::lng_group_invite_links_count( + lt_count, + countValue->value() | tr::to_count()); AddSinglePeerRow( container, admin, From f97064ed373f779900c517ac6578b728cc5ad549 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Feb 2021 11:25:43 +0400 Subject: [PATCH 353/396] Add auto-delete control to compose controls. --- Telegram/CMakeLists.txt | 2 + .../icons/chat/send_control_autodelete_1d.png | Bin 0 -> 1029 bytes .../chat/send_control_autodelete_1d@2x.png | Bin 0 -> 2082 bytes .../chat/send_control_autodelete_1d@3x.png | Bin 0 -> 3088 bytes .../icons/chat/send_control_autodelete_7d.png | Bin 0 -> 1039 bytes .../chat/send_control_autodelete_7d@2x.png | Bin 0 -> 2171 bytes .../chat/send_control_autodelete_7d@3x.png | Bin 0 -> 3306 bytes Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/data/data_changes.h | 117 +++++++++--------- Telegram/SourceFiles/data/data_peer.cpp | 13 +- .../SourceFiles/history/history_widget.cpp | 58 ++++++--- Telegram/SourceFiles/history/history_widget.h | 3 + .../history_view_compose_controls.cpp | 51 ++++++-- .../controls/history_view_compose_controls.h | 3 + .../view/controls/history_view_ttl_button.cpp | 94 ++++++++++++++ .../view/controls/history_view_ttl_button.h | 29 +++++ Telegram/SourceFiles/ui/chat/chat.style | 7 ++ 17 files changed, 293 insertions(+), 87 deletions(-) create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_1d.png create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_1d@2x.png create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_1d@3x.png create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_7d.png create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_7d@2x.png create mode 100644 Telegram/Resources/icons/chat/send_control_autodelete_7d@3x.png create mode 100644 Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp create mode 100644 Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index aef5cce01..235e68261 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -497,6 +497,8 @@ PRIVATE history/view/controls/compose_controls_common.h history/view/controls/history_view_compose_controls.cpp history/view/controls/history_view_compose_controls.h + history/view/controls/history_view_ttl_button.cpp + history/view/controls/history_view_ttl_button.h history/view/controls/history_view_voice_record_bar.cpp history/view/controls/history_view_voice_record_bar.h history/view/controls/history_view_voice_record_button.cpp diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d.png b/Telegram/Resources/icons/chat/send_control_autodelete_1d.png new file mode 100644 index 0000000000000000000000000000000000000000..82a7179ea756c2a4f7f931def662a0082080a33b GIT binary patch literal 1029 zcmV+g1p51lP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0Z%IT!R9Fe^R?91OQ50YI`s6KU zB$AXz85u}X42`7Blz|MS2os4ZBL*T9kCc)D@dqdwP!clpP9C{~>oM_utZ%K=cTVTr zZ|`&OCPm#-XP>>-`mMEpXRWm#J^(;^f6U|mF--mui4qEzk!XZ|U4;IGW31Dsq@+Mw zTN~Wo-on(>6g)pa>!8MR-iM6e@$qrAp1r+2tg5Oq_Mw^AoD4hK-Q5*F7Z(>6FQiCe ze0=;@iG7YxFc=gf85tSa)6;_w4-Yb4US7t;#Kf<VGAG1Q;OFu2(Vn=ds0giSt*)+O zQc{ww)1*eHkBm@eW+ukP#fg)=ygVUve0=P3%JS3GQ_)RKOt^Fgq{0*G=;*-r_jf!$ zKgZnMT&%6Fl}R~0p%ZbWtn}5?)cmIp#Vf|%gmQ9n@a5%22ynBqvVvn{V>0gR>r+nH z)zu~X?d@%)UO~c%^CmPhG9u2GmX<IO2uT0!?M=R=rKL_scArywd3h<APizNzLh0%0 zQn;M1!ootVtE-bqxY^p;vI(gEcwk^au!Dnxo;ZcAo=`(WgE)28N_TfRuC1+MWo4!3 zMAg;RV!OV+?uj!ZG&eUVPFq`BO`T#^R+iXaU0s>l>Lawjzb{UUi;GR2qTlbwv$Hci zJUleD)f2k8xe-EikeWIbJl9}aTAK7@Vq%a>pscJcoG(40>+5SF#K(go*DhIpem-)& zEG#Uz<d$7RLW20>L)nt4zMeNm+DFI+prWEezPg5%Hf(HcKzVsN(7vUFeSCZ<<)K1P zLbUe?o;E=6`Fx?aGNxLA(a}*@TwDaI7Z--22Ai9kaCdhHLqkJAXP-;28)!oq8yky! zUh&h}*{Q`@b#iocghar`5m9`7eZ}71UYwbki6~`E(srQg^JYllmbbmV9Vt>;Jd<}4 zIyg8$N~PdaQ&Z(U{89}uTTVqwOAB{lcz76gc6OktsR>F-N`O|5$aM`}Q{d_8Nz{DB zrD*A(F1hxP31#c*>+A94<V3=5HN4LxCnwvO&<IUVPRh~K*w~2u{rwT8%(JMSwCCsN zp`f4unwy(pc6Ju%9xcU6vp8X{J{LO|^z`&JWM^jsrO>5U4S4D3J*q4WWxP7+N(=Y* z_YqlsJE4eJI6x7n%whAb-6)}O;f+QpT!j7tuT<>(@j*7<00000NkvXXu0mjf@|f3K literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d@2x.png b/Telegram/Resources/icons/chat/send_control_autodelete_1d@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7b18d0760a4c5783ed1242bba76d43a829b5db36 GIT binary patch literal 2082 zcma)7dpy(M8~@BLG?!kaMkFPd5JNIyF0o}6O>zlS#$1zfX`wMCEAmA`LX^;qjEF)t zS?=VLJKqU&NvOHSejoq*e*gadc%SDv=XsykIj{3P@AD+sVXehPWkmr15JRIZFMt-c z3t=H}ytv4)1`XuK1#5GF-f@TnPAmydXm49vKppgj0Z6<rAh1gTAqxTk`12qDKWHJl zYk7SCjiU1S|L5-#lXa|?0ASBUw56Ft1Z2_0w+QPdJ+@Xmy{rWPBC$gGsqdA31jl;x z0HsEiBd8y{nzHg)!V<;0T-e4g9S%jK&CSgV+u+^`=~dYAK`!ER-MH@inX#oUE`80| zf6;TXDu>aD@Gpz1h?69n;vywk++u@1o+2LT(GcWMhM*|q!a<rMf3gt7mf>tBq$tLh z<;{qNB4q{i+Pwu~WIbVGR=cQrC!kO4LOPT<pbM@H8sKV=c6&V0a+~;)D2M#9I@1|E z(Fh@q)`sRI1j-GagJn+z2$y*@ylu4S)!*TXpSVPi8yOi{5pt*iY;(>yH#MDYApH@w zN;>46gFGjbck#93JLGc7_xdQKUb<UdTg<t?nvSm>HK#+vm&P=IjyH79-G><jW!Ho) zW)X+qmcG{HP%W2wcDA<$V_`a11RUs>VbkqT_joi!69Sp<@9-3apa+wSk|OwfHx3q@ z@`^LLF%9MSoM?;*QMjxb{G(-mKep}BVN?9?+3t$m&t%Mi%HCuta>p|xU49*{SbhKg z8f1R;W!}!V^LQNjoOm9Mrm_B;Bc)sK{(+QZbw1ZbK6-nzX)}8L7i491)p*NCk~RhS zb>|D}uK#F#qLwJH>-})=3)ku(v%ZpxxTFU#S_H&zutGW6!*{5%xb^-Ki+kGSjvV9p zIksNVP^E7GR7YC<^cErc(H=!oWPhov%F_DMcta^GV=nN=0ZkX?!_jh2N&sUk!mT*h z1r^p1z1<@_uY3~kb_QtA)U~cM>#=#F_4r;F)xI@OM9Rxm{pIURW#ZvKN9^N2eYm}* zsG_1$x?Jq_s#``r4-q=oI9z=#6LQL;Y*t(z0l?7Ll&T_(??sD-FAnXG-uYWB9>2_P z<{1GlZv$Ysk*|15sGy)AZ4-Rsm_*mQFwNBKTiifaM~ApZ^V0G4UTasgN{`L;<)gU2 z>&xtiD$}EN;k2MXbA2&l{{1vq<Q6yaD@@<d0dHu6T>qLN)J1b<WPIw(HI4V{EBW{~ zaM<ZXDrf9NU9P#>yg|?i%W`qJ`Xqrz#z%Y=m9V=jx_{y$We;X@V|B(g)y{o}^X(O_ z-J9<IVeTc$B^2aVH#uEeiGqdoPeNq%h?2PU{HfZ|`EwTW5{Em60t?H_?YJVOc(s5C z?n0(!OD%U{P`C2E-n4{7@~Y0z`&$f~Zri7)bgt4p8iI^teO0GZ2#*G9Lr<i>3!XF| zujgh64+I0<g(+MsKh9`fwT(Rq>eHDcrgA<<fV5ZNub0)QQnXUZ*H=MkO=rPzvy$R@ zV4V~rTz0Gyvv8P$g9nFNieA>#oUXu_Mp^hsZ*dnNz<DxltHKVO*UkzRgV%&}YDByv zvoS=nHo}{fmX@Y%k_v^y6-ycl*6S$C8|;(Kb1ZeyaqHTEgMmWtrso#%Q_|qZ^qV7= zGNRBwm+^F`B9RD8WP-u9F=lCQ*%`z?O{y*v9q#yY)!^LeVS{rPL~?!PhEj<`vC_;J zW?f@@(Nt?nku#(4dtG>$gXeTxCf{0MiiG);0Efm6MNDn}ncw>`yHD$%Sv6eYeeggR zI5zwA5$%Drx9_R@Pf)>}wC>b+q}{Yr(nEr3l8MOCnrUrqeITgl@_2rr>_A3Ka>Zd| z(lO49gf6VCloRF>rcl=eN~sII{&U1o2z$`*dXk8koxT0OZ~UF#V29Jd#6GHt+^CrC zEl$<fyKj0lew6NJWM5z<=C85QeA=c+H4Uqp-3%vssXC`A+*<zkE;LE4+BTQ@{ceQb z_df;S66KA;Scmp;C?_Pd^nCf&t*YWFs|)Qmj#R`NQw^c3{H&RE;G{eB*<=BmfFR}Q zT)n5?++AS(GA0^$oSxodq8<}-Y>Qdg2INaT&nC?E7DJggKNdzO${D0zJ^u`$eD*eE z>WulPW+~#O^CJ2_KkM26r2?>?j*1<Ly^w!>%BQOt{mvcmJf=C!VNw)uTi#v12`>47 z^booL?)cH#+|D8JKuCu4X83%6JH3T<add8B<Q;C}Rb+_5=8NNW76OjUpvXw6V7-iP zwW*6@%<iL8kBeYpP)+P(Sq9g%nkHeHFA^FrYW#9~+IN?r#BV88z>wK1qk%`*HOB8W zQvJ2{<&5mqf6aA_ImRNX1x!9kwTj4%kT(&_SDs*vNxvm$gPTBo3VKVIiX9hxT<Jl* z&^tSnDe;K`kBLvQXENUOyz>n7A}xG>L%#v<^BmG^(wLf1Q=Zo^>p&ki3}MO;qAp<6 z@UG%f0ww9H*sSD~Sav=KwE>E>B&q&)x##CIGXoGyc5le!C&3J(aII{UDA`%IP*s`{ zIUvs{Y=J1RNQ$~ml$%)FelD)qawpgH&X~++I@nLH_=NnBQIu1q+G}qECmd<ytIbJ} zXRW5UR+#@TnsmGtSDZT^0)A4;raLd>VvY#q-JvMkq@%i8jFDJ2d-^M{()XK?gQ@hu uq362PqDEdmlbgjW%O?OmBhLSCv*Q8d8PzWZD5i&Y`!o71){<_HkNpn@yw_*| literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d@3x.png b/Telegram/Resources/icons/chat/send_control_autodelete_1d@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..95b78d7a785597d65b00ba8f3fda684d23800f47 GIT binary patch literal 3088 zcmbtW`9Bl>AD`Qti%m#$yIHbaE7#1#<O=27Y$)XzLb}~o?u;bYsIQSEw4xo(<S6Do zVkGzV5fl2ZU%sC|;QPbt{d&D$*ZYU}^YMDV5^Su^MTKOA004lf1qxxu(Kr8!AV23! z^>*swD6U{Ta}z*Sx7<AEg1|dlc%#t(6^<<k;EKBr;QgDz5m}A^06c|U03MF!`s*v? z{$KQFA<w_|-$eH4h&%uwe8&P|>=4ei>=ybGf(F0v2`d@Gs@^^^G(IGH+13Wk1KASa zH|2v!zM;}JK;RD`9+#yRx}%6yNK>%Xc1@7?YUAq4(rCn_i0edmuKwol<uBd&2|3Nu zthGMtL%iRVi_0B16}a=DE$)11;u)*6ykL8$dMKKR6;vv#^2cxjAR8;Uak@4p@pvJT zLW#Lf2b&t54Gk6Ui&l<(h+SBaDg)Y!aJBR^)vYAUq`4DcRk-}ys70`|kk}MUxl-YX zccX-0U^n_bC!6kh#fcroy43sQ4IdmhywML@OdcJ}VxtS-xqo)Ik_^U6%u~aA3NB>7 zGUp~%m<~MT;>m}<-&3^C{~o%~Z_(NO%Gj9_GQaa7Y}J+5_VK3&e>Px#18XKRLL{^4 z{$pf2(j~A?K}~(Y<c9w^g#`P?@tLQ}7tDn7Vf{L@Rh{yVS?6Nzt&F|z_zDJ@eXgX> z%;0WsEnFFB2=c0M?(o42MoF1)f5df2oF~knejV&bYK?)Ps$I=Esz-<xmC<SjE3=(s z=!73%EV%ck7y@<S>sZISn^)^RY23f&dK9$eh0fvHsg7i4ns;}8M@B|Q^g3O>5HWc% zY{hls9L#@A_u0YL;^17^%Ghz%wHTMWn^U>xeCkMi#U`sxWibhq=AIXZqq5DzZ+)(I z<-ufbXS+|<Pc%lpeQpp|lPq&?<Xgpe;J!;tz`?KO(Mr)%8zNNUt$=X~EH1$1l*U%| z{rx*)<0VjWH3QtIUS$M#2&+|8C}5%*W2)5zB(OJT*^QCg?X_irzm|pzB8U+&GXXom zC>pl_qblL`a=^hJyX0|7P%mzF*0WRwrhs-%9!JLrK~Ut$j}emR+I6WzZ+*mb#f82W zy4L%T6E`i*Ht3UdPvsci-Jv&LL0tLjF6Zy>{&QVR6MQjz?IDBrLKQsfj0O{#K)RHT z-QV5vDnX`TQ)FRS&#@RvpV(47j#}wbk9v=a_wzYwa2r<~LbA;9PLfiWI4zz6@6}PU zFSEKkT!XKIRnGOi2$o}VsZ=R+=PK#k`N!AERi)9M-%(sl(qF~Wbf66V3_QE2W}Y!A znpXN)1T;|TqR1=s>7?gs@T0Bi7GW(kr>$#{7t^i33v1jUojC48j^LBi)TI{+pL$d; z^y1>Z8*j-u%J!|YauEmnzjb=jPnw$)KDJQJc(IB3oTUt$RZnmB6l4^!$3KKUZ%8|s z?aVQ!%AUU|EhC^No35+Orx^Kb3F%iX1P~U|v?(;A<4YZMQbTB<^WkfrLWhgU_|B*N z&c#Iy9+_@??g?@unin2gRzlg)Q%0<gOvMwfC^oAtHDZb$!`@z<>{#>e%472nt&D%@ z(7JeUH!HAS^-&-@TJ)%X7B6p%Q<dwX*aS*%s)8Y53A@i-aw%Iy?kOI10hug4<)U|S zUNr}*O{Ra0<7G{^B();8IGDmUpc;Z$$Vp?cOpc}-WhU|}P(m@M`u6fjLg3u;XuS^A zceJh&Dh7GDE=jIcy<n|~5R)=@Ti@3(e6lwqar--!OG04C3&VriT43Vf<)As-d|!Dz zDW#;QV18xt)2dqc+T}d$j&sjFX4{CVa5@6%g240^8lMMk%yclmjvVSjKi6kI)o8uc zn#KRc4!2$nftUSre_=X32TtdFAd-7+fvHM}Xfllx!`-zPtTTEQeA1Tf@#+3nXd25H zsiqmrX(}AoSAO}hN^GQ{S+}a&<73jhg?bBZsS3FDcQ=?43rpt1K{LnWs_$$qq(jV) zS8*qSn?ox&z^8mG=F*KP-Ca6R8mw^AEb1<%w5M4b`1dzPhV>3E2WE<Vc|FGU=TtaO zFvrL<q=PDiLD5ENV)F2x?_VK_n0&5k$fHNuE!jfbuvnVKNpi&I58S?|7-y20=zgK5 zo!aADrk*gBN>~iT1WsRJ@YXVJF&mi4gx}k%<;_Lu`<g(;g(avHAsz6`n4jlR4&3VT zpVJs<@d-U@<1!{}$Ie!)JI{8SinpUQ6J|fBL%2*emBlkb;#c5zcX~+~@hB%m2e)$c z6pi=u_{WG$i8yQi0q`^Ha4KE-t(jnNCNF+!kM1P!R0J7%Tvt0*9nkdjwCXwM0Iz$$ zf8lkpZXa%|M=b}{BqpkQC`Z%oKzK$AEz|F^H_nP!T8h=r=`ALzOGp`#uJmooz(*#O z;#7NVlC-x}9m&Ls|8!>y8ZN5gr`1Nlc`!dmewX3W#)#r%TZJJ;d*WeD`2@lgov-eC zUs=2dNnmm^TpIs=LLcl<<vP4w1MuyEZ~o}&#MG>O6KJw5m@l67ixn8BmnKV?nR<Tx zX*)3LR(TB;tKLy-@mWQ&{VoZ_Z{X9RUwea@nAHjTHQ(1JJv?=vT{}|>;xE61O70cX z+-*t++8sk=4(00w5B=zRCPNM&9gEw^_+D-3J7?)3cFBB0*}3{y+hUZ`;5)xT6x>hN zuwecB#1tdWtz}xny*z3ANbKRa-`x-RMP-K^JA?MS=W$OY)@ReWzrV6C&)<UXDPAtf zvXdWR?Xfqjv}hE1QDjJm*J-kSxwYJ8b<@LeS#VEoZcdusD%CCnS}oCFq{Y_{gc!Q# zbD&IspTg)v&j%C9!8Z(Ut3{_AHE?X8fjf6LW|M*tLckjR(?G`a_t0y#11C4`E|0*( z2Gvp5uZMe!jB}nlIXa~uwj!rIdN&>^%~a{C9rzTE_#81{WkIiQ;SfRVHJ#DT<^)L+ zD0WsqK&ntfDhWBnnMb#F^W@=<!rPJ3<E6fl^)=B`?8+QXkG#-#`z@wo;eII!ZkSs5 z%YkV#qOW4jX0uP`8MLnZ`do+>FjI>x01y>8Z^k%<+pT=?7T19tL)aH1fEBvnp1`(Z z%T&&Qu8t8tJTjwu4|r$TOa<fo;nrx~*`Ye`ikI`W(?kx)&O%p-#Ws5+_hJt9r&DNX z{Z<JP86{(-=g|I$owX?uq(kYhrw7ibJO8YI`<auakJC<A)n0qjsRSj9TXh-<GE&ri zRp_B7K10;6Wzo>ERtTs@{kQ|$IyV!#gM2gi+QXp4Y|kB&j=Fe(*F<_Dar{KGY|IS? zpk)V{ZxE)z3=j*QAjL-~xSuD3CjK1C5>AGaLGB2>fC+dHg<0kHY^qHzmMPM0b`Gi& z(VBBeP(b9?Ft$P<_!-)J3#@F{ypX*sr-X6NTxYc%`Q(f2TVPha<?Iex4VM#X7HCGH zLEv{Ie$(qemTLBp@grf}=H=~JPsnpjj(n*wPJ<UxlN~jf$yzVU(uaF5hB_6$`xril zSu1Ynr$<66Bl3Foe{YAiWb27X5tfbk=i7K5a8{JiE4q$c*JM2{b%CYt<kuyT&m~Qd z;0c79N;OyGa>v0wHO!fpz07o}DChX3*)Pr?qo>}KEe^iUY$@ru!H5;z+gWGPRh4lP zQad^#D1x4DbK6>oVD}(Gr}DkTzGVAODv}C*2J@4;dg{JQ(GhL`LQ_A{6<WxIfz_FR zY<>NHqy_xv_qtR*hkTKZyAaKbkJJISTPgK1xzbidD6LNH$4$b}Q7k)fpQ4WP7B-zI zanyyej5=9E_-^9aC`OH8n?V^!X$*+hIEL)gBt>JI0-@<FLK;&m_3($Kk#|XqpS{#7 zY)j&!Luc@Fnhx4sdVA&qy5@+ARu1p*=!sr>^b%NN1AL<fj(P%ON$NATF-0ZKF2~VC zor^`n%;=aW5&<#Vn2DWe4F^dn{YxJq-=ZT}k`x=X^G~I9N0j7`l*>O&VE=c~zW4=5 VNy^?fq4)1LZeeDPs50?<{9j%a#PI+C literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d.png b/Telegram/Resources/icons/chat/send_control_autodelete_7d.png new file mode 100644 index 0000000000000000000000000000000000000000..2f3a867fa9e8ce8a45b9e055928c7d32eb546fe6 GIT binary patch literal 1039 zcmV+q1n~QbP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0c}YY;R9Fe^mR~5lQ5eV1<|p!} zNEZ2%+|1N2<VsO<$&HjdcaoMh+0>+5yBK#XDRQ}7SSgVUlJX~oxRHgWA(V_(G~xN3 z=gHpe^`3V-l&GD0-<?0t`JT`FJZH~22F4ih=hqbDuOWE{NdqN|NZLbx&O`r_5sSw2 z^YfY0>12^egiTLRvsf%9GL`PfJS6>0PEJC+9335j&1RF<5UeW?gF*)f2gGx6aS`VN z?kUL5&i*dg*MM$sZwaKNqy&bBhT!q>k=o14%aD_k^BpL8AoK#3r>7@nVT;8Aac`}y ztwC;Xt}-SFjc7bIpwiM($jZthk?QJd0y;fCO%TQYv$HcAo0^(R7?XfX4%FY@53jGU zaDILcRaI5c*49RgVtCXGp{L^EYiViuG_JPSXzK%2R#w9E^D_b9Yjt%M#>dC0Jv=<D zxuDzart!VKJ<V|qh>v#qKt7+3#OLPb;QIQS>Mt)Z^jlwF|4~S(V`wigFUjT$=|Bur zSXf9K7sFLkQv>bo?X(EKc6WCb0Ie?`85tqlv9U2-JB_(wpw7-t68*SW1_uXWV`BrF znwoSMYHn_(xtp7tx^_}PGcz+J+S}VJD+*<0Wi<Ed>PmL5*h9f!kVI>1Yh^{jXf#4N z9ERiLW7)Z4pu4*}0^$d$tSH&z9xNy*keaJ|LE|($#EkK*OTT)r*49>bczDP>9*<&% zH-Y>6d!?O9MHh(Y5z=UEY}D1_iIdIG&oiDBS#fc(u5BuVLYF!m4hq@+{yrEC22DH6 z+z$^AfJqX|=;$b&_;{>%y<VDwW0>Xp`ueCnF)=~61%ak`4`pU%0t#bsxm=n+{EKOy zl$V#29RgwB%E}6yoSaa7XJ-dOp%5%BEzxGjn}Xl(7wL(CP#BZp+uIvG_=SS6f{KcY z1fYR|0czLP)zLX<x7#5ajY3011NCE$Lw$`$vopc4r>BR2Pzl4=)zt-7s}(XbG88*C z5N38P7@nJ(8@eOmnTR({JbH?Xij=u3sN_I6p`)XN+)k^!zcA0_<>isE8VKiLm=Goq z2$0S8_BLR+*4NiXFrfP{<DyELot<T7vzc{wcQYP;#_!Sem*#AWjk>b9xX4T<6XO6h zw6(RxxM$eH!h+gVX&ZP=tf-j%TqR`>{S;CfD4D0z9{O`0`VU}|D_OgZlS%*p002ov JPDHLkV1iEJ(!~G( literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d@2x.png b/Telegram/Resources/icons/chat/send_control_autodelete_7d@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1837649262ef17b5240eb15e04d8c63f36e73b0e GIT binary patch literal 2171 zcmZ`)c{tSV8vc#3%x4SPw?vD>*b|LyF!pAQrL2+V6GtfKlifs!BE~MuAWIZd(Ion; zV{BmxS+b4M*kUqd?TkOpIe(n%{PDcc^S<wWJ=c4`*L`14G8$#Z!zs=Q0057LIl`8i z%})Zt&RnmpzcyngP>8LW5l}ZINn>she>z(DAd!FqvxWemBtL-l1i}n)W&i*f4+6l< z3_3Z(v;61Q91s3)eF98ZGyMSo97`4mL;En$y0cleke=XBWT;8BFw_x^n0eQ5^GB;} znYExHwE7`l8G6%2e#5m|UiR|mgw{A76C3E9kv+l$0iB3!ccYgC{W={&{gyv4P^8l~ z8_TkbLZO0#hjL?ZEN34)a~Id;lixPC&irj6!`3@0WQ(=MLLrV5ey1!!jWRbqs`TX9 zdgHJOMLT#w87>y#(8>6J0~AQWmzrx<SxPQrWeX*@I%hJz*GC`h(Dxau0`uf5>);>X zS{t2S`y{XMbx^(ApWkR{irS-BfYbJN*Qc}yQG3BoFP-1w*zFquWD)F3*Lk_)g>Eh< zS65dY+)@}Xq<GyMi&OGg9edgM&o4ExlGR-fuN1D;G&tRxY6@7`m}!CXbWObS>i$ye zVDd;<Nj=Qs;<-u7$elG0DW9%~{Ee=J;NWi`!s-3FDvvo9W?Jqw2F<o5ubA`P93w(9 zGje}zEfU2bNBayi109r+tK#kx#W(vgHn-d~b#%{_ouZ@Xgq;T7_>ZTFnIFg!m~Y@t zZ|Gyt$Vp2`NHqmeke5$CV-VxrGhS^YS39ENGmvf&d+Z&~?4jb)bUR-&R=VAi7jK#> zt`=Wn5WC^O_I;#W1O7hzXXOiYX<QH?uRTQs-;*j~HK52?9si{2IgB^t#%tUf@}}IC z(2Cy2TxVx(;@pS|f3^sMblw^)(0tz=d$hl__Bf+1%l(Znzeec#<lBNOy7z%>iI_}_ zL%rMmKA%hxbtP#mbs*ns62EAb106}k6Z1Se9{f?TT!pEy%%-eQH4m22hl+F>F|^V9 zeFr4ozx$=|Wo5E^)IkX!tZW0KIdI0g!~6xpr}r{<tKi0PY5d{VVv>_<+8Kl6kD%ev z3ak8uz_`$^;9r~b&^;!D(o~n$2+hp9t7A$|j*g<vX%2E>&z8sH@3`%6&V%g^H|IMa zvv2LJyW}(lFZB&4YWfbtV~+N-Nn%%my?w7p!^21<9z0VkY6&I!(F%&!U&uG#{@O7* zgQ!9keGsH0-J1iYPQQ)Z4V*nZ+unOY5e8>6!R>C$cyWFzF_?YM8dF4kaz>+Aw$7D+ zLuSDJn98#CEL5Sk=~|DCfL3Nd8rTW9P*%Rw`Wq+lO7;Cp$y5I`W$1KM!mEHVIwe_P zDXIlS6L50F?w5lp(xwtxfdgnvXClu)p8t5=6=PMbr&yzX{@_zBjeIT<<?VsmKFVe) zJ0EYhwnTnu<nD%8il^vMv7U>xUWC1;P>oA-plNYKUpYsC2e}e?rGI<mtT_BeOQ_~w z7^+0b_?n%^P|uvaSE5}NY6(iVn(dYDF4l|04b0v@)Q!tJE=ng(93AZDV_>WrFqEu1 z{Ed!JM4^6k!N}e@fRA8=5qjgf5;mRD3up#RX3>l?Rmy)_r2Ai&)V>3ExVt%LDD_0g z7S1A<%0(CAB7>~cRJNF|e8>2il_cRbX~G5j$~6h)gjI!82NU?k@XRlKL|^J(eNWm_ z{;pa>`*>a;{2}2Yr9jiaC-ILWdiT!yRClRyQsJ4RUC&G@6ZEt50vG)>xFxk!agw%q z*-VQ*joMku|A+Ia97hiE#)T3LMH;~i9TTNoy@e5so334;kTO~7^(}25eukY0{ACnv zw8A_X0<j1Y(>ZFnA?|Z3?zh<ydhGByChB#7GqTNTNMJeJQQbe`j`4h34sE17iOP{3 zA641U+UY-#r^X${hR(czbc7um*GNwfjdqmoVTxXc_mT-Aw+1`;pI^;ae_T)T-ZKS5 z;-<%YMO54#Q|3Fvd5CP=9f>>=eU}3!eZBZ>fKn7$OD}L*JK&r+QR*td#J%`>u(M8H zY6LpMGL~fopgBq|iBEVjRtlBKr<F83|5^$y%`JV(NH=T~j$Vxrt}w2rGB##hh_sx} z<jhV~)yTXPkrv}ld?I<JY|&7bTTOv4<r^WSDo_le8cSIzQA+}Q7wAE1D>zS`(f|Cu zSm)l5MPff&?jy*ieuwnC1Y}ZdYXk#B0Cp0=T}ln8BAD1QmFaC#nv_nWP23U#3~8>R zx{^PHhlF~m-(5fuVvZs>WNw?_e>8Y>N{Yw6gWs8&Gh4pYW!?bKExzO@phL=Zuo*6t zy<lAo_0=?NnqUdtq_&H8LX7Qr`hPBe;#xtXqPI6}&|pr@!*tQmuqAFjg$j~0)Ug4u zk-EcOs;tb^wqmcQgd&=lCPiipKTw`*3RoT9-myW~j{s-+qYmvMTzq{AE5R{)GY<Xl z-R)SlN;l;f3fNUHDLB-vIn~ed86So5*LEauiBOnCBylK)b0ytnQNS=<Uk>(b5e(_` zI<?q#(XJ=YeXUfr-7EQCd>|OI2hwJt>M*Fj8`A`5;bLLALhZX9dkaD1bvFur)RB|Y zL}ctAC@wM0;@^EJYn;eULc<_%s#5J_czL0^eX9^!_dDNK4J-zmg`3!%r^-!D8M8Sz z?Isxl;<{lC0`pNe9HHczz~)bqDn<<p3Na6ZjsaWCxyrx(#)5E{b8xPr0=x7l(t5$Z z*~CM35JVk0=g08u@zTL2omN2TDVm1nWf=Z`#MiI>TT3pR#oKR2@P8)d{~e`J;0O%J Vk;43OMW0Uwzl8}3QD<~B?qAOi3ON7( literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d@3x.png b/Telegram/Resources/icons/chat/send_control_autodelete_7d@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0c54ebc5f3624f84e24656ee174462ba3f2b5171 GIT binary patch literal 3306 zcmb_fS6I_cv;QZQ&_WRcp?5+F9Rv}O66yU#igcwKqzbP<6p+w+7ZC_mktzzxCnbQi zAYB3JRq0Jy5aEkA=iHpjb1^%!v-9lkGduJ9JsWRkq6?+rpaB2?s;`H#AZhQvLPbd$ zQ{AlxNeUEbp{oVd4smUeCMY)>eRpGHK%8V#0iakffc$R=NjOLX0Ax52Kt|G_zdjuJ ze^$M5WdCdb4J6J?^8f&yr9Mj2@-b+~0Yddnl`$o3ul01Eb2dzHHyT&6*a=D?WATqz zMp=@x(Z9sK{s<A@Z8h`vWjURnzN($dXv+2Y?fC0_vj|_hv6f-KhaOuE=r`<yk1xK` z(o;xN5DE~Nz4Fumu{Kz1f+_&oJW4H*+6OK)^=V#wuFG#&{~uiZWXO=d)o<N3X2oFi zLYrEZeY4!}3!?nY2VdTqdKa?aCp&g<-rjd#)_<_RSU5XaY1~_AN1yoZ&8yWI=1LWZ zq`)8X@YIJMZ@TsN`-iXgG1ZXlD>=U<0O>VWHa(Cl%?+4i7uv(o5?0kC0c(U~X)qf- zo{%M`?ZTXXvfNEubH6v`io<gWs1E!&#MBX=6;~s9zbEO6+^?8@!h?_%&t(iU6DmNQ z?9Syov|;kT)j1U*V&!-^@VMwS8pn@*{G2L}M8l3ZVR=4`v@r3{^jymL^V2=wG_4RQ z)R|q*l@E4tevEE%?f_f0Jodl-@2>sH)}(X>LwtWG!ox5J=`vnnd|y99i0M!`R7>>y z#P*$gdQoM%pzht(x;s@l!7Dw`j9f&mQ@f*Kws^L%G?2Z2TI}UgB6aZR_h}BL|K?XQ zt8xQajDuU*p4|tZxv7a7JKF>nF(k3eFLU1))<(>~{&1NyjvdlXyXHIJ(oDH<Ju^3< z+OhR+)ULS=<wSvVJC-@la^zc;wb&DM(AI=75h|REw6*vYw8fe?$v5;|R`4FXjxBY7 zF$V8kkBfAf9I0D-A!*mtF3uS;;tCVP>;0-*6UEyHE0r5$3k3bSAO=nv^niL&xNr6a zq{UILaVS3cC?&12f2}>b!-A%Ay(^X>%))Q_y;mbM!X(X&|6r)#CWq08S~k1yBA8Q{ zRm%SP@%HyjDChMg(ydY>{n-i&=$&<im`Q0c8M}?Lzlok{l4M?!xcgo-|5kXLXjtdS z%DGOtQJ#g}d(ZUy##;v~y&Nf<8WJhILwT=dFlT-c>Ffm)vZ`20W}i2D1rsH=6JHY= zCj~d=EBW+c8wNIw53<e7&$vNhMcTHeg{u6&6uibhcWhY&n=2*4mhOoL7x@e%$U9vC zQBrA^s-~E;{o?Kh$7&l{_3uenX9wDbMe0wC3@9>lYzx)oTspDO2{}~vFhEx^aWG$L zCzOAWP1@009KDE-6k@PZFe>!S&XjUcX;n0bMNr%xC{zpk5`>bl!(t9V)lThCvmCMS zD~$6u3D=<Ae_m`apBk`?JZ0DP*DUXc&_OsDcvKlTqRU8y?KF^yNIh8_V3TV#FV$%i z^CtC-EBD@fYhv?EHt-RHr=){xh?B?XN;zDsi2u)slJC6dRQGv#(&5?wx*n-bkag{2 zT-ZF&cWjO*c7gp+E0ZI=#Vb=wUbRqEMPeL!qWZugUSQ=gnwAYyC!w>r3Z8g#M<RW` zkyK_|1kdjc<!P}78kcb1>2%&#%q2i#DgD#-ty8l?d;H1vckN9O28lGVX~W#;_dqPa zMA|mE=CtteT))>wYVw(?@x9&@Zcf=Er{hmZT^?tFQppF!z6))v!mdLvZMD(83!P>9 znMk6&dRdjKbB;h2#M;lA_$NzP%k~h+R}GP?jPMvPOdt65aQZzz5h^eRE+0xp9fx0> zjZSKskju=rzz+dtNiLH-x!a=fbH)U%GK{RaW-esVoRnnYs=B+4Z_M0pZJwCLaN!?J zRO_oNU-O6A2=Ue0?aVfhr?Wxv2R4M>-BcQfPr)z(GL)75k=IPJO3)_xp$eHwKmLM{ zw;%h=F2;|VTql>Vno$K-&FxLfK2yO1EJdghU`<YG#i}cHgf_zRLF;>xIWvbLfub2h z%r5d|+G+c2djKnYgMod!A(qckF-cS9QI(Fhy!iHfYrX)2ZYP`?eqP^Y-wy&$LD=a~ zTt_H@3u#ak6D%Hd-h>Fn=O)n9PbLZU7KoG^BF#(4iG0rM!W;3Z&&IHS_vVA*gur7R zqIrB2GPBP$qS2GJ=BwD!NX2deW}o`{aU`98i#|AL>7sKI$f45-;#XFnWCNVGGVZ{u zKfL8p3zgw|TWjB(5gGQf`i*L~1ci)E-92Z{M^qV#XsZ}6aa0{h%%Q|nx9^o{QAw`l z+E+zBw`F7ijd7+3l+3#6;~O3~(A8}#oCfIK{5y}~hDP+{i&K}xGatbyb5^&MaYByd zR+t0M+a-@w6Qj;0yJ-|{`kkuj1VgOC-;Jq|9BPEeT_oV{s~2WJ(PIxHH?Ls9;{$Ur zIqrim(jT?Q&wcuLQa5N_;;N29ETW_KnuV!nG~1JfK02OwPx|~Dfg&V4<c?~TOl|J! z+6@aYl=m=!rZSXh#nN+Q>KH6*mMU6F7<%&!ior1q*XFX0=)79&zDDT(#qK|PmwOD# zvm(CS{nPItuzR(Fk9L@ZDLbKKd#OBX50(P?aT1sE9z)8TWCID5bEQ!}5W18szwHXm zB0Yoa%ICx8Jl8}_Ug{m17OBs5NKcn*f72aV1t_h+u{g@e>0&J^9dV7%ll3mzP$fM$ zh;ER>=&K?DHbW233E5xN6s!t~u8h#8oLxpyRHtS@pGXrUF8O77l=vYsM_-~x1*EIk z+XL1TC^ONApvu@_L;0*59z$H?uv$`~y0_|J!QMyfqdE;CJL9iXwR=cd{d@yM&W#-r zF35!`{WEn7I@9#<-TwZC)Jg!*MbJ%xcihSpR(JpM+=c@2YsM6Yqlc9OrT<Zx9q5r@ zYS&yB@get?#JqO+)D&H`nF4`wxGpUX{L7vTf$uJao*#)_8M$*Q-B_$tl!{Jzk!ry~ z0VlYeCZb;5aa31T&SW^*8~^CPg1i{WkzDzHth{@<I&j31D_0HSKLUJlKCE)PMD*s@ z%HvE=8dhXqCRy^{bmM?U_<T!<?Z#NS@ryZAil$4sOCcV6ub)v*=HmwpOJEPxoytC1 z;_H`_IhEx6>;&EL!f;EA%L`)v(PL(@aOSBTbV3JN%mC^&=IQrxlLD0rG&y*1M*1z| zZ(0}DZGHi6nJl;c#g1(f8<p8OJGmlE3%{E7iLfv0VMDln6Mwxb*C7+KkBrzW%?sY1 zl2xN0XUU8~46*MrolHa<m9R}B@wbJfB9`*weh7??J|-Tn-S@LdeoBrtGVEJ80&$+f zzwk<W%cPC$Esek^?YsvSXk?5&3nFFq7njxA>Qz7LC5n8ekYS-)kb*1HxM4vw`AWV# zH=mq8!XaX(Kh?}O2g+aMbVPIsd;-B$dO9zLjpWbX+^KROlX*t3_Y^7F4V7WxU?_R1 z7Is}H8pO-;Fr7p)uJzI{BK!6GBm_I?;S-VSIi7!%z)h^r4L3i?OuPLYE>g;{A<;BE zI61iM9q$~!)irB;eJ6=LHf|kd$Fb~pkf?&Ns47M|0iJtJ%WOM=(<BoKbd4``kb%98 zi$NS@Nmvh+Eh**h%@b?sPoV2SRO`rUFV(St4P15hvx0O)XbB;;H?uT|Z#rVeTd*M8 zvv19f$9?s!uhi%JqG#0a=6vFYJ0ZU}Do+Lzb<{@fSLQo4^whlPTWrIb8MsxBVwZQG z{R6W9!PblolNcmC^o7<l_bywyV%n4Jr<Tuv9*4_`+);ifXG=+Pnte&$>Fs?}PziPT zj6AC`ZPy%5rUlWhsdD#pXE+sc7Wb^EXBU4Le$|5)QIGkg?bjxv;+6dK=Zf6r==#pg zha75IC*Hzv(?KyJwZgEeG3@M7-2oP$i`HRNRybZnaUcrdvBt|xMUX+QhL$4HDhSQ} zJ$fi;XRbuEx1G>n+LQd=v({pEwuA{g9K{N`aKYz5&<=FEPPY?SMq>wV1gwrIQFJ=} zlm2ucV>{Y&aPxZ5rloFhT?A3_r4o6^Tf|API2J`lJuV!Ba*9l>1ZcUyzF<*&%Z7xX z4~5Ml<6~wG&<|s$$Uq4^I2NA|(8&0n)4a3;u_}|#lbc0^j9V|x($wSm_eCPWU-H|) zKdmAt=p&UtEY9;sFy#+mq#soaH4gYyQHO7aIY(+Q8Uqy1m7Y)x)%+hvE#7+V6wWB{ TVZG|_KSTev3944h`PqK~No)L> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7fdf695e1..7af4eeaf7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -982,6 +982,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_messages_ttl_after2" = "After 7 days"; "lng_manage_messages_ttl_about" = "Turning on this setting will make auto-delete messages from this group after the selected period."; "lng_manage_messages_ttl_about_channel" = "Turning on this setting will make auto-delete messages from this channel after the selected period."; +"lng_ttl_about_tooltip" = "Messages in this chat will auto-delete in {duration}."; +"lng_ttl_about_duration1" = "24 hours"; +"lng_ttl_about_duration2" = "7 days"; "lng_report_title" = "Report channel"; "lng_report_group_title" = "Report group"; diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 0a162906f..bede05e23 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -50,46 +50,47 @@ struct PeerUpdate { None = 0, // Common flags - Name = (1 << 0), - Username = (1 << 1), - Photo = (1 << 2), - About = (1 << 3), - Notifications = (1 << 4), - Migration = (1 << 5), - UnavailableReason = (1 << 6), - PinnedMessages = (1 << 7), - IsBlocked = (1 << 8), + Name = (1U << 0), + Username = (1U << 1), + Photo = (1U << 2), + About = (1U << 3), + Notifications = (1U << 4), + Migration = (1U << 5), + UnavailableReason = (1U << 6), + PinnedMessages = (1U << 7), + IsBlocked = (1U << 8), + MessagesTTL = (1U << 9), // For users - CanShareContact = (1 << 9), - IsContact = (1 << 10), - PhoneNumber = (1 << 11), - OnlineStatus = (1 << 12), - BotCommands = (1 << 13), - BotCanBeInvited = (1 << 14), - BotStartToken = (1 << 15), - CommonChats = (1 << 16), - HasCalls = (1 << 17), - SupportInfo = (1 << 18), - IsBot = (1 << 19), + CanShareContact = (1U << 10), + IsContact = (1U << 11), + PhoneNumber = (1U << 12), + OnlineStatus = (1U << 13), + BotCommands = (1U << 14), + BotCanBeInvited = (1U << 15), + BotStartToken = (1U << 16), + CommonChats = (1U << 17), + HasCalls = (1U << 18), + SupportInfo = (1U << 19), + IsBot = (1U << 20), // For chats and channels - InviteLinks = (1 << 20), - Members = (1 << 21), - Admins = (1 << 22), - BannedUsers = (1 << 23), - Rights = (1 << 24), + InviteLinks = (1U << 21), + Members = (1U << 22), + Admins = (1U << 23), + BannedUsers = (1U << 24), + Rights = (1U << 25), // For channels - ChannelAmIn = (1 << 25), - StickersSet = (1 << 26), - ChannelLinkedChat = (1 << 27), - ChannelLocation = (1 << 28), - Slowmode = (1 << 29), - GroupCall = (1 << 30), + ChannelAmIn = (1U << 26), + StickersSet = (1U << 27), + ChannelLinkedChat = (1U << 28), + ChannelLocation = (1U << 29), + Slowmode = (1U << 30), + GroupCall = (1U << 31), // For iteration - LastUsedBit = (1 << 30), + LastUsedBit = (1U << 31), }; using Flags = base::flags<Flag>; friend inline constexpr auto is_flag_type(Flag) { return true; } @@ -103,22 +104,22 @@ struct HistoryUpdate { enum class Flag : uint32 { None = 0, - IsPinned = (1 << 0), - UnreadView = (1 << 1), - TopPromoted = (1 << 2), - Folder = (1 << 3), - UnreadMentions = (1 << 4), - LocalMessages = (1 << 5), - ChatOccupied = (1 << 6), - MessageSent = (1 << 7), - ScheduledSent = (1 << 8), - ForwardDraft = (1 << 9), - OutboxRead = (1 << 10), - BotKeyboard = (1 << 11), - CloudDraft = (1 << 12), - LocalDraftSet = (1 << 13), + IsPinned = (1U << 0), + UnreadView = (1U << 1), + TopPromoted = (1U << 2), + Folder = (1U << 3), + UnreadMentions = (1U << 4), + LocalMessages = (1U << 5), + ChatOccupied = (1U << 6), + MessageSent = (1U << 7), + ScheduledSent = (1U << 8), + ForwardDraft = (1U << 9), + OutboxRead = (1U << 10), + BotKeyboard = (1U << 11), + CloudDraft = (1U << 12), + LocalDraftSet = (1U << 13), - LastUsedBit = (1 << 13), + LastUsedBit = (1U << 13), }; using Flags = base::flags<Flag>; friend inline constexpr auto is_flag_type(Flag) { return true; } @@ -132,16 +133,16 @@ struct MessageUpdate { enum class Flag : uint32 { None = 0, - Edited = (1 << 0), - Destroyed = (1 << 1), - DialogRowRepaint = (1 << 2), - DialogRowRefresh = (1 << 3), - NewAdded = (1 << 4), - ReplyMarkup = (1 << 5), - BotCallbackSent = (1 << 6), - NewMaybeAdded = (1 << 7), + Edited = (1U << 0), + Destroyed = (1U << 1), + DialogRowRepaint = (1U << 2), + DialogRowRefresh = (1U << 3), + NewAdded = (1U << 4), + ReplyMarkup = (1U << 5), + BotCallbackSent = (1U << 6), + NewMaybeAdded = (1U << 7), - LastUsedBit = (1 << 7), + LastUsedBit = (1U << 7), }; using Flags = base::flags<Flag>; friend inline constexpr auto is_flag_type(Flag) { return true; } @@ -155,9 +156,9 @@ struct EntryUpdate { enum class Flag : uint32 { None = 0, - Repaint = (1 << 0), + Repaint = (1U << 0), - LastUsedBit = (1 << 0), + LastUsedBit = (1U << 0), }; using Flags = base::flags<Flag>; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 52c2c750f..8d3448043 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -955,9 +955,16 @@ void PeerData::setMessagesTTL( TimeId myPeriod, TimeId peerPeriod, bool oneSide) { - _ttlMyPeriod = myPeriod; - _ttlPeerPeriod = peerPeriod; - _ttlOneSide = oneSide; + if (_ttlMyPeriod != myPeriod + || _ttlPeerPeriod != peerPeriod + || _ttlOneSide != oneSide) { + _ttlMyPeriod = myPeriod; + _ttlPeerPeriod = peerPeriod; + _ttlOneSide = oneSide; + session().changes().peerUpdated( + this, + Data::PeerUpdate::Flag::MessagesTTL); + } } void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 1da6e2c10..c44b67773 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -65,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" //#include "history/feed/history_feed_section.h" // #feed #include "history/view/controls/history_view_voice_record_bar.h" +#include "history/view/controls/history_view_ttl_button.h" #include "history/view/history_view_service_message.h" #include "history/view/history_view_element.h" #include "history/view/history_view_scheduled_section.h" @@ -589,6 +590,7 @@ HistoryWidget::HistoryWidget( | UpdateFlag::Slowmode | UpdateFlag::BotStartToken | UpdateFlag::PinnedMessages + | UpdateFlag::MessagesTTL ) | rpl::filter([=](const Data::PeerUpdate &update) { if (_migrated && update.peer.get() == _migrated->peer) { if (_pinnedTracker @@ -638,6 +640,9 @@ HistoryWidget::HistoryWidget( if (_pinnedTracker && (flags & UpdateFlag::PinnedMessages)) { checkPinnedBarState(); } + if (flags & UpdateFlag::MessagesTTL) { + checkMessagesTTL(); + } }, lifetime()); rpl::merge( @@ -1267,6 +1272,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { } void HistoryWidget::orderWidgets() { + _voiceRecordBar->raise(); _send->raise(); if (_contactStatus) { _contactStatus->raise(); @@ -1942,6 +1948,7 @@ void HistoryWidget::showHistory( setupPinnedTracker(); setupGroupCallTracker(); + checkMessagesTTL(); if (_history->scrollTopItem || (_migrated && _migrated->scrollTopItem) || _history->isReadyFor(_showAtMsgId)) { @@ -1995,6 +2002,7 @@ void HistoryWidget::showHistory( } else { refreshTopBarActiveChat(); updateTopBarSelection(); + checkMessagesTTL(); clearFieldText(); doneShow(); @@ -2206,6 +2214,9 @@ void HistoryWidget::updateControlsVisibility() { if (_scheduled) { _scheduled->hide(); } + if (_ttlInfo) { + _ttlInfo->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); @@ -2268,6 +2279,9 @@ void HistoryWidget::updateControlsVisibility() { if (_scheduled) { _scheduled->show(); } + if (_ttlInfo) { + _ttlInfo->show(); + } updateFieldPlaceholder(); if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { @@ -2296,6 +2310,9 @@ void HistoryWidget::updateControlsVisibility() { if (_scheduled) { _scheduled->hide(); } + if (_ttlInfo) { + _ttlInfo->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); @@ -2492,23 +2509,6 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages } break; } - const auto ExtractFirstId = [&] { - return histList->empty() ? -1 : IdFromMessage(histList->front()); - }; - const auto ExtractLastId = [&] { - return histList->empty() ? -1 : IdFromMessage(histList->back()); - }; - const auto PeerString = [](PeerId peerId) { - if (peerIsUser(peerId)) { - return QString("User-%1").arg(peerToUser(peerId)); - } else if (peerIsChat(peerId)) { - return QString("Chat-%1").arg(peerToChat(peerId)); - } else if (peerIsChannel(peerId)) { - return QString("Channel-%1").arg(peerToChannel(peerId)); - } - return QString("Bad-%1").arg(peerId); - }; - if (_preloadRequest == requestId) { auto to = toMigrated ? _migrated : _history; addMessagesToFront(peer, *histList); @@ -3992,7 +3992,7 @@ void HistoryWidget::moveFieldControls() { } // _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel -// (_attachDocument|_attachPhoto) _field (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send +// (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send // (_botStart|_unblock|_joinChannel|_muteUnmute) auto buttonsBottom = bottom - _attachToggle->height(); @@ -4015,6 +4015,10 @@ void HistoryWidget::moveFieldControls() { } if (_scheduled) { _scheduled->moveToRight(right, buttonsBottom); + right += _scheduled->width(); + } + if (_ttlInfo) { + _ttlInfo->move(width() - right - _ttlInfo->width(), buttonsBottom); } _fieldBarCancel->moveToRight(0, _field->y() - st::historySendPadding - _fieldBarCancel->height()); @@ -4046,6 +4050,7 @@ void HistoryWidget::updateFieldSize() { if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); if (_silent) fieldWidth -= _silent->width(); if (_scheduled) fieldWidth -= _scheduled->width(); + if (_ttlInfo) fieldWidth -= _ttlInfo->width(); if (_field->width() != fieldWidth) { _field->resize(fieldWidth, _field->height()); @@ -5405,6 +5410,23 @@ void HistoryWidget::checkPinnedBarState() { } } +void HistoryWidget::checkMessagesTTL() { + if (!_peer || !_peer->messagesTTL()) { + if (_ttlInfo) { + _ttlInfo = nullptr; + updateControlsGeometry(); + updateControlsVisibility(); + } + } else if (!_ttlInfo) { + _ttlInfo = std::make_unique<HistoryView::Controls::TTLButton>( + this, + _peer); + orderWidgets(); + updateControlsGeometry(); + updateControlsVisibility(); + } +} + void HistoryWidget::refreshPinnedBarButton(bool many) { const auto close = !many; auto button = object_ptr<Ui::IconButton>( diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 3c52c09ac..13ccd8f69 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -99,6 +99,7 @@ class GroupCallTracker; namespace Controls { class RecordLock; class VoiceRecordBar; +class TTLButton; } // namespace Controls } // namespace HistoryView @@ -483,6 +484,7 @@ private: int wasScrollTop, int nowScrollTop); + void checkMessagesTTL(); void setupGroupCallTracker(); void sendInlineResult(InlineBots::ResultSelected result); @@ -692,6 +694,7 @@ private: object_ptr<Ui::IconButton> _botCommandStart; object_ptr<Ui::SilentToggle> _silent = { nullptr }; object_ptr<Ui::IconButton> _scheduled = { nullptr }; + std::unique_ptr<HistoryView::Controls::TTLButton> _ttlInfo; const std::unique_ptr<VoiceRecordBar> _voiceRecordBar; bool _cmdStartShown = false; object_ptr<Ui::InputField> _field; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 9b2bc1b74..e5fc9ce5e 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/controls/history_view_voice_record_bar.h" +#include "history/view/controls/history_view_ttl_button.h" #include "history/view/history_view_webpage_preview.h" #include "inline_bots/inline_results_widget.h" #include "inline_bots/inline_bot_result.h" @@ -635,6 +636,10 @@ Main::Session &ComposeControls::session() const { } void ComposeControls::setHistory(SetHistoryArgs &&args) { + // Right now only single non-null set of history is supported. + // Otherwise initWebpageProcess should be updated / rewritten. + Expects(!_history && *args.history); + _showSlowmodeError = std::move(args.showSlowmodeError); _slowmodeSecondsLeft = rpl::single(0) | rpl::then(std::move(args.slowmodeSecondsLeft)); @@ -643,20 +648,21 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { _writeRestriction = rpl::single(std::optional<QString>()) | rpl::then(std::move(args.writeRestriction)); const auto history = *args.history; - if (_history == history) { - return; - } + //if (_history == history) { + // return; + //} _history = history; _window->tabbedSelector()->setCurrentPeer( history ? history->peer.get() : nullptr); initWebpageProcess(); updateBotCommandShown(); + updateMessagesTTLShown(); updateControlsGeometry(_wrap->size()); updateControlsVisibility(); updateFieldPlaceholder(); - if (!_history) { - return; - } + //if (!_history) { + // return; + //} const auto peer = _history->peer; if (peer->isChat() && peer->asChat()->noParticipantInfo()) { session().api().requestFullPeer(peer); @@ -1736,7 +1742,7 @@ void ComposeControls::finishAnimating() { void ComposeControls::updateControlsGeometry(QSize size) { // _attachToggle -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel - // (_attachDocument|_attachPhoto) _field (_silent|_botCommandStart) _tabbedSelectorToggle _send + // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_silent|_botCommandStart) _tabbedSelectorToggle _send const auto fieldWidth = size.width() - _attachToggle->width() @@ -1744,7 +1750,8 @@ void ComposeControls::updateControlsGeometry(QSize size) { - _send->width() - _tabbedSelectorToggle->width() - (_botCommandShown ? _botCommandStart->width() : 0) - - (_silent ? _silent->width() : 0); + - (_silent ? _silent->width() : 0) + - (_ttlInfo ? _ttlInfo->width() : 0); { const auto oldFieldHeight = _field->height(); _field->resizeToWidth(fieldWidth); @@ -1775,8 +1782,15 @@ void ComposeControls::updateControlsGeometry(QSize size) { _tabbedSelectorToggle->moveToRight(right, buttonsTop); right += _tabbedSelectorToggle->width(); _botCommandStart->moveToRight(right, buttonsTop); + if (_botCommandShown) { + right += _botCommandStart->width(); + } if (_silent) { _silent->moveToRight(right, buttonsTop); + right += _silent->width(); + } + if (_ttlInfo) { + _ttlInfo->move(size.width() - right - _ttlInfo->width(), buttonsTop); } _voiceRecordBar->resizeToWidth(size.width()); @@ -1787,6 +1801,9 @@ void ComposeControls::updateControlsGeometry(QSize size) { void ComposeControls::updateControlsVisibility() { _botCommandStart->setVisible(_botCommandShown); + if (_ttlInfo) { + _ttlInfo->show(); + } } bool ComposeControls::updateBotCommandShown() { @@ -1818,6 +1835,20 @@ void ComposeControls::updateOuterGeometry(QRect rect) { } } +void ComposeControls::updateMessagesTTLShown() { + const auto peer = _history ? _history->peer.get() : nullptr; + const auto shown = peer && (peer->messagesTTL() > 0); + if (!shown && _ttlInfo) { + _ttlInfo = nullptr; + updateControlsVisibility(); + updateControlsGeometry(_wrap->size()); + } else if (shown && !_ttlInfo) { + _ttlInfo = std::make_unique<Controls::TTLButton>(_wrap.get(), peer); + updateControlsVisibility(); + updateControlsGeometry(_wrap->size()); + } +} + void ComposeControls::paintBackground(QRect clip) { Painter p(_wrap.get()); @@ -2176,6 +2207,7 @@ void ComposeControls::initWebpageProcess() { session().changes().peerUpdates( Data::PeerUpdate::Flag::Rights | Data::PeerUpdate::Flag::Notifications + | Data::PeerUpdate::Flag::MessagesTTL ) | rpl::filter([=](const Data::PeerUpdate &update) { return (update.peer.get() == peer); }) | rpl::map([](const Data::PeerUpdate &update) { @@ -2189,6 +2221,9 @@ void ComposeControls::initWebpageProcess() { if (flags & Data::PeerUpdate::Flag::Notifications) { updateSilentBroadcast(); } + if (flags & Data::PeerUpdate::Flag::MessagesTTL) { + updateMessagesTTLShown(); + } }, lifetime); base::ObservableViewer( diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 0919baff7..1504e5dcb 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -69,6 +69,7 @@ namespace HistoryView { namespace Controls { class VoiceRecordBar; +class TTLButton; } // namespace Controls class FieldHeader; @@ -201,6 +202,7 @@ private: void initKeyHandler(); void updateSubmitSettings(); void updateSendButtonType(); + void updateMessagesTTLShown(); void updateHeight(); void updateWrappingVisibility(); void updateControlsVisibility(); @@ -286,6 +288,7 @@ private: const not_null<Ui::InputField*> _field; const not_null<Ui::IconButton*> _botCommandStart; std::unique_ptr<Ui::SilentToggle> _silent; + std::unique_ptr<Controls::TTLButton> _ttlInfo; std::unique_ptr<InlineBots::Layout::Widget> _inlineResults; std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp new file mode 100644 index 000000000..646dee793 --- /dev/null +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -0,0 +1,94 @@ +/* +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 "history/view/controls/history_view_ttl_button.h" + +#include "data/data_peer.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_changes.h" +#include "main/main_session.h" +#include "lang/lang_keys.h" +#include "boxes/peers/edit_peer_info_box.h" +#include "ui/layers/generic_box.h" +#include "ui/toast/toast.h" +#include "apiwrap.h" +#include "styles/style_chat.h" + +namespace HistoryView::Controls { + +TTLButton::TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer) +: _button(parent, st::historyMessagesTTL) { + _button.setClickedCallback([=] { + const auto canEdit = peer->isUser() + || (peer->isChat() + && peer->asChat()->canDeleteMessages()) + || (peer->isChannel() + && peer->asChannel()->canDeleteMessages()); + if (!canEdit) { + const auto duration = (peer->messagesTTL() < 3 * 86400) + ? tr::lng_ttl_about_duration1(tr::now) + : tr::lng_ttl_about_duration2(tr::now); + Ui::Toast::Show(tr::lng_ttl_about_tooltip( + tr::now, + lt_duration, + duration)); + return; + } + const auto callback = crl::guard(&peer->session(), [=]( + TimeId period) { + using Flag = MTPmessages_SetHistoryTTL::Flag; + peer->session().api().request(MTPmessages_SetHistoryTTL( + MTP_flags(peer->oneSideTTL() + ? Flag::f_pm_oneside + : Flag(0)), + peer->input, + MTP_int(period) + )).done([=](const MTPUpdates &result) { + peer->session().api().applyUpdates(result); + }).fail([=](const RPCError &error) { + }).send(); + }); + Ui::show( + Box( + AutoDeleteSettingsBox, + peer->myMessagesTTL(), + callback), + Ui::LayerOption(0)); + }); + peer->session().changes().peerUpdates( + peer, + Data::PeerUpdate::Flag::MessagesTTL + ) | rpl::start_with_next([=] { + const auto ttl = peer->messagesTTL(); + if (ttl < 3 * 86400) { + _button.setIconOverride(nullptr, nullptr); + } else { + _button.setIconOverride( + &st::historyMessagesTTL2Icon, + &st::historyMessagesTTL2IconOver); + } + }, _button.lifetime()); +} + +void TTLButton::show() { + _button.show(); +} + +void TTLButton::hide() { + _button.hide(); +} + +void TTLButton::move(int x, int y) { + _button.move(x, y); +} + +int TTLButton::width() const { + return _button.width(); +} + +} // namespace HistoryView::Controls diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h new file mode 100644 index 000000000..37dda0141 --- /dev/null +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h @@ -0,0 +1,29 @@ +/* +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 "ui/widgets/buttons.h" + +namespace HistoryView::Controls { + +class TTLButton final { +public: + TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer); + + void show(); + void hide(); + void move(int x, int y); + + [[nodiscard]] int width() const; + +private: + Ui::IconButton _button; + +}; + +} // namespace HistoryView::Controls diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index c34328a02..1d4538692 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -308,6 +308,13 @@ historyAttachEmoji: IconButton(historyAttach) { iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; iconPosition: point(-1px, -1px); } +historyMessagesTTL: IconButton(historyAttach) { + icon: icon {{ "chat/send_control_autodelete_1d", historyComposeIconFg }}; + iconOver: icon {{ "chat/send_control_autodelete_1d", historyComposeIconFgOver }}; + iconPosition: point(-1px, -1px); +} +historyMessagesTTL2Icon: icon {{ "chat/send_control_autodelete_7d", historyComposeIconFg }}; +historyMessagesTTL2IconOver: icon {{ "chat/send_control_autodelete_7d", historyComposeIconFgOver }}; historyAttachEmojiFgActive: windowActiveTextFg; historyAttachEmojiActive: icon {{ "send_control_emoji", historyAttachEmojiFgActive }}; historyAttachEmojiTooltipDelta: 4px; From dd2dcd7fd90fda17358b665570f75c3f1731bba6 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Feb 2021 13:08:11 +0400 Subject: [PATCH 354/396] Update icons in the compose controls. --- .../Resources/icons/chat/input_attach.png | Bin 0 -> 1071 bytes .../Resources/icons/chat/input_attach@2x.png | Bin 0 -> 2151 bytes .../Resources/icons/chat/input_attach@3x.png | Bin 0 -> 3276 bytes ...odelete_1d.png => input_autodelete_1d.png} | Bin ...e_1d@2x.png => input_autodelete_1d@2x.png} | Bin ...e_1d@3x.png => input_autodelete_1d@3x.png} | Bin ...odelete_7d.png => input_autodelete_7d.png} | Bin ...e_7d@2x.png => input_autodelete_7d@2x.png} | Bin ...e_7d@3x.png => input_autodelete_7d@3x.png} | Bin .../icons/chat/input_bot_command.png | Bin 0 -> 654 bytes .../icons/chat/input_bot_command@2x.png | Bin 0 -> 1176 bytes .../icons/chat/input_bot_command@3x.png | Bin 0 -> 1806 bytes .../icons/chat/input_bot_keyboard.png | Bin 0 -> 585 bytes .../icons/chat/input_bot_keyboard@2x.png | Bin 0 -> 1140 bytes .../icons/chat/input_bot_keyboard@3x.png | Bin 0 -> 1687 bytes .../icons/chat/input_bot_keyboard_hide.png | Bin 0 -> 643 bytes .../icons/chat/input_bot_keyboard_hide@2x.png | Bin 0 -> 1153 bytes .../icons/chat/input_bot_keyboard_hide@3x.png | Bin 0 -> 1828 bytes .../Resources/icons/chat/input_record.png | Bin 0 -> 665 bytes .../Resources/icons/chat/input_record@2x.png | Bin 0 -> 1344 bytes .../Resources/icons/chat/input_record@3x.png | Bin 0 -> 2194 bytes .../Resources/icons/chat/input_scheduled.png | Bin 0 -> 496 bytes .../icons/chat/input_scheduled@2x.png | Bin 0 -> 1005 bytes .../icons/chat/input_scheduled@3x.png | Bin 0 -> 1577 bytes .../icons/chat/input_scheduled_dot.png | Bin 0 -> 329 bytes .../icons/chat/input_scheduled_dot@2x.png | Bin 0 -> 629 bytes .../icons/chat/input_scheduled_dot@3x.png | Bin 0 -> 888 bytes .../Resources/icons/chat/input_silent.png | Bin 0 -> 661 bytes .../Resources/icons/chat/input_silent@2x.png | Bin 0 -> 1264 bytes .../Resources/icons/chat/input_silent@3x.png | Bin 0 -> 1853 bytes Telegram/Resources/icons/chat/input_smile.png | Bin 0 -> 787 bytes .../Resources/icons/chat/input_smile@2x.png | Bin 0 -> 1571 bytes .../Resources/icons/chat/input_smile@3x.png | Bin 0 -> 2569 bytes .../Resources/icons/send_control_attach.png | Bin 919 -> 0 bytes .../icons/send_control_attach@2x.png | Bin 1869 -> 0 bytes .../icons/send_control_attach@3x.png | Bin 3123 -> 0 bytes .../icons/send_control_bot_command.png | Bin 401 -> 0 bytes .../icons/send_control_bot_command@2x.png | Bin 706 -> 0 bytes .../icons/send_control_bot_command@3x.png | Bin 1152 -> 0 bytes .../icons/send_control_bot_keyboard.png | Bin 320 -> 0 bytes .../icons/send_control_bot_keyboard@2x.png | Bin 546 -> 0 bytes .../icons/send_control_bot_keyboard@3x.png | Bin 946 -> 0 bytes .../icons/send_control_bot_keyboard_hide.png | Bin 342 -> 0 bytes .../send_control_bot_keyboard_hide@2x.png | Bin 669 -> 0 bytes .../send_control_bot_keyboard_hide@3x.png | Bin 1085 -> 0 bytes .../Resources/icons/send_control_record.png | Bin 401 -> 0 bytes .../icons/send_control_record@2x.png | Bin 877 -> 0 bytes .../icons/send_control_record@3x.png | Bin 1306 -> 0 bytes .../icons/send_control_scheduled.png | Bin 362 -> 0 bytes .../icons/send_control_scheduled@2x.png | Bin 652 -> 0 bytes .../icons/send_control_scheduled@3x.png | Bin 1078 -> 0 bytes .../icons/send_control_scheduled_dot.png | Bin 221 -> 0 bytes .../icons/send_control_scheduled_dot@2x.png | Bin 406 -> 0 bytes .../icons/send_control_scheduled_dot@3x.png | Bin 622 -> 0 bytes .../icons/send_control_silent_off.png | Bin 378 -> 0 bytes .../icons/send_control_silent_off@2x.png | Bin 660 -> 0 bytes .../icons/send_control_silent_off@3x.png | Bin 1325 -> 0 bytes .../SourceFiles/history/history_widget.cpp | 2 +- .../history_view_compose_controls.cpp | 4 +- Telegram/SourceFiles/ui/chat/chat.style | 64 ++++++++---------- 60 files changed, 34 insertions(+), 36 deletions(-) create mode 100644 Telegram/Resources/icons/chat/input_attach.png create mode 100644 Telegram/Resources/icons/chat/input_attach@2x.png create mode 100644 Telegram/Resources/icons/chat/input_attach@3x.png rename Telegram/Resources/icons/chat/{send_control_autodelete_1d.png => input_autodelete_1d.png} (100%) rename Telegram/Resources/icons/chat/{send_control_autodelete_1d@2x.png => input_autodelete_1d@2x.png} (100%) rename Telegram/Resources/icons/chat/{send_control_autodelete_1d@3x.png => input_autodelete_1d@3x.png} (100%) rename Telegram/Resources/icons/chat/{send_control_autodelete_7d.png => input_autodelete_7d.png} (100%) rename Telegram/Resources/icons/chat/{send_control_autodelete_7d@2x.png => input_autodelete_7d@2x.png} (100%) rename Telegram/Resources/icons/chat/{send_control_autodelete_7d@3x.png => input_autodelete_7d@3x.png} (100%) create mode 100644 Telegram/Resources/icons/chat/input_bot_command.png create mode 100644 Telegram/Resources/icons/chat/input_bot_command@2x.png create mode 100644 Telegram/Resources/icons/chat/input_bot_command@3x.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard@2x.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard@3x.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard_hide.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard_hide@2x.png create mode 100644 Telegram/Resources/icons/chat/input_bot_keyboard_hide@3x.png create mode 100644 Telegram/Resources/icons/chat/input_record.png create mode 100644 Telegram/Resources/icons/chat/input_record@2x.png create mode 100644 Telegram/Resources/icons/chat/input_record@3x.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled@2x.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled@3x.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled_dot.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled_dot@2x.png create mode 100644 Telegram/Resources/icons/chat/input_scheduled_dot@3x.png create mode 100644 Telegram/Resources/icons/chat/input_silent.png create mode 100644 Telegram/Resources/icons/chat/input_silent@2x.png create mode 100644 Telegram/Resources/icons/chat/input_silent@3x.png create mode 100644 Telegram/Resources/icons/chat/input_smile.png create mode 100644 Telegram/Resources/icons/chat/input_smile@2x.png create mode 100644 Telegram/Resources/icons/chat/input_smile@3x.png delete mode 100644 Telegram/Resources/icons/send_control_attach.png delete mode 100644 Telegram/Resources/icons/send_control_attach@2x.png delete mode 100644 Telegram/Resources/icons/send_control_attach@3x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_command.png delete mode 100644 Telegram/Resources/icons/send_control_bot_command@2x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_command@3x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard@2x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard@3x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard_hide.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard_hide@2x.png delete mode 100644 Telegram/Resources/icons/send_control_bot_keyboard_hide@3x.png delete mode 100644 Telegram/Resources/icons/send_control_record.png delete mode 100644 Telegram/Resources/icons/send_control_record@2x.png delete mode 100644 Telegram/Resources/icons/send_control_record@3x.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled@2x.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled@3x.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled_dot.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled_dot@2x.png delete mode 100644 Telegram/Resources/icons/send_control_scheduled_dot@3x.png delete mode 100644 Telegram/Resources/icons/send_control_silent_off.png delete mode 100644 Telegram/Resources/icons/send_control_silent_off@2x.png delete mode 100644 Telegram/Resources/icons/send_control_silent_off@3x.png diff --git a/Telegram/Resources/icons/chat/input_attach.png b/Telegram/Resources/icons/chat/input_attach.png new file mode 100644 index 0000000000000000000000000000000000000000..cfc7ae1957f2a2b73a14b4d2a421b9b804b672f7 GIT binary patch literal 1071 zcmV+~1kn45P)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T700001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K5nMp)JR9Fe^mrp2sQ546|jGvS! zStw#*YLq{PB4J^H#mqEiAseJ93rfmT7AzQ9`m>XNrN}H$W^198NdA=MPx+HS`Ijg= z=XcM^d%c<8{N}yuJ!SEx-rV=zIrrSpeDAsE-f;i`itE>YoWGXr3(N*IJ43dDwt{9A zFx%hGL9+_}F&jYJzp}Ct{C+>==jTImax&cC-^1qSCM+#2!S(gE)>Z1BwmMT+Mn(p% ztgPV6%S+TtfMQ=?A3B{*>2Vxia8kcW>s(M!0Ks4o($do4{rw%Lrlw$LX9sR>ZXho& z52~xHSyk#23WcDlsR=$mKeZur@1=j0h{$~e9G;z>v1+TUtC*FQ6*ZQ}<H6I@Q}!bz zmHr0X?@g~3Zhn3q<KyF1=+4eg?C9t~p`Z#wkdy*6-I<x0>U#r_@__$?>+kQ!r>7@1 z4<8>Nv8Sg;g_)h5Wm|W5xBAX<z-Vxk{1X!sJow$+9S#o<<H*PetM&Z+j1?6X4Aj!n z!afTN3qJ!$Ii}>CGOclOaX2|S$pc?qU7;v7>fnQegZ!RaQmVAHl%Lnv*Hs@q1*Pg> zad9!Erl#_06l#dr05dZ);Pd(5_4O4R8X910Ym490*T=^P-wTDg73GNAx3@QLjeZ&a zH6kFo-EQ_zNJzlN#YI-+{QMk?ii*PGl$Dj?#l;2NYin!SHZU;2&w)T7%$MTExMCWk zc1%o6#O38>9_-}g1jWt~=2ul!g<|Jm`}p`c+bBIK1!yH}(;x<z5>pN^DJcop*4B8S zqoX6t$;nXyaqIPZkroPpjE;_?!{Oi(M0Jwh(T-yPO6wyBI6Xbh105V3V0L!4HW1d= z*W<&(1KWp&hS)~+kroue9UmVfZHqq!C=LQV5anFtu0bV4se!${z5l?`>ZcMV2}l0M z6;zaHZf*`6sI9Gy!AYgAt}ZL#<Nyy34;dtKnl?5zkP32ZYpd08a)5h#dkh#ky)Ksv z3kwT>0!|KaZf=eN+uPe2CNe#=3&KRsMaDLsgEciZ40w5Yi6tc^VS!RoQjqpo%0mm` zQ2M|+HZ}&0jg5Th6vZ0$_xB+^Jss-m>iEzqRx;m<e5D0w$tO$+5i#a79S-OSMdvzF zOM8)m0ybuUz4(!KX$2KY#Gw<)%gZ4*Hy3ViZ((<L7q+*z6|-gSw*f69&<SM!an}(@ p*K8}OE0K<at)PxTy5?W6;9p(lV2!0JJ>38R002ovPDHLkV1jX~+d=>U literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_attach@2x.png b/Telegram/Resources/icons/chat/input_attach@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..19787fa84baf938af7812a5b9b7acf19629ce453 GIT binary patch literal 2151 zcmbVNX*iqP7XCCy#EDcjimP-m4>ez7h1RUZ(28iIhC?}2sUR_)nj)Mw=Anj$)?A7n zW1|{$Ff=G?Y|K>^Ln$#k>7RT5-sk>!*Spr<?|$~P*4ocrBpYi}7*qrb007L~%*c+- zHopMjX4^;n8&z!L2(&Z30#wsQm)L=khoiZtl@%bz_8|ZV(Hr3WC1FE^4FCX9H~<iv zIex8CPW)%rh64Jp|4XPceGLTw-~n?Z!|QmCm0a&K6iVp*lQ8Tnd0pf5D&MgSsp*jO zc7F3*l0Mz?mi$q%cz4;N=lnBVEo07(J*lTslOxQ>1X2|?qh6ODYai#REgnR&4!-uT ziD}rk(X|7==BV{8>c7<M>s35|friY2O4vA2@&xwDe}cTcpnMdNm)RSeNzDJv=ijJa zB`nVdeIF{M#enS>>G|azJ()w#F=~4o3%sp_&3Nb5P~$U~Zc!TVO*!2&odm1$`j6JQ zsz>bML{xu=3|HOMtUi>8rQB@v8%Vim>NQf0{W<rZJX-f}=XC$#obin1kr(1sj<wO; ze4^QoQzI{K5AJO(d#^ECNglk252xQW2QGf<&X6xRc_<S1eXc8CCn(XAo@ai$EmEVR zRIWV-aCC4m$<@49(v>D{+OW&ExNRmT^v5h$Yt(T;cTZ3GjY@}tjP124<f)y*eO7r< zQMB{xaAC}y$2s5HhqvAFZpTN5vxB9maro|+S0R044N_K*6C^D&Aq6(-yOrn{iQF)e z`E-;ai%APR*r}cFx|lNSn=;#*qbAY`0`o~!lme7tkTQLv`gYr;@p>P0@+S?S&!=^R znf>Ri^L6SedlitEJ3t_9uHN~1-PZSE0qQeS1U!yI#g`Pa#z55jjL>D##*Vf@r_Z?9 z;V*}6pPp#EcZMu>8LF_%uDV-KrecxLNVP4L2e-JMt#yZ<_o0q^`d}{+tjwe5+?~L_ zjkCW<Du*nN3>7dlHz&Qg=8tW`m0t*3oot@QLi^`i5<$v5RHynsNHJiz98d)85vce~ z#Kx+M?q;fV8K@=@29LX<XrcUIW}x75nAgv51C#XQ!44wan>kuraC>>No#la7zdvt= zly5pZ*iDN(+(YQ?YpQwnQf!}^?2;K75BO1LhGI9%7X`V6WxqIrvpJm)_qHxS3I5h! zm>BB#8|tw-$*IwA<q+?1gYtBJ_H7bESZ`g&rpsrr<l#o%=NQkUTLE$e^On0E@v!<a z0qjq9*Ncpk1f9Y{S*ukz+jI{D3+;wp3h&%uOVDyU%ln)6@CkI4(~V-ki89-ZI*|uj zG{v;ReBBVQ-fWeWnF{u!__Q+7xG+0Z>u$Or?5d<q<GQAc;nH&JgcP^hx;qb+Se4h+ zrG{>OFHUa_n2XoccT-5RoN9|gJIBSGOgP)k6Nww|l6#YMMu~gnVKfm~A_+(*9DbpQ z`Y8uL6h{{5M;$$%?|}=!oB!2=ahj&wooMI|<8;0IZ0J3g-!ruI=lk|0*Z5!F=@Xoc zNMCcIy2;zWlljYjGJ{KxukBHp{0Yud)Dwr_n{|CuXZDD2@#+jx1=7`7N5CYN+&&W5 z4oDsIiR<T$EX@9}bFo$EnGq9+pX4?Qm{%3`MNp0Gx<4$yR+D&>!J!;e*B$s+5G;-S zu^8ex*OR5>Q)fyRLlhSmC;Dm`#m5n<TVM<X+$HN0mh)g#naVB`6vw({(HTVG<g4LK z_27r{M#Zok(^c;iI`(G)=D<^;L7vj{(a%*kHI{kghYGKPX_g{Z+oQGa<9O1{`Gfp4 zY1DW$F?0pQoM?Q3-)81;yRchFKrFM5JR81X1AT(=Lxy8J1s0H}4qO>!cHLd7vXc{B zqpu^ve*9drH<p!Y3SJqfT5K&1O4|t<4*F-2qls(lCN~&sLRbC<Cr8{2SQ*!VF*X)@ zHsv(D`p?mF%^ovLta6RC!VzG2N6h)WiVqW@Ca-}DFkoXtHf~EK^Xz?e=i&F^N{uls ze0JPS(&D}YbZrIb3{~_sqz2`n3j^lf@#t3+(&oI9q;fg%UTJ6lq`t2-n2s2y?h7ZU z#86&y(Q`GGD%Y7DldWNmC;VyATq2yiWKGw0&Ie_+aM%c3TtC5ku*CbmaIIb&HdsNK zsp9|l+hdpc43wD6I-@mATFN@FEaRh)o~xorN1_`Za_W)CyhYy=B;0u}7Ewgk3Xqf@ zBK4z7t@G!7m5MqtMTA$;P$zs%Fm;8sHf0gmQLD2ciB!1Nl>9nk-y{L<H`!!6)@LxL z(7qYe-@?<TOn-%2O8q1W{!PfnKOeK)pepWz6`H|o$Ax^H4{2U~=}U-w#oO>(^*IN@ zWo?4Vf{L}e-=tPU_;oC%L9e{^$H`|`NT-RS^qSbKXh?Samzt(~2a@Y=-trj?^^_VR z<YSH7-*$(1!uhky5`y@@yp-T}ho1|#WvG8T$9NTj>V2$Xsr$zRWxZ`b!lr3jmyDk4 zVS-YoB$a45b5^zQXWaTDrrU7?Ve>0v06R|<2wtu5$v@*~xih9?5Pni^iJcWhdOb}_ z|D6f@Z+nW2I+8#rS`b8u$M3hZY7F#o-;!C#v*8S~R%#wAO*FG~cXM&D;qHW9Gxfyq zb&8Cj%yRstk6~|P?RmAjA9R>Twwvs+C6`D33K^GSe%x88!zt<*U<jhxAe%f4tu|>L zBiQWguvUq1aVV7V&eA|%GQW8AwT_Q6bGwfN2f%oQ5_Nh1S61iTQ6d`?ovwGdr~Df7 N=El}W)mL1i{{e2&{e}Pl literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_attach@3x.png b/Telegram/Resources/icons/chat/input_attach@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d24e17ddf1fe0350770c6ea51a360b5cb5d0c630 GIT binary patch literal 3276 zcmb7Hc{tQx7ypiZ8HCi>5)us>Teif{5;H_&-^RW#X$oTtW64goMArH>Xe^C=M2vln z5|brNLfI*6#5?`-zJI@eoO{l__dNIWJfC~cx#uP#jr7=9_*eh{U_-!lOsG2euP`xC zM-^AaF{%Rjo9Jl))guBc)QOIp6~f)n5P(u`CIA%g1<?I<p$Z>W0D!gx1kh47=<iwy z&3|`;OKAUR|8-<qFy90Kup&Z7(<~UYVHeoXV|Y=6>=UGfaSRFyIunLMlaicE5~33x zgP=@FNl79vquCloFpQYwS++*y`6KDAw)xQFyxEyib$<^^ZCKgp(MDdO|MIXeRD|n9 zUelEQ6*>F5Ho09u2JktRfMD53&;9@L|CvI^kG=67yS&KDtO)pwfZh+tbNK?~0@~+U z9#<xTN)<*0@liMdWaZE9X;hTqNK052_)wrG+QH!XUZq4OL^t~XJ7^gwn<!8h3*nV> zE$MvBY8pjDFX7PkC|e^kLUL>98Ej>5?Q`r}oyX{pz9&M1swP@mS|C)Vm8|znW7VF| zcsVETZt)z?zgzR|pK4tCJm=fO>m$!1LVqqM`b214EnP73YS$pzwO|yy#;)LP8oYHN zC?Yy)d8E*Qk>gTIc~S5MG9M+5jel(Xwdrf4LRI6Xm4PgAujS#qfZw|-<z)Wk!FoDn zDsT0!16-@q4L(xA`woTqO7}}Y{;T(?>Hhp_@~@dx3~&)<UqF82aF<8YB8BzM8~b<q z;m7;9vb)8-xOYhYk1UklRQ^xTZ}>=9R--g`zP`^_3OL@s=R-bOosg_Zxm9u9vRYt? zid-H#*A{L-%K$DhEi<Bjs!^0z4<gz&`PDo3u;}NL?PP<uDIh1$p5eoVo@9OdGX1AX z-sDQJ_r$P+O+yIpm(I~*4d(D|(f-`qWj}=EEeHxp975_6k)NyWxTV}g44*3qe;VXS zg{TD+h#|j9Ya{Bz4>s+LU%@ld*Ph6p?J)+86sSZ8&L!}U_Hx!ZcF<U+Ke==ZD(5~7 zd0=ZycWa4eU99=Uo<|Loq*}+wE+8>EjiY<+z+-%0o`z(4m1Iv*58Z{2S6IT_Q~aju zUrYAa_}<hZm|h}vU~MZbs&b=z<RO`rp<7$i4U)?nGk?%6r9^9mm^D?~(~~3lz4fWA zkz$R}fC}wK->GuzI(M@dTJ(eYN-$<&Cj1yR7*_f-M0fYrECZeNSlY7^#U0af&*q)? zHyG<mVMPk=$sQUng`2_URxOySwaR?U%<fvZA)Q1nF}35^#An`^_mt2aj<`*xU%~Q< zoeVFvTwhU2jS6k<6sy~{MxNQLR5GwPA>UCi>-$t>IWo#^<Lq|wg*_=DgVWLk`ZIX^ zQl)j>SdO%<REsPa(!l^RBv{pi?s+dT>R6}f(Jdef72X-<D~=ro-!A<cSK3oigPu-2 zk81U4x;LZC#;3qk7E*EN=;zY4pv^A<6cWfwF!J=IX3Xs#C2G3fE2|!*CNlBFP0i-O zQ`7|Qlr`7vU*`63*K<%lq(5DFyxHHwO+8oL4YdCN)<<LA?AddKxgdCri0+zd3van$ zU?ICkO_}<z1HZQqmy+DNs!eb#fj>TIGWY*EK1kfN{r%@SC=)9DnhKtA9Z49}mB>d4 zTZ@D41Z3{>qCQ(+Pl5P_`n}M4+}-T|J)2wT4z|&67V64fLgkR<QqohmnWp>2ui6#a z)NJ5GJ0Gf~T%M&)Oay5MyviIZu{N=O8{oB^r{+6Jhzi|X72B(cKCks8;`F4s9^IEF zh}%i1e<naHKgk{*wX<`$C5i1&YZB9diRunyib<nqbM^`LY7ZCTo!xMr4<l5XUkyy) z&9iz%$AC&#He+g7N-ObCgjQc9T9tVGy1etSu$D*B{@hwa&V+v6%6u%hxDmXLI02JR zAI@mYpu7!SGudCC!mV%9G@0P^$-caE5In*2qsVi$kXon;qIJ-0rIe#0&iSdUxR(M0 z=jJAdtS8c3%r^HG!k*&XzsQ_*g9a{1ffL`3<SX5MDwW!&Ey{|I5P`XG)&hFJ7g?wv z)Vegb^R4e}A?6|x9Y0&+5-&;oRBFh|iwbZ)Ioyfd>pATz65hX;DQVALi?!n#;CUq4 z;rLIndbsEMWKHZ>zS8!iU*8Qp-uAN=e^2FCGRcv$-nQhEx$^BDzc)afJi5#p&n}pG z+ccH$My2^{l8$e9$A#QQ@IdLmvLBrqW?Mu2JG@iOzQ?8|)6-W@#F=VrVUc9kKR5N# z1ja&-oHBD<7&W|3(Z4Sk@j4)6xGB5@HhO&8;0fcN$9TChot@ISOxV6JpR99i?c^BF zVC7ri6U5k5#^_i@))jQ@VC=Oa{tBXNcxP^T$;{RmF1aEfoO2Mm`-3we95dtVeqL=T zN5ACVdVIrd<mr)~jZ*84jYm<IbK6&?U77^reYU}A+8)`%l!H1)Mt?C;YaUKtJ&PR6 zJ;CcW)LLLdW+hP`vi>ToWDoWcxpmI!MB~vRp+2Ru2-15bM2YzGJ27Z$4w1*C+ZMc| zRqr>e>grA-Ft_}&@?xOF+W>@jT_Y&Upc;(o-dVe{s6Mr)yR&*98<ICN!5ZUH{t}UO zRajomwDeB2xs;ePkPvi@NwG*`EzY2t1r>1s9lFig>Nvd(Bl*f($el|GKRK)^cW950 z(K&q@Ur2q8kx?<h;5V&BCzdMWGU((UA5C+{iU)(WBcTiEt-Dz9JGmvUmvVn^f7R{? zi`Q}&33F#!S4V<k4sV6+GpW%*mt9_SL9_7eMN5)H_|+7r)-wR(?@VhNvV$_KMya@7 zy&z?UEKu>+Wd^VIs@BYqxnby#46W_c#q{GG8NkKUe+J$=bBiA3pzX#46HcOLc1v^% zl>{6#cE6R>6BCco+OSReb*vZtBI!#@&|PMln>becevZ5-U(pLneqX48FksjAsCjKf z8;0IN)}ER7hbPMte>{l<>|29txai;x%3~c77S$YVBs(Jd1-3@JC)ttoI6njbNUrag zmLlgiD8a_NuKNw2jED3mUm&}JFXJQR-G|ZiS0Bf$2{HHP81OP7Z+A|;?mJGS3vjhA z#C+*a<PK!~ECh7*4#nrfJ1F0hP@3ro*qxk72Upr_Ek9pI`@G|WR~tI~<oGCU+n5~< z(vHio(PX$DX7F~T)9L5>e0#fL>n@&?*m-g2ag|Mjb7dxjR6}!fKV43_T}xvF>B-Vf zYit*l_^zsO{n~(TS)zq%V{1RnDnvD)e8~)Ea9~>i1FoCnv$J8!LUP$MzLPR0QLLjZ z7@CrIVF%EPaW>SsRaUI|TzZq=tVy)60TLnDSWY3xDH;zZSwB#XX6=0RHK6RLE_h-# z`(8f+IN4p{4&0i%CET&cMrx1vqfY}Cp=OJCs^#s@r;HwE)A?QK;me9yJL+QQoEvk^ za#}cf#0aD*nAp{C#=CC@uHaC%h|;4UtyO)WV;+3uoM6G$Gf`pr>{fcw1Lgjip()ho zAivB0k3cd;jT!U>BbSoXB`DEBZGaVyy}|Qiv>DWlND}8Fwt*meOW)vgB%ly8n0+nk zqtn@41$2kRyg(<79VDlmgr!Ap%Yug!M41zrs^7y#r>=UWgj_n*mrWm=Qn1J-sGj%J zpolFjYd`J@6?*eM&QR%aiJ!SQh+|t?)ECMM-Y1`1SL{Y#(uXK`o~uGNXHFVg*ht}~ za&S7qI=Z{D=viY)4v!nkRRaOeH`DG7h(Mm1RSJLm2ia1vj;~vsCLV4=#4tm`z8`mg zZ_>29x8ZLoan}6a<MdJ{WWhxM`37wtZwsdzp#RfG3n}dU>ZB}a)DUwnM1)x;)p>v; zzb}xZ?L!Q0Udre|;OaOc#M{vE6nUyG*maQ21ww82Jmd1E?M++lZW|<ThTOcKSux-K z;5_p^d~I8{V@qADPdeIWEmzh>M3n*L;YzV$u<AYe4U)`X_H+oB?l5xRQIFGOAY+`) zJ-9i-v_$wM1m!V$-t&NDg@t#gI|G>L80}A#zMi&ma!};Puh%m9arFz7y8^gTcFc$~ zb4alW$O!(b^UCz+EsBhnT#o>?dA^zZ;#(ESO3zGCffp+{{9%U-REpx+pMy#4?mvQk z05YdJqalMMqkKT?4PY$+A9IzK5W}lF$A=Bnnb6DvO?H3<@3WCqU#^x60PBu%X-y1{ ug2sU#Tmg)XcAqURA+221Q||wP`6GmHwW{uGo0j<dlR(@y(y7*RjQ$Tw&m71A literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d.png b/Telegram/Resources/icons/chat/input_autodelete_1d.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_1d.png rename to Telegram/Resources/icons/chat/input_autodelete_1d.png diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d@2x.png b/Telegram/Resources/icons/chat/input_autodelete_1d@2x.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_1d@2x.png rename to Telegram/Resources/icons/chat/input_autodelete_1d@2x.png diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_1d@3x.png b/Telegram/Resources/icons/chat/input_autodelete_1d@3x.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_1d@3x.png rename to Telegram/Resources/icons/chat/input_autodelete_1d@3x.png diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d.png b/Telegram/Resources/icons/chat/input_autodelete_7d.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_7d.png rename to Telegram/Resources/icons/chat/input_autodelete_7d.png diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d@2x.png b/Telegram/Resources/icons/chat/input_autodelete_7d@2x.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_7d@2x.png rename to Telegram/Resources/icons/chat/input_autodelete_7d@2x.png diff --git a/Telegram/Resources/icons/chat/send_control_autodelete_7d@3x.png b/Telegram/Resources/icons/chat/input_autodelete_7d@3x.png similarity index 100% rename from Telegram/Resources/icons/chat/send_control_autodelete_7d@3x.png rename to Telegram/Resources/icons/chat/input_autodelete_7d@3x.png diff --git a/Telegram/Resources/icons/chat/input_bot_command.png b/Telegram/Resources/icons/chat/input_bot_command.png new file mode 100644 index 0000000000000000000000000000000000000000..f9863a78a8ad8f8954d894e2b17e7e1f4563c9b8 GIT binary patch literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{?1!g|V@L$& z+c3v<O$GwG_A*X<i@onwu!-yJY2u7K+M1q_zNbmtz)&TpCp<v8^j^!q*2Xd|)%kx6 zJT-5bug{m$H3{;{%xdnPe^6_6YcJDoyM-4IUOc#tujK6i`0N6!xvOl>U;SDY8kN1i zP3B3{Ve=2Fh1+yamxP8cSslG6PM=rr<Q&sux_M@^H}3fTcb)m{UcX64(wUAavG07) z;87HG>Y<oNe_QTh7P+bBNe3GlmQOgXJK-At<hsXqmU9#+9QbK@?ThuQ|4Y`{cJ#9^ z<|wFUD7KKf^7v!G>8G0n-yCLVN>^PWp?FYFjQiNU^UuR<m|JQN-fw>Wbyn={_uucd z@7NON^~6SPZH3Xy7tcSR<o&MpfAjQDiux;>GiR-`==<4Pv-@s`y$?{;mn8Pg;uniF zZp`^vllSyXp@3@CNrSvYZKkuCl@zbFP5m(QPeT8VoW8qx$M0v%QizCHy)@G2;9859 z&lsk+oPThEy>P?&vyM!bQF9jt1|F@@RF0YLd@8-*Me?qazSB<^o!S&~{P9NPaN)+w z2lw0PbX<GzvgFaBu+^c#jdA}a`3`G(mRWTiR^^-7Y`Y<7YSr$$n@-#>KR4Zcugp(H z0bajHDmmAd^t(M#xc+#>`Qk3VHIqs&iMws_{531)kTuto9Vv%<_-@^q*wf4B6|*Uk c?fnDkOYa{a6~8Tc2b2~(UHx3vIVCg!0C5%<ZU6uP literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_command@2x.png b/Telegram/Resources/icons/chat/input_bot_command@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..11b5db90e3ce477e04d3423f8fe8f136a6184815 GIT binary patch literal 1176 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z{2F|;uuoF z_;$9f-)9GbHuH%}Qc+TR0xD4|RaU!Pn%KNp?zAdjSnA<;T=az{gGY!<fPz(qfY9%^ zv-O4d%x<|cFVaTPIOxg!nYG_<M)q&?J#G2?@cA`rjb*;}#j^zr8qS0mG_XzZY<QU` znZYoVWyZT{jYM%*NG2@5T-e^;o@XJm>-X>K@87;X+w(oLK7%=Tq3;%6-krStUyj!Q zt-Eh;W5W`6R?5V|yU)%q>C~a@?YI9udbB7qt2nA%E#ovpkk|sZiX+0eLYR+)c)or8 z`gD9;+^@TLWB2ddH*Ya>pwE`ZZ5PyT`RN`mnRfC(!Nz$zpV`{m|K7WI?}}zgqlCm7 zpk1zqO$DP^OHVNHh^}8zZ1c<akg~&t3291OE)_N|<vCDrTY&M1h>(D~5OY))+uEjE zDGY7Z>lYpSSn3g=vik52H8I{O)~zQQc+5Ar_U;cdIOXJ!s42B&+E?Aec{~ReIjotU zvE!2E@87@cOr(AuKknZDb=61XSC{z<a&yn_-?PU@l3|HhgZ4**hYubc$j!-NN&CO{ zYSzW9$ET+<RFsv8HSXHAt83frdn;^Yn;uBcfB9$DnlskR&+S{gcCE>l_hq|{-|BbI zWs5AUyZQK)vBBAWvuDr#_;}R?N7JxMljcv7^JSEK9)0}vORCk*%4$|-^}Kl{@ggcY zOO8w4-fZxS<+S35A3rR(xMwu1a+NPxG4IgJFS0yr?;bx^esL{ztD8xQ;-~H)*Mu$) zQT|JsYx^IqI(xw6w}}}4EcQS(!JDhRPYO=ck-Nh0Su0Y!sb_go(FT(k&&Cy7Tpx?@ zzw$SfyOwERzOsmG{`~pvb+xmvJ#yr7b#u;YS=#OX;>nKspkEqtx>r*lXo>QdO3qkZ z5wPsZDF=rT<JMxJ?s&b1Rm(oC66H^k_i<%Ar!9A5x$Au;*({?EU%rSmTbllPT3B9g ze)rlQC8JY!vd>(;94xZvUGi#%s=WF|4-%6vR+@f0pT8kXVV%Ps4W+}}OLq0I6??ht z#+I0pxZb~3439!p<06?-=3bw6+RE=i#?Ci?xQ?eX{JSdtI>}X~hE<g9?d@&bx9>NX ziu2yOB+ByB5r%vB?zvg{`S~q+t7aagyE1H@*~G4t`P+3h{wJ1rF>d46@oxIXxuW)w zHp4|bqjaY&^~DKHOEmjl{t^Fok?nrxLXQ-~KeI#EUzh%0R#tZB*|TNb@!e^e-A()U z@3+45{9)MBU%#sU{QmQ2$>B$**DxHts>P#Gro47T{(hyC3`U014$LQ14wP)?F=_B& h@+m(}95;pO4|_hhtO4tZ%LhQEf~TvW%Q~loCIB`51Oos7 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_command@3x.png b/Telegram/Resources/icons/chat/input_bot_command@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6116615c7a3db7023eb49490c739cd08eac5d5c8 GIT binary patch literal 1806 zcmcJQX;czu8-@W{lw9}{H?mPnZ4jEs(Q-{wGjIg=4=ZL=N=GA?w1hK?5oY9)Wm<!{ zgdGcYl#mbHO2u-S(lH&yC7lT?he5%t#w8nd@W(8_=bZ1y{oc>@+|N1B`{O<5%?&0U zHGrew006*%fcFd4I%7+^Q0?h@>cEd$1Ez%@^#KS6Emd0Zi;X125s3f?ZLJFcauNXG ztr4wIS^)q$d>}wa>%grZAM{ts;OqQdZw>0cj935wpbUcF;cx~}Q{nZw2NBVy91RQb zHnI7NWoGSL!~oWT9ZM0VbiS@_Xpxs*X*u3{m$6>N2eVqAqRFp$WqaekUKk7t3QCR& zlFQZ03qPE@;N||Hy?Nk3!Ty}}`2W;-UmI$1s=o(<N3K@!Stg<s$R&<ocO-Bd*_**| zWB(r-n`iUy3j}?Efq~rCR@cuD9ZK@|_dn#dzP?^hVKSM{bUOX^?f(8R(lRs83_g4I zLth^Z<JD!c`l4EWyQ8CnnwZ$Y$jrpf9hcFs0I4(lgb~i~DN3a>zpSjREwy3QpwT-p z&|0Z89q*L4w6v7x9jcGm(ap6u_lXR#Ix|y+yfy4%-8*poJ>Zr{D)Tvl)*BLNWG)QS zJXNU@8YPnOf~!|e4rC9agGI@UdYL*_UE}F?ubfLwai_Qq+w11%=c#dV_Me{a4H+7< zTC~W#W^%<fES<Q`JZU#|R;{i#N1;w*?8!q4_ih9RkpX?70!&%$zq8Vs^4qwDPzScZ z+uw0|Pc}3u>JHCvadb3Xtq{&8xI%SF<BcFxcgMHilVA)Q4P$=L7M;uPA4|f%1sQyL z(vHakckbmp9Ucz(o<ci;y~l;tvh5)HMut8;_VzYu4T+6hDymxlmb&EayLa0?j-)+& zCS`9D%&wrry|X);*2~@$7e?<mx9FX7G}$#EIr;wN^t98;%E~Xx%VTHGoG};0ltc#Z zH^8kGNDrs`<rKz?j`fK%vD0~9L_dBf_>nQfl4tGFIPu&k9he`e$25H#=26b}O%osG z&p1oV<9Dq*Jv}_QGcz+*O&7kLLF`kZ*P?O)-nRGj9C`IPC(C8yNN?23H^*hmHiv_o zi;Ii-?SuXjTA@i*><LRDLGkMK>r(?aA8mL>_M1QNS!HI_7r*(4z&2lIs{qBUeC_k# zW5{H3e9GbG$gWXIc2nCnoQFQ+!Udz#oev+@v)o;%cXmM{q`jSz4^wZ1LLu}gMn(n( z3WJ3(_0s(;@5==b{Bh-%ePii#Gn3DLMPW*Kbd%`1*H3}rG9skhx|XJ`d#YG^K;Klz z_+)SwNr)5)#KyL<?PBrEr8OyUS8M87u)D;+xuRyF&=zxxqS0vVj48l_CU~Z8*dFp{ zpFje^T({Ox+0RuPFd;;V7g8+0;?CAhk;3~spjxzSxlz-sXfl?Ifm`Hqjn+D+rBt9Y z;O{II_;_z{O|+M3!A1CzLDR%WuG$gU93WJ*mE#AjG!BY;Aar-qO$zXLBy%m%L2-$d zW=}ShfJk5$dg@(PyYG}9J3>kN$L;{Ji!IrZLKr=fGL?J!7|T^l<$Y4h`M>DT%$0S* zjx4&09}-(>?-rouSjs3jFfftr+};)qQd+Y~<W*Eic@h1CgCzNcooNUwMwrk!u}Q_j zV6cu>+Sqq_qb}z%@`Q}e_V&ce%1VdjB*zlMMFTgLn2vTJH?B!X_94?zmmiV(f+rFz z;H$|@QH*qRD*Y*-J?BN!`@Z!NpY;XGLE<Av98M9lew>+=W$&rL2MsS4W(QmB+-Xos zOUx&J3pVZuHZ^4l1OoI)dlkksOl=)Bi$q5=+zB;UEcViXw5f;M*3x2qEnm&!7Z;i4 z{Q3lc-YsTkL~kl>M46GpR^4@;yQCm2#NTj2d1~fMHYDFXhIqku%MUQou-ad$D{jJy zJO0ZFq>Bp!T2kVM%uW`mNMplBGDGp}BK^kLta*$0N+Z$y4&jrApYJ03+ghJ5&6Zru zhKp&;cI-y<?uvaVl9>kXAsILGYYwY@uTcZ&3vCONxu9yJ9d2jwD_s%Zz*lomgwBuw zLp`A{+?*Vg-hhry$EWNJm9=~$rV)Yv^JULp2ldK}zofyrJGWCfNSv@zeLPI2zOHbq zW}RI+HO>RYalqp?kb>{_JQzdb+-+M7(a{voN$EY-q)pd({J&iB(Q4{)!NED#pPq01 NMT8?HKcP=F`wy;aG*SQn literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard.png b/Telegram/Resources/icons/chat/input_bot_keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..c39fed3379ce002b39cb1fa5a6434e57d7cf441e GIT binary patch literal 585 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{Y_q3}V@L$& z+pxe*%?dn6%DMT2*N5Kzx7GYpKo#@cx`<h4jeF#|7)y`6JkX=6dUBJVghr3yw0W1e z?Z0d^yCbzJI$T40hUlGD2d@3Of2(==>7ypT$+xzdUc0q|-SPyhTh3ump_+H!cmJ)i zJCYqOef%PmxAAikVYTp3(aF{O?|-Y>dux+Uvf6`)Cko;c3Z7nBhKE=(Gb7A=1U^3N z-gv-)d6|&5&xCLM&0R&?Z=d~Edp`L`W9;><voAbnXOl80S3c44MY*B4=+WUB*Yqug z-oNfX>3O&6_wzKLK!rK`G@k9c|NraE-k)o&H1e%@m}dtBU72ENaU||Dci%GqpbuZG zEQOq2{H)pa)2U8;g~6n63z)7;E3G;`?;ESS<_qRKx2L;Icr|C?rbp+JZiKMQZl7?U zRf{)#zee4~WvUO={>8ATpHkEPsPn(TxHDnJxz<01`Ad~<l*%Sway(zV;@r<aO^P$5 zHg(IhsqXrCIevS=&LgUyw}^+W_*r2yW66{`SN=`V-mham<w((KR<kyhq6yN4SD))H zv+6dL{<%WpYIx0+CsKEO0v1owSJQqG?Ed&PS60ZUqjTa8>Mw6PYBPtU<qs$tJzf1= J);T3K0RUc}^0@#2 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard@2x.png b/Telegram/Resources/icons/chat/input_bot_keyboard@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0d6b9e60d5b8df54898ce6952e3f93528f7ad73a GIT binary patch literal 1140 zcmV-)1dIELP)<h;3K|Lk000e1NJLTq002+`002-31^@s6juG;$00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91P@n?<1ONa40RR91Pyhe`05RZ9PXGV|-bqA3RCodHn@>_IF%ZTB_1t*^ z&p{P?PoN7A;x#+~3-;_e2p6!FEf>(8RnF^=2hXIFQ97Lg(Jxg~OgjB<ek7Sp7)Bw4 zP7anz0^>?Mn(YV>ptCCwAOSiFIL+Au3D8NvY0e%<fKCEVbM`<2bP{lyvj-BOlYrBl zJ&*vM1f1sVfduFz;526s{LTO`E-u2|-CbBL7UA*nF}%FIgs$rbJrD!25EHSH!_Wm= z`pu8q+uQE>`B`|Kwjc-OGQ>6Re71!*9*?_+hlkTrD?~?5$ZY_SZ2^8qczd46?OOxH zP|@Z6{k@yd=iPKV?Jh4bhq!>mCt@HLVj{LK0}SUjJy0g=Y|h(kHr@60b#j3N^nhOf zh>aZLf;kbnHq4_9K)K_^d2YY`Q^MN=LJqnoBG-L;gU>bqW!aDb#1LwbToWJ2B`(Od zVQy^z%GxW=aXSyiNsdON?*9J1dwP0G^!63>60|`Fy0I<JJ@%Lr<APkZe@DFyKv}@X zIc7HufH|~4S6`6F9ygY_AXn|*QEvk<P7vohp}wA{?NEnqYkM6>`>(B!c82odE0SR1 zK!#jW*X>E8sz6s3y@|B7z0NNQW~lrz=r&c=7Z3x|ZKc3k$7)~lStCoo<{A&b+=c+1 z9e@Cx0FO2YVvdUEt$5DT*HCKn+Uppt>PPE=>-GBJoJ+bTS)W6TZl8}s<wpZJo6Qc* zDU@_evOb3v-98@{dA4X3W3x<#SW>hP8NUA?<@Ccn3@wjxHTFqzQqlR<){nN3mIO~) z{xSPVA)b#vr1rC;4Pkp~=V3vB&hLo;&jldn9NCfzveQ!hU07}m+cxNw<|nVclhdc> zP3}lv-Q_&{d6ND14z6SL)e#Fe@u79&Hs^U7sMgt6335MB$bMD?VA8CIp>9u<RRy}- z4^&We^)tY_P1~Zbb>c5bx0UQ$$#@rbk|aO^bP{lyb3f1t@MuHWvPW0aEy?<>-J;v) z<4}1sfUmEwNlf`MJE>dt=t{aJSrQ<sXkDF)&NVWvI!#vW;-RW;d9|ZvKTnn>)%%HO z@oEQOE~@N#`yR{J+nfOTf~6B6dLm!(Z2*xknL8o!Zw^j?`<n`e^Kba0O|zdh1zc9} zH>;bQo9!`~Ov2UGRnUvt%*ZLgB3a(d53AKGESJme@%HxS^xAJeD8T-(Kc}VHqukFy zV!ILowDHQc2{1!q6A++{SEfyX84{a-0ByW7Z34`Y*aQS<<CSR>V1~pdAV3?hOq&2R zBsKv7+IVH!1ehVQ2?)@}E7K;x42exZfHq#4HUVZxY=XZFZ-!)xn87{(0000<MNUMn GLSTYfMfh?6 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard@3x.png b/Telegram/Resources/icons/chat/input_bot_keyboard@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3f85cd407f4e8693e5cb31300f9db4d3fc0d110a GIT binary patch literal 1687 zcmbVNdo<K}6#tnAnvH20dFD}hOe*xyXc*-Y@@%wryfQ)_<25GoX0SP8W>6VzOiJm2 zX$+<iNg9cn$|JAZ%m^{$m0IkY9i83n&pl`V_}tIE-+Rx!=X}n+=bPi^VkZYvg8={_ z=U{Jxm2mt9WThq6EYP$^LQ*8GofW|3YfMQ3n;@LS8E0o;m&BI^q_RT+$VQ0-Y7zhd zxJ(KFOIT{-tqk;UHogq}-QOsb75?-I0HmcHY>u9cmzwo8aS<Ka68p-?;7CT%57EHk ztahdRQJ4=|-R&scE}aV2);aNt2pAyq?;Occ6Q?8>iXGxiUG_Dh8DbB%6<2g6VJ<=F zJ2IGA5jxCbnFZlHk$c-#&!mIU@0ARUp>h0lQhXI2Hd7)sIgfjS7&HU(1vME+7D>9~ zX#mt6Ke-u|-2tm;<*P=^)0CXZrTa%UG!2VE=-^f}bC3UMuofiuzNZIU+uZzaW_I?V zx3@Qs%jFKR+3eH1`e`)U#LCJFq2cl4C?=EnYg1Fx#L`mlNrt_&`0B|N&r_!&X;kVk zolfW6mumonS-8>9=<HSDr@6TYuCA^r{juAl@KSm^wht)A8>`fHbaW^}5=;PfV~?$k z)K(ysNEFyQ{wN~}8>p=e!*f0n_-fp<aVU2o)T^nbB@zyYkMMXr)4Vc*0)j#*$yblD zVfK1G3yAR~o3uH}-O<$28hQ8boxxqDN_-gRxoU%>T(KdA*#*lB&FYJciW*s7UcMXX zrKHvX&g@HXw)!*<k=HkAPuTlq9g~Yj$gA8u&NWxn*u1uNb+`UJpHHnLeO(OpRFJhC zz=CCZ;;zh$LogN=6d_kzSMgz3>MgP|${?ryWB<+)Z)$y4S66XDAR2`V0r~SpB7)+V z{0|wJkGT<z9F28dCj}W_Qt55K8jERxK*#Gzab#9|dDqa8=h>@!kA)kqY#S!6$W-9K zGEWD8!Hl&}$gZ!iYd+o0Cs_yCuDv{+1>fZ8A}-eNP($S}b_N>hM{hO`S1_JJ>8<1p z3CfGaI1I~S<pT@eQhJ{D$1I%Fi4yqC-Za5QoBMiu=Q2fM$w^6swm#iqiw9!Qg<}_e zUWIt!bILqT9MNuj#8^zNaJuLH+ilx?3yWm$>Eh@mgk|Jm=X=n_G^!)Hw9=LQ&xWCu zAJWT1s$a8jMnsCSZ#Ck2mGUkkaygf1eT_`w!Q``)gRYjnpfOA7tr1AAyRp7wVZVP= zBQR1cpcqTbYcjpG2rLFGN<}#&zn(9MzR$il*f|#+p36zM>ed+;f8^g?vR;nQB&syM zy6wb_11*8LK^8trFlCvY$~(}jWl_`n1SqD@x}5{%PZLec$#cER>Y({k56TD!(x!Y8 z>y|Q(^>&OxYlus&05q;e-R=9WJj8(g)q0!OZis5X9LhYcI9D?@`jKF7+pRO1BiqiK zN`pkt&=9|E76r!CXHo<jDVK$#koFsUV%v5+NJF066s6bXOHT0nEF3PzT)e#2GX8em z8k&8hBwY7p;FYbo?IDHiI@|4aMQO-u2QI9<`Lalr&nffD;Mt{IsYb!~J|)!~ARekM z2Z7v2_nH5kYelX+;iqQ|{g!Cfuq9B6bl;HH=)wusRVJR2M0J<w)L2+8C?`}UtV#xq zF~+{B9Qe1Qwoq=se1LPJ&ttAd$A;E5<^iUi9n;fcRY{7PZ3^|12QJX3t=#%!lk>16 zjj4G(Jf5@4V<%Jd!bA1@1?*GrKjlhq>>RR6_05z==&qxf7pw?Z7neU^-Pr}4YEgE< zBC(CHrJr{#3bW<WTb+fqXirmZttr2vIqjH*bU!1?1&Xn_-oLM#9&;z8To^b}pyOBR zp7-@MIduji;HbB#RCeDF-bN;dq(5gEst%^taQ)}66)!_H^`6k(&-80oF7cC$jtvxB zEiAJe6A{fuhfmJ*ag}XcZ-rFIPe2&Rq&UWlBd<kI+-v=1Vx<jOhi`E-uz2spJHgiE zeq{Z4K4_%J+Cm*x<#KmVHe#?yb9%@PAA3B)>}D(Hv)jw_pi1|!E*&W6R(7H(0#}Ol zmPfMoSoGCiZ=Jq09Ai(8n7Yo8gL>TVE3$@#JHnU4|K|un)QjL2qBL(vcH>iUuywIv ITH!PP0xpFS!T<mO literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard_hide.png b/Telegram/Resources/icons/chat/input_bot_keyboard_hide.png new file mode 100644 index 0000000000000000000000000000000000000000..c76fed6918b04e95337637053c033863d5bef6d0 GIT binary patch literal 643 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{?5(GZV@L$& z+c4jAO$GwG@sA`s*e}k_i+CX!Goyk_crRP^`v-zC#oUYI9L0m4`|8MNHu$ZSNSd)k z?Ai0Pj|07xmrhxBG5Ysw-Oc738xvkgONq#c@PsOC`M3SrgB-Kq<BtPl*M~3N`b}zu zqpm>>Z@kd+8*j_3&KExtO*{WQnA!W%=edv7%;%q<dFS!R3%B2f^&d~3&o-x9>IxIH zil?V$+iZosQqy>OZRYJU5|=PY;M#bHCG)RAhV;`Mp#u$!m-|BRy63C=es!ym=}}tJ zKAZnuey;JuTR)Yzgc*1WZH>^GBRl={Qm4EQOL1*6)1Fx(kBfG0(Dx0tmFnf%f3lI^ zP@<?;e)Gmn>mUBwpBWn@`XFoTrSHGzIxoNMdAu-Y!U5A7gC`2xd(JZc()=y8?zSMy zgA%K&&p*%nojA`&%_e95-9OnZ7jJ~E_Pc6ya=nnt<4;x2pLdmJdi)aj^TKWK&76+r zp!UO!&v(fyolrb^?0;ZWX-m$2{)5+kFSxs9b+zEZ+i#Eked#QJ=5T_9kNAR)glVZg zo8#8UR-D^$&|t<nCa;3L85S{*CfvNutSy(c|Gu&3X4frqtt~%EPhF_ucjy_zYiA+T z6<;l0eV#3vYdop+QfS``|4X68obMb076k>LTaYl>YSN7b3tW~in{!Y52UCOEoOiMs RVkV#j;OXk;vd$@?2>=1@3UdGe literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard_hide@2x.png b/Telegram/Resources/icons/chat/input_bot_keyboard_hide@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7540076dc84382a22e52b640d84221fc9a2e97ab GIT binary patch literal 1153 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;NuTe{@T;UF{Fa= z?d*+ypA7_#-{mrMTei>1yyeI}-p+-x5n5*V+E~-dzS%MQH{6)G%VG6ChEQ>pKR@FW zPnO3|(tj5_tLCXr>yfi}f4+}D*C%~xUh%p1v#0u4ep&yTd7Gb+S;L2wIfJ2<rRCkU zMz#PChZlL0#BpQUDr#!ptXseS?&ZsqtE;Pv=g*&iuKNCqHnR(2w_I*txKMIXx%O+W z(aaw|Yvd}nMlWEDn)TwbzfWw%-@5zv@7~?&y>>xqM$<MnhgVE*l=hquzGcNL;M_X> z{rmj?KYqN>*VFrR@}#G*tb*Z1wp&iKGiFa`YuoI{)^_sOddvK+Q9mzcxLDnAV4HHx z{q}_kVrM!ptXkpBp0MNbQmgIZ`+X|}1x!Bpv|Ou*OI*)vpd-!Uz@x}osAaf7RTrqx zmpvh(da2a*&l47T1hyNhFJp}o-TIP&hx<mz<G4JDQpE?yCW&RNef?X1_q~3{8z;R4 zJea;zaonAIX^P!%J)J$r*e)<|adO<%W#(Nlq2a<^qYw4W6_u4Q#kyO6|M*dG`DKX4 z{oG^)u_UE3Y1h=inR+3GGFCx5TF(hx{{6eUzN921BU+4k{$cignT~Ia=3n7Eo?2#I z6L;t09fwj@z3JY0uYiI1_vOpXw|)o3?Dn+i==2`UE&d&zxIexjJUqN^^UV+cetiF4 zUSC!g#-&ikV7B2YtD&Ivt&g_bpFd~)`j@HYf)<Clx%pCog1qn_?5qL;r`r^*PCY-m z=%*gjO6xz1YwvIy^~FE<o1P%TZ@|ybztKNM{$NOBTmD^X0Zuusz~)2yY#Rb=u3pfY z{eD$jRnP2q{89>5QD*v|`mXQ2x#UB^8E$+172D@G0@W0-Hdw4#Tzl?cI0MUsP10=p zH$FMgtgFBCpXj%{Cl7?A%n{$1b8_zcOBNq@Xg18PTO$AL=!N>{aq;ont542nn6M-J z@9Bk?89e)2r!Hf-^;qX&t6>8;A+DR&`1<QC56=H*)V`W&$8cZQy10zZU=OQn2~WFo zQ2ypaTMO0-<z_L&$HjH@-M$?S6$;AL@>hHFb=!wh^_b4N=G6y-81$RAhu4TI-12_r z)bY)jUGYGP#s4<>TPtjRG@`g3q=!BJSh0KV{Q0{NCOr7DXxj<@Pm*FMBc3O&G@5;O z+wJSur>mqa&0ox*9c?#Zau)YX=7$sS@-olp?Xi+@IHkNhjbZb|Kz3%2%9QIo31&vG z5*Rf5-^^k><K`yIqwsWMtZ~E56Dyh-m)LB|W;2kMEH!AD;$C)^Ax(KPAKQdaC)P<G i$Vs_!kReF!=2rV<<trbZe_>V*%Ko0NelF{r5}E+VllgrB literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_bot_keyboard_hide@3x.png b/Telegram/Resources/icons/chat/input_bot_keyboard_hide@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..83c00a7e16e63d57effa301db4d7e5c1347e6dcf GIT binary patch literal 1828 zcmcgt>s!(Z7XAT>q@&hsnuzQ+)rw|b3UjbbGo{OuVtFGKlCc~u5(vDFqJhe4YGq?4 zmOGClGu{%>P_WX(Tb7nXDgqf|lT7RVM&%`R@x$!?1N-cU_q^{p=RKd!d7g9q4DN)f zv9&P(0H&u-`UdJbc@r?GKDu9Y8_*Mo5qRPlP}grWp-+6H&z<_--yhhk*DwH-eF-qw zwCG{22LM3IKmbI~pv_tt_&?p`GRS}RrV;jg$SeRrZ=Uk?4o(Km{L>3J?XvZmaNgM1 z?r1q+cGSr5Yr|tSg95uTFoBJb0!D1`mT+H=fdzq;kpa#suv;Gd@dK5Ew^cMOaWNO# z8X7TEnD-@f;{J<1ZW9*Rvo*zc{4A_`U7fZ}od$tQS59~XCSxxxo_lzdw1Y7W!vp34 z-`4~E1oq3$Lij!w8ozUC{vz~$VOgi;QDbAP&V`5X9hS@UI2=w?O3LF6Ya}vSGr6{w zx-dU4y{3Bmb~_4%>Xyku7FJeX^afxu)wT)i)^NCeQet9nWo6|78Cbmy67{ZZqS##3 zsZuEH=H}*tG^?G|t_&{s=&Tt{Aa_VgO1cKSa~cxmlaI`tg}Q*fJUm`<enp#gDfW+3 zfDayw2aVgv*+PsbX&e>l?&aky7K{6oN@XC@JITHv;mWdsdTclIW04tyPLN<aY7dt^ zZf$kM<MI6RM2tzxx+G?Kc5luj9&f6dwQ#T_#L3C2cVXeGfJ^Z1OzIljo6`zWzXye) z_Lr&|3ljnGI%u{P0S#9d{>+|5RNpj8{^1AZ$9Fiocp99gYI<-KB2RQ|>VYt6G|~-( zk-ej#yo4xc?{Lk5lZQ5J-@Sdvw?QJ6_X;VD?(u;#K8Xi@=4EyTi{vW)ti2-fW)6&w zj@CYjy{TptjkOIJ?pGSSF7sz>M?OD2^q@vp4i=e}<1}F-LDccB)FER0q$E%q2~?is zvws6Ov(MQlT1e~z$zwu*vNJT7Xh_Q)vR>ZDLgW-h;X$RlnxLL0W6-Ey6-6q}T*5A# zzrO{UOx8K|^z?KuEis5m4-*Ot3ptO+E2wi?ZDTT%xkrfXJk!YFi5SAzAn%mOyu3V3 zUtgcZpnrlwp`?gS8i;bE3)~OVcMxXv$Z-Jz6A>nz+AT}JT5VI^<(`a)+E`qqOVtF$ z$cRIBc6O$LIsC+Dw!cDTv0T0;lZoP9hl>48rUrm3{pB^Zn3xz%SC>Hvt={l{-Qw!1 zwd-B-n&r;nl|m><{?+iVQI)EQ!C)k?q5Mo!MUBIOOr*Q)@8Qo;;lwRxYa+Fe{Inxp zo}TGN#kZ;7@#-W-%U}O4?87}5Wu*;p+hNJ+>7$3A?7TeKLMW#CpX4g%zX@hCA{g%R zspaVtz;VgbFIAkcUiA%q^0RwWd^zsZ_4-mv5-bBQZflE_&Ge*3o;Q0TcFAp6*PPU4 z`$U><E4!Ckq6Nrokx10yK3G)jv*;-5wn}NKXaa1PeZPIx7pI9pFhPERRfpRPUV@r> zut54HF7vYJQmX9D^k2SnR}mZSak1y?a*1ghf|X${))^l+^*JT&mqX@~@Y;fwT8`V4 z`j3bsPs8qidKE1U$kMa`KK0U;S}b){=e9jp6v_7yqb7_0MuH9QappPS=BlTI+b2KA z<%e8A2S{Lwq^`HxeEf|ZwPZ5gI<{$8znm5DFgSFHj#vr36+IkMzuKxqb<o#Y-;{fj zHHV*I?VF+DeEXhx<*Fc^SwZIy@a%>{;JU{%UdFWvIb1K%ngEE9Q5tSTNRMhBVkI_y zCM2`%;aYD%Z0BS&2<AdKmT6lhj=6p9@xhX2)2I9@#NEv!M<5WwRe{q+lSnMHvT*t7 zD#-zjMvMOVWrQF;ovRb>uw>ywLx23oCB~>l;gk<W=>6JKZ^K17Os?8RFb3l4xOD;n zuP0g;kh7T4LZloIzZ<5`52m0>S7x#r@CAsB!8!Pm)RAULucPk$<c+L=pU7!$nxAdX z9=vr(G1j@~mh5^(Y9kv;+ux~aKleKRmYeiji8Z|+4R1*JGK7vDMUsxV(djDTof^68 z)oae?m5t0QTKo;cnZ@L=H(Pdmyti-KiHZH|)|sFxWlAFJ{v+qsG-F>~Oy+O!umAxI zsF2?&!h&8J8?~ar-19-9mWD14Hrjp|DaAi5Y`dhECOy2HHE}RH`Ya&q@Se;6KMi!Z Y%~$XESGB41+2)5jg~j>S9V2D@8*1cP3jhEB literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record.png b/Telegram/Resources/icons/chat/input_record.png new file mode 100644 index 0000000000000000000000000000000000000000..35efc9b52fc285a703d20765d892bbd9e45a3a36 GIT binary patch literal 665 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbQk0fr;7E#W5s; z^X)Y2zC#8AZF5s!E^sRlm*Q(&VZmt2Bz4Zk@vx}CafcEQ6AsP~zoZ|AdN}+LR8g_H zq;jr8uKr)4OuCn5?w91J&yy$KZBT94DC@<e#+EGE(fhy8^v2t=t4SLJZodtiVz_Np zi@oOtzvaQTd*ez2^`>{H>8X9!HItc>=*j$~#Ome8iZ!?2ZqrE4IrC7M(@17P=28Ln z1xg$_9Ck-7&p$6{NIT2PF+*GIXxoF}IiD-f<(+%>On=!vmQ=~iQ=hW-$pmhzIc^qQ z_x_bm=fMcQy%KM}R&nu8y-@KyH(~$%*M}c2$lf~Z$d4T3BLd!hpPSCTI{M&aMabE- z;H^=*iiLaQPS;w<O!L@k_S%_e<C71f&$mae4STb<%0R-S=luFUi53~Q1^v6M``wf^ zH{HxB*&8=qY4c5=e>;sP?ta40{X?jE!sA>9>E5<`Z_9!*?B@R$Z}R-NbB*Ve!haJQ z^qq7p@3Bod^0zL&Ywr1U-^9~T7B4#Uuvy>fjq-kRv+rWJT_&G&;VN2Kx@NuKa^-mQ zc<tnzZEJrBCO@`)|BpeFL3-O=h6=SGEJo?^8axULk^VYW`+Tm+sb^0yG<(K$Z4Rs2 z_Z5$)F;=R#xmMPd{n=GqR`cQ33fbbYSKp#jw>>RdsnZ?$bnTX-p^c}W|NJ4Q-te=m mwMV#Fam~|Yq2@|6r(^Z^whI5hT+bE{N*123elF{r5}E+ieGw-B literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record@2x.png b/Telegram/Resources/icons/chat/input_record@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3bfefbbb1120bd79f54a94d133ce9d4d29528011 GIT binary patch literal 1344 zcmZ`(X;czu6#ZC46mv<%Fc&0`$HFpGDhmW18g*PMC(9|zLQzBoSH!Kbayu2djBDkf z=FDh|RPIMRj?qzLG_$6#K}E|9mm^|e*qDQVreE{pzI)$$_t!h`y@Dfw-kbC-^#K5z ze0``vT0XJ?BtjecCdzwSf+Pibdx85yR&s4ZrHA-N1_Xc|T8#vdyl4R5uxMea1pqJs z1i-Wm*{BJi|8<WDVBhqH(M9He7yx*qFV*uH7qU?Gd>6^ZtS^I2%Mq0!QM7P6J;1(~ zFESN|&ur(yA4FU!6&>V)&BsMS_BgcHg|)!s5j8*Q!+2m+?nLySj)j|K(wfvYZsxG} z_WD6PIt4lgSrE<N;DM|DIuJ*&O^;gThtTiwffjuz&W7$Trrhp`5P+-p#?iYl|I?O* zLHE14-EM7deI=JSbar<KH#IelW*->xMBHjMCyucHkw_%Nsi~<|_~C5UR@zs7%}?Ah z)(Kl1n?z~)dtr7Qin13KZ_F)oh+U82wVORrs#NvY`TS2)Q?)i2%z)vm1jC2IW?3T3 z0X9e*cAX)SNb>3FY1>Eodp@@7B;hg%6$OL94c{oX6YtWm)`8#JZ8|A_w)JxyLNHp! z1qlUZ*2Ls7Xc#@t^dkW_Jiic%7`=Id6JKHatr%N;8Zk;c%83sNm2Noiyq?QE=tfqd zVLfxgm!6K(es{Ap)r^1+i(fUuAV@#1)*Ox<%q!KWpiS;!LA1x+W8h&vLkHrse6Ai^ zc!-KeQmTyb<~%3dNjpqWOAA4zP!wAK{^s=TJbB<+dv^K6VjT;ld(Y<Sg|43?UTQ6& z%OsMJ>g1A=5@vF;8@rBH?Ub>g(WEs65WL2QEmpmLX>ETXNeW(bfNe2kW@bvSXjWI> z#<1D!jJQUV+1$syk9$KLC)S=ZVfzV$>8G58gvpjv4(I2z(8XJLCPlZCrI(eHRS(bK z-__C3s9su1@X)PIjE|S8N|g1RR})dR24<+v`4J$MN|XBpY!)jWQ8e2U`MDE3Ff-_C zOo|4EMn-chE2&1b7pK}9!|v7@wP6t=<s87tmY0)Ko!<JZ-x51l5I;`;X05p9Ax-3P zKB&q;M3n(mfI^{2i}Bh;K<Qo<yQ|;Tc6XD?Pru$r^8tgZN<%D^?C!pVP%SOhl$VuB zf${(v-DTgGapuo(N$fj{^$hll91s6|ncXH>6e(onojr1S^qNvR#rGqV$?M33LM|}! z7`y24Vg>tkwb>VC{71!?pf!y~&UY50Lx(1BN)9W8IoEeDYezZXcj7WDorh#yxG(`@ zY_0fo<#-f}BGj`mm@~~vP@6xSgD~uJD;c`ivT%EQ@OXSWi#7Bm2oi>qSw#%%t)#uy z&3t3vyk@oK>&y1$UiTN!d`-@COXpMjp3I3UDASUI$NLqoEuNrPH}kLNv+c6j?(;mf zy_zA3%}aHnQ+g3M-dn2E(8ng(;kFi-{4|%glgyNq;CoF?cPc9}1Q(Z@$f&5&4!?n9 z|FZ3m28OEb#wRB3h{b*>DJk{i<Kv080l_CBLWaTt)%`M3Jk|2|h>w~m+zIoy&ePtj z6IkET)7RIR$zYMlqqRRwDr2W&J`wjm<q`_&YBnV{EVXgWSw}Ax)%08WOikuM3!|t^ zBxSRavl)-LE$BN?%;naA_n0C}Xrbr{N(cKq|CKS%-sX%WNc;YFF0fxxctQGIaRfNF lAkRXdQf<;;2YiWL3R`2eET;6aVe7{C@;wwtz3+AQ;=lbqQ3wD4 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record@3x.png b/Telegram/Resources/icons/chat/input_record@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..851e517bd23669944716d000015ecc5bbcbb8d6a GIT binary patch literal 2194 zcmcJRS6CBR7RLjGk{Du$(xeNFG=UM9qKqLBgoGAk&;$|*MTm+50@4)tBBAU60-?yz zODNJEDIp++lBEVItDxu*kS<LmblkWPv#;~A5C8i=_jk^BUhcW~y9ro(D{=4{FaQ7$ zM_Z$=@jdj1#6<Y3iI?Fh-vk1$S(yWBIr2;V4eAyS?QLfV(Bj8p0D-vM0N{@jzRvIk z00<Qd0EGB1@Pic!{*?_a6#8HMqfl(dc?|#%Jw&6BzlI8|5}6K?4yP4w#+rMQu{s(T zMZ(Qzg(NO3CM5hEdkqdJ7tSh~ojOr-UdOunB~9iMTv$*t=rq~govW?(6Sks_XY^-m zJ7Vnt=hn{J&4=^;_AeMrA0JNc*5e585TB5Rx3Zps%nC8f!kc8mnYSu}nEDM1B<MPY zs;X>iIz&>(84GrHjY0ko;BNXEx5tm$83D7gjHvIgmX?<<+^8jhq`MDRKQ#N}U0n^< zKeZcZYirL>Pn&6-<~E~uub{g}r>5j>?d*gRQOA%paDOp$8N=dQ%U_7MG&`wiD)uV! z==bz@o^x1f!{lgH_m5GOmE~nhxkG`XYPE~7FRKhzeDsqHsx54*%td!Hyk0ZLs{_O_ z2%PopYKqq3Ws!3A9Mbmo_DDfN7Z(>7#W)2BC0$$`^uQi2u;7INu_gWJC!P-%AVfpf zrc8boaum5`f|Sd5BOajj@831X69^C&7Z-aqqvc3PsbFKIt0Vqo)RF20)T4ff(aXO0 z`D#oNG^?-0;cAW}emL?-b@y$JE8&Iua;6F84Ka{n$Ounf;nTgX1+|kc<bJSW2$yR3 z$9vu4S6L1;{U+PK56ym>!CDGFPwpCIYzl%S(E5t<SP#8)jlUoT9Pw`3*Uyheyto#4 zJ~mrwq7Zn`yOD@GYVEqZ`V`>-QqF6%_^tFx&5O6Zv3(l7EsGeG89nSm<e&B|rXH>% zFTmyA7SU#PO28EJz5fRAr{wKDdRbF51Q8df`gcTq|Mckl@ki}NBClh^NV1j5eDY^U zRMySX*O!7a-5+R5Cf4}RjV*h6+vMcty*v(lc&D@+S?%bl<~}empbjgMTr^g?v)9j| zo{&B(-5=T9)P#!~-5>68R(-q){JytU4zcw;JlN4AlgY}+qi;)N4PUX-IVIJ~ED?1w z?S;4!_bvf;%K7+Zusy+Rb#-;JWngZK5m?sP*hmO#p=!!#EG;b+jaLTciq{U=Wa-N8 zo{`on3(X#zct|0Am`iJTQ1{&9zBoqlmvB_2(~_}VRaI4~0)gPz*@Mccp>sf#sKZ?( zyH_GH!AXM=vYy{iP=IB^m-thsk)4-EAEze}XD&0I&CK0tUj?#whgaEQ+sn?^p5g-{ zkM=FsBDR+^8kEvf541ELMT0ewu4G!;-FLN)ce;8|Qnf{+Ac~QpVJ}QnM8x`+GSl$Q z8DYly{`QK<WM-^ax~0N69e6KbOlNy%$H^HYeG1>g?(eMQ?eFi;NEdN=9FD#J=8WYi zLi!3I&Tk_X-t83@=&tz*l(N0E?glO=g@!&8u3XfVpO#aP?<v9y{PUcQRjWdNX+2m< zN(x7Dtm{SN{+mnvLRGpCJKW$}fUB@Z#>TyZ*T*knHWE&C&wy(=uteRVPcA9XYU=84 z<eCepEoArAJKs*$v0VTZXdofb;Ndkd{p8)v%>eKpSR)gYFpulgxrE#E3U7D0$)!%R zoznKOCl-?{Qmmw;q%SB9CX>nf$P94+o}6?V=(FYuJt_jyT9if9TROu!Ml&|&=e<dy z2)ID}{nMw)N%xaZDqR7F;-bs3xO%7pugG>(Ht3Dvqv`Gu^w?BO@1^kZ500wPu9VzT zHKoSvV1;`#ucu2#XNRiq`BbkfviMW+#S{mS!KarpIbybz<z}uK7o@kytcAAs1Cz-S zK-JedMt*6-_!3BGn7@a|*?=7vaq?p8h)4*BsuwB+ajR;G!URvL$*>TShsHCsqn=d^ z{owN*WQ^U~^~DN<jfVGz_LccepiuNVhkW@S8u4Eh@K`9LVMUEptn2Hq`s*gUyu6%v z&S=9UGcPq&V(aUi&DSB24Iz7CZT!*U?#STKP_~}itMub&p{Iwuot>^1ynp0u+><c% zV|(ZoLh=dQJl^kRWn~v4#$zA9Cb8M<!NEaD;|G(xEvq6+9n)d@UP)4D(_Evtl00I8 zy%)s%O@uK*(f&8485@?`JISg^>AEqXo_n?p8ueXNzv)R8wnb40JT=}SZ-W0u9vDF; zsu6ar^r0vS$YUxi!Yq~grb5@ZL|imZi3XF)*rZKYG_xiQ6o<L8Iy<9gC|sJ`MD+QI zpf8nJ6z;j3UTyLD?1XMO{5bC;|4Fy=@hNZ6YseYM(vxdr`ih?K_nlVIj$nEDX2$CE zauuG_>GX}cOEwSi_yQx8#Gr(`LDp2pyU+B}0IT%5xJFg`O1V9iLf}FiotBwSI(NHo zSX12?p_BKr6nGhJ6KS6ZqEs#^YY-#rTRoR-VTb%1;~DSN>Pd~!xR#VS@vL3j5Ag<? z|4W3_#M728;gvUgGBz#+!`)_aDX40#M1v8_8`Q)U45l7-mZ6_cg379dtK-B{2{IV{ zxI|bxTqnj_7oz^m;%fX&kd(QabS!*OMHN*CJfV_y!U8m(B2j#ULWT8Fi-3W>Vk6ot y^Z3u5Sc-{P%wh&f1@*vPgC&2#{M+AXe~c($n;)#Nzc2amA3$5$qiW4bl>Y#z^7Z`y literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled.png b/Telegram/Resources/icons/chat/input_scheduled.png new file mode 100644 index 0000000000000000000000000000000000000000..05494865163d65cfd72aeba7eb3d2baf48eb0b0b GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{EXvcxF(iWX zZP-EHW&<9R(CrLi56-XRVG#b1F2VBXAkWIV)9hAVo5Q(fMc2}htYW)({f&=WI%L*g zKK69WDY-+74qOoK;&WqnmgPA6|Gf2$ZMr9;xJ8zKRN0eR=Q(Hha+S^{^?V}VXEt)! zJ<>V7B2!B0_{Wp#Ok1`xv?qSpd1#~R5x!ExjkBN6+}hGwz;Qil@ATT(ISn^0*Y~`i z;(hr_)Pe3vF`c{Ct^N6$>1T({&$jyiBFa034t;$z;g@Wd(6dy(MO&ABP<Wcm&3{M5 z>#p<mbz<L+GJ5-IeA_Q`=QUqcjP2g$zy(h&?kD=j{^>jL>9&cl*pB0Mp$U=G(i-Dy zJ<q(dV9c77dgRpS8t>i(G9C%nOZRz%hJN9m_{!wqsXseX=CY^mnRMp@tL2UFwxX_% zHm4{09QSzhHP6|%iNlJE*|H*}u*ICqCFYXBMaQ$UDf(6&JC-CaYMy0%;vTO8d%^O? S6S^}%q3!AF=d#Wzp$P!i<if20 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled@2x.png b/Telegram/Resources/icons/chat/input_scheduled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e080db29e4150a4db283f37f4607f6a85015d786 GIT binary patch literal 1005 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;NuTe9`5Pl7*fIb zcDA>lR-ix|zq$DHQw$emIo>oX?qKwdXkTscQE>5U#hx3DjK^%$_<GOP%&G3`2=k7* zy~^s&vd{CEIGdi0|N7m1F59lzlZwx^vpRb`=nr7OY0bzGvW<y>X~Cq1pgukp2UW(U zGO`>EUQAw=RssxDS*8?~C@^SoYCL`6zz`x7aOz?s!wQuJQx-FCXHD3CyY%zVnw<e_ zWUu`+se3V{?u+Vy&#N6bZd+Drw{rfa-3B&CWEtZYIjnS9`t-2X{3|EpR|c%U%yi}2 z_Dk-+1v0lry?gcQl&_lb_b*?zJp2Ck?b*y}>p52*tFVc?w&d1qnOBbk4Sr8dPES96 z|K7b{*RNl{s+j+{KV-Je1o3yeL4S6BzQvSMVIYws6A|V3X%k~d8AE<v-lcCx+h0nR zdTRU3+tT~7`5s%s=9@9Iw{5<8MqXb2chp+3YV~QRE5xooX<lYr`J1sRZ8z_M)2F@P zRqs^Zy?eJ*veMgo_wGr#e419>u=t_|<A*0tRF=KWFAmPNI}!bk(az4U=={F8^~X*A z=H{(B%U;>ddg0A<4+9ySy1xH^T`Pm@7S&Gt=MwpbgJJ4>eFiN?H|v=nzDg|lGBeuJ zD&Xfe21r<5>}#z2Ew|#y6s_YTey)>qnHka^Px+<A;Dd+loXB{|^!e2XA75FnK7NQv zhOb?|r{d4YC4m7qj4xfyx;l5>JUzp?bLZ~-{kvK@<)ld~>x?^RE3#YX9{M;z)tEV9 z(((1z&oH!kw|74J{rh+4*G21QFfO@W{xf6y?V7C@HlB2S-rdbzr+Zspj5~g1h))sw zugY`D$?Gm={Fpv{x@pJWxbyNdGCvBMuBMz+^pS4Zk_?Ox&IZG<6`T9}`aWHmSjKSr z>8AJJe;e*lEsDDHH2><!G<l_i6RPX#%D(*Xz0j$5XS%z;t7K!^ls%W01TH)L@Zmyx zd;9O@<>mh#K0Ns5-IbRmudL_#iJWJAu>SL`zf&gftG>O?c<!Owe9n*L8C0*#U}KQv z@#JU_%JN}u;AvA~ao{vv#^}K2EX1^+Md~txLbD<#V}Mg{Cc}h-6IvKF79KNUIFaDt w#4sfwS&AXWKt++kYlR^%gOS7}0l0BL_;0rb)l{kPW(Q?8Pgg&ebxsLQ00p?UI{*Lx literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled@3x.png b/Telegram/Resources/icons/chat/input_scheduled@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..cab04cfadd555b0be8847d514ac3d2a0cfa082e9 GIT binary patch literal 1577 zcmb_cdo&Yz9RJNQZPp7BA{@;#u4i@TisR9+lIJW6vDGG%M;`MS>PikZZ(Xl*OUbL1 zNpZ`xt}Ir|n0YVU%NWWUrE_xm=iYPfIrrRu?jN7;=kxu3Ki}_P-}BA&KIx8xYr+8l zNX!WrpDj+>hLYk|JQ`s)utktKA9rWa@>W~CmAFLWFa#_Xm~3e!0Li%s<hLzb(A)w5 z&{_z9ZZTxLRx9^cH>no-x861?P5UncpiqQyaq>%oELCw2n>lIxloy9nQWqt5yM<$# zFv5c$6q_WBm>sQqKgiu;h~Q>d&*+62eBWpHJs^E!aQO9!w_Y-@s`%mIslZ8(W7Z^6 z!{mBe1OA<*t$9!RD;*>HA)e*~3*m+bpc&M_fijDZ+WpL-DHIf-Gkcf96reG)J6xi7 znQG7sP92P;;<|x$vK)Pk4ObNPs$3t}39tn2)yHSo7+H`btMdBoIcop4;+Mb}Dz$S& zD70*EZx=hQKR6t3Odj`sG2e>O--WPwVD6`mrv%L?t{SQwu4prDFQwqsjjpfc`U;-v zrM9IekSMcip2Nlsb=M+tOe6EPUef2My7iPdnLlQbZN^7$B#Zm7%A(9nNGpR8N+1%s zB2leaEarUv_~{e<E3OxI_S`vs<JZ1E&$!sw@PdMZ{-GiNUOj~P+qdbLYdz^*4<A~L zOii6KV|E}+#A<gdm_<2OkEPm0x+MSdkVhP;v^Bl}E2G0!ePo7`C)zABnar7+Bl15o zZ4O$@Vl;vW*t^OJRIixEqFZRp8II&(HkC@{j*L`@C6c?GA%>9UwM0IvMPSdV%4*5M zHI3_KW$M;wbX0V7^o_#8PF(>X9#R~khKG!ejd6rR+`#+y-3@*Bgz@MBm(dgF?^yFD zC(rU<r1qHBW8=;%Ad5e*ucKC0R!+XS(t}b_Q7O#2SbvRKNC?!sIono09=pbdB_$*b zmAH3k9zvn&<fognZHUbgtG89WPQROjrlzDU$p6~f>RY^jE&WPjVpX+7OHYEgI;5&A zl1k%<8Z$F9J}<ve4&0sa?<Kp5#$~Cr*2zIe;NbW@gw{=FCp@_Xl*=LukuOtPEZQtE z3uk&;=n_OmDcvD4wh<~C-}7b}&?2Rr=AE=SccV@yDCMHskL}rfPft+0J$B%jC9a!( zN)G(vrd)zuUvHdcUVWDB%an&hY(Wew#xNeq+G`{~2v`ty3C|XI*6#L(0+@23^Dr>W zbUuy*+LpmPLZFO)8&E%<`2^o&-qJB@r|k%hff(!R=m=!f4UObie%)*PHHV2w^=~)) z(wJ>+hKq}f9GNU3g_qV)Btq0+!||?>$ZX&EAdYoDg5~8@W~&U!O3?hLpkFmUh|Q3D zQj?OnAz4_672L7PkZYo?EH78yaCdX7l-2jw08>$39^(FVU7aD(!6XkuTT=MCy82l9 zeCH^cL?Ye5=HxG-@_!==mVGOp@v9utP<%e$(Ku<}!!Aiwi)x(yg-Jw6#0a;QJ3oIR z<osx5{N>B<h30_EW@jg-q*zfX6fxoMXHE|9^;DwQVub7IZmXgwAMufiiHY26b7=pv zpyZ5<ny5>c4A(p#;iUC{?EP4}q8eiRgS|cK3SyJX#e$ZsH*uYhAM5Y6N;3}7IVV@s z*Ok?%$MZ-}O*NDD2ix0S+7mpa&Ng3M4Al}k{{H35k%<En%R-Vwr*9J-AlL<i!6K0d zB7y~7F#Fi!Q$;q8juZlc(9h#}Z#>6TQo;thguIL}Ka-Nr3(x5R*qgbKCSw~c-qXRA z4EJaZ00NJ<zV?8sSl6~6kPYV^Xwe+92S$}?ZH3Z7B&*u#;w42Eo|C+w82YZ3X$K8$ zo<%1>8ANaV&k#mHY;^}jXl>NY1vt~ArzU`8f3bi7no26~M}swG53=?DwEjE5CGQJ2 TFwykfe$yD&lP)dJp;><d`KQmo literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled_dot.png b/Telegram/Resources/icons/chat/input_scheduled_dot.png new file mode 100644 index 0000000000000000000000000000000000000000..30dc17fb09f943e4f900e264fa5b99a87b7f7656 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbL~+ZT56=42j@; zJ0p;*!GOnkwY>H3a~lu8mD((5zfIAC$Ej7MYwH?`xXtqB#@X`{llfP_5mIY>)8-Pu z63O&tkBvr?4r5uIoQUHz25Wu(E(Py~xzpKQC#W7cce;7M!i=Czb2eX%I`7hZFYCee z<I7(*-I95sTlYP>P~hQ{wA%E1*9Cf=!JC4(<ecw(ZDYQBdX;$G{LkDMA6TxD>Xfp* w{$a-L(iNAbS5INTS=+IIV-w4bpC9CG7+AR1w;KxG_zLodr>mdKI;Vst01)VEJpcdz literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled_dot@2x.png b/Telegram/Resources/icons/chat/input_scheduled_dot@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..81abce9aba27be172368bdf44035e4852c25603a GIT binary patch literal 629 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;NuTecHh&*F{Fa= z?VW?U%?<*s4^`7O8kl0mmKH2$)M~W8>F5|{q*C$6tZ&n=i2-X;=PBFSKDOQIshRup zNk!0uw<l#b@-s3V)nedSprLRqQ=wr|gT(SihAyV0Ic!WCTpfi{11s?$y>VIQrMG3? zmra(YMt-{-cY9Xs@7}|8C5yT?zyF;pbNub@!|QJPJp8?uS%3R$_dU#41#-73B*`}X zRIZYU{kJueVZTk;r{Z@)?<6+d3~Klmveo?e_x9SvExErh)UJ6wYuCE!jBi;Txo4(c zIQlh<;d$l09an!pJ@Ll2^xrC}1Bnvn-hW6jHL4aW_dY7O;^*A|Z}#3eCUPKMIhNt# ztc>3_w^u#MfA)9F^=)Al%zNu^t-5(^2m9r2u~k2XDko0=ZO&jd=>}7Sp}slq0_9F! h28qu_mq`@*%a_5%Y>@ro%py>fd%F6$taD0e0svdB?Dzlx literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_scheduled_dot@3x.png b/Telegram/Resources/icons/chat/input_scheduled_dot@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e67ec31231541d660651977a9399dd8451f7b171 GIT binary patch literal 888 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ASip>6gA{T+3Vp}G!1Tz|#WAFU z@$JmBxrZDiSbfh0u%#YoReI6O_kt;qmABw2Q+c|*NRdN!Wq8hynLVo7DSOv^PODIx z;d#BZ_SW46Ec+Jh?D^{KaPY(QrY2Dyb%y@*<`GVPNZ4~V>uRpq>~{qgCC@+q+;#l1 zM0fK04PRgO7pzY)n1BBF-Ms7lxrXY`_D$GWl>Ja&tovVyRqxKfwsOUfEw?_;G>JcG zs<61%v?zbtV@v(4O-<P>(_^OP2feR9R-AqAYR0-IZ^vczv);`O{V$h2W#0OuHuh?& zT}y9Q?bVxWWwHLKRC9MapN0i<YDUJ+7(Q7ox%`c8A5UI7;(6=wq=#V+3U<%^mS1j~ zc{o^ay0`i4vu)}oc1g|B%(dH}io_&7sjvw<bIw0^qg&n6KP#F($81{s`>&mKobipM z-dUeDcQjoq+r4(i-1)bXdjFg{yx9M5%(Q<!{dV)st?XhR+~B?tJ+<KAN2U*_+v*i8 zXT=ol?6cJWHR<8Eupc+ycP3B2vfobb`?Sl`f7kw+wBO&{X<fBV-^TR&<>ykr`h_iW ry7y;>%eL8@G$RfcFny>nqY8CDvch-o?TO{zL8;!;)z4*}Q$iB}rt44A literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_silent.png b/Telegram/Resources/icons/chat/input_silent.png new file mode 100644 index 0000000000000000000000000000000000000000..a3258b08357828b486f9757f9a5efb3d1066b5be GIT binary patch literal 661 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{?7ydrV@L$& z+o}84IXVg)yZ%vq;RFp46Dj@$;TN)O7`(L3KVTKnvf8@Brrk}^t6w2dL&fFH-TX%? zde7(Wwyx&tG(NfMS<U@3=hy5uUpPf+ubtVRe%0!RqYdlSMf?rsEqKim^Y{K|=`&A@ zvW#YaIAnL~K)uj&QyIRK{BIVo__^XC+w$eEi!XM3`)zyoeR<ps<@R*JhEt5Mzgji_ zoLpVYu3^$Q!RSELTDR-3r_Q|g+H~vfT&<~Idp5Z9G4gKVoA9k+rcc|p+q%p?%Y(ht zCg(GpIGbjC&4IgL;F8>i`|pb%-^~-hB@u8z;lV_Pu6GB+RxhpAG@XBbs-4q4_Pq}z zO*rlgFX2Aj$)G)z>-wD>v&60SnKkG4w~04$7|cFfH1qMtA3^W_E?)o9_I=lf9u9*& zDO>!L9j;eK`5VkXzdC7S#$9o_3f06?d)?{Y`>ub9FL)ul!f}V*JEsi655FqA%RhHl z8Auc?zWmbVYSz^ql3P9~1RQ4Ki!wgow9w)C=b5uhtYmMivG6mQo_e~e_dd^tix~yY z8gaAErhREx8PXNDTJ&z7x#8odMGKE6Ile5pC1LQ<B1z_}d)rr2;V-&Xd*@wB{=0S8 z-Mk4QQ;g-duajHx)u#X0V?$X!c3?n$pMFp6zpT5CmAP}U%6`YgaY~Uq8dA3>YtDOc j*}&y?Mbn~<wlnJ(gep1Loi1=S1tkejS3j3^P6<r_X+aYS literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_silent@2x.png b/Telegram/Resources/icons/chat/input_silent@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a66514ca89b6811bbf8ef84d5152140d53795ac9 GIT binary patch literal 1264 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z!K%@;uuoF z_;%J=?V3c9<M02Z?a|qjvT%d)5|M42CeDqxaYL(fs{Nzy>>A$E+}YegIx4PZVPz2= z8WSgN5Gb^{Kl|a%{<L*7i|0SLEAhMgY@f~Z>T^HO?9?xQev-w}Axltu@`3$Ee~-Lv zZeU2+*2Eyv;KSro{?dW*gvx=E>3qa-)egv9zCWwl*Vos;!b0NJ`!{b``uqE@B^9=Q z^t!)#>FU+p=gyzk%#S#3cmLC$6UX%{JhHO0wa>r%Ed0BMWyZ<ikoos+{QX-S5FIUj z^7+GuiYtIR-h56}UOnl@duOE+3zsifpD}YLCmS1E)2dagJfhB@KkukFo%`LpcTKNf zzdo^Xi+PZ}X<K{y%Lfl0d@kvYJ{a*sqrgt*rRv2SH#}y~o?W<yORintB#3P$pF`=c zOD|t$I$GG<GVo?B<+x~j;M=#dU3>TbJ@(@3*QsIQ;h9NEO;*;{p0?a=+}AepA2&E= zlHq8q+L6b2?fUi04<9Z(*|2T<_LaMLTZ=IrOIVY6p{0yV;4V{4M1(>1@}EB|Po-_P z*ON<rm@KvBzlp<~E`5FdiMzzb#ZT)=?c3!jZ7Abha=p>p+q-J-!<8|Sk&$HwKbY#X zcYIW5T-E*{-@I?*M#Hj=7cTIciySH9xaiBy$IJV&^wQI(s;NejWeYDfOv~$d%C%tg z_r}`V+EvT+Z{N7V5ppr!`czCa+m;gJ<d?@M#g(&dSh`eo((j8G15;*iPRyFQ(c)I& zzH<y_=H~82J9q8s$|*MUS-WhRT3?JOP&h0k#6{@v?Ws%7TnQ?FzGO*q=^VzMvrne; z2{fG1_haC4P-9%SyJP`FicrEXZ}xv;4bO@djNdPMr<ZK`^1#1;{}M_{wq#uNlylKJ zQj!{WKgPtw<igFHGyiRB$;-?0_#5Y|qqk+jO6eq}ZA}@~8>JIcQ(f10-@A7&BPB)U z<DvzexpxJAhcnNaJNN2q&g;*gJzFEnntORdb81JKRlwfKi<d87?qU1;_wOZoAFgpV znI*kwlbO)%+HsOO<a5-EA3q}2H(!|}mX({kHPT~6uSCaE>7s{!z6&wiS2kR}e7Wl1 z!(Tss>?p1H=_Gi1%gK|T+8@`R33&VVZRl2)+{#MJU57u4rPbXu&_3U&uC8uYl$yCJ za-Giqq;s9y&F1YtQ-A7A@TsTYjvj428yRylp84u_*WBFPE!(zzd)xF*XwIywUcdx+ z<>JMO6_XdZ>BRYc)GgcfqwLHn(+k^`a-TgGbuf)I`m;H9?lV?<!(-~7xfbk7JGSZP z$r%w7Ti7FZA6RAMeQDjgb<6hd-P`nCQs(fCpi<p_{k0yaPqql_E?>4RqrBX_#<lf& zNW}4dL28M&&Ka-QFHdxBN{)+VwrpMURGVSu6INKnpv4n@t`Xxt=I?ItyJ}banu1Cj MPgg&ebxsLQ02gvW{Qv*} literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_silent@3x.png b/Telegram/Resources/icons/chat/input_silent@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..05c56955e319ed27e0496f0706d02638412cba24 GIT binary patch literal 1853 zcmcgt`8OMg7EYwnG?r6qVvVKkJkugEY9>vl))_@b#oClqo6#mkF_zL1lM+=+8iXlD zEg9PkK}T!Lpma!DQ7x&832l**B4Z7TzWC+6zu>(ezWd$fyXV|<&$;JjoWr@QLG_^k z06@*{j0;}D3HyM66&C%2-75tFWALs{0M3x%ydrQ34RpKW=?VB!;X?qxbPC|WzC;0i z1pok0IS>F+FmOLtuJoU3LOJNa{=N|M-fsy20N1#=IQk|47fW9pzv2xaU{3i5n&fBW zlRYWgud*+pPI_o#R6gHIYf#4cu<8qP)G{kAviQ1v_`GVpqZ%qsE9NEWsa8Fen4ynx zT=}0Lun%gk54L|mfB2MSFXzhQrsR`unoegPMIVkcDIy04y}o!5j$C%W1^g=Hz4~Em zW&jd&YzeTulA!!QNDzfkoR=5u4bg`S?GXqBm*e96O3KPy?RM@y9@A{w%3gRa6dsky zWV6!_1{>a--N=a8cM^%@8H>dl2uJKOsB_MGHuLx9s;iIXvRLfRdC;ff^dED;K?K6~ zk<P$vEc#BL5l@LzTYK2g&(Dj%JU!=u=5Y_!3IEldv}M-X+KQOGO4?OM^ZtTE?WzQy zFGp3{VzDP08yiWpl!bH@Qn!L--3^qlt?@=ie9Vy(MsO{XR-hnRX=rdTUopnxoy$Xg zhK0{b!+Suh6DRVe*L(+ah$4-cOE`q-7rQzQ$(tJE<KyoKn21U)7yDa?vR{2eL&nxt zV)Mg?W*c(3xj|lLW~SK%5{XTYh|pPIUpKQj<X`AqO3%1`8(cx`5R@8<rv<r1d3k0q z7>sSjo0zzOwy`nG%E}^fRH(VBf0J*uS2#wmQC3|ShKJADex6-fd01ax-(rVohno`| zKp^$Ekbts1m(}3A3<f_tI~!KaMD-60u!oc_jU6{g%D>Bl+4C2>lNp`w;3L|@y|n4J zObiC&NqinT)y1EgnQ1e)i$XoNDCcYOmKsAwss_a3;G%*8ox+!JgP1v``|v13|K-jb zve>=vMID`;ps)2}3YA1y+QJ6Z7!Dua?rd(>;CZZ)LK5owgF5D9vKz)ts|eV8C%V=5 zuC8g@BF6QApb6wb$*b=!Tqx#@x7%H1_A1sT?#*a9x<dMPFceDmbtx5>5T8h)toW1R z5fK@srACWgCi*CtWHil<&549Z;6VK+Nx?hY>mFl<<YEer)$i~ZmK0?ToK8)3NvGhz zJ4%{cTU(+9HrqVnryNE#+EYJubfu)Zl=!5su1-W)T3TvZbhNuKv8AZupU3pa>;Dr2 zeb;sKAiQZ3X49ZrsOnBNRSlG>6#0YCf~&d?gX`sKXODx9rQ$C_K(9YJRTltRj&;^_ z;76+I$TJ$hd=%D8Tkp!D#OXF|Tji4}l;}w^neHFZcRrL#r4q5v-Nmye<-*yhrB5yh z`()cm$ZdnCr;|2wq6CE_u`&9hit~=~(J>y{#-&ZItyj127W@8w)K&}CYEeG^a^Q3u zC{WBcSdY)Gs;Z(V!9*>Z%SIG3Qu%tEqgwS&jz|prdtxGN1!rt*{46CUWmu33CZm+E zK@R;&A$|=Q6AJmo#Rd(L37nY^FUmZXk8q6$<!-QA;G=I2pp$x}&8jPM+0?0SvxwBx zR4YB*mr9=14wkii4c<G@gQ6l-qE7oXk=iWK`NKHY!KbALc-G6yeoaj;wvA>_-Ts08 zQ&Q3i4|n$<l+@m67|f_n_`J9KGupIA9rjOeZwenVJ~_FR;^=+Sfo-9<>PpEmrT96f zcQ4gGa+2%)=v9_L;JLi9IB`xzWfwm?K7PIyd;pzDAP{o;25jT!Xk%kzBuy!)MmE)r zF~y9^53Sc<NlZ+n+%#ByyepNuzaAZ>OB+sgAb)QA;(Q_E#S@yTuakJ-2<FtOAlgT8 zhy#bi`NhY_2QN<lXn8g1EfRw{9u^iBeCxQgeOA8*#5GEto}8x>M8Vn^hV(pTf_|h4 zNt<amwOU0v9+o^{GJi^F&5zsaiIQY{6=}mPrH5oMPHVa1xRM*;C7Xp(ymx45=*ru- zCRccKS%&wn-C^tAq+L>ffJ^UoD}%0iBjV4cWp$91_5$v0Gi_&{KBez$t=4ec+U|sJ z8b!R3HzI^(NfpkyRfaP|l@@&u>lna^Hi@@7Xan*K!xtPrecu(W`o=ua4zJh52s55V vhA>0M&xH2aaT4?UXy2xUniu?^8io*-QPBtHzg!I3e_d|h;aoUQAu0a`Zew6I literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_smile.png b/Telegram/Resources/icons/chat/input_smile.png new file mode 100644 index 0000000000000000000000000000000000000000..c23f30d0ae0f2bb222bfe8cd114f097fd44c93b5 GIT binary patch literal 787 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbQk0fvMKh#W5s; z^KJOXRa%AuXKOVgG7^;+wJv?&{zCo6?F{9Jo4*<VHPk0+W~47w>WGNYSh1EXtn1eK z)12ksUTvzJy|?m^n6<#>*(vAd_@t#jnl8|k!oBtnr|#zbBtgg9!d*5Jl^3otZ~L{d z|KN<Xe3xHtSrN8+)l@IliKm~6-1%7}cKdA@4;%B<b=Oz-N``s{`1V9AO<dr&+<E)$ z-ajna8^o*}bM~w;RcBjTW9NVR>7|FJa{ViB<^*kwSdwD2Qfi)Io2A$bhwn!>yxeT* zC}APjf9hG?{^edvA7z6yGu@4!V4~cioOIq-O?cAB=iYsLTen_lWKe!kI9t#~#_>U~ z!jm}9P3nzDAG7VXcNBQUf9Od>soO$s#;^w8U&1*iQd|;_>y7iQ&dKu`_?GktnHI?| zx&MCky!u^t*HzfaG3|{mDegHqBhPky`Te<X%B-I{F5%~VVB+c5ySQHV_~tx534w%N zrF-u6+ME!5RAS}XBmeSi)uW#^Twn4oo;74k-t~3Qz1s3a_Z*a_F@)((^~&7)t~K|G z<CU-upG~W_|4ck@xJ-VUprc5g$Ih6#!_vKONmg^KgxCdV&N*6OQFA%5=WN<%vw!Qw z?<-$kI`e1Ey1v&%I|KH{Eq7YDz$<cXn6hMc7GL&~!sV>K#tQE=Ue7D-w3>YKLe|zG zmpygYUYn+Fj%5Fz+$nXWbg7<Lci`%)i*DvD(>n6EZu!w9$G2s(AAQ{6%s2Z_#7gFa z7vjYnK5f6g_VB|6N)Hxa*X{G4@FZJ&jnWI};>6TR7I{Zh?icK_6Oh}y_fvhX^wijb ziH-k1+WUsFH0NvXXbV*3<oo^KM5$@pz8<6$v0vtl)5~c;9-aavNKaQkmvv4FO#p}K BP(c6y literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_smile@2x.png b/Telegram/Resources/icons/chat/input_smile@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..89c9afb8ccd62d7cc13211e453b2de2682b24e71 GIT binary patch literal 1571 zcmbVMdo<Gv9RCe7(<RS&ByY8&5SojdR}7nI@`#LiT*i@Cn`@*nx-2Plly{6m^2klX zX`XA5<Pmv|A&)jIM`KtWcmBEe-+S&KpYP}U`26ub=lh^w+?=E&RV4uckU}{-ToN+o z5E9}-dslX=K}aCNC8zU1<FMKnVc-zzg9>wX1@wi!1OUp60K^U@LZ}J>0HPHjKvc+} z!(4^PKUGYH=)eA<(3o}MA^?DMP!4vUBoMd6>NLVtfx)NADnbh|aH6Lr_5N;FQ*D+r z9F8Q)wK~?xqoKDl;8|qJ95gjITtPxrSC<@Ez85p}ey&{*#2>=)SFKJ?Z_G>+R)@Zo zmlau=nv}0W6Pv4bH*shyZ-G`fK&k8oR5bGd_cEPakQ`KI!b0S~g%ZtoYGaUA_XkJ@ z1_l8*+@nt;Bdm#uiO{2383_pqYs~riea6bl<F?LDPFZOw34TgEO)2FXkx0y1Sqaqw zp<n}ImLs=vy0#8#=6m}hqoNcuGcyOWkmscQn)jSEk3cZA@?ZkNR0P~0@zUK%%P!1Q zdGIffyr$x|=g$|tE?<UBe&_K%^P>a;!TiL8*UJy~2R?p&96n`xdpiJ+uUWY4IIW_t zz9TIyEy9V58{acBv$8U<wy_cIi;RqPdC}IE-O|#66QtoQMVXp*{<R*x`u&S9^XYVY zh1u=p<&e?INyli-tKHtY6<<Yci4Yn0KuKyySlFASzCcs>;1L;0G!UE=Wahj>IKLA# zxmXA1u-S68_DZ`f($f$PO~+ma!|C<$n|-Rsj+OOz$hc=hWohPWWn9s-HTqd15A$kR zb*5~SDh=zpYwz%#fN>>)j(ns90mT<Dg||d0u|`MT?=C27Evs^#&Z(6xmZW!VfE@5q zw{z;yGmcf!9%{e9cYwxc&pz*FQ=(_y%#N9!oOQOHYWW$$kha|$D0t}A^NAdam!C~P z+M$^(iZj>`g;ZFO0@*U#iQWMLhA<c`W9a)Hq%qE@F$lHIO1F62a;|U+WNu^AbTASe zACHg|Pc0%}j|0ZndzAcHkytC}AQ!;;`HkOD2nh+vbJO0s(DT}yF}|L2@7|gx7`M#c zi)wF0$W|}Lx6^2o2S3Ims~JS&8x!yH1AY^Q>*$Rs?Oj<ZfG|xpB$H08mp9_~gXJ~Z z&*g@VjEpRrP)H=QnpvHmwq=^5o;(Vs>0gHp{{aYR#<9LVIEXHB%fEWqNIf`~CKcIq z=7D;=ohr?zpGW5vEY%=V8wDz8o%hxscm=_;Dz>Kq#q2_wf}ZV$@(A+g3r@<aKcC0j zt&b`E%H#9b3K+~(X`(Um3zwUdtad7~qmaf9jc9Bj)}m}yqNAf*e(nndoyy9}yR8~b zCUc3)HK;eT`;+}(s0KUsJNZuOX#ks?oSZd1J^l6@{-ZGhp(nfp0x`(N(T(M9G9Y#q zsMGeRj``Z#+b?lAE-7<ev{$cW2m}JPz1{aMosedM_3*ggHGDlOshEFGxM#}U`6zrP z2qpits;Vk6QYkS*J32WfC7VK_U`TooF<=M6I0lPOiH*g~Ei5<%N*X4SNHRJ)I+dHO z?4q&En4moLD~-8?YDBp7iSV0q)JtAo1wL1<$SGe~6048ma5&T_Pfo}=4jAf#l4n9) zQ%g%r6=r8=mo_$rE@musZvXg!-QaTfY#h|gKjOyT6^8>x&s-!Ta6ykVL3HfgYQu48 z&(<n6quZ1B1wTI@2GZ6ZO)i@V&!!mCU{vo-o`%>b63lW{*LT{%A3Qa=nm|Cd(i8A_ zMjs5P3(Yll_m!`%iq)(q>w@K=2lWOteexO05)U;+gxPR2H};s{R5w~9PO)O4+<4mT zS1os-_rVH%>x_>b|Hx5H+8a-ZAflP6U1tmFXs+D&NpBClQE?Sp`}VBYdRLMiYCF?h zX|^j#5!{Vw(Zd8rxmJtKu=|nX&i#Y`^T};$6q%U>gWwd{;e$pY-5eUv2a^8=2uQuD literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_smile@3x.png b/Telegram/Resources/icons/chat/input_smile@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5af6ecc1deac19f766acfc9c50cf7e73ace046da GIT binary patch literal 2569 zcmcguS5y;P7EK^x05L!yFSJJ#1SAHC3{jCHB}gdJO9Vp4hf)PJbOZuY6qE;{Dj?`X zFhqIu8amQsC_$um2pEcy641dlYt4Ml-+b(|&$)M>vhK%OH^KDQO+Fq`9smHqhtt<J zKVjl8fVocWH8;#ZPY4)bep4Gj84zDP33T0UaURCT0QnOi3;@P^0zkhcClEaW0Kl0K z1aO`(@Yh*B$A4~#`JDgte+j|!R$l=C?f{&wmL(Cm>2U3_kg*WOfgWJhK6=B{NXSG< zKq>~yi5_l|8vA6bq+)5NjMd^4U|MSND~{-zZs}U`cVJIJ)C3jFnwvTgv+_#lRw~2C zb@gG8xaqHyh_1aLD;pbn>86E&=Pnc{^yr7~!gK16^9Yb^1QJZs_L~Pw|Ndgc>D*O+ zkRZh5E|ov`Dn~-Rtcor8miVLgwlvX8Alauk=I3yHuxRK_#P(JZ7cVVADwRkyk0H&r z3+St<mHh8AV*042&lPt{(~&lBu}QF!Q@pUh4t7^c*M{GeUbJ}arrFNk{8GKKw>B)x zIywkoeOt3&E%rcz7-J}v%a@;vO5=S5chW=;7)&O;)TVqa9wIq5_(FZSktguRZ-Lqm zE&M&V>$BPivXpN!ryK2>BaZ8AZuKw<#4t{!r>L)wr!GaW^yj=u5x-U#`Qr#DXz`7E zJlc5N2Qk!-Anq*ogv>OBAJWSmvAe&OL8|6B!#w$iShB>ax}b@`i0A~ldPLYG6Jtaz zvpmhiLQ_ODqG6&rvU#G)+m0F#XU|$4k_tLL3}WqXFO<a}t&bvEEC#)7<qUS!%p_0M zsz2}g6^5rG{(~+b1X5aTNPgR-aYIn%G~{>7FV(&_nh{6f`usxk$_D7r``XYwA5-_E z-IWwf>^jP>YBfvw&I1Vug>-f`dVv2?iV8D!v^;^(K=e0d!liSs1?c5GoTA`V(mw{H zP96e0wCde<$H1neYYJ$+G?FNc{MMBs*_ghvK3W=bu(MPg!y`srg6^N0DQ!h{`GkY@ zP+QN~^Bug64>oT<KsZCIc0o;asti5GNv$g^21chi4D0CVY%ESp0KaUVGvU|if6+4v z^$O{oJ}Vpt>3S+*CsLc?whwUJ0ZAyfyOoy^?bK<f&x#|q50BZ^m~vbYkP)dl3k`b( zLR%S?RXU<*aHZ}v)#vvTVtj+m(`c#d;LcRXX|Qm%Hm`?6b#T9mODT=&5d?CzCkqC8 ztZ((Prfj#+<qITuxT=AMBOh)5vQGHb7KVO$<C1O59FoODf+K(MSn<(rru<$k?|Xq+ z`@X1(+!cEH!sE(DwFM1L8quVvX~kvaF?wIy+f0h^xU%)3YeY2Ku=N6bZwVf8iNO`k z92r+}QRt=IR8*v8uIN6ob@rQC@Y)=d;vyrK6PJnGg2_Ez3bZ`OPMdRI#kkO>mGJig z>^6<<@U}QUna*3a{tJ2-Qsu5u#ZxCy1s8vA;rdc9v#xP&Lgn4`;k|{fl<O%Lr@N9x zUOm_tFZeoHo7O+l0%KpDekW9%mPkr$w2Ra!cfE({D+;t><f?e0mC<Je7H2uQB>bj9 zk}RaTtV=<*rxQQJE@f#UYPDm0xj!clq)FUD$p2wskr%R|m7Bt<_#^Le&xIDmST*5e z1!hQJ)}LFZyQ91FiC91qd!Sz7yTiiu8A@7WLTmo<Vc~Z?w;m<cP>GG!lT){jXEvbd zb*x{Cx4e-S=#F(yx>)_70X3N9cNo7|4&P4S%X|2F=iepdO`JbPgqyL;KvKPf7Ze?v z0wrWp@~%I8sumAz+NC6g)At9e8SVC7WK(#}M_5quX2H+7w$nDvQir`}&(sN=F0jZU z)os$rv@H{E6FaOzW3DKqhq}*X&1$|DIHYO$*d`}z#w#gYoC&KrA9P-Z42bbLW-P0Y zjR+SJGh3d#e0A!g{Blz7Y7F0uPA=t7SbGAoHkKul+!sPd*vP3AxD+bMA%EQFPu8W5 z#3o+!jD9Z@XPCM~&NfAx!T%K2yvNr`hLQc6kLttZ4&lO3lGf1ocN*5wCFID(mDe(s zMnYWf9fJl+8S?kJpO?G3IXT_foTxNx3};(6hVCh_#kvv&ck7D!gmR3kM^jwU+8w&i z{I9N8UHTG=H+#wJ5KuSpLjB9|bt(|m1{}V7D^GQFo9CX-UrxC{J`a)r&u;hfWU*jw zGh-uf&vN+9ep?$>f993p`)MSQF#hepeb9HRZgfr3rP>`{9VUebCmOJnUecpu-hjE@ zt_;4US9tXEjJZF0f$`4P@Sl%OgNrAhgTJpSDzp?QL?>kiuG}rO10J6&(~M{E=dB5I zxZQU)&B42`t*~7)q2D*S>pa&nYXg%l4WQr9-Vf&66AnM7BPFE&mfYW2O44|+kx6Kd zXe3m5Wnmoef191nC)D^A5Gp*LV37O71kyk+S&_jFl>rGH4VvV1@L@-9^ttln?Vo+F z=ukH@;5o;>;yc~vlbD)_fX@7^f?<m(`PL2%#P@#Y?^;im-aX|-xMhGpr~bM@??Tl2 zJC4arto0V&XJsJokiEyuAit>Yo}8(FSgwZ@Fb^=3Q9L-qu#a26SRl?+9_KO*Jd{(t zLbU!azsH<`L>P_RN{9blynNe~ijo3~tE?^dq<xl-GX3zZEs0)goP)v2<{5Ha)lvW} z1aQaW(q!CYB~%0T9c%owPFg71#qgHoGH06U5J`<by)x=RXR?TLosV|OUdW{V&m+OJ z2t3UB;;{g}4(Pj?>i(y}QoiL9;l5cCdVbLk4>NX_fVn?{1ovOg_P~Stx9vOY(dqEB zpAh<&O7Hu_#^WHt=M6<vyRoQF6POgLSOtakG#&Oz@Sgg3zm>FWR?|x9snmTX!_L0< zk2H9F=d1?TwT()wcLB7nL~UlwL$qnkPsBM1cMRsj5oOq_1ejZDlrQx|QKT%nEQ>F$ zMox<>>d4}$Tz4+X+lsOYi#ZpZlrfRrS4mP-!Q(Ko6$5v@ePU91@x4yAP*?iMVTSl( zD<<qO8qWvxI6bAWLF|lU!B&ZY>qmPIJ1Ty=un=8TmEjGeWrPK`Dk)1BRZnd`0ZdVW zc4I!A)&0`r98azhGP~pFbnY7XoS~H*hkm}jSICzzbp9tI<R5N50&1#ipI!Tn1#1oj z_@fWdcPiyT6|McgM&RYH+|fe*KAtPh=yREp0oF;{O32Bh&RD02K0JztR_3=W2K_%J a>`T6>K}LZ??IeDE!*JMJx)g1v=zjr~GSYMa literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_attach.png b/Telegram/Resources/icons/send_control_attach.png deleted file mode 100644 index a16628b77bae20e790e8735ddd77fbe26791d517..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 919 zcmV;I18Dq-P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv000ACNkl<ZNQt$S z*(<zl7=UlamSu)1i6mQ=WQ_ym512+#ws0cJc0?iv&Lu)}B3Xu<IFpiwCQKv84w#G? zlVvP9n8IY4$@Lw4dB1P6{?zZOx1M_L`?{XD`?>D`5kdaq3jEWA>gsA)U0s#S%S(B9 zc#xx`Bbl0-lET8mPjmRm7a1AJ^71lo>-P4R<Ktr<A0Gkm`F!;D_I{N6Jv=HZip|YU z0QUFyDK9TaqtPHDBqSuz)zyW^;{l+rukX(UYPA}Z$pnDeY(}fqei&C)R>s}k9X_9r zqN1Xo3H}e?-riPj9U2-+SXkJ*`0nm*0OscAz86GCN3*rH1;EzU7HYK`5uvB22fN+Q z$;k=*{r!G9adB}xJUrlVIKCFd#Kf?@y$yiLWFjgm3K3y&a8N1e%gYOZUtv`$6;7v< z`}_OP1zN2Zv)K&5=H{jX9~v40;O6Frl9Ceg^743kdgA2dgpiOBL<EgSgWK)K<#K&2 z&}y|<EEWJZHa3Weh(JUb9v)WU#l^*l2ztF9uh)y!Y9%;07!jeiwibYum6i8``1p8s zcXt6W8jXa9ha)14jEn$qeSNLGj*5y3o}QinXl!gmL`X_X!s&DZpwsEz3l<g@09apN zSKy<gqX1l8T~Sa_fQV38S;_PBGXNbO9SZ#5-~fQBsi}|qAUivo@$qp5K0ZDUfXn3~ zKR+K4p{lA%DQ<gvJ0e1Iax#a9hX5=sEfEwH^nIgEOiTc9adDx*Yieo~cx!7bB0@?^ z3RbHXfW^f{R4Uc?qhfM$5`gpbb8>TYmEw85UI1EJS`ZOZQ&TxQIs(98Fnk907Yqyx z0C0A8Movx+B0^nV9X_8Afad0AM1-`oG;B5-0Q2+n1O^6vHE#ahBqk=}@py2%-OA1N zdc86|o0^&s5z^DsvD@tc%+Agd5D@U?EK>wkRaF2?Pfsg{si~=0EEd|@+7J;kGBR*D z901JB%=`rYpP;_J9)Pj2F~5Mf-_Oj<RN&Lo(?5m#KhxLOSDa4gyOErgm8CS%2k-o6 z0ujMrFaWT(w?}DdDY3D!=yW<xPfvdf_ZO(uYSz})l)~QK-6{LY$;scq-}iw^rJ}R5 tlbxL%Y&IK4qmhP&hTp;oc(eSi^$+k&!?qvSje-CG002ovPDHLkV1m?Vu(kjI diff --git a/Telegram/Resources/icons/send_control_attach@2x.png b/Telegram/Resources/icons/send_control_attach@2x.png deleted file mode 100644 index 8b0c4d3e15242b60672ac679bce239388815941a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1869 zcmV-T2eSByP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm000LRNkl<ZXo1z5 zYe<vt8^`Z?*s|2+&;pB!Ov|XuL(3eRY38H|sTV><Zwe*o1Vs@E6$wR3(N-x_Qc*gR z6xnIQjFpJ>q7c-T<x2A~bslQ4NZt4ULK5#?&va8)|KAPIo9){7`h2fl-p~D9Q^Xhp z`wuUVe+@9x<@Gm``1ts+q@*O4kdVOSayeVJY#H<Q^<~4u!>p^Ti#0Sfus3hsFr7}v zj7Fo&`f%4J7K?G<zyUNgG{BkF(a{05T8$-3mbj9~J@BESp?LZ7rBmSctF5gK85tS> z&OlUD6h3|WBna2p*@?QkI_Pvd==FLuH#cK+bd<!|Y&M)ZbLQ_Gkjv%h?Cc~!)z#I= z$jHF*<;%HvnM{U+gall>b`2Jbg?uh2C+F`Oh>ng%dwV+vFf=rTtgI}jJ|7bkgLm)V zk#8b1Gt>PB;^N}a(b2&r@9XP>QmJ%FM=F(~s;Y|9>FMb~NJxnL48+F9qN}TmOWNDp zi^RmlY3T(71mNx4x163vqj8UcxVSjd^L>4N*s^8IjC3O-Bk}X+Pfl~iiWReKAR!?E zy}i90e0O&@HgDc681L`z52;k@w7yEE;<U4~vuDqMLZLu+cQ=>7WHKQ>KAyy;rKREa z?b~Q<Y=psJ!2SF8k)EDDWsdmxc>MbHYh1sgqGEOoBqk=Jr>AGi%8NuI<mTpLcz780 z%Vx7dtyV)U7CX!%lgYUI$@};3XKo-dF%f-zeH=_@XD2po+C*aW^YaO~{Zgq^ljf_d ztK;&_$bh5DD-;S6d-?KZ0&6fB(A3n#y*4^Jif!ArIm}sGTRR?WGMT1tASERQ0|NtG z^7i(2#Ky*wwz+cU3W2|O?;e7Jf)E%O2#rPqo6R=<UTJBmU`~ldf)5`)jK{UKv`o)H zQc@DkW-|x${rh*w<#G~x_3BjufA{WP`1tsY$4I48w6?a6zh7Tp4{vX8qP246O0HeC zTCM8_Qd3jW-``K*?R(zS(^C-s&Ye4$GiMGLyMFz8t`%Ruehp7gPm()3JDbzGaN&Y$ z29!!A1_uW@k*{CBA}T71w9WPF*9m-ic{x{k#u&oG!_nN_%*Ey9<&oTeetu|dY#fjI z{rk70`-|hs3-x?-bTk*|;o$+bT20_9Dk{hp85R~s;0Fc<uxiyRl0PpmkBh6VtrcuD zK?6H>?BH(W0MOdnN-FQ=<%NQR0s>!FRtAYgLMm@C7`QmA)rwuac9Hx?jvV3c9X6W{ z+qZ9b)G7Q6>FMd@Ty8WP5fKqVDqmPwNZ?CLOG)KduU?IomKH9~VzFTN?%f3F@ZrPU z4h4)I<U}Hoa|ZVB-_HpcjYd*=Z*Olw_>z(m@->HthvVbNj|5(*@<)#zC7LxgHB+6R z=!;odSpde`X6@Rwq-~0dib$g3;$l+yh=>S$`SQgn_+!V8kw;6tUXP%lpvmAJ{}J!q zyB9GrF$BK2xR}5f6%~<faLt-E1pdd5AK1BbC&_jE_;I4C*Xyxx;liol9UHK}#9}dS z-MU2*X*3#md3kZM>(;Hq=g*%><%Le5Cr+FoJzrB(gTTN*r?{Ll;O*^=va&J)Ur<m$ zDjyjciErP&adCr#gMz!r$&)9E=Brn)T!Ek3fJ7n@gfA>CgolR*>G_FA1Yqo;UI_lw zsZ&H#r_;F#Ke++1Sd8-Wa*{}`R+F}o%jIZmYjdjdr%#_In$^|S2nYyp6@GF9LR+O; zttN3o<COmXex#(Nkl37@9HOb!Y9W)!rUUQTKxSqpNu*M#NZV}Kuz^&5XlMv&X=zTu zzj*Ni^XJc>F1)Y-`-9Y<%1^u-ibNtP6biwK#MZ4_iRRg}X9@iC=g(&fFKj@mRC0-G zYilug?p!X$-`}6iKh0({l9Q8(-nnz<2>i2W&s>^NxMX1BNzi7qVc))eB*xRz6MOdT zfz@gSFgC~%s{Hx$=LvjORTcdF{AL4Q*g$1v<#;ly)rzpNFoyye85wA5Y7(q`Zf-7t zfAZwXY*pT|fr+z8i^YP)ix*ESBD8}D!9RZd7{0!~vk5P3VB(QsWMstUG2z9F7YY2M zM~~bAFKpo8!Gm1#n>TMdEhscjsjRHTym|B70WWMIBqW4;sIgkDlg@4!8{1YdUAjcz zA3l6Yb}+XaV2t6$jT;<DZ*MPBQ&R<F9Ze)2Jb2*F%1>@!(V|7<Nor(d1S*vZadB}7 z4i3iBrAwhyD)IE`Q^B^eAE*3P1B@|j+_;ha>H>_7I9gj<$@%BcvL9sqGXso`optQz znUk%Ok`gkJ_@@RKV_2|Y0rK<nVKSK<g15A^;LxE%f)h4(UZOuPoLQlvp)5H$nMFrO zv*6%hHZn58j7B4?udioXt(KY1W@n{lZMkIN|8f22|6ApMZ*>S*d~F~^00000NkvXX Hu0mjfMBA=E diff --git a/Telegram/Resources/icons/send_control_attach@3x.png b/Telegram/Resources/icons/send_control_attach@3x.png deleted file mode 100644 index 73c7c8ebaf96765d8f18a5d798097f9a95708833..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3123 zcmV-349xS1P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000a6Nkl<Zc%1E= zdr*|s8^_PC><w7h1r%ktDURskrHmpR3c9F`xLO)unN()|(S>QONn|Kd48uxMGr-i! za<ohxbIi&@Y%D1R$_N%TqJ<?H1O!@emxb@|4`zR(?fabFg@vWYXXgC1@Ao{<`Rwza z`>u#F26od(6T3Tr(QY>Z*)b1y2SAxzE@wVIJ}f3ChTV17U2OXF=`125f~nPN=HcPN zu3fvvnwy(hU0oe3FE3|BMMbQ-x|$h{M%LTg>(W>-nu?E)58~qDuzUAzbar;a8M}1p z60)<i5g8c?xm@n5nA{JAF$S?%jOgfS95`^mDJVi_G#ZhUlY_9Zu>S@il}d$|UU~_C z|NZxn;aqPzoeo;979JiR|6V|Wfq~e!Z=XxSTyMR-y;!?;Exf(G2Nj>lO358IVZsDf zP*A`YEn38VuB)qyRaI3ngTcT$J3Co_e?OB*BuuSVvyhMwrqO7aL?YqVk(Zapva+&R zM@NU#ak?#YPMkQw{q`?hxPaW;TqGqWVbY{Y5R1h()S*x)5ET`L)YMcI78atZsfk-( zUS1wNJv~SND;5wCz=hM<*@>K-9L$<E%c*)35)$y?haZylS}Yc%r>8sHCygu|wOWnO zKmVL;gifc!Lk~SP==zmPCDPK;U^1Bm>%V&SDiRYDM<*a(Utb(Mc8r8mQBi@Im>8G% zTDEK%y1Kdq{nXahA|xbabOG}5@!`U`aNz=Oz4ca?`b<elL2qxbpuY_pHb5qmjSfI6 zl?q3W93kOUR#v(k4r2@=kqBvNX@Wkxy1FoF(j@l-QmIrpeE2Z>`}+k0t*WXT7C6@8 z>+6e;KmJ(I_u92<9qF)#500Oo9}XQlM8c`At{zr6))F5dk1JQM2xhL2kB@r*`TP6h zlTSV&Un`wXXBUn_p@3SgMnFIS)N1vh7oakk4Eg!_f^kGgM~^(9fPet(-@l)PQ(Ie$ z+its!n|br+&&T%d+fiIxjGur08K+O5Ms98{;^X7t>+9=uOv%Z~Fq_Rbv8-ObdSn6l z`}>pcJOEt0c#-?9&73(CM~@yQ^#HJM-#$k>*#8%eMuUoq3Y)qP95^s?fc*UYxNvG~ zYjMXNcaZwn*jUum)NtWgt*opJx7>1z!?AgKdgAcm!#4GlmX?kTAeBmmk3RZ{Y*0;2 z4Ps+sN&TI7-ifNJDu>`$t)ij=lP6DhIL3m40-HJ-8yklokW#7S!l|yVMqFGRsgH?? zL1ks7Q*f+y@Zdp5Mj|lAuyyNJ8+}__+wcPN^z`KJ=je1g+<o`mq&_-2nhU42v=saI z?}tvO6a3s{GGWo8MGnQfZQC{*eM?Kr@Bxy^Wcc8N56H${ym%4u@$sa7_UzgC?YG}Z z{h>pL5ET^#rBaF8Z@-;fsuUI$!pqCcZoGT;?6J|-)zu9TAgNS}_uqe?Y+PMk9T(25 zS+mHj0Dw<F{S@QJkH0}PWy%zkmzUc-TT)VDck$`(?~gCP{L)5y=FAz_1CmOmg5QSK z>g(&daAwY&iR$WVE}RJyCJ1WRty^dFyk4(IWMm{a=GfR+)YsSBX!G;)T?<Gqmt*(t z-RSG<6KtGbujj&<F=GaIwEFC`&qz3)o}PH`z4vT1mo8nhJDNTB+;f64uUN6db$}EK z1$OP)1&hTZ*to{VM%;7HJ)}M=DvI0Z96frJgd-M<v1-*ST)TG7M)&pCU)#Kn*O#E6 zAe=mT(ne>oSP&i_?ixVe-rguEC?GS(U@&muXfzu9`s=TP+W!8296NRlfq{V}oF|`r zQgEpN46MrkxnF&KS-EnhVB9B8oN#21+A%mgckU#=`KG2OBqSt|`svfB<CkB4A@#?P zAII3SV@WtqJn;mXwG9mo+!^(Z88gVNU9w~eL?Y4904bG9Y~Q|}Y~1C`mvP^H_i^C} zt%0m|^5jVpjz}azYHBLED`+$t@zhgKk-o=`8;64j4+?6}pFa;jKR-v_9)9NV;1~=B z%$+-z3#Y8CY*26*V_30b1-bd&-QA5fYu1ojKR-Y0-Md#%YciRj)oLB;T1Y^Ffq{ZE zPpdUFG+_Sx`P|elEiD~1oYd4*^z`%yYI}Nmkdcu=YE>!~cN(&H?_Ot4T7^GINl76c z==FN;(TPT*!MStiNWHyL=ds5gBhTs0W;0%W^;K?{uxr;Y(syNLB|<|(9fd;!nl^2k z;LNk3p@Ey8Q>RYl!Z~&76oP_+xNxpsy(*~f>+3^iW+t~wC@d@_egFRZ?}Is3qCX4@ z3c3MOV`C#1&Xg%rP*PIDg~MACEMLAHt*x!3o|icag@OyGzP_G24H*<5#u$QwgK_%w zX>@jWV)5d|<a3iJPsWcw{zwM+)mL9}CuNU3@(5a5T1fqCuf2vbW5x(-WipxIUXRuE zdOdff9TFhM7$PDfxDF;xoM<<5A|fJ4eR6U#nwy(ReO6W$Boc|BRw|Wp=V%6lVJL7M z1H>y~VPV`p=bLZ7;qD(VUAmN;IqTQ2Cr>kEG8x`^=N)nq+1%VbR5(Ke6dD?eAAb0O zY+O-M5hhNY$c59^)<)_#Y}f#WLLsPCC=}SSV+T2jY-wq61)RYH3JD3p*|TTK#+^BH zhMPGLKKLN_l4f>xHn~pn^72AnULFajwY3%Z-+#X=;0z8>aBwiULq2=<EVn~mym&DQ z$6~Qy<Hn8TYvt|jjV)WYkgrvHdpmb_KkR_Q!^2TrTuj3G{`>E_aI{)2IyyQ8wPv#! zn>KACZ(S&rN^IG(h4kIt-i~?m=D8A%bAZCa!tm|4-;x2Al$3DcEL^w{?d|O(oXwjz zlcSZlw>RE=^G(usYilbWc;Eq7!*LvtzrR2C+MGQ&iHV71hio>R@zz^!k@FlcFE4D~ zyqWZU`SN8fSg>F?;5Y_IE|+sx_2uQ|+|f#_)sjPbUtb^Ie*0~5!%Lx1AU8Lc^xf3d zgoO(i4i_B904-RsfZTTY>8GE#qm@>x<xT>(Zrw`4k;!CSIDh^1m&0db!v@IL*B8f+ z9~TU;w6v6anvj^72!p}Eg+pCnl1L=T$;lzL&CShNvSi6{!?6!&&YU@d^Nm0M{FA(h zAdyIrl#~R$UQae~+qP|xN~MDOF=NJX;as_L1xuGM9SJymKoW@r85tRZK|cTd^Q2BL zmy=taRyfoQSu7Ug_19k~4+q-X+C~x%ACOwD7Tgok>2%!1r${8i?AfygCzCsN>>zjZ zL?RKgv$M(5i>|J&k%q$u6ciLB_?qPB=aU(5ebHz%H*CA^+_@7<rIO5?_3PIQo_hfU zd(aO*{P0M_;R6Z_3lj`DJw4rF2O5nAI-L%C_UwU5r4oF`!@~pX)~)02LAwbKA5eIB zxM099yzqikPC`RN$$N!TsT7%+nPldeOeQ2JCp&U}HS&N$LP7)=m04L?E^#iC$&ite zLH0Rjvl%HVDei>B2Q+^Cc)?T2BS(%PARxdcaHu=fMxznSmMwEP96lhWQYm<yx3#sE zdl}PSkxHdVPfr&F0{|Tz9az46xx2yG2V}ibA!P5q`|hBA3o4OF@bb$qlW^MG+a0-* zGCBY;#t<19Np4X*^UO0&IrH%Fz?wB{NH}e6ZFv0g$K4IYIY0`90&l$WhG2utW;0f; zTIG-<u~>{(UU`L^IV)GLbT<s=0WmgkTKdNye+V|rWHKQ$Gn4xp_2A%OY}~k!?2vnU zdbqbVMm-?L29AWgySvE@C@wC>>eZ`p@4fe8>eQ)-jEqD=LIPfV@kJCB6_L8`?ruE$ z?6dBMFmymNnG9K3S!6@4rq}CHR#t}d=g$jXn*3+>J^l34?gro*K#UD+eQw&c$u1;E z%wn-P-N}!7K#UFCG)+rOBiBuin9*n)jiZ%o05LYOp%oVwhcCYP!f80)efJ&rcN?Ry z3|_2OtC5tHgj1(Z*#%QlQi4YxeUy8vY%~^^{C9_!mlqp1ZXBC4XAYY-Z5kUpb}SQ% z#jK^Jh1Jy5uyg0mv8JXbW;7ZZFl2WlYHs~=lYfKV{0}obb`y{t`yYD(S~$VT`~Uy| N002ovPDHLkV1kl;@nZl0 diff --git a/Telegram/Resources/icons/send_control_bot_command.png b/Telegram/Resources/icons/send_control_bot_command.png deleted file mode 100644 index 34cc9d6eb02cb8f106965bb64e867f5a2fe0936c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmV;C0dD?@P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00044Nkl<ZNQvE* zF^<A85Ji6|k~EZQ;UGw+;sCh;6r6#I3OE4b4!A>zIyKiwnFCN5qezbe1$I~3g@n-T zmqxPe@qd<QWCxPa2i2(t#u!;^<(w1HD4cV$*2)<34T3R-wlx}!sBQ;qEda~qk~og5 zh1wCvF^k0lfZcBQ066CW-ZHNuiXs5c=QENBBmuWN2SLzEVZZuyp5(r*+JMPqB0zbb ze+Dp}P9;g(?e^t8_tw&r0`~iT%Cf|JPm(0R=Kb({9kAJK0LZfJ&%DNfq9_1J)3kj+ znx+60Me+0?jR0Ae0kB@L+Xo~`!sT+IEK7R5Ui*O6Y6ZaYczo%(s13;T9Dwn7TpRFD z-lEg#BuVOayKfstQ6x$7-ai0`!$FcXo6Qu4VPo?!3^kw6B}x1J{yxuOFraM>hr>q~ vNw<I5T8sC-v8neSYc0l@dq)Ko<x{!>T}<%!p$)|f00000NkvXXu0mjfdmOY@ diff --git a/Telegram/Resources/icons/send_control_bot_command@2x.png b/Telegram/Resources/icons/send_control_bot_command@2x.png deleted file mode 100644 index faaf97c16886966bf5831514f41133467fdbe2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 706 zcmV;z0zLhSP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0007sNkl<ZXo2mS z!K%7I6vj^)L~Hj#LP;;+d-wo}w)O?0WiQYJ6x5<d=ndS4+t#f@Xl&K0Re{a<Fa9L= zSYEofw7m4efy9}w@7HKE%v1mXp~RA29pF)A0@$_<j4|MxgAf7`vCtAi0OuSSV_@6% zw-ETX8jS|}zK`!F-}kZ6Xka=o0njuJ!!Uex)=Y(Ah?=Hl1H9uepO)|Yw*hS1zG^(3 zPEpr&lqBia(B)c^B%!YBIGs+fa@n?h4KSb2Nu$YRQh4aBnoK4n8)NJmV7uLtM!K%Q zJou$_T_@Q%=gGawg(8k)kYyPzs#dFTKA+(oBuNs<f`}kV|7U;%01-*2N~Ka<!aJ0e z@9)mUWzh%V9`*Zuvd-CTHc#^|1fbLDkn7oO_EdiafUDUKp7bAX7zPR<65rG5go>j4 z*yo#k`2u*JM|^LrW!@K{Ua#Zfa3H>>X_g1TbzR~aMbWRlvhwBn5_h{@a=lnAa`~QI zMxfnplb1J+V^mf3=_8OEz<4|+enAjC<X0$wEX%mx?}?vfS>*w+EQ@&U_j{COxjX<t z5D>4?X!P(SSr~w-su;&{;v<B>cDr33fZ=dRd{(Q~!{G}Bh@y!2xUO3s0Mj&y$Ki0m zTCG+dfb_<g=Xp;FUkHGrD0n;`lm0>oGz_CW0E5AR^k1*nPYYiNz-F@{{d>J$ApoC! zp;zkz@M<Lh>1XV+dHN1YXZe~nxf4YZY1C{sbB=Yt(ph%9UGg~rST2{OQLop_IoAD3 zXGv#8fb1itR;%^0Bc@iXb^j3)K*<mJ0RF%+D?rH)y1$m=ILH`7&N<oNDM~^J<eVd8 o3?0YGd{>gSP+9dpv(+#455P0?uWd;j3;+NC07*qoM6N<$f(pS=m;e9( diff --git a/Telegram/Resources/icons/send_control_bot_command@3x.png b/Telegram/Resources/icons/send_control_bot_command@3x.png deleted file mode 100644 index 31d772e5157babe0990ef435bc5ad71e06f7f61a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmV-`1b_R9P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000C>Nkl<Zc%1E= zK}+jO6vt2GnJVhix=Ra6z}kKSR~7_UZrfxb?sO+2m{mVO1wV!jx)4E-E=1i3ZVQ&K zstDPnx=<6LR+D)P@4ei-%rx=cbY^m!AIzesnanwVW{z`GCjtN<NFXx8+5z541Pb7v zuop^_1gfgS($W&-aygiuo`zH^HAvqg!?tZO3<Dk>9^mTg3Jwns;r{+Ubbr*ZNs@$W zwHo^T{6v2Xj^m(Ot%f8?>KpryM*ebjbrrq7zOs&IU|5!g6h--e0&Q(=A;)o8$1reQ z*G1dg+uswYR4O6Yby@pP4cB#1sZ<IlkR(aSvMkm<Q^&F_Qcm(34h|0b-e_-c4;2ap zG&VMd1VM;Jo0^(Jxm*ratJS`->AK!upiCx1%sVeHFGy9@Fj=4LrYH(}etyQr=r|6_ zWHMxdG)*I_8zS4srYVX-j8oGzvOx8E9owl|tuhZP>PA{_^?IEwkYO0uPWgPEc|cLO zd_Iqj)i4atb;3`T006q(F1CCaA0LNqx9hXVnN%u;jj7k`c{K0{gb?y|000;n86kcb zR|G)-*L8{C{UoX814sl4AQ32lM4$i?fdUBqM3FYKEW`5hvd`XXwcz67f~qgSHbo=^ zL7={oBkk%dl}aS{7X*PAuje}8bRT5fHcU)Rcx;_c2WDnwBDsI8_d)yn`#y0108UR& z8F<6t5^FRXSU-xzVq{~4U1Hn`R4f*;J~SGQNXH2)5O+&VRaLD0{QR7<Ctq-vq)pR= zbUN*^y}iA`<m6<e<HRyaS1J{sH~;`=XJ-t&`GPBf78e(>_T%GY%AR=PB1y}#j2*k( zZVzO<*d^)R-CeBx<m801Cw{n?LG5-MJGLy#OlJ@m0+q{U?6{a;7>09+t*@_R?W{_R z|H5<z$+C=W+xCrVn&v=fZER-H#>NI$g((iEGl)iEifge*(xk%l`ucjH;(SLSrb}#d za}#U7xw)b22^prvDy_m42lvG)kA*1?W&*9OtYGb|3sW4_lQiwZ6xX;;(mWTYIH=Dc z(=>f!*tR{e!W7rI-3PHKO#eh6>Pw9G!W0L!8N{kE#kH_Ai26S-Xqx7;wOTFOZiI%r zpM7Y-{Z2YABmxDH2oyjfPymTQ0eA#@e}Bhb6qnP@-$4{bti9Lkc|LmtdU|@ocAA}? zjmRy2n46o!+8-YuJ)b=SHJeRrr?s`Uh}_}_QcSnEw?5miZFO>>F5p02Hk%##BbnK3 zmMjneAYIpq>LT=Y`mj(aAW;;VAJz@qL{UWZ^Yedxs23nP79J=Nz}G{qVSBMaxm+eK zOZ-|LrdLz!?CkW*TjYc`3@<kc@rS2>)|Vs+>AL<`%_hfjkgn^YUXSB>VAhY{U$C^W zumI_F8b(J)!*pfN$HxbBIvr>>n{at~2}egqaCdhXs&8n4hLxn64<HdJfPVpJ6jgoS SkwMV_0000<MNUMnLSTZ<q7Fp> diff --git a/Telegram/Resources/icons/send_control_bot_keyboard.png b/Telegram/Resources/icons/send_control_bot_keyboard.png deleted file mode 100644 index b9423a7161b39846baae16a0c23df80079fc75b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 zcmV-G0l)r<P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00038Nkl<ZNQv#0 zF^+>U5JkUJ*lsuxlBqZo6&1P5bO><;!-*)2tG~tKWXW!JSwfU1Pa4VgxBkeUkwyU^ zp2#UJoO2Kngb+Y-SA-BCB5=;FlF&KFqbbXh>31k1B+2vnOlxft{vFm@Hcdm4Y}@vl zAcR1Y51FSh#*ifEc_siV03<)_tdu&WuwVU7_bz$wR|WLmZ_;kL`+{_~y1eMWX`5eg z6t7D#48v+aj^ifnme+OBQM}vqPu^|%Bj0K;O%nhh%d&^fjWGa#7$bn?i~7C?08~{4 zt@Unmtu^Yp1^{$jcR6RC=ZBBXqA0Et4a-~>5ylvIo5mO^B6QAOCY(xHp2Y`BW&9uV SDPo=g0000<MNUMnLSTX%b%xFW diff --git a/Telegram/Resources/icons/send_control_bot_keyboard@2x.png b/Telegram/Resources/icons/send_control_bot_keyboard@2x.png deleted file mode 100644 index 482e40831cdd76a42faadf85e19783b9fcdb14eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 546 zcmV+-0^R+IP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0005$Nkl<ZXo2mS zPpX435XQ%}7g4A=R^l;w8{IT_$hH>|nszBA-L{#xcqk_RA<w5(TYoShWCp%Z$s{-d z03fUwGPi*L(rJKm4nhb}N`Wy35b>>Hi~*$-2qA!T-fIHu5r!d3DRF5?DKQK~boE*W zqA0>V&*#?KNuK8zMNzkbCBAtVDP_9>&Uqs_%QB>tA|a&h(@h$L5Tul1mSqiHob#rE z)9GX-rD^(h(Or?IsijQ_(KK+mT&yHY>FmPGpp;tLlv34NP`Y?NpRn8Qpk%+_!|U~e zB|r$Vv>+l7@_PbQ1`yFI3WDH$3O(r3_qiwWS{P)2F$P-e)+>P48W>}v)cAq`(CD?$ z(-pMVeP<17tw*V;N0+`qo7l~5!>u1u(^tQ3;-#<|n43Wc9*;-w`u%<%rN);(w^7Cz z7Ddt84vV5d#@Hw|FjX6!gRPA&mG3mLUFJM%V5%<U=C-k2<~-R9%*}Ti*e-LPH852h zorA57E|toF`-#e*r%S-qQrB9oT(4Iv>2NqqEXyBP%k6fno+|_A^Vv!|9*+~t^2gQU zYDIwV5mOw;vyGVIIQAbg0jzw;2k-%7R)Cccy6cf72?`-lDTT(Ew+lALpi&Bj5SS!M k=U%C<5^H5Sr`14y02~##wkZfwO#lD@07*qoM6N<$g1g7_WB>pF diff --git a/Telegram/Resources/icons/send_control_bot_keyboard@3x.png b/Telegram/Resources/icons/send_control_bot_keyboard@3x.png deleted file mode 100644 index 423c86be42a91a94b836259020686083ecf3e927..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 946 zcmV;j15NyiP)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000AdNkl<Zc%1E= zu}<qS6hN<=M+yTYjBX`ZDD6k^1FUU2@CyhrR4Qz+luux%5qv>6_z1d!kTO((0Z4~Z zNbVcn1Ce0I1{~7Td!##%_*%Y4#EmUE3;+-aDTTZTFzJvW1%GrKa?YXIY{JII22?5) zSXx?wVzHRz*o-k84#D$0xVgE3i;D|5Jw1i%>uc?NObgCAcDr4Cd3nL#LKueF?RJrK zo?83=kl)<g+{DMn$Gq#wj35Zmah$IS+TPyAFbwmqA#<WA!kwL+&k3s6>lj5*-ednU zqA0?8y{;#SbB;j}<UQseCkO)BoD{!sa&nT|(b3Tn)@n7JpPxs@*i^*g;v!Zm73_36 zskOPTn=YtSDoN*^hldAjHk&%L{zQl4ICy`5FRU>PLoAg_azU+DOKMkRwx61C97kGb ztJRVV>h*fUq)w-kdr_uC_PF(WJ-Hyy^Mpy&YBl$QOh>g^71ru`UaTE^wFCe#8jXa@ zhlPa&7>!0Bu1_<?Vo_Mrcs!0dhzUZ(4+#JOg+f7kZ(3oDK@>&Od#})HUI7VGK!OyI zAO*$+S(XLub{htR0U%;(3<d*ex7%P@R_1+uhqvE+8H_RQ_lv{BLuo%U4h{}7?<*OM zF=-!(wq^zSzW?c>eBaN!udfO+{vM>;F3J0Mk1YBUwW0SQ1CSsEBuD`XQb2+fkRSyl zNP%%dx3{;(a!mI1Rg4QdK0Y><W3sRBV05WrSr+c??P)C&eBa0Y{e847EAzgRk=s&Z zTA-zdAxMw{5~P3xDWEM^4QR_XL82{fxh9FVd$j~WTdtyfnxqwwAO$2y0SQt-f)tP- z1tdrTZMiC-EmsX_%k?Lh8q)$TH4H(56p$bVF+opHPr^pia=Q65*tRXaJ|2%_f5!yf z-Q5Y3R#sLF4Qp~(U0oGkPrgUR1oiuUVba#tmZ4!y4zijqFE2k_d;86&19gf6b>(t- z=8t5S%VoJB06^DurFIejoj$D9YG~Vb?uT`C!?tZ)TU-17p<Vzw7OoTo0N7|WX70lR zjYdQESc;$3S^6}^?(S~dyd|H|X5r%|8b3Vgvp(k>UDy3p|4m^SqU*X^pT~)9cck(D z154}c>#)4M40CgHI;pwy{QL~Q??b=ehx7AuI6FIotE(%mTx~(K3a#c9kRSzr0ot{w UW180KUH||907*qoM6N<$f<q&%Q~&?~ diff --git a/Telegram/Resources/icons/send_control_bot_keyboard_hide.png b/Telegram/Resources/icons/send_control_bot_keyboard_hide.png deleted file mode 100644 index ceaaccf73cffb9a36e91007f709b16526385de0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmV-c0jd6pP)<h;3K|Lk000e1NJLTq000;O000mO1^@s63?#pm0003UNkl<ZIE~d* zF^+^V3`{Qki0!CS(NU&?GS5hhpo*wcQqu7Q{uBwN2nw35$ldJ$%N6K!mRwk4k8ODp zN(ceD(LU}Z|C<JF+Y+VJuZfgWq-mO644Zddht2A`##ijj>$;A0UDv(hpMjJT>$=8d ztu-j6hs2apXsu)Zx~`B?9ycI_psFf_5Mq%rX158Y6vmjC7eYW;mV1NzoJ<ITWm#fz z-}hjQMVv8)zV9RMy@wFuXw&`mi=u${K9UT>fFwzfBngIL_(on7#o3LHkC*2;oO6+E z97l}f7-Q!g@;rYhf^&s%&N0t3UTvOdaL%u&?7a-LEQ7T+l3Qz$W!Y63U(@>I@xU}q on5GG7nqGb*bn9}L{r}JI2_12bkP5+Y#Q*>R07*qoM6N<$f@N5nJ^%m! diff --git a/Telegram/Resources/icons/send_control_bot_keyboard_hide@2x.png b/Telegram/Resources/icons/send_control_bot_keyboard_hide@2x.png deleted file mode 100644 index 89cb528dfc04d035ef3280db3d5635c15f5e9fa1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 669 zcmV;O0%HA%P)<h;3K|Lk000e1NJLTq001xm001Be1^@s6RMh(%0007HNkl<ZScUCa zEwAE06ur|1lBNlyq5@HYs;ENM)ZtM5H<chU2%aEdNI-!C6beio0u-VOfk2RuRFtIZ ztl04K0z34JU0}mWuIjz#gPEa|G9m)KF@$Fu$cj&ZXX6v#*@z6l7^7CJMULbA3qM3e z<TwtsS}kIX#p(yI7-Q&kI=G--uXB4JRgFgDTd&jUfH4*|^XCVmD5Br*;~LFo^RJn2 zHk&tk{eB;!C?*du7z}R6E@-#gaekTD#Tdia*B4iBI2<MqVB0prFyxYUyIlx^klDN- z2<Y{CT%9lsVcT}{07QgZt%mdY%q1TT22sm<w?t9AyVB?L8MRt1-5I!4wOU0G1YD8P zXoOrYm)LwRm&15G=JEzXfNHfGhbvmlvMl(%&lQ_YCdlXWvCZf6c}%BME}!rFuq-Ps zUp!IMG;unexT5p<9FinO@<@_|`FzghIh{^0O*6@A#$WbIrGn$}$Q55K7La9`J1@&J zmdhn~{&+m1QmG`}j>-0}VHh|Z4qOwi>!MI7+{_h=MYyiZojV*3FbpH{SxOv$h@k5_ z_WM28%xblQqA1_}ilSh(T5<dL`#p4BPh&n^03t%UT*hv<<C<Ep*HBdzs;XkM*>L-I zyB*5qa$5800}v53O@rroTyxv)7TfKX``q(9XquMAe6|2Ygi@(=*W|r$rBW%Y`RoCR zC~})zW3$;nRn<qBe@p<P=!1H}^#OmG07TJ$m9FceSS&u${9^+U@n2TU<?`-J?qvcH z{k|^zyz9(nvsl-RG{r6{-WC7<SBY20C&07u_5pqYT)1R!E6d$!00000NkvXXu0mjf D$<jXs diff --git a/Telegram/Resources/icons/send_control_bot_keyboard_hide@3x.png b/Telegram/Resources/icons/send_control_bot_keyboard_hide@3x.png deleted file mode 100644 index f259f9edc93a4fe20ae597310064c56b12070c9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1085 zcmV-D1j74?P)<h;3K|Lk000e1NJLTq002k;001xu1^@s6vYb5Y000C8Nkl<ZXo2n8 zKS*+I7zXg$L`4xv5Ge-{3RJKmTm%OphXNNN8r_821ZfGPrv3;G4mG&QI|#KDxCkyn zNr8jZAP8b1LJ1-!qKKki-w?jT+ZX3ikDi8e!LxPl`+9yj_$LVwfyDfn1)&T4_J}|n z5rH@&0&zqH;)n>u5fO+ZA`nL=0#Yax^ykkX3Iqat_u)N(K!BprD9PpWiDzJ(qtoe- zPNy--!NI{;&(HH5931@ZnNFvn)9EIe!~Vw>iv`7E5&!1o<V0|cf6wXZ>9?N6Vi6XL zWi~)=w;T0({oB$}!r?F^5{ck25{U%iaQMfb^?Ds{x0_!ezu%8uulM89Q6iBD78Vu+ z!;wm*h(scsJ$t<#{C+=wK<DS@oJ&WE#bS_3rGntZVln2PXJ=>p0TB@n4-c7_-`?Ir zE|<>@M=qD+?(UAc-{IlmIP=+y1cN~gheM_ki9`ZQrE=ytN~IEsM1rZ)a5zLT7#w%U zc!l=%_AnR>m<pv*DX7)znc=9_YNS#rrY?iQ0K2=plk6G4n9t`!zu#vn_W1aS#l=P5 zaTXUB@$~e>)S=(+!{_r&x@)q6J3Bk*cDqakv)SyVZ_@tv=yW<{vstFy-EJ4#+uPIZ zo4lyU<3Xp>VJe!-<*>B0G&Roh@-lL{9MjHDrvs12Gwsgl3cKBIv|25u!i7QsMx$}! zT}Go3g+hU8SF6>+#>NI8uh{8d@=m7{jYfm1c&Svv%F4>vJ62a$Q7V;~b~G9dIGs*D zaQFctBG~PA)M_=R3%tL-!)moM&$e2v`1tr>nq8~aV7J@(!r>2yh_JS{hDxQvbdhSc ziuLt%&YA1$>!?<%OfxH$3T!qTpE&#i5fRL0Gs@*M(}n8wIvftikFy*O2R=VPnP!#C zWth!ozH#^mA|jYfCcM49F<q?LY{KPoeVgHOxzKDjnP$AczQSZO%>rjOKtu$C!GL@| z&ve0dyN%7w&A-oXZEd05ZZkcb&*xz<7-oetJ0K#0Ua!Z?%L~&*yWK9lUN5{}FY_DF z=jUhW_4--j%od1<pw()T$z+%=-0%09Kf+`(8ECcIS>wzeh=`!kXz=jxz;tnz+~40r zqtVO*XFfng1eHpKWHLE5PBNK<N~M|?&isIg2nvM)@pyb<oOnDAg+eh;ocRI~5o9tM zZf<VI#<{+}hD;`#H_rTlh`zo*jz*)*IMHZSkngz#10wqRdFJZsiWBGZ@>1xZlLT7~ zi9~`>DD)#vC=?PBMtGpnIX*uAhI4duBzP|(f@Gv5boTf6$!4<=5m7Feql=3Rfg640 z*8u(2i2onN5fO+ZA`nMJAdZMY91(#yA_8$l1mehl6+YRqYylx200000NkvXXu0mjf DTd586 diff --git a/Telegram/Resources/icons/send_control_record.png b/Telegram/Resources/icons/send_control_record.png deleted file mode 100644 index 6dda533a182558bebd4d4d3d54f17333ae4c03fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmV;C0dD?@P)<h;3K|Lk000e1NJLTq000sI000;W1^@s6_f@Si00044Nkl<ZIF0R8 zJ&%Gw5M3|E25SC+k;13}e<c(^FRdK~t)ZhM{0w^oHkc6R^4>-74*4=MSKUh{JNstl z?PjwxGo_TMH{hdBV!2$ZD2h~36g2qYh9GTQtyXfmT%-+fh;`MdE;5QDN*s?zF${wS zA3$ufP*D^tg20`F4_vR;PNB}n7WtxPGla)^HT2l8;$P^mh0-*oy^407G)=?Lwpc7C ze@I=|xv%S*d)_F^GAde@#WhJ1uD<Up!!T&@fz#>4b-iA5&GY>GMy~5pJqQA-o2J>@ zw1^=Gxd3V(y=GY!$*QVkyWKt}02;I`OB(!p=WjL}<^rhw)wE$4GH^bhWw+b)bvO?~ z4uG1@?vLY`3Afu#eBYP-elMD)(cpta3_$MS7&#mcPjAx(awg3q+qT8?JjwH%Ujqgo v9AcB_(N7G`pr@bx4$Q|y9LE_yM=A9Q+V`*8s)c2S00000NkvXXu0mjfyNt7k diff --git a/Telegram/Resources/icons/send_control_record@2x.png b/Telegram/Resources/icons/send_control_record@2x.png deleted file mode 100644 index 6bf9fa581211cac863aa11cd0d86bea78cd08c19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 877 zcmV-z1CsoSP)<h;3K|Lk000e1NJLTq001Na001xu1^@s6c}Lh40009tNkl<ZScUCb zy=p2!7@fV`1T=~XX)LwU2T%ktppAiuPZ3FKYdfomPoP*zewsjHmqsCWrbriJVHGeC zvGA|3nadei@667wv$}gP79JQf?tb6-&d$!x&e>>$5K49kzjjFs@O#_DY&J_$sT4Uo zJ0tOUoE_`+dgS%>m2|sZ^78VswJ5qtJ3c<9cXxMGh1=U(8VCe7;?}2+?cnqCb2=Cd zlwx!s7CHY!5UzF;F}x^@s_TMq3ygSvex{jBhWdOy*3fUYT2dTtv8#fz*{oEgR4Q4< z<D3!Ta7|FTTo&jt3aT889uIK1CaB$R3*@*psvN}d0EcUWrqiiF?)UqZIf&r_4m-7M zy*elrLo`jZx4><Yw{x$Ky&x6#f>hWGQeiJhg}opZc2m&9!vk3?7OXX!&Hq;X@$r!? zmrF7pk4dA^SRJRc*=)5@n9t|*@bJ*__5)j59;m^eM~%f|v5L^QCv1c!lgUb**Xt#x zr>DY|yuG~%zhkkO^6GW3rURah2Wogcp4E}*bjtoRL7$(WLU1%16@J6~@hBFHN<qdL zBS$0>5q|gk{l9{Y!EmEI;6!=k^LbJ#l`O^J9AegS7;EqvBv<kN{w~c4Gq?Km^u+eg zM@L7jp>N#WTiGDg;K3P6LOmW2olGV|Kq{3I2w?WyGK%2lf!bcLSKQ=RDitB9)9Fx6 z(*z<ntTZ@{E(WzF*OANRB*Lq!D`}`tPEP3k{k>8QZj+gX2B#6IZEAyRwVDu$H7Suu zn65LJcQAjrTEwBDk7HIY6<mnpQLopneOWjhmKMR$Xk;mBlLiiO@jxxqTx%2yg@QDs zSW&O9ucbL<%lK&;%QcP+fLbOwGD)>sHJulj=*eW#wiv`3C$J9Gu*fqfvl0b-e0<QG zn;W+71cO0(aB#pH`Z$Jjh_S*NG1h|6E-x>o^~njqWpSJA2|^2nLekT>4e&I9%f_v? zHy`r(JX^qietvArK`e5RYx^P4Rc!+PlW;nn7BV}Xj-yY*j=$t@`>DnX`%S(Ijs*>e z!&U$L``Z;!j^0rh7Z-Fm9MZ3^FV=U;w_E?7B82<_c<cDXPT{_W00000NkvXXu0mjf De&L`X diff --git a/Telegram/Resources/icons/send_control_record@3x.png b/Telegram/Resources/icons/send_control_record@3x.png deleted file mode 100644 index 2669a856d47dff02912bc65ef43c5a744f0fb154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1306 zcmV+#1?BpQP)<h;3K|Lk000e1NJLTq001@s002k`1^@s6y=?|;000ExNkl<ZXo2lp zyGt`m9G&PjK2StcDg+fJrV*7uBKW8*tSqz<ANU{G*(r7wzJd@GI|WhDsGyC7VuDey z(n1nzF&0Jy8y}FlocX@Zy_%WHZnBAP*aHKT+4-H{IlHs7kJ(rVA(Z4H3i8nu$!BB# zWQ(||sfqOW_mke<UgC5*iOptX?Xg&l+}_@j<KttpzrRoJ?(VWBft_QzmG16t8VCfa zTA(jt^wy!9v67M!y0WrD<MFs^Qwk7+SaAF*W7XBwG!zPHHuN3f0N1ZDR#sL<FE1}M zHl+Yu;QVo8Yinz|nNk4GA2o)PUUp8syu8ry@p0PN*hq_ui&;S(ZP+FPICYp%(hm*> zgF<m$ub0~Gc3~{Gp^pgQ{t;s>EiFP(i;Ih@dC*q`<jA?PnVA{k@RJ=W-jNdxOXSG8 zv6GV%u9%^rA<bNf;o<c3H220H9v--2YHMpXb0LNY<jA?P_xE?M7|eWHxe&txa-^#- z?dLH{`C$bG1;TA2SS%J2kH@)Nu`L~#&tpb|Y0PLajTsH5F{8mWW;B?_j0V$~(O?=g z8cbtGL;f4HTCK$E^|FPz=jUfuN84|O8olhT&eGCS(tD!W*;(ORAQ3DUi}p847K@Ni z6r)1#pGqtiOX`TW-}?GZh2B3E(W;5fX5$k7<O@@1;sG9%K8bS-W0i-Rni_6<I2`7- zcXf4XJ}X5rkw`?7rl+T;L_J~b?Cgx&+2L?-+k?R%x83LS5xd>a-Npm@B1TINk)}W( zkf<k&g+d{2XSdtUZTI{A-1f@KN-{AqA+hom=!+O0c6WC*Y5MMgjY*fw#T~cT*Vn>x zL$>gTb+1$lf3meP*}@+*@c<7=lLyM%+Z#8)>2z`vs9CBV8ylky4Gq+4wX%Xb+EOjm z_Vn~{b3zLb(EhFOe?N|nj<`W{b92JIx4OD26qqi+Dar>eJV3k9*vQBTH|X{CRXFYE z@^c7$0|G5PK)cXbU0ogh`1s%k%GQ-(S`TyJ5bZ!BS+9m>XcrnoSzcb|27i8j()RXt zVbapl(u_Yjl6|<++S<xA@Bq!6n6C|Pw_7M691hcpiVEpn1SXX8^K;dv&{s53D5bo- zoaGk5<ME_6hO)4*AS5|BIG}}vg;F6X4u^wIPEN8#_-HiBh6@VnXu~%2NyUR)$SDG} z3SyHR>+9=<Cn>P8v7yRbrUg0H*VlzQpb=VC8$%f$9+nQFy}dm)M6zWH+<w1bssS1$ z9Afh6>1io(Bod)*ZEd=n0w=g-fVLDo@+6y^o6-cBE%DG-RaKR-A#i}}Q!x7}fF>1u zsS_R^9!e9@zqe1gD`;+R)@%qdh?U)DDd5~;q;CvmYilcIQUy0RH*B^V92{gHdf|?t zsHlh))X|1*=!3p$vAw;mWrwNXvr|)3szvAx<kmP6v<(}~M6yeP-axJtv!6c3$lvSS z{QP{@O(BQmI;FocrF3+3P@m5yym3wk-~uPOGtrijvHu^z(a}*hdx{=bWk3vK!I8lc z`cks;ZzPw?MLIh>iO1t10|NuxK07-*<l^Fjy|B5uy3$($$@<@{M4$Mj`?;Q4n5!*D zgK5lYFpZ^)Xf&D>`S|#l?X(QKTo6U2udk22=TLBef6v;ooi{)KE=&md7qRa&D9uX% Q9{>OV07*qoM6N<$f^HUgIRF3v diff --git a/Telegram/Resources/icons/send_control_scheduled.png b/Telegram/Resources/icons/send_control_scheduled.png deleted file mode 100644 index dfa00bdcdf5978e31f561410e530f6afac90c9b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmV-w0hRuVP)<h;3K|Lk000e1NJLTq0015U0015c1^@s6J20-I00004XF*Lt006O% z3;baP0003YNkl<ZNDb{)(Gi0n4253D@Z7)wT-5;_!EdM0FV7KZ9+g(46lkpSVeUB5 z<h_>w8l@0I(Ky_0hbu0E`3ftg1V+eW;|xz;6&FPz+O`$LFqi~&)uyUXR}%;fu;2(8 z*6}q6pr&aeZk~YyTx;&l{4ELxWvdmtt_uM_l0pnv7`QfK>w8<6mP&&H*{%tkIj}Hr z`Gn=-pv%)JUQeWi>vs$BoOY`3zM4$NK0o()eiiSv$G(+0t>R1Aw<Wtj_r6YCrMj-g zJkMU5zkRv9ii;zk!!?eh&WS$sp`=-Ur{ccvHDS{<QO%sAZnZA7wA3Ggs;VpwN+#ys z4T3nDxarN*Fuqz@mN7zBjB~0vOP7Lm_BaL1iM@n5tCxg*0F4-S<TR2p)c^nh07*qo IM6N<$f~6>%!2kdN diff --git a/Telegram/Resources/icons/send_control_scheduled@2x.png b/Telegram/Resources/icons/send_control_scheduled@2x.png deleted file mode 100644 index 12cba3777cf01704a08046cb414071060b251a2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmV;70(1R|P)<h;3K|Lk000e1NJLTq002Ay002A)1^@s6I{evk00004XF*Lt006O% z3;baP0006*Nkl<ZXa((BL9W6e5T!pq9>ASv@Dgr4f~RuhRa|<EUO<-`W1?HbpJ%>G z(SifDEp#SngbK`i^M;l{3#F8%YcTdR7^eS3g>deQi^8I?Cj#sBT4}9s7s|2L^msO& zf^I~;mO$3DqQH>h*7SHb5}CtxL~^UM6`oF~YPnphvMkm4e13_?eLjc&+5^G|e8DGt zH({8B-fJ`(>D6kbuh%Pa>Bit27=Yyy%r$L3BfHsbx?%aoE3ib6_7g@_`)0sYqoD7; zn=s9rmo(n(c6zZ`=*eW_8$9}d_<%3?G!5wuYyqU*gy~^n`nfqCkH;a7qWZw6O&Zu5 zCrnSs3SmS$z)2&0;M+7{3&7%5m|hE}k5<ySjs()%G+?W1TX)~Pv?K@A_i3?9$LC04 zrC2=dg-007{`3;U{n6q(NPfA)+H;88zw7pr(p@X?ZU2K)m_3L4_8#VD#NZSb!rT-| zt-UCmnqpkMqA)jNskIk{Q&WtKH#vn*r_+Pu@pyccIR)pwkhzD1G7a46HJ{IKyO-F( zM45{xxjik8lJD9km(uS@aPA3VPP9^N>s9!2xm0-3W_YEZTE$CvyWLhe&1SPoJ;jRi z(cYKCGzzQ}C*yzjKwTe~GKhQ{5I12sY~w)ua5w}lBmx5BCydVv+W6!FtET`0+&+H7 z0MW)rExjUqk38ly9%ML-w$G03bvLKJ-|t&VY42K^h-PKp_i$EhiHs1=O;MYVC~QY0 mx4I~to1!)!QP_@1ZuLKz3cVekkVUNk0000<MNUMnLSTXuGB}6; diff --git a/Telegram/Resources/icons/send_control_scheduled@3x.png b/Telegram/Resources/icons/send_control_scheduled@3x.png deleted file mode 100644 index a37f1171e2b3e0cf447efbb6d11e0a39e310ee60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1078 zcmV-61j+k}P)<h;3K|Lk000e1NJLTq003G5003GD1^@s6IH*Aa00004XF*Lt006O% z3;baP000B+Nkl<ZcmeI4PfqJF5XRl7C)l(XK<wBcaRBygE73!+MB)}mtYHVIU;$?- z3w9L;00aU7e*XGe3101ICLT}I@QoBv#>xEod15DN<DPTQhmBI1z^}^oX?{k4pp8m_ zpb6TDfRP(kG(j5?Fml6+CTJr9MsBKC^zrerdwF?r-uvl>&(PJDW%*6z)e8+(?+gKU zZEfv~|JT=7x4ypqMW_AGvizp<s16#H9FtxTuItIN{H8LhK6IoU|1>>88}&Fr8-f0< z>1T6u(~U+WHyjS#*4CEW*w{##PmQ6;1C$yb9v<A?-JQF=y>(YtSMKui(oH54CF}n; zgW0NR3qbwF#f5)+dkg$lOAs?+M-HVNGRK!e-rwK%_xJa!MK=;HazHK_SS9VvK_49* z`S<ttNZ6~XLN3Uu66|K6(feJ3JTDF8RHL#R-LPPi8;{3sFc^f+@19`7+}_@vPMVt8 ziBA<w9x3_w`0(fF=YDs0*Z2E<vF(sp)ngD7Vnd9GH6_SVITl%B;Sw%+brCL`{NCQ4 zc7WCFAy&j36I_iTS4G$|(1(YIvACwyu}YBV01<PlvXSe2KZU<C(AU@3QP4OQ6@poc z4a6Q3<f@pH4D`*-O)RGAxThGDW`bE;7_(!7TorSYfetfrET&dYeBXtH2{I<g^}9B5 z`-v~_LO{XhqSSuAyy#I^Q#&s{z0>Wlq&0$0qS0+2XkDI3YXqG{quW5xx;&HC)V^U! z&Du%NKdgZjZPeieP0&UJjNGuI3EGH&ksGzEj;=oI(CP;c9a<zab_hBHNUNEkwMb^{ z5OfBRRx?3sk<8d3=nNpOW`fotnXyCA89-Xi1g%9fV~3zKfV7$kT8m`H4nb!CX*CnH z7Rihqg3bWaY9{Cz$>-<i1*T6=PYb%H3WA=IJ3T#JV0d<RwxBCfh<-5{`_(0MA%K<M z!U_!j<m6=f1_0gyz-Q>R^5U;aOG;F5EQbyOtdd;`x`dwKSb`S7D%q8wOXvxXC1?Sx zl3m51A0Hp1{=y44Q9YG^35Y>sQIVMJ?CiwU)XfNpL1X2Xm>e7&#MIP%A|Sf^>DnvG zfrBXjg8gNVy7bb00kkHaBLj`#R(Tl-W4NC?L=GAYJ+*NiCA?Cs{V+(eW8|RWyv<Af z0Jb6^EnCe%BayZ)UWLb;odP|t?6wUfodgZoZChZlb3@0!rzbA`aGi~nTd=4|YHg3y zw=|`CoLOz148`BD1h|w21jM)C4SevBK!Bi)%7CB=+K7OW8&)(y8xb&a!-^(oBLYTl wSkVM+M8L=mE1IB<2pG9xMH93U0V6km0L<>df`nJK_y7O^07*qoM6N<$f~+n1tpET3 diff --git a/Telegram/Resources/icons/send_control_scheduled_dot.png b/Telegram/Resources/icons/send_control_scheduled_dot.png deleted file mode 100644 index ce83855fca0d4b7b34fad2e7e9cee084861c9aff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3Ea{HEjtmSN`?>!lvI6;2JY5_^ zJb2$uGvqs<z{Bj!e(#)hh0Og0hDj@@E?ld?+_lsrA=FSta+b@I?47TR{O2?ZzHze) zV1Jd%lC)#n!l%a?XLaV>268iY7A{Crn*ZH0U2~~NcCN^^4>}L^iz71w(hnLNU29#m z^WK!=_TvYi^MtPr)&D-_=|k@B|M~vxD<Atd%Cwr=ob$YVfH9`@ob>*qWz0u9Cr&Ia SE;azVkHOQ`&t;ucLK6VCTU9jx diff --git a/Telegram/Resources/icons/send_control_scheduled_dot@2x.png b/Telegram/Resources/icons/send_control_scheduled_dot@2x.png deleted file mode 100644 index 7422c92d6225536c78e6b308456b0637be7bddcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P)<h;3K|Lk000e1NJLTq002Ay002A)1^@s6I{evk00004XF*Lt006O% z3;baP0003^Nkl<ZXa((>y$ysg6obRnFaQHG7NgL!0Fyw^#pqI52k)v2#*re>izTqX z{7}%Ol=j>o(_W8p`Y~$5>cmahgvAo@lokU@c1)Ne5i6Lm7*Mif!W4;E!Gy(tk{uJK zNW=;zEC!V9m@q{mRxn{Ppk&8{DH5@Q35x+GJ0?t#h!sp&3@F(#VTweoV8UWR$&LwA zBw_^<76VFlOqe36q|h`?8ipaw^PJXoJv#1D!`LqkzaCcI`@Y|&Y1+Rvj6oChQMcJl z@6>Dw0j8l1n6BG!p0dT$<rUi78_w(W(r6Jtdu14_9inO*HmJ63L+`h3Tk5**RO@5u zG@d^UF3S>rYD8N-;c*<phNIfcg`S`IH_hXYC4~K`505gtKpN*Pj-I@8A079o1wS~B zSz7(E6kfnVVfC9<&_`Hi!{MlIDm3Anf(yU)8}2Q4g<U%GUH||907*qoM6N<$g0S_j A6#xJL diff --git a/Telegram/Resources/icons/send_control_scheduled_dot@3x.png b/Telegram/Resources/icons/send_control_scheduled_dot@3x.png deleted file mode 100644 index b1f4a9852c8cb63163ae0b9a8155256ff285af22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 622 zcmeAS@N?(olHy`uVBq!ia0vp^Q6S901|%(3I5Gh#mUKs7M+SzC{oH>NSs56ZR6SiB zLn;{G-Z_|i$U(&QqGGm2vr&Sx;1@UclExKHY&SMF?lqQg)d^5}B)Nm*uk|ERr#F>P zo+Q4BObPOmeD$ni<ALkl&&z+wGcH`H#i47W!f^CP#zI9#jbm=yyqpe(W>Fa)3|$*9 zIyo~1^z@2J2{k0;Y`HLjK_q!`Ya7b~3F*P4rKB}(ZCL8&n^O)y3^@1vb7}YSk1>DV zL@NKQFFBQKH=lp^-Mk8ur1>|tU)EIrl(bR8Z~0~A$M@$S-&g3vUS{_q<GI9>*<~`9 zFTSa{@ZU1_PQm;A$2Rt^J;xerf9Ks>w|e>Fo}+%{S2xD3UmbWqW2Um~m!h3-g4elk zKV!J^_|e-6XFFa$d0BGn>+d+d=|;29x}BZ4?&9K-c{_@&UOfJI<Lq@KcdHkF>+bX3 z1u@>1-R8Y(b^Jy4p<iD1za;V_7T7P?{y?B)k8*4X)5gU&|6BGRv6Jog+<y5@`uzO) zA1ej3#Y-pFuju~kym#TwypI<1vLmnmcx}3LxA7W|e94!3@9!;&ac*sU(uxsDGx8&| WmF5*IPu&enKn$L)elF{r5}E*4b`AOf diff --git a/Telegram/Resources/icons/send_control_silent_off.png b/Telegram/Resources/icons/send_control_silent_off.png deleted file mode 100644 index 8e25352d736fc04a8e85e19f547e25a874769b48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378 zcmV-=0fqjFP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0003&Nkl<ZNQv#0 zL8^l=5QZnyY`lb}chGf9y-ev5QiPnqMVEpn5L@t6;<_%R#J`J2r4O1k5ejedewbA< z^Cj`0Q4kU6i{j7(A;k9p=kuBF_d69u;eQ|scp_sAF*6xsNJ{CI<8_1meosx)5cALW z)oMje)6j0W^Mb$@Qc6@+1((XQgp|@+!wGm6zuj(7N+FJ8Ff-QcHBP4!hGCc?!GJ8w z=Hf|`yxo(g=?vL!w_$+G<pKauO1VylJkJ3DbzO%6`o0GM#BuBdY&IJJK-YC|*R3|4 zrip|Q#LNWPoh*c)X_^SY?6Ox`ZTjq3SMk?%TZadaictLnmI1WZi;CIue)(2w?VLTI zq_uYU!6#e#=+&}8|AQs+{>aDUkpRqbI2^no&RO$}D2mXwEyi)g^?F4VMP7M-=ZTi; Y0rVMdBpSFzD*ylh07*qoM6N<$f|N<ET>t<8 diff --git a/Telegram/Resources/icons/send_control_silent_off@2x.png b/Telegram/Resources/icons/send_control_silent_off@2x.png deleted file mode 100644 index 4315d367d82d8a80c699981191f3a8aa49ede85c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmV;F0&D$=P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00078Nkl<ZXo2mS z!Ro3&6voGUF_l`>BUliviWU(;89hfmK~G`OBLrf!ZW}G67UH5JY9GBp5J4eniwHV* zaS5(X^4FTt8~DLlgv|NQZ)Tj1${>UQeX)LpUBIV`0KyhE0G{WOs;cO6xgguNk)kMw z=Xok#KqC}IfoJu4JsMtg0M6$#24I>daUbSEL0SN>(=rT0)aN%sNG6%EP$-~wyN&Ag z`pfGK!=U5whz^GXF;5~(DM=D677J|KxM?<<LA6>9{~izo0s8&E=fa<*C<<^K7Y;xw zm4fwp{dVyWEtgA3CX>Me2!a5s)e0--e!qjJY0zvoK@>$`Sr%fk7^Kr_Xti1}nM_<E zc%BaqU@#b9MQk=3D3wY-jxCqVu-$I4@v5o@1JGzRFmqkkAs&x=&XGtYz%UFfk0eR- z0hY@pHuHXp{}<wUCFb)vU4UY-i0yeV+r}*_l?t{B`Fx%pK)2g<m}#2!cJYsxPN$BX zoleK|Sl``fwOV!j9u9|oQ($_|QLEK_jrTQM`~BWATcRj_yaJg_#*uTk+tJ%Xx7*EO z#<J|k0363Ta$c`j&trXeqwv&alIkws%^N*ZhOG!7Y()TJD*^~x5kT1T1t7~Zr7}MA z^3*iRvWz`=z~2UjNW79P%VfGyx8j442Z;Z-crOq?w$tG9k~V0$wbx&N3Q>E#9+bX* z1*ibnwv7PbWmuL))Qbv$VHmF0bX_OzN9@n&5xHCrOw+`U!_(;m*=&}yA9;WW#CSXg u%d)_-EEtVOG>WJ9kuNAkr%>4XdVoKp0W-LW36?Pc0000<MNUMnLSTXzA|~4a diff --git a/Telegram/Resources/icons/send_control_silent_off@3x.png b/Telegram/Resources/icons/send_control_silent_off@3x.png deleted file mode 100644 index 794c67ae1a40d5c7de7c964d0ebf61cca3bfa3d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1325 zcmV+|1=9M7P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000E^Nkl<Zc%1E< zy-)I77>Dm-`3Mmw0s|TcNKM2AqcP!6fRH%rLV|<=L!xoi<egYZsuSC@J2<(*V8TX- z#KjPGZ~@fd076qj+xwYJR0@ZWLhJLMD>+Lk=l=ET7o5{F0DwRlh)M4NMu!R_Km`$? zf(TGS1X#TTe8uf{!`9Xo*zI-@1OZB=61={?>KzNv2yJg~5AW~q@hkWD_qeyWr*}5} z<wPP8R1^i(P!t6tk;wm75X-VyEEZMed{QhHk!4w3^67q&<~R<#UhmL#yk0MG95<&# z-_c#r;^HEhOv7WCOeR=dT%1#)AGLL<`UO!Gfnk`T>o5!hqA1QO(RUUA;J>*SU08sP zjSV<FJcN^z6Ifkc9V*FUvB1vG4lFM(L!;4v_xE=|#JT02>qRdd4&(Lp^}xFp2}M!x z`uZBf;qbq87h7Ln$E&L=lx2A$F+Y)I8LzIcaD9FK_X~<fqxkmrriqxJyuH0)G#dS# zf*6LubULlsQ8NfxmNA`9Bf~J-Z^NghS-|r=9334I*Aqn%Zf|el>FEg?jRr`P1T4#f z)9D1C&j*{En_#oqi2a<OpTqI-u@=$4F_WX`^z@Xt=kM<BFqKN-+S(dA91hif91aJr zt*v1ymBPEbJ7S-wr>Dk0#dddhhu)>?^*Sb#Nwiw6WA$sbS}~bSV!d7;*teo6xVyV+ zTtO=<EBN^MIB)|Bg#!Bh{;6vC{eCPI3Il6AK0e~g%8D@sU0hrY44%*D(dl$*Qpf3Z zVm_ZASnJ~A!gzu>j>Ar;qZ+teE~Cfe(R62cJRU5U%c}J{oepvwXDmTH&#Q(N1ObD= zpr&#@TQC?zK@e2y@;q-GK`xgIE0xMDgO0spDwPVlTrOh>3WY-0>-G9VHX04B4)uYJ z<2Y<I8hy2Uy&i@_p{e@TY`9~4dmC7`e-0p*%fZ9LgH~l!d3bn$TrStQoMl<q-rk<7 zZ%qW5OeP2f0;-?g+}vnUg2<pcfk1#bAU6z61leph@cDdw%Ri@X=0T}c0znY^miv6Z zk^ZI(LK8t|vl-lOci(bB5a9XwS&I@xo}Zsp1-adBFq_R2_p8|{X1Ck>mP?WZ&1O@J z5=5HKCP<Rhx7=>G>wk(d45NC1Mnu)nvp|w0)nQncWr<_n!_a-u-sn(41gIbaR1g6w zhyWEtfC?f&1reZv2v9);s2~DV5CJNP02M?)6G3`C9ycn}<kLjZ{{H?{qjs~zY&OIG z{{B>bOf@YMi^WhBMb&w!TCFzI>7JjhR;#Jz5JeGVvDid?PW7%vEEa=oHVYPuMfG!r zVdnby?x2xFtJMnGY!(0j{`~nfcE1yuJvcbPZnvwt$Bat1+r@)}gK@WYd^z!WTy+-p zH_+?#FdmQ3PEaC|_;p9lPrBVMCK8E}ws-7j6kDwpI2?{~zG6r|K0aV+X^FVr*n%WU zf`x^JNpl$nS(a6&s(v!|@1Sb6I#D2_G3s{A^fBOn0s3Ez02M?)S3#Lf1`u)NmdRx1 zo>5mpFE1}MU0*Ba`iJss^LoA5ZnsDJao29Q(d+f<kyAfG0Dxy_XCnzZJ3G@WqrQR| zhC!a^hrTz@^ZZO+b@~;8K3g~(#>>k~tX8YoYPGOht>We7<vd@#RNsN0mMH4b-$4YZ jAOchn0V;?96-3}Kn|L8%VL8n|00000NkvXXu0mjfRfKPV diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c44b67773..60725698b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3996,7 +3996,7 @@ void HistoryWidget::moveFieldControls() { // (_botStart|_unblock|_joinChannel|_muteUnmute) auto buttonsBottom = bottom - _attachToggle->height(); - auto left = 0; + auto left = st::historySendRight; _attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width(); _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); auto right = st::historySendRight; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index e5fc9ce5e..dabc2afb5 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1047,6 +1047,7 @@ void ComposeControls::init() { } void ComposeControls::orderControls() { + _voiceRecordBar->raise(); _send->raise(); } @@ -1764,7 +1765,7 @@ void ComposeControls::updateControlsGeometry(QSize size) { const auto buttonsTop = size.height() - _attachToggle->height(); - auto left = 0; + auto left = st::historySendRight; _attachToggle->moveToLeft(left, buttonsTop); left += _attachToggle->width(); _field->moveToLeft( @@ -1844,6 +1845,7 @@ void ComposeControls::updateMessagesTTLShown() { updateControlsGeometry(_wrap->size()); } else if (shown && !_ttlInfo) { _ttlInfo = std::make_unique<Controls::TTLButton>(_wrap.get(), peer); + orderControls(); updateControlsVisibility(); updateControlsGeometry(_wrap->size()); } diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 1d4538692..2dec590e4 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -281,22 +281,21 @@ historyContactStatusMinSkip: 16px; historySendIcon: icon {{ "send_control_send", historySendIconFg }}; historySendIconOver: icon {{ "send_control_send", historySendIconFgOver }}; -historySendIconPosition: point(11px, 11px); -historySendSize: size(46px, 46px); +historySendIconPosition: point(10px, 11px); +historySendSize: size(44px, 46px); historyScheduleIcon: icon {{ "send_control_schedule", historyComposeAreaBg }}; -historyScheduleIconPosition: point(8px, 8px); -historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }}; -historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(3px, 7px) }}; +historyScheduleIconPosition: point(7px, 8px); +historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(2px, 7px) }}; +historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(2px, 7px) }}; -historyAttach: IconButton { - width: 46px; +historyAttach: IconButton(defaultIconButton) { + width: 44px; height: 46px; - icon: icon {{ "send_control_attach", historyComposeIconFg }}; - iconOver: icon {{ "send_control_attach", historyComposeIconFgOver }}; - iconPosition: point(11px, 11px); + icon: icon {{ "chat/input_attach", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_attach", historyComposeIconFgOver }}; - rippleAreaPosition: point(3px, 3px); + rippleAreaPosition: point(2px, 3px); rippleAreaSize: 40px; ripple: RippleAnimation(defaultRippleAnimation) { color: windowBgOver; @@ -306,15 +305,13 @@ historyAttach: IconButton { historyAttachEmoji: IconButton(historyAttach) { icon: icon {{ "send_control_emoji", historyComposeIconFg }}; iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; - iconPosition: point(-1px, -1px); } historyMessagesTTL: IconButton(historyAttach) { - icon: icon {{ "chat/send_control_autodelete_1d", historyComposeIconFg }}; - iconOver: icon {{ "chat/send_control_autodelete_1d", historyComposeIconFgOver }}; - iconPosition: point(-1px, -1px); + icon: icon {{ "chat/input_autodelete_1d", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_autodelete_1d", historyComposeIconFgOver }}; } -historyMessagesTTL2Icon: icon {{ "chat/send_control_autodelete_7d", historyComposeIconFg }}; -historyMessagesTTL2IconOver: icon {{ "chat/send_control_autodelete_7d", historyComposeIconFgOver }}; +historyMessagesTTL2Icon: icon {{ "chat/input_autodelete_7d", historyComposeIconFg }}; +historyMessagesTTL2IconOver: icon {{ "chat/input_autodelete_7d", historyComposeIconFgOver }}; historyAttachEmojiFgActive: windowActiveTextFg; historyAttachEmojiActive: icon {{ "send_control_emoji", historyAttachEmojiFgActive }}; historyAttachEmojiTooltipDelta: 4px; @@ -327,28 +324,26 @@ historyEmojiCircleFg: historyComposeIconFg; historyEmojiCircleFgOver: historyComposeIconFgOver; historyEmojiCirclePart: 3.5; historyBotKeyboardShow: IconButton(historyAttach) { - icon: icon {{ "send_control_bot_keyboard", historyComposeIconFg }}; - iconOver: icon {{ "send_control_bot_keyboard", historyComposeIconFgOver }}; + icon: icon {{ "chat/input_bot_keyboard", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_bot_keyboard", historyComposeIconFgOver }}; } historyBotKeyboardHide: IconButton(historyAttach) { - icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; - iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; - iconPosition: point(11px, 16px); + icon: icon {{ "chat/input_bot_keyboard_hide", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_bot_keyboard_hide", historyComposeIconFgOver }}; } historyBotCommandStart: IconButton(historyAttach) { - icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; - iconOver: icon {{ "send_control_bot_command", historyComposeIconFgOver }}; + icon: icon {{ "chat/input_bot_command", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_bot_command", historyComposeIconFgOver }}; } historyScheduledToggle: IconButton(historyAttach) { icon: icon { - { "send_control_scheduled", historyComposeIconFg }, - { "send_control_scheduled_dot", attentionButtonFg } + { "chat/input_scheduled", historyComposeIconFg }, + { "chat/input_scheduled_dot", attentionButtonFg } }; iconOver: icon { - { "send_control_scheduled", historyComposeIconFgOver }, - { "send_control_scheduled_dot", attentionButtonFg } + { "chat/input_scheduled", historyComposeIconFgOver }, + { "chat/input_scheduled_dot", attentionButtonFg } }; - iconPosition: point(-1px, -1px); } historyRecordVoiceFg: historyComposeIconFg; @@ -358,8 +353,8 @@ historyRecordVoiceFgActive: windowBgActive; historyRecordVoiceFgActiveIcon: windowFgActive; historyRecordVoiceShowDuration: 120; historyRecordVoiceDuration: 120; -historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }}; -historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }}; +historyRecordVoice: icon {{ "chat/input_record", historyRecordVoiceFg }}; +historyRecordVoiceOver: icon {{ "chat/input_record", historyRecordVoiceFgOver }}; historyRecordVoiceActive: icon {{ "send_control_record_active", historyRecordVoiceFgActiveIcon }}; historyRecordSendIconPosition: point(2px, 0px); historyRecordVoiceRippleBgActive: lightButtonBgOver; @@ -410,6 +405,7 @@ historyRecordLockRippleMargin: margins(6px, 6px, 6px, 6px); historyRecordDelete: IconButton(historyAttach) { icon: icon {{ "info_media_delete", historyComposeIconFg }}; iconOver: icon {{ "info_media_delete", historyComposeIconFgOver }}; + iconPosition: point(10px, 11px); } historyRecordWaveformRightSkip: 10px; historyRecordWaveformBgMargins: margins(5px, 7px, 5px, 7px); @@ -422,13 +418,13 @@ historyRecordCancelButtonWidth: 100px; historyRecordCancelButtonFg: lightButtonFg; historySilentToggle: IconButton(historyBotKeyboardShow) { - icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; - iconOver: icon {{ "send_control_silent_off", historyComposeIconFgOver }}; + icon: icon {{ "chat/input_silent", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_silent", historyComposeIconFgOver }}; } historySilentToggleCrossLine: CrossLineAnimation { fg: historyComposeIconFg; - icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; + icon: icon {{ "chat/input_silent", historyComposeIconFg }}; startPosition: point(5px, 3px); endPosition: point(21px, 18px); stroke: 2px; From 15e03687f87d1ee8fdb5c954d2592a4f93950785 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Feb 2021 18:46:46 +0400 Subject: [PATCH 355/396] Add auto-delete period edit box. --- Telegram/Resources/langs/lang.strings | 13 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 22 +- .../boxes/peers/edit_peer_info_box.cpp | 110 ++---- .../boxes/peers/edit_peer_info_box.h | 5 - .../boxes/peers/edit_peer_invite_links.cpp | 1 + Telegram/SourceFiles/data/data_peer.cpp | 2 +- .../view/controls/history_view_ttl_button.cpp | 120 +++++-- .../view/controls/history_view_ttl_button.h | 8 + .../view/media_view_playback_controls.cpp | 16 +- .../ui/boxes/auto_delete_settings.cpp | 320 ++++++++++++++++++ .../ui/boxes/auto_delete_settings.h | 22 ++ Telegram/SourceFiles/ui/chat/chat.style | 27 ++ .../SourceFiles/ui/toasts/common_toasts.cpp | 3 + .../SourceFiles/ui/toasts/common_toasts.h | 1 + Telegram/cmake/td_ui.cmake | 2 + 15 files changed, 525 insertions(+), 147 deletions(-) create mode 100644 Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/auto_delete_settings.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7af4eeaf7..2f9016e49 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -982,7 +982,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_messages_ttl_after2" = "After 7 days"; "lng_manage_messages_ttl_about" = "Turning on this setting will make auto-delete messages from this group after the selected period."; "lng_manage_messages_ttl_about_channel" = "Turning on this setting will make auto-delete messages from this channel after the selected period."; + +"lng_ttl_edit_title" = "Auto-delete messages in this chat"; +"lng_ttl_edit_about" = "Automatically delete new messages sent in this chat after a certain period of time."; +"lng_ttl_edit_about_other" = "{user} has set messages to auto-delete in {duration} for both of you."; +"lng_ttl_edit_about_you" = "You have set messages to auto-delete in {duration} for both you and {user}."; +"lng_ttl_edit_about_you_only" = "You have set messages to auto-delete in {duration} only for yourself."; +"lng_ttl_also_checkbox" = "Also delete for {user}"; +"lng_ttl_about_tooltip_on_title" = "Auto-delete On – {duration}"; "lng_ttl_about_tooltip" = "Messages in this chat will auto-delete in {duration}."; +"lng_ttl_about_tooltip_no_longer" = "{user} has set messages to auto-delete in {duration}. You can't make this interval longer."; +"lng_ttl_about_tooltip_no_cancel" = "{user} has set messages to auto-delete in {duration}. You can't cancel this."; +"lng_ttl_about_tooltip_off" = "Auto-delete is now Off."; "lng_ttl_about_duration1" = "24 hours"; "lng_ttl_about_duration2" = "7 days"; @@ -1750,7 +1761,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_saved_to" = "Image was saved to your {downloads} folder"; "lng_mediaview_downloads" = "Downloads"; "lng_mediaview_video_loading" = "Loading - {percent}"; -"lng_mediaview_playback_speed" = "Playback speed"; +"lng_mediaview_playback_speed" = "Playback speed: {speed}"; "lng_mediaview_rotate_video" = "Rotate video"; "lng_theme_preview_title" = "Theme Preview"; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index e560fe0b2..5ae466802 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -37,7 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "data/data_changes.h" #include "base/unixtime.h" -#include "boxes/peers/edit_peer_info_box.h" +#include "history/view/controls/history_view_ttl_button.h" #include "main/main_session.h" #include "mtproto/mtproto_config.h" #include "facades.h" // Ui::showChatsList @@ -657,28 +657,16 @@ void DeleteMessagesBox::prepare() { && (_wipeHistoryPeer->isUser() || _wipeHistoryPeer->isMegagroup() || _wipeHistoryPeer->isChat())) { + _wipeHistoryPeer->updateFull(); _autoDeleteSettings.create( this, tr::lng_edit_auto_delete_settings(tr::now), st::boxLinkButton); - const auto peer = _wipeHistoryPeer; - const auto callback = crl::guard(&peer->session(), [=](TimeId period) { - using Flag = MTPmessages_SetHistoryTTL::Flag; - peer->session().api().request(MTPmessages_SetHistoryTTL( - MTP_flags(peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), - peer->input, - MTP_int(period) - )).done([=](const MTPUpdates &result) { - peer->session().api().applyUpdates(result); - }).fail([=](const RPCError &error) { - }).send(); - }); _autoDeleteSettings->setClickedCallback([=] { getDelegate()->show( Box( - AutoDeleteSettingsBox, - _wipeHistoryPeer->myMessagesTTL(), - callback), + HistoryView::Controls::AutoDeleteSettingsBox, + _wipeHistoryPeer), Ui::LayerOption(0)); }); } @@ -703,7 +691,7 @@ void DeleteMessagesBox::prepare() { fullHeight += st::boxMediumSkip + _revoke->heightNoMargins(); } if (_autoDeleteSettings) { - fullHeight += st::boxMediumSkip + _autoDeleteSettings->height(); + fullHeight += st::boxMediumSkip + _autoDeleteSettings->height() + st::boxLittleSkip; } setDimensions(st::boxWidth, fullHeight); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index c0ae442d4..d5924277f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_changes.h" #include "history/admin_log/history_admin_log_section.h" +#include "history/view/controls/history_view_ttl_button.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "mainwidget.h" @@ -315,7 +316,6 @@ private: std::optional<QString> title; std::optional<QString> description; std::optional<bool> hiddenPreHistory; - std::optional<TimeId> messagesTTL; std::optional<bool> signatures; std::optional<ChannelData*> linkedChat; }; @@ -351,7 +351,6 @@ private: bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; - bool validateMessagesTTL(Saving &to) const; bool validateSignatures(Saving &to) const; void save(); @@ -360,7 +359,6 @@ private: void saveTitle(); void saveDescription(); void saveHistoryVisibility(); - void saveMessagesTTL(); void saveSignatures(); void savePhoto(); void pushSaveStage(FnMut<void()> &&lambda); @@ -377,7 +375,6 @@ private: void migrate(not_null<ChannelData*> channel); std::optional<Privacy> _privacySavedValue; - std::optional<TimeId> _ttlSavedValue; std::optional<ChannelData*> _linkedChatSavedValue; ChannelData *_linkedChatOriginalValue = nullptr; bool _channelHasLocationOriginalValue = false; @@ -889,36 +886,30 @@ void Controller::fillHistoryVisibilityButton() { void Controller::fillSetMessagesTTLButton() { Expects(_controls.buttonsLayout != nullptr); - _ttlSavedValue = _peer->messagesTTL(); + auto label = _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::MessagesTTL + ) | rpl::map([=] { + const auto period = _peer->messagesTTL(); + return !period + ? tr::lng_manage_messages_ttl_never() + : (period == 5) // for debugging + ? rpl::single<QString>("5 seconds") // for debugging + : (period < 3 * 86400) + ? tr::lng_manage_messages_ttl_after1() + : tr::lng_manage_messages_ttl_after2(); + }) | rpl::flatten_latest(); - const auto updateMessagesTTL = - std::make_shared<rpl::event_stream<TimeId>>(); - - const auto boxCallback = crl::guard(this, [=](TimeId value) { - updateMessagesTTL->fire_copy(value); - _ttlSavedValue = value; - }); const auto buttonCallback = [=] { Ui::show( - Box(AutoDeleteSettingsBox, *_ttlSavedValue, boxCallback), + Box(HistoryView::Controls::AutoDeleteSettingsBox, _peer), Ui::LayerOption::KeepOther); }; AddButtonWithText( _controls.buttonsLayout, tr::lng_manage_messages_ttl_title(), - updateMessagesTTL->events( - ) | rpl::map([](TimeId value) { - return !value - ? tr::lng_manage_messages_ttl_never() - : (value == 5) AssertIsDebug() - ? rpl::single<QString>("5 seconds") AssertIsDebug() - : (value < 3 * 86400) - ? tr::lng_manage_messages_ttl_after1() - : tr::lng_manage_messages_ttl_after2(); - }) | rpl::flatten_latest(), + std::move(label), buttonCallback); - - updateMessagesTTL->fire_copy(*_ttlSavedValue); } void Controller::fillManageSection() { @@ -945,9 +936,11 @@ void Controller::fillManageSection() { : chat->canEditPreHistoryHidden(); }(); const auto canSetMessagesTTL = [&] { + // Leave this entry point only for channels for now. + // Groups and users have their entry point in 'Clear History' box. return isChannel - ? channel->canDeleteMessages() - : chat->canDeleteMessages(); + && !channel->isMegagroup() + && channel->canDeleteMessages(); }(); const auto canEditPermissions = [&] { @@ -1184,7 +1177,6 @@ std::optional<Controller::Saving> Controller::validate() const { && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) - && validateMessagesTTL(result) && validateSignatures(result)) { return result; } @@ -1252,14 +1244,6 @@ bool Controller::validateHistoryVisibility(Saving &to) const { return true; } -bool Controller::validateMessagesTTL(Saving &to) const { - if (!_ttlSavedValue) { - return true; - } - to.messagesTTL = _ttlSavedValue; - return true; -} - bool Controller::validateSignatures(Saving &to) const { if (!_signaturesSavedValue.has_value()) { return true; @@ -1281,7 +1265,6 @@ void Controller::save() { pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); - pushSaveStage([=] { saveMessagesTTL(); }); pushSaveStage([=] { saveSignatures(); }); pushSaveStage([=] { savePhoto(); }); continueSave(); @@ -1489,24 +1472,6 @@ void Controller::saveHistoryVisibility() { [=] { cancelSave(); }); } -void Controller::saveMessagesTTL() { - if (!_savingData.messagesTTL - || *_savingData.messagesTTL == _peer->messagesTTL()) { - return continueSave(); - } - using Flag = MTPmessages_SetHistoryTTL::Flag; - _api.request(MTPmessages_SetHistoryTTL( - MTP_flags(_peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), - _peer->input, - MTP_int(*_savingData.messagesTTL) - )).done([=](const MTPUpdates &result) { - _peer->session().api().applyUpdates(result); - continueSave(); - }).fail([=](const RPCError &error) { - cancelSave(); - }).send(); -} - void Controller::togglePreHistoryHidden( not_null<ChannelData*> channel, bool hidden, @@ -1615,41 +1580,6 @@ void Controller::deleteChannel() { } // namespace -void AutoDeleteSettingsBox( - not_null<Ui::GenericBox*> box, - TimeId ttlPeriod, - Fn<void(TimeId)> callback) { - const auto options = { - tr::lng_manage_messages_ttl_never(tr::now), - tr::lng_manage_messages_ttl_after1(tr::now), - tr::lng_manage_messages_ttl_after2(tr::now), - u"5 seconds"_q, AssertIsDebug() - }; - const auto initial = !ttlPeriod - ? 0 - : (ttlPeriod == 5) AssertIsDebug() - ? 3 AssertIsDebug() - : (ttlPeriod < 3 * 86400) - ? 1 - : 2; - const auto callbackFromOption = [=](int option) { - const auto period = !option - ? 0 - : (option == 1) - ? 86400 - : (option == 3) AssertIsDebug() - ? 5 AssertIsDebug() - : 7 * 86400; - callback(period); - }; - SingleChoiceBox(box, { - .title = tr::lng_manage_messages_ttl_title(), - .options = options, - .initialSelection = initial, - .callback = callbackFromOption, - }); -} - EditPeerInfoBox::EditPeerInfoBox( QWidget*, not_null<Window::SessionNavigation*> navigation, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 3a81e6fa1..11ffd36ec 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -23,11 +23,6 @@ class VerticalLayout; class SettingsButton; } // namespace Ui -void AutoDeleteSettingsBox( - not_null<Ui::GenericBox*> box, - TimeId ttlPeriod, - Fn<void(TimeId)> callback); - class EditPeerInfoBox : public Ui::BoxContent { public: EditPeerInfoBox( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 8062305ba..017a1ae84 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -858,6 +858,7 @@ void ManageInviteLinksBox( using namespace Settings; box->setTitle(tr::lng_group_invite_title()); + box->setWidth(st::boxWideWidth); const auto container = box->verticalLayout(); const auto permanentFromList = box->lifetime().make_state< diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 8d3448043..0f43b1100 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -971,7 +971,7 @@ void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { ttl.match([&](const MTPDpeerHistoryTTL &data) { setMessagesTTL( data.vttl_period().v, - data.vttl_period().v, + 0, false); }, [&](const MTPDpeerHistoryTTLPM &data) { setMessagesTTL( diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp index 646dee793..4990955f3 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -14,12 +14,101 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "lang/lang_keys.h" #include "boxes/peers/edit_peer_info_box.h" -#include "ui/layers/generic_box.h" +#include "ui/boxes/auto_delete_settings.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" +#include "ui/text/text_utilities.h" #include "apiwrap.h" #include "styles/style_chat.h" namespace HistoryView::Controls { +namespace { + +constexpr auto kToastDuration = crl::time(3500); + +} // namespace + +void ShowAutoDeleteToast(not_null<PeerData*> peer) { + const auto period = peer->messagesTTL(); + if (!period) { + Ui::Toast::Show(tr::lng_ttl_about_tooltip_off(tr::now)); + return; + } + + const auto duration = (period == 5) + ? u"5 seconds"_q + : (period < 3 * 86400) + ? tr::lng_ttl_about_duration1(tr::now) + : tr::lng_ttl_about_duration2(tr::now); + auto rich = Ui::Text::Bold( + tr::lng_ttl_about_tooltip_on_title(tr::now, lt_duration, duration) + ).append('\n'); + + const auto myPeriod = peer->myMessagesTTL(); + rich.append((period == myPeriod) + ? tr::lng_ttl_about_tooltip(tr::now, lt_duration, duration) + : (myPeriod + ? tr::lng_ttl_about_tooltip_no_longer + : tr::lng_ttl_about_tooltip_no_cancel)( + tr::now, + lt_user, + peer->shortName(), + lt_duration, + duration)); + Ui::ShowMultilineToast({ + .text = std::move(rich), + .duration = kToastDuration, + }); +} + +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + not_null<PeerData*> peer) { + struct State { + TimeId savingPeriod = 0; + bool savingOneSide = false; + mtpRequestId savingRequestId = 0; + QPointer<Ui::GenericBox> weak; + }; + const auto state = std::make_shared<State>(State{ .weak = box.get() }); + auto callback = [=](TimeId period, bool oneSide) { + auto &api = peer->session().api(); + if (state->savingRequestId) { + if (period == state->savingPeriod + && oneSide == state->savingOneSide) { + return; + } + api.request(state->savingRequestId).cancel(); + } + state->savingPeriod = period; + state->savingOneSide = oneSide; + using Flag = MTPmessages_SetHistoryTTL::Flag; + state->savingRequestId = api.request(MTPmessages_SetHistoryTTL( + MTP_flags((oneSide && peer->isUser()) + ? Flag::f_pm_oneside + : Flag(0)), + peer->input, + MTP_int(period) + )).done([=](const MTPUpdates &result) { + peer->session().api().applyUpdates(result); + ShowAutoDeleteToast(peer); + if (const auto strong = state->weak.data()) { + strong->closeBox(); + } + }).fail([=](const RPCError &error) { + state->savingRequestId = 0; + }).send(); + }; + Ui::AutoDeleteSettingsBox( + box, + peer->myMessagesTTL(), + peer->peerMessagesTTL(), + peer->oneSideTTL(), + (peer->isUser() + ? std::make_optional(peer->shortName()) + : std::nullopt), + std::move(callback)); +} TTLButton::TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer) : _button(parent, st::historyMessagesTTL) { @@ -30,37 +119,14 @@ TTLButton::TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer) || (peer->isChannel() && peer->asChannel()->canDeleteMessages()); if (!canEdit) { - const auto duration = (peer->messagesTTL() < 3 * 86400) - ? tr::lng_ttl_about_duration1(tr::now) - : tr::lng_ttl_about_duration2(tr::now); - Ui::Toast::Show(tr::lng_ttl_about_tooltip( - tr::now, - lt_duration, - duration)); + ShowAutoDeleteToast(peer); return; } - const auto callback = crl::guard(&peer->session(), [=]( - TimeId period) { - using Flag = MTPmessages_SetHistoryTTL::Flag; - peer->session().api().request(MTPmessages_SetHistoryTTL( - MTP_flags(peer->oneSideTTL() - ? Flag::f_pm_oneside - : Flag(0)), - peer->input, - MTP_int(period) - )).done([=](const MTPUpdates &result) { - peer->session().api().applyUpdates(result); - }).fail([=](const RPCError &error) { - }).send(); - }); Ui::show( - Box( - AutoDeleteSettingsBox, - peer->myMessagesTTL(), - callback), + Box(AutoDeleteSettingsBox, peer), Ui::LayerOption(0)); }); - peer->session().changes().peerUpdates( + peer->session().changes().peerFlagsValue( peer, Data::PeerUpdate::Flag::MessagesTTL ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h index 37dda0141..f1dc37f22 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h @@ -9,8 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" +namespace Ui { +class GenericBox; +} // namespace Ui + namespace HistoryView::Controls { +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + not_null<PeerData*> peer); + class TTLButton final { public: TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 89f1e8c26..16d06deb5 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -99,18 +99,22 @@ MenuSpeedItem::MenuSpeedItem( return anim::interpolate(kMinSpeed, kMaxSpeed, value) / 100.; }; const auto speedString = [=](float64 value) { - return u"%1: %2x"_q - .arg(tr::lng_mediaview_playback_speed(tr::now)) - .arg(QString::number(computeSpeed(value), 'f', 2)); + return tr::lng_mediaview_playback_speed( + tr::now, + lt_speed, + QString::number(computeSpeed(value), 'f', 2) + 'x'); }; _slider->setAlwaysDisplayMarker(true); _slider->setValue((std::round(startSpeed * 100.) - kMinSpeed) / (kMaxSpeed - kMinSpeed)); - _slider->addDivider( - kSpeedStickedValues[1].first, - st::speedSliderDividerSize); + for (const auto &sticked : kSpeedStickedValues) { + _slider->addDivider(sticked.first, st::speedSliderDividerSize); + } + //_slider->addDivider( + // kSpeedStickedValues[1].first, + // st::speedSliderDividerSize); { const auto goodWidth = st.itemPadding.left() diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp new file mode 100644 index 000000000..c2234ad97 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp @@ -0,0 +1,320 @@ +/* +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 "ui/boxes/auto_delete_settings.h" + +#include "ui/widgets/checkbox.h" +#include "lang/lang_keys.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +object_ptr<Ui::RpWidget> CreateSliderForTTL( + not_null<QWidget*> parent, + std::vector<QString> labels, + int dashedAfterIndex, + int selected, + Fn<void(int)> callback) { + Expects(labels.size() > 1); + Expects(selected >= 0 && selected < labels.size()); + Expects(dashedAfterIndex >= 0 && dashedAfterIndex < labels.size()); + + struct State { + std::vector<int> points; + std::vector<QString> labels; + int selected = 0; + }; + static const auto st = &st::defaultSliderForTTL; + const auto height = st->font->height + st->skip + st->chosenSize; + const auto count = int(labels.size()); + + auto result = object_ptr<Ui::FixedHeightWidget>(parent.get(), height); + const auto raw = result.data(); + const auto slider = Ui::CreateChild<Ui::FixedHeightWidget>( + raw, + st->chosenSize); + slider->setCursor(style::cur_pointer); + slider->move(0, height - slider->height()); + + auto &lifetime = raw->lifetime(); + const auto state = lifetime.make_state<State>(State{ + .labels = std::move(labels), + .selected = selected + }); + state->points.resize(count, 0); + + raw->widthValue( + ) | rpl::start_with_next([=](int width) { + for (auto i = 0; i != count; ++i) { + state->points[i] = (width * i) / (count - 1); + } + slider->resize(width, slider->height()); + }, lifetime); + + raw->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(raw); + + p.setFont(st->font); + for (auto i = 0; i != count; ++i) { + // Label + p.setPen(st->textFg); + const auto &text = state->labels[i]; + const auto textWidth = st->font->width(text); + const auto shift = (i == count - 1) + ? textWidth + : (i > 0) + ? (textWidth / 2) + : 0; + const auto x = state->points[i] - shift; + const auto y = st->font->ascent; + p.drawText(x, y, text); + } + }, lifetime); + + slider->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(slider); + auto hq = PainterHighQualityEnabler(p); + + p.setFont(st->font); + for (auto i = 0; i != count; ++i) { + const auto middle = (st->chosenSize / 2.); + + // Point + const auto size = (i == state->selected) + ? st->chosenSize + : st->pointSize; + const auto pointfg = (i <= state->selected) + ? st->activeFg + : st->inactiveFg; + const auto shift = (i == count - 1) + ? float64(size) + : (i > 0) + ? (size / 2.) + : 0.; + const auto pointx = state->points[i] - shift; + const auto pointy = middle - (size / 2.); + + p.setPen(Qt::NoPen); + p.setBrush(pointfg); + p.drawEllipse(QRectF{ pointx, pointy, size * 1., size * 1. }); + + // Line + if (i + 1 == count) { + break; + } + const auto nextSize = (i + 1 == state->selected) + ? st->chosenSize + : st->pointSize; + const auto nextShift = (i + 1 == count - 1) + ? float64(nextSize) + : (nextSize / 2.); + const auto &linefg = (i + 1 <= state->selected) + ? st->activeFg + : st->inactiveFg; + const auto from = pointx + size + st->stroke * 1.5; + const auto till = state->points[i + 1] - nextShift - st->stroke * 1.5; + + auto pen = linefg->p; + pen.setWidthF(st->stroke); + if (i >= dashedAfterIndex) { + // Try to fill the line with exact number of dash segments. + // UPD Doesn't work so well because it changes when clicking. + //const auto length = till - from; + //const auto offSegmentsCount = int(std::round( + // (length - st->dashOn) / (st->dashOn + st->dashOff))); + //const auto onSegmentsCount = offSegmentsCount + 1; + //const auto idealLength = offSegmentsCount * st->dashOff + // + onSegmentsCount * st->dashOn; + //const auto multiplier = length / float64(idealLength); + + const auto multiplier = 1.; + auto dashPattern = QVector<qreal>{ + st->dashOn * multiplier / st->stroke, + st->dashOff * multiplier / st->stroke + }; + pen.setDashPattern(dashPattern); + } + pen.setCapStyle(Qt::RoundCap); + p.setPen(pen); + + p.setBrush(Qt::NoBrush); + p.drawLine(QPointF(from, middle), QPointF(till, middle)); + } + }, lifetime); + + slider->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::MouseButtonPress) + && (static_cast<QMouseEvent*>(e.get())->button() + == Qt::LeftButton) + && (state->points[1] > 0); + }) | rpl::map([=](not_null<QEvent*> e) { + return rpl::single( + static_cast<QMouseEvent*>(e.get())->pos() + ) | rpl::then(slider->events( + ) | rpl::take_while([=](not_null<QEvent*> e) { + return (e->type() != QEvent::MouseButtonRelease) + || (static_cast<QMouseEvent*>(e.get())->button() + != Qt::LeftButton); + }) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::MouseMove); + }) | rpl::map([=](not_null<QEvent*> e) { + return static_cast<QMouseEvent*>(e.get())->pos(); + })); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](QPoint position) { + state->selected = std::clamp( + (position.x() + (state->points[1] / 2)) / state->points[1], + 0, + count - 1); + slider->update(); + callback(state->selected); + }, lifetime); + + return result; +} + +} // namespace + +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + TimeId ttlMyPeriod, + TimeId ttlPeerPeriod, + bool ttlOneSide, + std::optional<QString> userFirstName, + Fn<void(TimeId, bool)> callback) { + box->setTitle(tr::lng_manage_messages_ttl_title()); + box->setWidth(st::boxWideWidth); + + struct State { + TimeId my = 0; + bool oneSide = false; + rpl::event_stream<rpl::producer<QString>> aboutTexts; + Fn<void()> update; + }; + + const auto state = box->lifetime().make_state<State>(State{ + .my = ttlMyPeriod, + .oneSide = ttlOneSide, + }); + + const auto options = std::vector<QString>{ + u"5 seconds"_q, AssertIsDebug() + tr::lng_manage_messages_ttl_after1(tr::now), + tr::lng_manage_messages_ttl_after2(tr::now), + tr::lng_manage_messages_ttl_never(tr::now), + }; + const auto periodToIndex = [&](TimeId period) { + return !period + ? 3 + : (period == 5) AssertIsDebug() + ? 0 AssertIsDebug() + : (period < 3 * 86400) + ? 1 + : 2; + }; + const auto indexToPeriod = [&](int index) { + return !index + ? 5 AssertIsDebug() + : (index == 1) AssertIsDebug() + ? 86400 + : (index == 2) + ? 7 * 86400 + : 0; + }; + const auto sliderCallback = [=](int index) { + state->my = indexToPeriod(index); + state->update(); + }; + const auto slider = box->addRow( + CreateSliderForTTL( + box, + options | ranges::to_vector, + periodToIndex(ttlPeerPeriod), + periodToIndex(ttlMyPeriod), + sliderCallback), + { + st::boxRowPadding.left(), + 0, + st::boxRowPadding.right(), + st::boxMediumSkip }); + + const auto bothSides = userFirstName + ? box->addRow( + object_ptr<Ui::Checkbox>( + box, + tr::lng_ttl_also_checkbox(tr::now, lt_user, *userFirstName), + !ttlOneSide), + { + st::boxRowPadding.left(), + 0, + st::boxRowPadding.right(), + st::boxMediumSkip }) + : nullptr; + + const auto description = box->addRow( + object_ptr<Ui::DividerLabel>( + box, + object_ptr<Ui::FlatLabel>( + box, + state->aboutTexts.events() | rpl::flatten_latest(), + st::boxDividerLabel), + st::ttlDividerLabelPadding), + style::margins()); + + if (bothSides) { + bothSides->checkedChanges( + ) | rpl::start_with_next([=](bool checked) { + state->oneSide = !checked; + state->update(); + }, bothSides->lifetime()); + } + + state->update = [=] { + const auto his = ttlPeerPeriod; + const auto wrap = [](TimeId period) { + Expects(period > 0); + + return (period == 5) AssertIsDebug() + ? rpl::single(u"5 seconds"_q) AssertIsDebug() + : (period < 3 * 86400) + ? tr::lng_ttl_about_duration1() + : tr::lng_ttl_about_duration2(); + }; + state->aboutTexts.fire(((!state->my && !his) || !userFirstName) + ? tr::lng_ttl_edit_about() + : (his > 0 && (!state->my || his < state->my)) + ? tr::lng_ttl_edit_about_other( + lt_user, + rpl::single(*userFirstName), + lt_duration, + wrap(his)) + : state->oneSide + ? tr::lng_ttl_edit_about_you_only(lt_duration, wrap(state->my)) + : tr::lng_ttl_edit_about_you( + lt_duration, + wrap(state->my), + lt_user, + rpl::single(*userFirstName))); + }; + state->update(); + + box->addButton(tr::lng_settings_save(), [=] { + const auto period = state->my; + const auto oneSide = state->oneSide; + box->closeBox(); + + callback(period, oneSide); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h new file mode 100644 index 000000000..0422fa446 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h @@ -0,0 +1,22 @@ +/* +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 "ui/layers/generic_box.h" + +namespace Ui { + +void AutoDeleteSettingsBox( + not_null<Ui::GenericBox*> box, + TimeId ttlMyPeriod, + TimeId ttlPeerPeriod, + bool ttlOneSide, + std::optional<QString> userFirstName, + Fn<void(TimeId, bool)> callback); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 2dec590e4..dd43299bf 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -850,3 +850,30 @@ videoIcon: icon { { "media_video_play_bg", videoPlayIconBg }, { "media_video_play", videoPlayIconFg, point(12px, 12px) }, }; + +SliderForTTL { + font: font; + textFg: color; + pointSize: pixels; + chosenSize: pixels; + skip: pixels; + stroke: pixels; + activeFg: color; + inactiveFg: color; + dashOn: pixels; + dashOff: pixels; +} + +defaultSliderForTTL: SliderForTTL { + font: normalFont; + textFg: windowSubTextFg; + pointSize: 6px; + chosenSize: 12px; + skip: 8px; + stroke: 2px; + activeFg: mediaPlayerActiveFg; + inactiveFg: mediaPlayerInactiveFg; + dashOn: 8px; + dashOff: 5px; +} +ttlDividerLabelPadding: margins(22px, 10px, 22px, 19px); diff --git a/Telegram/SourceFiles/ui/toasts/common_toasts.cpp b/Telegram/SourceFiles/ui/toasts/common_toasts.cpp index ad68b758c..87de13737 100644 --- a/Telegram/SourceFiles/ui/toasts/common_toasts.cpp +++ b/Telegram/SourceFiles/ui/toasts/common_toasts.cpp @@ -16,6 +16,9 @@ void ShowMultilineToast(MultilineToastArgs &&args) { Ui::Toast::Show(Ui::Toast::Config{ .text = std::move(args.text), .st = &st::defaultMultilineToast, + .durationMs = (args.duration + ? args.duration + : Ui::Toast::kDefaultDuration), .multiline = true, }); } diff --git a/Telegram/SourceFiles/ui/toasts/common_toasts.h b/Telegram/SourceFiles/ui/toasts/common_toasts.h index ea2fe9282..820e486ff 100644 --- a/Telegram/SourceFiles/ui/toasts/common_toasts.h +++ b/Telegram/SourceFiles/ui/toasts/common_toasts.h @@ -13,6 +13,7 @@ namespace Ui { struct MultilineToastArgs { TextWithEntities text; + crl::time duration = 0; }; void ShowMultilineToast(MultilineToastArgs &&args); diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 0c9020a3a..73528d4dc 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -64,6 +64,8 @@ PRIVATE platform/mac/file_bookmark_mac.mm platform/platform_file_bookmark.h + ui/boxes/auto_delete_settings.cpp + ui/boxes/auto_delete_settings.h ui/boxes/calendar_box.cpp ui/boxes/calendar_box.h ui/boxes/choose_date_time.cpp From 18d62c070d2ae09054e9d92f2b1770ab91cea62e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Feb 2021 20:10:15 +0400 Subject: [PATCH 356/396] Show auto-delete info in message context menu. --- Telegram/Resources/langs/lang.strings | 1 + .../SourceFiles/calls/calls_group_members.cpp | 2 +- .../history/history_inner_widget.cpp | 22 +- .../view/history_view_context_menu.cpp | 21 +- .../info/media/info_media_list_widget.cpp | 11 +- Telegram/SourceFiles/ui/chat/chat.style | 3 + .../delete_message_context_action.cpp | 253 ++++++++++++++++++ .../controls/delete_message_context_action.h | 26 ++ Telegram/cmake/td_ui.cmake | 2 + 9 files changed, 322 insertions(+), 19 deletions(-) create mode 100644 Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp create mode 100644 Telegram/SourceFiles/ui/controls/delete_message_context_action.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2f9016e49..e1c0d72d2 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1613,6 +1613,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_send_now_msg" = "Send now"; "lng_context_reschedule" = "Reschedule"; "lng_context_delete_msg" = "Delete Message"; +"lng_context_auto_delete_in" = "auto-delete in {duration}"; "lng_context_select_msg" = "Select Message"; "lng_context_report_msg" = "Report Message"; "lng_context_pin_msg" = "Pin Message"; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 7229dad6a..3f2ba724c 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -1414,7 +1414,7 @@ void MembersController::addMuteActionsToContextMenu( : rpl::never<Group::ParticipantState>() | rpl::type_erased(); auto volumeItem = base::make_unique_q<MenuVolumeItem>( - menu, + menu->menu(), st::groupCallPopupMenu.menu, otherParticipantStateValue, row->volume(), diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 495376acc..fb88bd32e 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/toast/toast.h" #include "ui/text/text_options.h" +#include "ui/controls/delete_message_context_action.h" #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" #include "ui/inactive_press.h" @@ -1690,9 +1691,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } if (item->canDelete()) { - _menu->addAction(tr::lng_context_delete_msg(tr::now), [=] { - deleteItem(itemId); - }); + _menu->addAction(Ui::DeleteMessageContextAction( + _menu->menu(), + [=] { deleteItem(itemId); }, + item->ttlDestroyAt(), + [=] { _menu = nullptr; })); } if (!blockSender && item->suggestReport()) { _menu->addAction(tr::lng_context_report_msg(tr::now), [=] { @@ -1822,9 +1825,18 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } if (canDelete) { - _menu->addAction((msg && msg->uploading()) ? tr::lng_context_cancel_upload(tr::now) : tr::lng_context_delete_msg(tr::now), [=] { + const auto callback = [=] { deleteAsGroup(itemId); - }); + }; + if (msg && msg->uploading()) { + _menu->addAction(tr::lng_context_cancel_upload(tr::now), callback); + } else { + _menu->addAction(Ui::DeleteMessageContextAction( + _menu->menu(), + callback, + item->ttlDestroyAt(), + [=] { _menu = nullptr; })); + } } if (!canBlockSender && canReport) { _menu->addAction(tr::lng_context_report_msg(tr::now), [=] { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 73ca05504..bf2c7743a 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/image/image.h" #include "ui/toast/toast.h" +#include "ui/controls/delete_message_context_action.h" #include "ui/ui_utility.h" #include "chat_helpers/send_context_menu.h" #include "boxes/confirm_box.h" @@ -740,15 +741,19 @@ bool AddDeleteMessageAction( Ui::show(Box<DeleteMessagesBox>(item, suggestModerateActions)); } }); - const auto text = [&] { - if (const auto message = item->toHistoryMessage()) { - if (message->uploading()) { - return tr::lng_context_cancel_upload; - } + if (const auto message = item->toHistoryMessage()) { + if (message->uploading()) { + menu->addAction( + tr::lng_context_cancel_upload(tr::now), + callback); + return true; } - return tr::lng_context_delete_msg; - }()(tr::now); - menu->addAction(text, callback); + } + menu->addAction(Ui::DeleteMessageContextAction( + menu->menu(), + callback, + item->ttlDestroyAt(), + [=] { delete menu; })); return true; } diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index be74d7251..0670b44b7 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "ui/widgets/popup_menu.h" +#include "ui/controls/delete_message_context_action.h" #include "ui/ui_utility.h" #include "ui/inactive_press.h" #include "lang/lang_keys.h" @@ -1414,11 +1415,11 @@ void ListWidget::showContextMenu( })); } if (item->canDelete()) { - _contextMenu->addAction( - tr::lng_context_delete_msg(tr::now), - crl::guard(this, [this, universalId] { - deleteItem(universalId); - })); + _contextMenu->addAction(Ui::DeleteMessageContextAction( + _contextMenu->menu(), + [=] { deleteItem(universalId); }, + item->ttlDestroyAt(), + [=] { _contextMenu = nullptr; })); } } _contextMenu->addAction( diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index dd43299bf..7b7e0475f 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -877,3 +877,6 @@ defaultSliderForTTL: SliderForTTL { dashOff: 5px; } ttlDividerLabelPadding: margins(22px, 10px, 22px, 19px); + +ttlItemPadding: margins(0px, 4px, 0px, 5px); +ttlItemTimerFont: font(11px); diff --git a/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp new file mode 100644 index 000000000..bda1c0436 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp @@ -0,0 +1,253 @@ +/* +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 "ui/controls/delete_message_context_action.h" + +#include "ui/widgets/menu/menu_action.h" +#include "ui/effects/ripple_animation.h" +#include "lang/lang_keys.h" +#include "base/call_delayed.h" +#include "base/unixtime.h" +#include "base/timer.h" +#include "styles/style_chat.h" + +namespace Ui { +namespace { + +class ActionWithTimer final : public Menu::ItemBase { +public: + ActionWithTimer( + not_null<RpWidget*> parent, + const style::Menu &st, + TimeId destroyAt, + Fn<void()> callback, + Fn<void()> destroyByTimerCallback); + + bool isEnabled() const override; + not_null<QAction*> action() const override; + + void handleKeyPress(not_null<QKeyEvent*> e) override; + +protected: + QPoint prepareRippleStartPosition() const override; + QImage prepareRippleMask() const override; + + int contentHeight() const override; + +private: + void prepare(); + void refreshAutoDeleteText(); + void paint(Painter &p); + + const not_null<QAction*> _dummyAction; + const style::Menu &_st; + const TimeId _destroyAt = 0; + const Fn<void()> _destroyByTimerCallback; + const crl::time _startedAt = 0; + crl::time _lastCheckAt = 0; + base::Timer _refreshTimer; + + Text::String _text; + int _textWidth = 0; + QString _autoDeleteText; + const int _height; + +}; + +TextParseOptions MenuTextOptions = { + TextParseLinks | TextParseRichText, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir +}; + +ActionWithTimer::ActionWithTimer( + not_null<RpWidget*> parent, + const style::Menu &st, + TimeId destroyAt, + Fn<void()> callback, + Fn<void()> destroyByTimerCallback) +: ItemBase(parent, st) +, _dummyAction(new QAction(parent)) +, _st(st) +, _destroyAt(destroyAt) +, _destroyByTimerCallback(destroyByTimerCallback) +, _startedAt(crl::now()) +, _refreshTimer([=] { refreshAutoDeleteText(); }) +, _height(st::ttlItemPadding.top() + + _st.itemStyle.font->height + + st::ttlItemTimerFont->height + + st::ttlItemPadding.bottom()) { + setAcceptBoth(true); + initResizeHook(parent->sizeValue()); + setClickedCallback(std::move(callback)); + + paintRequest( + ) | rpl::start_with_next([=] { + Painter p(this); + paint(p); + }, lifetime()); + + enableMouseSelecting(); + prepare(); +} + +void ActionWithTimer::paint(Painter &p) { + const auto selected = isSelected(); + if (selected && _st.itemBgOver->c.alpha() < 255) { + p.fillRect(0, 0, width(), _height, _st.itemBg); + } + p.fillRect(0, 0, width(), _height, selected ? _st.itemBgOver : _st.itemBg); + if (isEnabled()) { + paintRipple(p, 0, 0); + } + p.setPen(selected ? _st.itemFgOver : _st.itemFg); + _text.drawLeftElided( + p, + _st.itemPadding.left(), + st::ttlItemPadding.top(), + _textWidth, + width()); + + p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut); + p.drawTextLeft( + _st.itemPadding.left(), + st::ttlItemPadding.top() + _st.itemStyle.font->height, + width(), + _autoDeleteText); +} + +void ActionWithTimer::refreshAutoDeleteText() { + const auto now = base::unixtime::now(); + const auto left = (_destroyAt > now) ? (_destroyAt - now) : 0; + const auto text = [&] { + const auto duration = (left >= 86400) + ? tr::lng_group_call_duration_days( + tr::now, + lt_count, + ((left + 43200) / 86400)) + : (left >= 3600) + ? QString("%1:%2:%3" + ).arg(left / 3600 + ).arg((left % 3600) / 60, 2, 10, QChar('0') + ).arg(left % 60, 2, 10, QChar('0')) + : QString("%1:%2" + ).arg(left / 60 + ).arg(left % 60, 2, 10, QChar('0')); + return tr::lng_context_auto_delete_in( + tr::now, + lt_duration, + duration); + }(); + if (_autoDeleteText != text) { + _autoDeleteText = text; + update(); + } + + if (!left) { + base::call_delayed(crl::time(100), this, _destroyByTimerCallback); + return; + } + const auto nextCall = (left >= 86400) + ? ((left % 43200) + 1) * crl::time(1000) + : crl::time(500) - ((crl::now() - _startedAt) % 500); + _refreshTimer.callOnce(nextCall); +} + +void ActionWithTimer::prepare() { + refreshAutoDeleteText(); + + _text.setMarkedText( + _st.itemStyle, + { tr::lng_context_delete_msg(tr::now) }, + MenuTextOptions); + const auto textWidth = _text.maxWidth(); + const auto &padding = _st.itemPadding; + + const auto goodWidth = padding.left() + + textWidth + + padding.right(); + const auto ttlMaxWidth = [&](const QString &duration) { + return padding.left() + + st::ttlItemTimerFont->width(tr::lng_context_auto_delete_in( + tr::now, + lt_duration, + duration)) + + padding.right(); + }; + const auto maxWidth1 = ttlMaxWidth("23:59:59"); + const auto maxWidth2 = ttlMaxWidth(tr::lng_group_call_duration_days( + tr::now, + lt_count, + 7)); + + const auto w = std::clamp( + std::max({ goodWidth, maxWidth1, maxWidth2 }), + _st.widthMin, + _st.widthMax); + _textWidth = w - (goodWidth - textWidth); + setMinWidth(w); + update(); +} + +bool ActionWithTimer::isEnabled() const { + return true; +} + +not_null<QAction*> ActionWithTimer::action() const { + return _dummyAction; +} + +QPoint ActionWithTimer::prepareRippleStartPosition() const { + return mapFromGlobal(QCursor::pos()); +} + +QImage ActionWithTimer::prepareRippleMask() const { + return Ui::RippleAnimation::rectMask(size()); +} + +int ActionWithTimer::contentHeight() const { + return _height; +} + +void ActionWithTimer::handleKeyPress(not_null<QKeyEvent*> e) { + if (!isSelected()) { + return; + } + const auto key = e->key(); + if (key == Qt::Key_Enter || key == Qt::Key_Return) { + setClicked(Menu::TriggeredSource::Keyboard); + } +} + +} // namespace + +base::unique_qptr<Menu::ItemBase> DeleteMessageContextAction( + not_null<Menu::Menu*> menu, + Fn<void()> callback, + TimeId destroyAt, + Fn<void()> destroyByTimerCallback) { + if (destroyAt <= 0) { + return base::make_unique_q<Menu::Action>( + menu, + menu->st(), + Menu::CreateAction( + menu, + tr::lng_context_delete_msg(tr::now), + std::move(callback)), + nullptr, + nullptr); + } + return base::make_unique_q<ActionWithTimer>( + menu, + menu->st(), + destroyAt, + std::move(callback), + std::move(destroyByTimerCallback)); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/delete_message_context_action.h b/Telegram/SourceFiles/ui/controls/delete_message_context_action.h new file mode 100644 index 000000000..3ed2c4f0b --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/delete_message_context_action.h @@ -0,0 +1,26 @@ +/* +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/unique_qptr.h" + +namespace Ui { +namespace Menu { +class Menu; +class ItemBase; +} // namespace Menu + +class PopupMenu; + +[[nodiscard]] base::unique_qptr<Menu::ItemBase> DeleteMessageContextAction( + not_null<Menu::Menu*> menu, + Fn<void()> callback, + TimeId destroyAt, + Fn<void()> destroyByTimerCallback); + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 73528d4dc..60acbe619 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -94,6 +94,8 @@ PRIVATE ui/chat/message_bar.h ui/chat/pinned_bar.cpp ui/chat/pinned_bar.h + ui/controls/delete_message_context_action.cpp + ui/controls/delete_message_context_action.h ui/controls/emoji_button.cpp ui/controls/emoji_button.h ui/controls/invite_link_buttons.cpp From 73ae29ae25871e41f648ef68afd61e623dec3c4c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 14:59:51 +0400 Subject: [PATCH 357/396] Update API scheme. --- Telegram/Resources/tl/api.tl | 20 ++++++++++--------- Telegram/SourceFiles/boxes/report_box.cpp | 13 ++++++++---- Telegram/SourceFiles/data/data_histories.cpp | 1 + .../data/data_scheduled_messages.cpp | 3 ++- .../admin_log/history_admin_log_item.cpp | 6 ++++-- Telegram/SourceFiles/history/history_item.cpp | 3 ++- Telegram/SourceFiles/main/main_app_config.cpp | 1 + 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 45c0aa92c..99c92a4fb 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -124,11 +124,11 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; chatFull#e22542a0 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl:flags.14?PeerHistoryTTL = ChatFull; -channelFull#fc048a06 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl:flags.24?PeerHistoryTTL = ChatFull; +channelFull#7c62b528 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl:flags.24?PeerHistoryTTL pending_suggestions:flags.25?Vector<string> = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -142,7 +142,7 @@ chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message; -messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message; +messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia; @@ -226,7 +226,7 @@ inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonChildAbuse#adf44ee3 = ReportReason; -inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonOther#c1e4a2b1 = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; inputReportReasonFake#f5ddd6e7 = ReportReason; @@ -1233,7 +1233,7 @@ messages.exportedChatInviteReplaced#222600ef invite:ExportedChatInvite new_invit messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImporter> users:Vector<User> = messages.ChatInviteImporters; -chatAdminWithInvites#deaa220c admin_id:int invites_count:int = ChatAdminWithInvites; +chatAdminWithInvites#dfd2330f admin_id:int invites_count:int revoked_invites_count:int = ChatAdminWithInvites; messages.chatAdminsWithInvites#b69b72d7 admins:Vector<ChatAdminWithInvites> users:Vector<User> = messages.ChatAdminsWithInvites; @@ -1274,7 +1274,7 @@ account.resetNotifySettings#db7e1747 = Bool; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.updateStatus#6628562c offline:Bool = Bool; account.getWallPapers#aabb1763 hash:int = account.WallPapers; -account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; +account.reportPeer#c5ba3d86 peer:InputPeer reason:ReportReason message:string = Bool; account.checkUsername#2714d86c username:string = Bool; account.updateUsername#3e0bdd7c username:string = User; account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules; @@ -1333,6 +1333,7 @@ account.getContentSettings#8b9b4dae = account.ContentSettings; account.getMultiWallPapers#65ad71dc wallpapers:Vector<InputWallPaper> = Vector<WallPaper>; account.getGlobalPrivacySettings#eb2b4cf6 = GlobalPrivacySettings; account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = GlobalPrivacySettings; +account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1373,7 +1374,7 @@ messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; -messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool; +messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool; messages.getChats#3c6aa187 id:Vector<int> = messages.Chats; messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; messages.editChatTitle#dc452855 chat_id:int title:string = Updates; @@ -1538,7 +1539,7 @@ help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo; help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo; help.getPromoData#c0977421 = help.PromoData; help.hidePromoData#1e251c95 peer:InputPeer = Bool; -help.dismissSuggestion#77fa99f suggestion:string = Bool; +help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool; help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; @@ -1567,7 +1568,7 @@ channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_right channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool; -channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; +channels.deleteHistory#bda41f3f flags:# for_everyone:flags.0?true channel:InputChannel max_id:int = Bool; channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; channels.getLeftChannels#8341ecc0 offset:int = messages.Chats; channels.getGroupsForDiscussion#f5dad378 = messages.Chats; @@ -1576,6 +1577,7 @@ channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:In channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool; channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; channels.getInactiveChannels#11e831ee = messages.InactiveChats; +channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 41b3d24f3..81487ac66 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -143,7 +143,10 @@ void ReportBox::report() { return; } - if (_reasonOtherText && _reasonOtherText->getLastText().trimmed().isEmpty()) { + const auto text = _reasonOtherText + ? _reasonOtherText->getLastText().trimmed() + : QString(); + if (_reasonOtherText && text.isEmpty()) { _reasonOtherText->showError(); return; } @@ -155,7 +158,7 @@ void ReportBox::report() { case Reason::Violence: return MTP_inputReportReasonViolence(); case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse(); case Reason::Pornography: return MTP_inputReportReasonPornography(); - case Reason::Other: return MTP_inputReportReasonOther(MTP_string(_reasonOtherText->getLastText())); + case Reason::Other: return MTP_inputReportReasonOther(); } Unexpected("Bad reason group value."); }(); @@ -167,7 +170,8 @@ void ReportBox::report() { _requestId = _api.request(MTPmessages_Report( _peer->input, MTP_vector<MTPint>(ids), - reason + reason, + MTP_string(text) )).done([=](const MTPBool &result) { reportDone(result); }).fail([=](const RPCError &error) { @@ -176,7 +180,8 @@ void ReportBox::report() { } else { _requestId = _api.request(MTPaccount_ReportPeer( _peer->input, - reason + reason, + MTP_string(text) )).done([=](const MTPBool &result) { reportDone(result); }).fail([=](const RPCError &error) { diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 268d7a8d8..5c7ccff83 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -607,6 +607,7 @@ void Histories::deleteAllMessages( }).send(); } else if (channel) { return session().api().request(MTPchannels_DeleteHistory( + MTP_flags(0), channel->inputChannel, MTP_int(deleteTillId) )).done([=](const MTPBool &result) { diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 75ebf0027..842e1ba71 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -48,7 +48,8 @@ MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) { data.vpeer_id(), data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(), data.vdate(), - data.vaction()); + data.vaction(), + MTP_int(data.vttl_period().value_or_empty())); }, [&](const MTPDmessage &data) { return MTP_message( MTP_flags(data.vflags().v | MTPDmessage::Flag::f_from_scheduled), diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 02b820bdb..f5e7f32c7 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -68,7 +68,8 @@ MTPMessage PrepareLogMessage( }, [&](const MTPDmessageService &data) { const auto removeFlags = MTPDmessageService::Flag::f_out | MTPDmessageService::Flag::f_post - | MTPDmessageService::Flag::f_reply_to; + | MTPDmessageService::Flag::f_reply_to + | MTPDmessageService::Flag::f_ttl_period; return MTP_messageService( MTP_flags(data.vflags().v & ~removeFlags), MTP_int(newId), @@ -76,7 +77,8 @@ MTPMessage PrepareLogMessage( data.vpeer_id(), MTPMessageReplyHeader(), MTP_int(newDate), - data.vaction()); + data.vaction(), + MTPint()); // ttl_period }, [&](const MTPDmessage &data) { const auto removeFlags = MTPDmessage::Flag::f_out | MTPDmessage::Flag::f_post diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 5d887c9dc..e823d29f1 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -487,7 +487,8 @@ void HistoryItem::applyEditionToHistoryCleared() { peerToMTP(history()->peer->id), MTPMessageReplyHeader(), MTP_int(date()), - MTP_messageActionHistoryClear() + MTP_messageActionHistoryClear(), + MTPint() // ttl_period ).c_messageService()); } diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp index 9311eb6a0..00f5082b9 100644 --- a/Telegram/SourceFiles/main/main_app_config.cpp +++ b/Telegram/SourceFiles/main/main_app_config.cpp @@ -182,6 +182,7 @@ void AppConfig::dismissSuggestion(const QString &key) { return; } _api->request(MTPhelp_DismissSuggestion( + MTP_inputPeerEmpty(), MTP_string(key) )).send(); } From 6511d0dfcfd7d77be191b3ceb524bf316428125d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 16:40:36 +0400 Subject: [PATCH 358/396] Update more icons in the compose controls. --- Telegram/Resources/icons/chat/input_edit.png | Bin 0 -> 438 bytes .../Resources/icons/chat/input_edit@2x.png | Bin 0 -> 785 bytes .../Resources/icons/chat/input_edit@3x.png | Bin 0 -> 1267 bytes .../Resources/icons/chat/input_forward.png | Bin 0 -> 402 bytes .../Resources/icons/chat/input_forward@2x.png | Bin 0 -> 858 bytes .../Resources/icons/chat/input_forward@3x.png | Bin 0 -> 1305 bytes .../icons/chat/input_record_filled.png | Bin 0 -> 618 bytes .../icons/chat/input_record_filled@2x.png | Bin 0 -> 1258 bytes .../icons/chat/input_record_filled@3x.png | Bin 0 -> 1944 bytes Telegram/Resources/icons/chat/input_reply.png | Bin 0 -> 593 bytes .../Resources/icons/chat/input_reply@2x.png | Bin 0 -> 1257 bytes .../Resources/icons/chat/input_reply@3x.png | Bin 0 -> 1894 bytes Telegram/Resources/icons/chat/input_save.png | Bin 0 -> 597 bytes .../Resources/icons/chat/input_save@2x.png | Bin 0 -> 1077 bytes .../Resources/icons/chat/input_save@3x.png | Bin 0 -> 1733 bytes .../input_schedule.png} | Bin .../input_schedule@2x.png} | Bin .../input_schedule@3x.png} | Bin .../input_send.png} | Bin .../input_send@2x.png} | Bin .../input_send@3x.png} | Bin Telegram/Resources/icons/chat/input_smile.png | Bin 787 -> 0 bytes .../Resources/icons/chat/input_smile@2x.png | Bin 1571 -> 0 bytes .../Resources/icons/chat/input_smile@3x.png | Bin 2569 -> 0 bytes .../Resources/icons/chat/input_smile_face.png | Bin 0 -> 453 bytes .../icons/chat/input_smile_face@2x.png | Bin 0 -> 894 bytes .../icons/chat/input_smile_face@3x.png | Bin 0 -> 1356 bytes .../Resources/icons/history_action_edit.png | Bin 445 -> 0 bytes .../icons/history_action_edit@2x.png | Bin 1011 -> 0 bytes .../icons/history_action_edit@3x.png | Bin 584 -> 0 bytes .../icons/history_action_forward.png | Bin 311 -> 0 bytes .../icons/history_action_forward@2x.png | Bin 600 -> 0 bytes .../icons/history_action_forward@3x.png | Bin 504 -> 0 bytes .../Resources/icons/history_action_reply.png | Bin 446 -> 0 bytes .../icons/history_action_reply@2x.png | Bin 864 -> 0 bytes .../icons/history_action_reply@3x.png | Bin 994 -> 0 bytes .../Resources/icons/send_control_emoji.png | Bin 251 -> 0 bytes .../Resources/icons/send_control_emoji@2x.png | Bin 528 -> 0 bytes .../Resources/icons/send_control_emoji@3x.png | Bin 722 -> 0 bytes .../icons/send_control_record_active.png | Bin 869 -> 0 bytes .../icons/send_control_record_active@2x.png | Bin 1396 -> 0 bytes .../icons/send_control_record_active@3x.png | Bin 1877 -> 0 bytes .../Resources/icons/send_control_save.png | Bin 358 -> 0 bytes .../Resources/icons/send_control_save@2x.png | Bin 687 -> 0 bytes .../Resources/icons/send_control_save@3x.png | Bin 933 -> 0 bytes .../chat_helpers/chat_helpers.style | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 30 +++++++++--------- .../SourceFiles/ui/controls/send_button.cpp | 2 +- 48 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 Telegram/Resources/icons/chat/input_edit.png create mode 100644 Telegram/Resources/icons/chat/input_edit@2x.png create mode 100644 Telegram/Resources/icons/chat/input_edit@3x.png create mode 100644 Telegram/Resources/icons/chat/input_forward.png create mode 100644 Telegram/Resources/icons/chat/input_forward@2x.png create mode 100644 Telegram/Resources/icons/chat/input_forward@3x.png create mode 100644 Telegram/Resources/icons/chat/input_record_filled.png create mode 100644 Telegram/Resources/icons/chat/input_record_filled@2x.png create mode 100644 Telegram/Resources/icons/chat/input_record_filled@3x.png create mode 100644 Telegram/Resources/icons/chat/input_reply.png create mode 100644 Telegram/Resources/icons/chat/input_reply@2x.png create mode 100644 Telegram/Resources/icons/chat/input_reply@3x.png create mode 100644 Telegram/Resources/icons/chat/input_save.png create mode 100644 Telegram/Resources/icons/chat/input_save@2x.png create mode 100644 Telegram/Resources/icons/chat/input_save@3x.png rename Telegram/Resources/icons/{send_control_schedule.png => chat/input_schedule.png} (100%) rename Telegram/Resources/icons/{send_control_schedule@2x.png => chat/input_schedule@2x.png} (100%) rename Telegram/Resources/icons/{send_control_schedule@3x.png => chat/input_schedule@3x.png} (100%) rename Telegram/Resources/icons/{send_control_send.png => chat/input_send.png} (100%) rename Telegram/Resources/icons/{send_control_send@2x.png => chat/input_send@2x.png} (100%) rename Telegram/Resources/icons/{send_control_send@3x.png => chat/input_send@3x.png} (100%) delete mode 100644 Telegram/Resources/icons/chat/input_smile.png delete mode 100644 Telegram/Resources/icons/chat/input_smile@2x.png delete mode 100644 Telegram/Resources/icons/chat/input_smile@3x.png create mode 100644 Telegram/Resources/icons/chat/input_smile_face.png create mode 100644 Telegram/Resources/icons/chat/input_smile_face@2x.png create mode 100644 Telegram/Resources/icons/chat/input_smile_face@3x.png delete mode 100644 Telegram/Resources/icons/history_action_edit.png delete mode 100644 Telegram/Resources/icons/history_action_edit@2x.png delete mode 100644 Telegram/Resources/icons/history_action_edit@3x.png delete mode 100644 Telegram/Resources/icons/history_action_forward.png delete mode 100644 Telegram/Resources/icons/history_action_forward@2x.png delete mode 100644 Telegram/Resources/icons/history_action_forward@3x.png delete mode 100644 Telegram/Resources/icons/history_action_reply.png delete mode 100644 Telegram/Resources/icons/history_action_reply@2x.png delete mode 100644 Telegram/Resources/icons/history_action_reply@3x.png delete mode 100644 Telegram/Resources/icons/send_control_emoji.png delete mode 100644 Telegram/Resources/icons/send_control_emoji@2x.png delete mode 100644 Telegram/Resources/icons/send_control_emoji@3x.png delete mode 100644 Telegram/Resources/icons/send_control_record_active.png delete mode 100644 Telegram/Resources/icons/send_control_record_active@2x.png delete mode 100644 Telegram/Resources/icons/send_control_record_active@3x.png delete mode 100644 Telegram/Resources/icons/send_control_save.png delete mode 100644 Telegram/Resources/icons/send_control_save@2x.png delete mode 100644 Telegram/Resources/icons/send_control_save@3x.png diff --git a/Telegram/Resources/icons/chat/input_edit.png b/Telegram/Resources/icons/chat/input_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..28639bab229e3d35050caf1b4d5a5a649589a9d6 GIT binary patch literal 438 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{Ou^H|F(iWX z?KDTO76TsF*D`*4Tjx0++?$=h!o6Uj^h=YZC0xc6mR!{n(pc;9;`Dz-6XRnM+b8w< zZ%SdG!Q>?y#2V_jlIc>-TgT9VRShQgRs3SEiN^{}YVU83e7I2{)zV_OPWMrXw#H(A z84HbFnNlg%<;zt>jcgP&!b7j}T`IlH$=;qh>mI91^!D7%rLpk}*GspiM($(pDA1V6 z&sk>tV8ILnvHK5eJhsfPoG$Z0T*kpD_v>v3HVH|;w2#aMH3#Yo4CXwv-~9YtWl6Vu z%VO;x1~Re*^U~)iOt;;zcHdc(ZvBws?k*O)nhdW0Vc21F{%@D_{7kNGxx4w}8(4NL zAHMlUf;G?Ec+vh9OH;KMXlaBx%vx`|g3C*E!ZY>z&GGA}uP%4k*pd$lGf!7Pmvv4F FO#qI9m5~4d literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_edit@2x.png b/Telegram/Resources/icons/chat/input_edit@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7bbf2eda83ff066ac915611a57103a1ef60556 GIT binary patch literal 785 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z*Oz&;uuoF z`1Y3Xx+Vhw_76?p1J*w}bo)h^`8`Mehn_vv`3v0g7jRFnHQOjyW5IkT`{jp!DlDmK zO#kMH{W;NN_-$uWrsjn0Yn*R-Gc+6uV^CmdWja(;LNV1V+3S|J{r1e;Z_}>5-YUt% z<}@ws>AUvQt*?d8&+}7{j@fp5ZLQL=D&N0{K2H!7EM*ESop=26ZI2Es#`MiI%ayhZ z3Y0SKs;u4`HL=2O$ITq4bL~|OcQ?%SQB&=)>iGV9Z^-B4;=ZyCb?2&W<jSn(PIIt0 z^0zMj)c^7Zx%2!d)jFPkzS(#D@qg|go!40mndVg|Uy?m^|HCT<w?p3I6AmvHc>cNa zMPl?Ep-Zn1?5Yq+Ix=ypSVHg~rAN<o{Zx*WFx-A*d8DH_uJFe)V|J~h-pm1pao-zG zecZ+RhB2<Z!S>*5wt%MR`}UQ8otPK+N=E%%uFv@okIEAGKR#moCZf-m(Esre^EVlN zn+FdIKFt3voh!WOmerb7TZ_^|lUCREUWq$-{PD$I=l&KQTKU$W`@%!s_QO{utoZE7 z9-ug{*_Sy$Gp^#m>8FSMn!AAJ=!tO)1y}+_Y|g(e+PUTW>#yQVb{`EaJ6Mpg)U^6- zL2=>-)zfz#-a8%q<*&=j#VZ=TdQV(FHvf#LoE?Y5y^b>s3MUV<GoFy1D9NBw`9p!B zQrU>PL4{wQMIqlMjbXx*hs_L6dQb2$OtPsFXxJk-lhI*PJ3rF|b0uR2k53;Q89uqC durXj7X8-fzpEIkAeuaUOqo=E%%Q~loCIB*nMT7tV literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_edit@3x.png b/Telegram/Resources/icons/chat/input_edit@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..7da15659a2f53f7ddc98349b8a8de6821d523291 GIT binary patch literal 1267 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ASip>6gA{T+3Vp}Gz!K}};uuoF z`1Y>1-(v@XV}<E%HhzaDc^7aTVS9HlD@>t~Sz!Bvy$P(3G*mjc8zL(1DCboASm#ZT zkA1p-sYc)1vR(V;-}ZA75%Zk1<Yw>545s}(*IXm-b28-o31E;&c+4rG-!I6s;Ui1W z^y3_CH!7Hpola(9&aq*9{M4|C(QIDB$4?Rm8KnIWR8;mPH1w(`?6Gk(IB-nZAWlx1 z3fftwZQr?b=A3!+)J{J&s;sT`_4D%++U|4sNnK0p?O8nAwr#s~`}XZER<ZH%%O#86 z+-%(aj$!@bvl~Sfm}A)Qw5(>4Opq_oSjUvou=+r5p|U&M4z4}>_s^fbbzlF7Sv&=z zH*D?f?DBeNn|E;jEDt*~|IM)w&j&AGYX1E7%j?^>Z(7;2<lnt{qarOWee&_+<j=>H za|D0?-f{5v^L0BVyS`U0QLfw?Rr!5-W#8VKFX}S84{SaEyx{%2cbC3@|6WpFetq{^ zmY;9mp53kA;BIrM_|BhOH`PDgyeWA=Smvzji_klE6XoUPg0}zHe64N!{g2<p(jB!5 z)_2~%eH&2_YyXV(PRngpOQ$=PjO~xSCMX}B%U=+E$LxgQ<8#arUD8c81qA}PO|LJU zmjB;YW4(dleNl6kokAHoJ8!bS;|{mn<ojS>;pr2l^4V@ylio5~$J{SpQM5$w`^5bV zKE=xAFgE`_{P{T3{lEoDk~`wO-#dvec$zJ@V{6}5?JElWoz?aA^1pxIQB%Kr;DOmM z)4U5MEk4`i?kMbD(5vRG6Q6$i^8>cstoahWMmyG?iORLH{4`<5yWNp_|DPJSHdfjm zc+j=RrPs~=)Im|}PU*fjo;>x%e!cAOY}+^cKbPfcvr$?1JA3l&{f***zNSYwdGhl0 z_P$;5(t|6GKX1L--gi0@8{b{w$a^oh_x%RpPe+fsPS=mQvTZLgDV#RlKC3?FfV{l? zR*y@f#>Yx;6f$}XzP@!<=DL}|j{j`y&kFM7J&ZaNqJM6(%{uF=+all9v81ZWC$&i2 zWa@h<o~Kk<D=c0xJ^bQ>D&4)@&Tan`Zrj+~Z?}{dzugr5I%0Q{z=36#uRVO2*eGVj z{4VP7R&901eS7xsZ1r8gZk@@Ww9hJ*b^ObIG{he7>+8ETefsn-pFdyje!K4K^XJbW znAHSqJu!9jZ;j<g50zfDPyO^yR7Y2+LvF_lP2)u@Kh|q6y79$?DWZSsiscO5NtZ8k zB-n;6n#ZKm<?FB9@HL{dife<0nQbq_wPe*-ng<42@js>sje)XH4xW7rDsVhq{an^L HB{Ts5G2b=* literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_forward.png b/Telegram/Resources/icons/chat/input_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..c04e20ceb36fcc112bb8177c773f554fad0ec04b GIT binary patch literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbL~+{qb~h42j@; zJ6(~l&48!%rhk(IkMU90vOv8Cr$+b1>4tYwTh%5c{7c!=9J1x4(c$L)?d*4b0!0dV zHhvZME^E?MC~v*uSUTY~L-=~h5Oz(ziuanE_Dj7v8tL96xN9AAvQOXdXRU{)?_Kr2 zTI2L2?w@iBO@5bILPHA`?z6g_sGPTOUA9(=)q`!hKWu_@Pc!*m4p6wv=$T?3$iX<L z*k5r!v&*&8*oC>b)hFC%QhH**Q^e?M{iBL)XZ*&k-%ehNyS^;lV5@4<xp!Z0)=yxv z=v#cpPidQY_x-anzYl6Zcyn{kPAj#UOWW1n?OM_NO5r-=x;Wz%d_}SkUL8#@V0W8h Vv}Cu+e``>9c)I$ztaD0e0syVdjv)X5 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_forward@2x.png b/Telegram/Resources/icons/chat/input_forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c500804d00c95acedb1168d6edb3fca389c357d8 GIT binary patch literal 858 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z;wjZ#WAFU z@$Ies{hEOy>>si_og5cm(%fp*IW6o5^A(rX)BbY*;s3`_wbg53=&KMBDM6)ovnOx= zyvg#UQTln=&yQ>KjjXG$TTY&N-d4Oi-RPLa>)ed}B0qkl%3a#W!mvVzm4PWhbAgXL zD@ByQ4=W!Vv$0&iuiE6mxJ|!HcInIfep|LXGHdhAGijS|TFlM=^D(1g|Ms6Xd7ppQ z#0X4g_n2||=_N6)Rtp6+;RyoatEcLUbq6Y|iBAwPpWUlF{dAy`n)C#L?YGyi4bciz zRFj_|P`-Qa+OXAu3d@)$2z>v&ckR`z_ijAw1x7P(oPO$b{p+<d3z?j~arNp+77c+9 zekzw*&CS_-vql`q{dd%5tNr}*+wSJ=XEvPMU{Y}X_0_W7cQ*)L=Jx3L{`>B?-+v7R zFY|hI^dCRm=eBr*z-4}qjw@Dk`-+boS;M|7{%CQz%k#yDrqBP^S6^ap*ed$%hd|(i zi`QqwtrtJVvpDkG*1x}x{%@YV_tCDoOVs{#fBFAueatoO!~Xfj_x4EJFT0pABS!D} zpBv}-Ds1F@S6?-H{q<MmjLX;O_(Z<%akBNBP&s$iQ=RJz)-!Be@M}@ie8wct|EzMM zpZ$5wr!qbG%i^)!)O%^riuZdN?{yuj{mfs$)W}f5`si5Ifm5H^A6P#~+IV2Gteww= z+8BAgMd?Q~xQ_1QZFhcb%bmRJvS#g~BhPAtnI)tR%akGlQWY-gF)U5ek!#>_xy8C5 zq~|E}ghr3u3{$!`RxlU|WQsQg8H({cuqnM|3eY<Ci1CEOl2`_>qY*X?k{w&P9ac(o ovnw!9EMwFVP5#J`LX2Vm*`L=Pi_?&QpbARYp00i_>zopr0ADj!5dZ)H literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_forward@3x.png b/Telegram/Resources/icons/chat/input_forward@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9df3f2369c21a0899785b8ae95eeb6f50cdf51a4 GIT binary patch literal 1305 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ASip>6gA{T+3Vp}Gz|!pL;uuoF z`1a05eVss&;~#hXFS?KsVztz%F-%EHii_*w2lfl6|FNn_as6ko&5DYWS|l`=jkT%e z!M>gPdveUIv&+iQo!gnde{#;Ivb1w|e*Zp``u5(;NyX>X`NbA<{xQ6zzw7H<h6Bg^ zSsNJ5<~4kLXV^3_bm<q4dpB+r^!NAYWoMr*EiIk7l<)hC7blMY{=NHu<hSqNYs<^G zAC<8ZHlK6l>gk6M7ruY_a$_4$f$Eo!A2+^#`*v#!kX=wwS5@`u_pe`T6qsXpW$O0r z+4JS^-@jrL7}v4N#O>a<?;A5S^G0RnIczd<`*-gA*xcM~sLUM0Z?R_g?%ki2m6bOt zG5?V+i1`2E!-M0;jwQLUS#THJc=q(^QGY+bV?f)&9&EUK`}XYlvuAg6N<MIFIJ`|> zUcN3fQ&a58qsHUMkN>@THFUA1_^!X#uTPJUjO=9GS@GaTf%f?|MLXBX0@<eyB-QNO zw{M-K<O3VORd3(s#%oPAabljsUa(^0=FN{McQUV<;C(;-o4D0%#goe}JbqsJp}CoP z`-Tk<CY@xdoR+&w?0~QP^nH&nUYt04?%dM#>({@O-kQmKz{zp`oH;vQzI<6<ysw3C z;!2$ZGn2CN7H{5cY;J1$^xgaSv$ZE2j$?Q(khE9m`Sa&_6%{vX>*}uE_Q{EL_n$55 zy>-!t3l}Cx%gU~;Si0ArQS99BM~@Dv7Vl~4pA;we;7d+aobW$3e;*%_Z?7#&7#>WM z%ogW(zlb?+V*Un(b-9;6t$z6b!H0K0U%p)VG-|C_0?YPU30MD}R5i#;OLIHgF0(L` zA)ImRr^oN!t(!f6e)wm@r~dI={|ju^^Z)K%b9lRF>AD0RA+`SG^=sFP+IdX6IO|&5 z712!VC;zOBtz39ry3?&Bljcs|x@XUvmGz}=;nv^u<#w!KnDzIoOj@X(p58WQ_5Ngr zI~*l5#OKyrpY8tP?JCBCu!MCv9`)+i*KOXq@Nr5j@9o0epp+dC-`8DhUCph0<Sf^j zuLmbSN@IM+^}nOAp81)a?8L{{n4jrcvlU!!*y%q_@J`f$J1@@(<T2FM)&2WF|I)pC za^F|+ER3jHvu@q5+qa{E325cJMH}}Ng&BUdeaX`uz~pmNkSokPvSF(S$5!Tqg$Y}y luts#Z1XweO4PoJ%`zPA{E#|d(0xIhmJYD@<);T3K0RXmHPs#uQ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record_filled.png b/Telegram/Resources/icons/chat/input_record_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..48b9786b40fc1a497cfc454371a702cc9329925b GIT binary patch literal 618 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{?24z0V@L$& z+c4`sCPSXHYqmY%@G$W?&$?jY{Ko-B0(=K}+xR#Ox%3!hMSMIbe(l~M>dGXPzNLFw zgi)c8{qwWNpI7!6PWzMl_I2*eyPcv<J7u-F#8`_(PaLg3R$pKtGwW+ruHW*>#YZAu zpZ=g0Xf3$$`RAK$ZKiLYUfGxY^+`a(6|S8pWvc7_Cfw3$ImA%ClSO;lM7ODeE0^zB zbx5%4chByK_2;uw0zYhZJ8%4NdQ<LgsU6q0{CJTbdcE{rpt%GOi|}t57PdC2lE#bk zt~`4D@x>A8{^LrI^0rU5;Q3>Ftf1H6`$e112X*_)OJ^;cSk-;>P>i1V%;E;qHyaJx zvOiRvk<k5KV^^NLD{g(6i4-TFJZr`KMRyC1{A*__YHVD{Hqq<jk`(XUV)oqa(cbGf zPG?tS-)WE_d{WWAHzD@=v|H*bwW^1sZX_2SV32<1a^^C}q6ax<!jm%Y7VX?J`Q(vD zeyz&Kq_&GbS$274Tg<Menxg^Dx8FXg`Oqx0w5Z1MC6|$B&yUON!?dd{WZt|jEAErX z?vifuJUQd9t%u4{4;S(0b<AFlT$AQC+kM#n=)E2PtsJwq&^N2|*6Y6Oo)Z83ZFCIB pueX<uxH|S`*)7v}v3g-=J;Q;?E0wSFXo6A|gQu&X%Q~loCICf9{we?f literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record_filled@2x.png b/Telegram/Resources/icons/chat/input_record_filled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3ff966ba0c97b4f524e6689c962ee1f5f3b2f339 GIT binary patch literal 1258 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z!Ku=;uuoF z_;%KQzuyJ|$78i-I4~J19^rD1XIPn(kl>J%*xvj6foQ^u6Ve;Xm;y@^Q=2C;3iNm! zXggqEzj&F&vHH4`M+;ld1zqW`eZKqmlSw-~t(-Vkn&pXdo(b8KyqS}c%|nNk?F2(7 zOUJXAMxF^C4H@S|iR0GuCak|Mtgf#9=-aojJ^S~kOQ(EET+_?j|7CK1adB~xfy9aR z>-Aq|Ni_47+!pw((pGk0_pV(<g}<-z-xYK8;TH%E-F9%9YUm8H1-sT&`bUdL%sIEg zm)G!T?2i{OJkFe%!q05BA=lZvO602Swa}D<bvKr+J8({=A%$JYd+N`hKfV6h+u0pC zbjV5d_O6~u%ps|6r<PuJ<a!zPv)g}HMD)v<j6(d}%qa~nOfJRi9N0Wm988{bp>cDx z58S;Q`|-z*8MzZ?=lTBX|K#CuaJF4v(;9}T?JI*^51PLJ|Ev7w^cUOJ!;+Y^XKdfT zok{5HjgzaE%cYBcIrZkiH8J&?m%JNqe0lYvyNtJ{vNG`F-z`(8PMt67TUAkU;oGTB zz39u+CA}^6>)vKC%%4A>dtP{WIQQ?L=Z+qA)tmOO^yaL6XCl0;KS{VSXM`QUc5Rwz z&*sg>AOHT%mHk|Lh;7@pv|mQa8*c{uS`;JC$It)u^n^V-cNYFy`FpEc&C9nB9vpZX z-MF}KqWT854O_OT*xTEGwRv*vSX-#L{>j%>8~Nm`oOwEyvIVTOe8k{#VDUwd^RDye z&)?m2VxNhFYLNZg2L8Q@Z5<yB%555c|NdR2Hxp?3OJ5HC(^uPFgx2e9^f|~TS-VjD z<&4`o9g1mvfs9HYUcY|r<oEsi_oB+mo!^!is6V?VUE|)?{_}CD!n1eZe^>d<w2|w7 z`sPhe?#V>GGnr;T`sT)$G<ZDt^5x1IUGE)zUPoliDn8s~*z-fQLuJ?9bs^pN;spv% zwXW;5<Je&8A8pa7|KrQeYZhYPMb0tD#>7l1FE8J(u!y<b@4Ars_UrWvv+w18?tI9+ zujKEEB4dV$EJB+@ljhlo9XNjccu{qA^t$!yy}zFI+2-gbf9#BFQC@fanY#vi()<$l zG8JUqT-RyUp=I<e=fjq(+Ix0#U2%NGm#J5{s^PE<@7>(ej9qis68MC<WB)`O{Qved z!AabVS7%abK$zvv=eb-d*RK2z-MD?bdRbZ7kz2QB1%#?z`K2cve4u63mN#DyJbk)U zg0KD2*RQ2XGrui2W$@)$#l#)EazC>U%i^~#)p9F-{<y5ftRff3_9L9p&eqm9`0~@t z^lp~dZzGRR-x9x9VtU2)Zr-9-6*2#!tHtvI8!Me3W-k!;ub9BtCRu4JK^^7?d8POT Ub*z(3(?R8or>mdKI;Vst079)ShX4Qo literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_record_filled@3x.png b/Telegram/Resources/icons/chat/input_record_filled@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..481fbc14a36d63a06ebaf72b79360d2af479993c GIT binary patch literal 1944 zcmcgt=|9`~8vP~I9(3$lMcW&#iqcv~DWN6AzH1q6(2-V@R#8MOCAQdFVw==hYA=Hj zsV4SfP#ICFUW#T=xkW{52qxa#`w!fEU!3oGo^#Ht^Laj>Z<>>XHAGBV3;+NK!p6#l z&rwGZ5#qnb0R{{{fgZS6!-1ypldJs2D$olNWN!~>@@EkMlo|{O9!2<2<^uo(o`V1Z zK7)?5=iq<4QO^bbGam&-7Tv!9fG`+gW#JYD`d68W9miaDXsF`}7IfT7tAANTddlZ5 zV|*GeYeGxxHaLBH2O`@>Zf<pvb(WU3vS^qZRG@7Agwdu@9~iV!hkySRCDc3EIvmZ5 zFS-(xKU4WMoqIN`7CeSr%lqv>Qw>)XDhBofE_>plqZvuZbRHZd7HXLO4}^Ckl-F}} ze)@)nFM@)Crs#Cs=hfBo&-Qk9YA~Up%!P&W_w)0fQc_Z-f#*z1l9%&mkjepV-Q6m2 z@$uHdkWqi{hCW1+6)cwiYYmIVTG`&#&$>JC-Ai~4n!IpnLU?0j#Fo3Y<qDdZ#zib7 zDFvLuP3|ctoCp=%=xNYUP}L!jwmAi;8a8QTV1;-ymTrU?>FUwQ_l;-)t=Xy9x3YZ< zfl}YT$Vqht>{;=56c`LvY)OWgEl^h)H*mW6T+h)zBK?dD_h482Go9GXuiP@vaW=N3 z4K!5LOYZ0}WHOo0><-q~*XyWM6NUMvQu$NOX3-<=_0>=tw+|jWz7NByBJ(N0ql(s! zbKsd0E(R>_Z}?+O#%WwPVjbH2-!tw@<7Q`PnY+7Py{_>@9jP16J}ZC4(7t;LG<Jq4 z89<C8>b!JON%oRipF)>tJ%NmC_jJrP&;Il7g=Tt?OvmkkOo)N8ULGy$@OpvhVl_28 zTWU#LDN=87d=NeM-S=5%g^cmjj)@6J=H#R^=Fvf<^pJnPxfA><P@pM7U`6Ynn0F(% z*+;W&|KgYRDqa0$&{7Gwf#mf2g~no@5NPMimvN%rKm5XPy}EPe$Q#)_I52zJhW>g& z@Z;LrT@<6QukU&c17BFPl1k36M*g`V^ZF0vXJ_19i)jrsDewlHUDcw!U)eqRi{2u{ zm`bH0T%lVV!CT){G_C8Zk$Lr#X-qc$ThVMd3WfSKU>_eJkG|Pm!u^_jHM~L@J>uG` z9J?$$ZXmkPbL8@P_omPh5lq{2s_QG}nCcWCr~Ht8<BQ9xI$|GKEZtzaG7`ypY;4Tq zHYIjcG8}#s2~Y9JIMe|g9UO|=G+FZU@(to}$EM{cTKtQApJ?IWW(gx>PiiR$`EwW; z3?{R9)sA43E#{q(f(rj7v}=vS$;`>GEl<Hcns}=vL4Sk7V&^Kohsk7e5X7iS^rEx3 zk54**KsX)8`eAjlijf>Wv*Bxc(Z@pD%@PvEGdJr4Wx@`*+^L$fQ!fcnDD+iE>dBmp zTJ`Vr$Dv-PiZ^&%ZX1G{2f6biE-r3P?TkSLIa@F{My=bR$lA!%NM4xgSXWk7c5PRo zGyvrA=W&^q+LY2Ip1_`jl#Y<JpP%13m>rzT8OFOqw8Pt#ayA9cuF$1#qtTiST5VoV zOZ~$z<n>H6HSiPurfE-W+6kz?2)VP<20W->#OhHBffYKD)$X4coSzbPFI5b0+g=T( z+y`0a@O$*K`zrIUQZcGoWwQ>d7fV-&C@VpNDehZxZz69AU_wIL6D1aOAL2I`9urde zeY*5zBbtfAH}}m(KZ1nA91G?ct|fDFby-;u=Ja$ymzMn7=TkjB30+-XyOAcCSaC^g zW%i5ma`h)9-r&$sBM%!FSIcGxwzszjw#SNyAEU{kb8U?m1FGM%BBC#j?uPbD@{&jT zFL8VyN*EsWH?_Tw2%mOB#pAsvny!w|F5PJXhTylR@XFJf<pG;%iU|a#qQ~{a9!NTn z750<F7griwAFOD$U_x&Rl<7y47r}&>%WBBa^ZuKbG3^#<dLO+M%sys-W`5xImR`F{ z^srBIuBfQ6B}vrg<{sBKGOFSJ{FG66<FN7Np+?e=#>UQ_d&dpmsHm#eXJm+1873YU zT-a6R03|r5x6QiuLoGfSjQR$UwnhC(pH1*EoC=<EIT`p?`#Na`Jd^Oo%#tTYcXV`= zQ!cqDC$6i*-(q57<Z6)Q^e05cI>>{T9S7Pmjou8G4Q&XLm}<X;y1Jj9APH4zX}W|R z(C*R>BsQGI72zsJr0GvDAvs{8s7;*&2zkD*U;F<g#{+h}s-h2+-$svqMZ`4+t0uUA F;@`~Ag<t>x literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_reply.png b/Telegram/Resources/icons/chat/input_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..f20b997732252fb8460a49a83b8826649e0a6243 GIT binary patch literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{Y`3S2V@L$& z+i4q59WoF&etT8(zV_9#AKd!I<d^qIYlqcNi`y%`3kBzBb@(ehQn71@-rl!#a`#fc zN40wwE%JD^G*DBXvt`@y*mu|e$9jag1}e<kDI1k{a$SN@z!s+<$CV3KFfIDHiG69) zBCd{O+}{&ch6IUqw{D7B>k>KRV@2c%6Yf@~BLxkQ3oKHinkQIG&snv~VD?#|9WhL| zw$-+umR*oJ%fH}9p^e<axb@C=CePP1jqh|{e3ARkV#O|{8#!hd-j*#4T3N$1jn6`Y zM=tW-zTf-iHze&BGGjj7&UWMV*LzGGWu~28+A+)IfC5LvPtR|6^Vpvj&0HEZYcAW3 ztgR1f>?S8f_^n833%blu_|R`T^YqiLQ|G7TZ@;}~LWx7moA19>1g1@Xz3afv%pVMb zMHVvqp70+QSantFN|vbsk8)tO)!e4%mHPY6rcHKtt#fFR=|Aq@dHGyY?DfRmcklhx zJ^jm0!O2f;a`hz>hc8DvZELLNChWiepDp0BNr8-d;}Qm?l*JbrY>ww&Xi48DbYDzQ ztlMDz`NX|(pH)<Uue$E?QF*@e54XQ>?@wx2#O2Kj3CGiNf4+15W8CxCL%>k+y#**b NJzf1=);T3K0RSy>^9BF_ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_reply@2x.png b/Telegram/Resources/icons/chat/input_reply@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e44f5f79eb0d624cb436d5b4bd0b266f074245c6 GIT binary patch literal 1257 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0z!L1~;uuoF z`1aOWZPP#rh7XV9=KtjBw0F=DD?8<vc$7(6M<Z1^%<R;o*b{C+WmA?+nJT(5P(z5D zd()?#L3UjpKkM#&_IbZY^83BdcID^hSg(I+X?k|9<^BDY&(58hJ6olNVP`>+ra)0- z|LVVj2@Yoi5**mBh+J56PVhv-ET&mkEtw<(f*m$nxhpVdab<)(=SXqzWjy;zrjcib z=7Pw%$`cq(Sxr_KvluN{){s`(hpxGqDIzFHDJ(qv=*N#6GxGAT%?V3d{kXd{e)^$9 zP8L>HOxCh8GC!>5S=jzLdiwO~=D&YyZ=Z{rKX<O7!c&d3)YOCL&h;_#n@ddiFm=Mj ziHxsbzZR_E@i^e^>)SeY>eLk#JU{pk6j<<dcXu0pln7vy;A%~%tBdPA-oSO><;$1# zkN%n**sv$+-=yx3CWeNJzkdDl_^YC>&Ry9mwsYssNAKSG+1T1neDyQ*#1Gr+Cr)r^ zeo7LxvbEjn_jlSfF%M5qL%X<2g-0)y*B$z3k~39SN~-JWqpbg5znb>+_dC0~y533d z6mGk<&+<THBV)M76tDU7=NnjCb00l=^h)O}t#q~x%a^kkOj>sN@L^^iKE7X-S=IbE z?%m^)mzQ7mR_1MfPL6}4W8+GV;H|H96M}<<_w3zksWyE@@8rqC8v6R-zSmslZEvVJ z`o;L}{rk?ozP+3)R^(Rw>;Cfg?b`V5$zl5|-tXA6N9WY3Q$l8aD>min|15gYuwANd z`RC7{U1AsRwQUvLw`-TwhmcR!5B2Txm=*h~?lC7l-@0|Hg8achW-aH=`CaZvV-Vc9 za3Nz{TwIOoo}D`lYpav_1Oz|+{QCFr-S8t9F9uEy%n%b0+`Vg;(5Dcg*BTGM7pA2x z6Fj0aXF*KQ@_+y83@t4^Up@c%Gt#4^u(VY5iMGGue1kQnb;n%embj#*s;<=C9Z+7r zea7tBo6T)wr7ew$S_I$n@$(<&5;|UYD%$do;KId=nbXqJ9z1$<DA4}av0JyIj#Mn_ zy?AuriOyXaEjBwgZrqshx%kauhC2D=*jU+|++53BKX-ln^^42W((*}RU4Hz=xe*RB zC$3z%a($<3O&N2B|Au}0>|X6od$VNgR#S<FA4QpoiHc0;?k!}_aNf3c>%qs57vEj7 zb=v)d2N{ldp877gVat{$GZyT9$g1!yFf{b(kGeazZ*%i0_!-#R+P>N^yoc$CT~}9E zLUr|RE3@g-rypcG!kLzye)!$Hb+R?rZh1SrQP$Pvt*WkGxMhn;MT5n`KYwa0Y;68? zcl^J0=&yasPkXkm@1?V6&sO-Kz}ef|YtPCr^i4%+vF3!6pW~M>uUs-igjuUVBa+7> zFlb7m11q0uH>0Lkml4m30}E3O6<j?7+ZaVHrs%LO(a=0)uz;z3($NMHRaZ&2kOu*q zBql6cqH(x^OU6r#IcSPVszgAe^T{NKu1Sk{uo}9bafZUaS%3biZ3GoFp00i_>zopr E0NQ6L4*&oF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_reply@3x.png b/Telegram/Resources/icons/chat/input_reply@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..97688789f3449d4230f8628d4b4a4dceead488eb GIT binary patch literal 1894 zcmd6o`#;l*AICo*n@uxP4jZvZ!virixsJqF9*lD5IPByyLRL~_8Fg3}r1MS1%uy1` z<*0Z#Q+!a{an0q-ZMltHlDU*L*YEst&VO(o-ydG@*ZcDP>HT=TAMcx|+??c4swe;e z<eX1Bct|{9S274mv<Nlpl^7W3;bad~cB#!t3WqR1=WsF^(39jc0LY94;Jb)KsuBSJ ztPliX5(jsCg;IY{6AEGf<h!7Z&|3rmh;C<x<6a5iLcYsMrPEm6ohte-3a9{7Znlwv z@goIS%q^Mpn4@0seIH~D;rNJ0Lx-*xrWINhWuiO|U{H^rR9-o9_%w=c0C!#nwMi@d z0kI)DurH3C#GcO=Z^{QwkDA8K(06uX8xq;3iW<KF;tBh|Qtcjd{Q85DSi&+n6*Q#? z<rN<AR60^_TG<8Ss05g{2sodZ--Lcx2YdVRJmOygs1~%vQJdwtKAma~M{o3XqE>TD z3mXsZ?Gd{8ourFK-^kiBw&80l!gsR!hr60?AU<Sdp#6e_FH`5&x0fe*E^-gw8k^(s zju{ylH%d!O6|L&JySo+RACxsOrzk2azS~$^baZv?2or_}JTC%yJYM~{@Lv0zyty(V z+#uXAoPeo;Wx+Fe0oK#P>Ib)}NHwIniHQeVPHs<P|7U-d_wg3vy3oFT`_9m4r~aJ( ziA(JTc>uSay}g}$m&J<mP&G=IH<zN(Xul_He5=qkFbF*xWLao44ncN3eSKOu5`DJ# zXpowEw<^ufE+t`Wy++r_h#D|MvlArMu-R-K(UGJd&(mmtr>AGPKp?xix=NerQkOxY zdVO9_MZ`znuj>942yURy9dYSvkBy0Gnw{0PW^AjWP$)Yb!r<o6URwDL7hA~vHxf~< z{gyOFS$TZuB{e%co09g3Q*puwB9qBJgfEfmjf@>cA``8rr#Ie~qt8iQA>ytA1AM+1 z&OOM;%gs#{3eN<S`E!gbKxJhmqC;b(4T6Bfc>El^L*Vxr8R+AKU^1DU>MhLs(NWyE z^BDiR?;D~o^W%c_=6DlqaBOUc`m`%pTSsSNx|3yT_eC4A0nE(Iw1`BNU9RqLYhS-c z4-E~CVx!kyd;{*c{0LS#vHg;_)}_6@y)Rui6*SnSG$wC-a;W8<ITsT{jC#RtAA%)N zsWT7#-L`rL2P1T>O=AQw?4q2fZbGE5zkeh*qW*q0@^`f>Fv*G@LD(e|lsi@&^p#<Q zzp#}5yo4zgMh~6-wji*yuhqGpS+T{<JU}>h%qi8FzP+h3HLEzHA&|u$ALGQ!Dq}Ft zcH9C&$Rt1XtS#l}AtF&}O6#{QW`-{^FW%}Ch}EQqg*9=x>64m`No#H;2K`8aeFI~M z6dfCDrD_snSzJ;wFXe9p$^21T`e*#yL}=T{$Oszy^D|O$adE(9&4QZc2})yKop~`S z=iKvo*mmV9x3Bs{BC&EUdVFc<%e|~Dd1Es(?dIlYIBEOZ;sO+T@#3p&9S8eMAm`~o zRWQa}Uhr&Xs#Q^Q<8DO&`~7_NRsH|o)enh?h%ndpx9;L;zT97aVM`Pf!b}bd!dWM8 zVwW7|cy|us2CnvZ#Ev!Oo*l43*wTGj^Gq&%|A*e*3-=$ii6w_Zs<}D*v7m*&cDu+T zoJ!4!NyxjKn>!b`p)9VlrTnn|iLdWY(wku;mv#^{Q&V*&0@j3MXJ;q+J44I6v7W=Z zr{Qf{4v&7DcFbg0ARx;T*Ow+>FtZG+s9WwDUsqSl{0agV_d@^4Z*6T&DKfOlDJs%} z+>|D5&0N{lx6Ly4?AZf~73y@{?{t3Ap=fEV*}i%8>SFm%YX|XoJbIAO-r*HA4SoM! zSI=x)#DLh4tjx@`iHQl%$%v?^5GWkt7=>JdAV-ipxTJ7lSjCn!JB{0rW?b$!93D|i zERuR!QsUmJTC{QqJW$Q=@0Y?QWxlAb<)+BRy=DtjQpMuLmUcx?E1)Rd=??A>QOP6? z$3jk99J`*GdAtElwxB?U5SB%U9Sp3$)Y<j<T`hYrmiVN&m~BY`kl#-C%-)ut7nQg7 zFBpt9GB7V#r=!oE!^xf~7+2f4s=~+~ZeCVbOIV8t3zMEYFa{cx9sbK@qlZ(!WD?He z=e2%*Tw#{pwvHtz{fbgY`~8g0Ig4;jdt#4-`Cv~J=YgJ@W!+6FO59Ur;gHu!y^91& hnz7e2mH!WX6a1k!kuo`KR<`@&oE_a9D(xxP{s;9LRq_A; literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_save.png b/Telegram/Resources/icons/chat/input_save.png new file mode 100644 index 0000000000000000000000000000000000000000..766d7585aea89e3501e9f3546f05eb1ffca97293 GIT binary patch literal 597 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{Y`>?AV@L$& z+iMHgH3dkp7bLJS&yFf#o9mbx(sa`AJ8KQ&`~&76Snu$t?M_)MwQ=FuC9O`0#h2fI zN-sM0Ze6%VgYulsvW^z=&!7L7F;DTFr14^Du2*KN{tSk+SjCA8JPfj#B3~bs6X8tr zRmlDu{{2CM!GvFb>#~gF#JJv1KG`zweEJRMj9EXsj`to<uCkG9VN02iRN1gB^U~is z{=y$K{FY}HNJPgpExy=Mb3c8@&75gs;W8Htcs|+mKYaGtl3&j@VA|7u;g`Go*1a~m zleF<i#OL!~YSE9fwtDqFZjo&$=YR9Hs$_4RWZvGm$%{1dkMFPcdsAifpui$!^1Zz6 z_WwKImd)0i&MloDDYkOeEY|j0DGoYs(>5ngW@ZWqxu0S3qQq+B@7;I%w5D?HmhXLR zp~n2^;EaOZca2ya8m?xUZpxh<YcIpMBr{5G#}iJs{W8ML0;UJP{jU8owePs{)z@G5 z6ufL~U}8A&zRYg^%9}aQIv+DLadJ2W9-jB6#H#Y~k4A@WQMyrj)3-#eU2*%ZSfldK zt=pTNEpLb4XRlNeaF}=Hk*U(b3MrfCN{;C4rT!n-XTAP$_s6#dS)eHOboFyt=akR{ E0QSoBvj6}9 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_save@2x.png b/Telegram/Resources/icons/chat/input_save@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f8314d4e2e99cc977954328696c5ac200b71c60a GIT binary patch literal 1077 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;NuTeKHt;DF{Fa= z?XA7~)qxW1A8wl|Ds7x0(b7B7Ph#(y85_H9naWy89Faeuu+bn=B4^E-9-TvJNrnow zm!Fr*|NOdS=gzge?*G13<A3SPon7bm+Rm@budXsljhuGF+^nA|v%gg5x4wge0T+vk zf(FAh<3<)1)&oUq6B-sUM4V$05)ffHJ=1}yi7CNS*ux=!K_{JwlY^__sgDAqBjbiS zoS2&KZRI?Zw)xt{ix<!5W@T}uWW}%B@AG2S?N6UJg=mRpn!LTez_EAZ?hntNKVKT6 zW%}mK-nqvd7<RuBnm2RiMm;^fHTUk_`*wZK`Fh?bQ#pU<CKeS1-MfF^_t7ltt68CM zz8_q0Y~~;Dikh03xcK<lXMOg^tv`M1R#ZSm)twaPzP`Rwr%!h?7MB(6jG0hzl1)ZR zYSQxM>dR-|Joo&w<6~9vf`Wp8ckkcxPTRbB_ikynI3q@OHnu0<zFo^q+s4nwcj%C} zTta;O{FwN7X`S@vPoB60trTIrYj>xrs_Mz7PnUqI&YwT8I{9Qvr+5z2Th6r^Np}qX z{hD!T&fK|@Kn<^6d5Qd3fBb7={r}~LFH55M`1zNHt?u+o+S<?Kr+@PC<7A*y_U+vH z@onoThFA6r&h4GUyK~RC@89RP8{aN3FJC0DCCBzIy!U^@^5x6Z8qUh<=YO!0&Acin zC+D)<Sn$!yoqySN#JblW)0wr*@BGfad+&a-06NIX#^%n)jaIhH+yDMyfAju*yZX-A zvuEpm{IdG?jT;d%ADTQD?CUylea+RYp+EoqD`TyxuAbc4*}3BZEBlI${k0dbPqEaW zxpe8$`Tlw4wz2CjrD~`99=ZK5{?J+GtnF8pXkOl8`fdK~*~PKGVQ0>t_n$d;uIX>H ztfT^YodtX!wjcHLz5C_MmYSNH9jyMRo^I07(>o^UKR@_GLlvKI|FRQnU;a0UTC4Ut zr6x61bzQ|nMy*GO4juBJbw}W?+~uoJzJC2`@wk<HMaL{P-%$13>}>6K6%U!U7A^C- ze07~DSF6ecS7xB(y0F~*e0|M|i$Lwij<wa))=Ij^?!0;Prl-)nB_#^qbT^q9871A^ zueRZhs=?{FX`AHa<&~@T;{0SR7fe@|T|R5wuS|E54J<5IIf+ZiM5KNe#{Y~Gxu%Pj Sv<g>&vaF}8pUXO@geCwnYu1tg literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_save@3x.png b/Telegram/Resources/icons/chat/input_save@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..fb49ed6a5b304f5c884b56e87c0aa50a2d0046a2 GIT binary patch literal 1733 zcmc&#`#TeQ82@f_nM<J(4;yhhR_fW%+(M_!B^J8SrZ|YXw9FzJHO(GIRH&yj_glq~ zOIC<nBA0UOu_VcDQj>!@p60f3=9hE+gnszEpU?YwKhOKi`@?(tw5#I|MJ+`Dzz(OA zc4wtb+Xh@tS}g<22BidvIO}K&D*F+$(#DQNbP9BF0ftfy2av=NAhR8jLQ4t&U<D8W zlQLx6D}eqtr4_*b*V{oj-(vv)IgFFt33nP~KBp05?W5UwJv}Y0;J8deJ(lzSK=7GI z4j3`yfID+<VPuGRQMl87{;mZ49?tuMg5#>p74xc_gs7E_TrN4|$l{{q+rXfS+@9#X zfmcC6{jX$TaljLUE!??ZVMx|<3jutmN}vE%O_h)D!O8t<ZiHJLd~0phH<zBm+8U~K z#^YglJU*_mv2j2H!)CL$hNh6I?OP5CRWdf5s;ZL2MEPV2g4>!sd1%rghB^H4<Df)B z$BtGFoHu|`i@wD)d6P)LkByJVz7|vBxS!U&MOr2%7UK_|JboOH#$a0fwbZz=51Lr` z`tBM*Cr?pH2}UN9<HX{dU43R|W{Ban6m{!AdJd;I3vd()IyWcBE}(QQv7`i5Q~B8C z{ftUP(=mmbnwsESw<hid*@=Jte3+G$b&AnL2fiqkK^`xJOg;oH8cVFJGrt@fYATBc zR<B)LT%J69sBV>~wv?Kx@<SjnQb2<)XwLonw#&=QYO4Np!NP*s4m22reA^hFot-6G z|JFP=KX0nGZRzOhrUK2mcA>DklnPn3YQ^Dj&u9)e&5e!yOuWE7xn5iaB|9_I*w1iq zR;E4lEIgb*V52WyAQJaA!<9nGLMw12M<j~kk0Nqw8ycK%ZUh*85H>@oc)Ze*OoHp^ z__#sds!+jep~RBG<#G?AP~YA9$XBjBX0tVrwb!WB<JP2*`$1atB^@JzJpzfOXZH3) zMn<+!_Up2B6$RMX?p76Ttg>k+G7Lf>5O%xuJJwC)@hVD7^<w`l=k*T^lyW%TSZaf0 zX|Hg7y`iE@x%uTwPePI?MqOR~T115ZjW0q>Wo6~5(u%>48m8}P@)nksnR)s7?GpFD zqT!*z!6)5wYH}&2a@XVW;q_S5JEIo5Xs40(clY5p4iSFZ8ugM70wxoB)XHkw`>0vn zy*1vk#_FazMzq<<5(zeY^@n(MmH7R1{-fmVM=YhZ*zoWhN}tL?2RCiL+M4TJnl?Rr z<j4b&Nc4Pi^W$RdD|*ybdKiSO+FW$k!w2EYN+gyGU9?TyoUc$Qti`sdHHjqm^XJdc zSLXHOC#;KC7$Y3of>ShSC{uN6_RY);>0-f@j}S`=3i=+VH9yqc2YS<9o}O>K!KbPg zlF*Fl!eMmE>WS7bU%Y}NyH@h@bVlhT$ad7#2*CC8sxG#|^x_t;FSot+O)D-gMijEn z3l<mIitNZt*^G_$1agC0RQ*2m`rH0bpNL7h(;MC0NdfhN(7<l5*5Xmxft@zlNCe`; z*c}5D>Pfq)h7oRIZjQ(5z=c~6X-ZEV%ju*}%V5Kjc5aPKGR^8n`FuX1Gf@{Ws2O35 zTti=pDEGW&;SdrMLJ45`i2RQER-XB$_p{_~xv`0fi5S{h5u2rw!Og%F(Slu`BXx9k z5$?c|EdSYm<bCDZafei%E!FaP=ic@7q>z|j7sap)y=B!#F85L2DxcprFr-Pzp>0BY z)1o;udc&pOK0fC>Ju|fp)}nPcVN$nyY;5cWsW)|Rd0ClQxwE2qW496Rn^IL=9E5*r z$BV8mM=d1s?%F<PouuUzb1VKr*e0c;qaz>sRI!^OJ34X6>+dGbR&<R1Wx}eta}l-d z5+MGxbC+S*Pq}x1>L0f`aFEhgnhfzB+I>b1>`geQ4r?t#73qV+iY10}3=cOudqCXd z>Y)tE^OBN5^?{r+O>o{uO&#KU#=^rGJdM#egtq>w#pzohF0*!1E_-<U=>R8tSG!7E GU*^BT;t3A` literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_schedule.png b/Telegram/Resources/icons/chat/input_schedule.png similarity index 100% rename from Telegram/Resources/icons/send_control_schedule.png rename to Telegram/Resources/icons/chat/input_schedule.png diff --git a/Telegram/Resources/icons/send_control_schedule@2x.png b/Telegram/Resources/icons/chat/input_schedule@2x.png similarity index 100% rename from Telegram/Resources/icons/send_control_schedule@2x.png rename to Telegram/Resources/icons/chat/input_schedule@2x.png diff --git a/Telegram/Resources/icons/send_control_schedule@3x.png b/Telegram/Resources/icons/chat/input_schedule@3x.png similarity index 100% rename from Telegram/Resources/icons/send_control_schedule@3x.png rename to Telegram/Resources/icons/chat/input_schedule@3x.png diff --git a/Telegram/Resources/icons/send_control_send.png b/Telegram/Resources/icons/chat/input_send.png similarity index 100% rename from Telegram/Resources/icons/send_control_send.png rename to Telegram/Resources/icons/chat/input_send.png diff --git a/Telegram/Resources/icons/send_control_send@2x.png b/Telegram/Resources/icons/chat/input_send@2x.png similarity index 100% rename from Telegram/Resources/icons/send_control_send@2x.png rename to Telegram/Resources/icons/chat/input_send@2x.png diff --git a/Telegram/Resources/icons/send_control_send@3x.png b/Telegram/Resources/icons/chat/input_send@3x.png similarity index 100% rename from Telegram/Resources/icons/send_control_send@3x.png rename to Telegram/Resources/icons/chat/input_send@3x.png diff --git a/Telegram/Resources/icons/chat/input_smile.png b/Telegram/Resources/icons/chat/input_smile.png deleted file mode 100644 index c23f30d0ae0f2bb222bfe8cd114f097fd44c93b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbQk0fvMKh#W5s; z^KJOXRa%AuXKOVgG7^;+wJv?&{zCo6?F{9Jo4*<VHPk0+W~47w>WGNYSh1EXtn1eK z)12ksUTvzJy|?m^n6<#>*(vAd_@t#jnl8|k!oBtnr|#zbBtgg9!d*5Jl^3otZ~L{d z|KN<Xe3xHtSrN8+)l@IliKm~6-1%7}cKdA@4;%B<b=Oz-N``s{`1V9AO<dr&+<E)$ z-ajna8^o*}bM~w;RcBjTW9NVR>7|FJa{ViB<^*kwSdwD2Qfi)Io2A$bhwn!>yxeT* zC}APjf9hG?{^edvA7z6yGu@4!V4~cioOIq-O?cAB=iYsLTen_lWKe!kI9t#~#_>U~ z!jm}9P3nzDAG7VXcNBQUf9Od>soO$s#;^w8U&1*iQd|;_>y7iQ&dKu`_?GktnHI?| zx&MCky!u^t*HzfaG3|{mDegHqBhPky`Te<X%B-I{F5%~VVB+c5ySQHV_~tx534w%N zrF-u6+ME!5RAS}XBmeSi)uW#^Twn4oo;74k-t~3Qz1s3a_Z*a_F@)((^~&7)t~K|G z<CU-upG~W_|4ck@xJ-VUprc5g$Ih6#!_vKONmg^KgxCdV&N*6OQFA%5=WN<%vw!Qw z?<-$kI`e1Ey1v&%I|KH{Eq7YDz$<cXn6hMc7GL&~!sV>K#tQE=Ue7D-w3>YKLe|zG zmpygYUYn+Fj%5Fz+$nXWbg7<Lci`%)i*DvD(>n6EZu!w9$G2s(AAQ{6%s2Z_#7gFa z7vjYnK5f6g_VB|6N)Hxa*X{G4@FZJ&jnWI};>6TR7I{Zh?icK_6Oh}y_fvhX^wijb ziH-k1+WUsFH0NvXXbV*3<oo^KM5$@pz8<6$v0vtl)5~c;9-aavNKaQkmvv4FO#p}K BP(c6y diff --git a/Telegram/Resources/icons/chat/input_smile@2x.png b/Telegram/Resources/icons/chat/input_smile@2x.png deleted file mode 100644 index 89c9afb8ccd62d7cc13211e453b2de2682b24e71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1571 zcmbVMdo<Gv9RCe7(<RS&ByY8&5SojdR}7nI@`#LiT*i@Cn`@*nx-2Plly{6m^2klX zX`XA5<Pmv|A&)jIM`KtWcmBEe-+S&KpYP}U`26ub=lh^w+?=E&RV4uckU}{-ToN+o z5E9}-dslX=K}aCNC8zU1<FMKnVc-zzg9>wX1@wi!1OUp60K^U@LZ}J>0HPHjKvc+} z!(4^PKUGYH=)eA<(3o}MA^?DMP!4vUBoMd6>NLVtfx)NADnbh|aH6Lr_5N;FQ*D+r z9F8Q)wK~?xqoKDl;8|qJ95gjITtPxrSC<@Ez85p}ey&{*#2>=)SFKJ?Z_G>+R)@Zo zmlau=nv}0W6Pv4bH*shyZ-G`fK&k8oR5bGd_cEPakQ`KI!b0S~g%ZtoYGaUA_XkJ@ z1_l8*+@nt;Bdm#uiO{2383_pqYs~riea6bl<F?LDPFZOw34TgEO)2FXkx0y1Sqaqw zp<n}ImLs=vy0#8#=6m}hqoNcuGcyOWkmscQn)jSEk3cZA@?ZkNR0P~0@zUK%%P!1Q zdGIffyr$x|=g$|tE?<UBe&_K%^P>a;!TiL8*UJy~2R?p&96n`xdpiJ+uUWY4IIW_t zz9TIyEy9V58{acBv$8U<wy_cIi;RqPdC}IE-O|#66QtoQMVXp*{<R*x`u&S9^XYVY zh1u=p<&e?INyli-tKHtY6<<Yci4Yn0KuKyySlFASzCcs>;1L;0G!UE=Wahj>IKLA# zxmXA1u-S68_DZ`f($f$PO~+ma!|C<$n|-Rsj+OOz$hc=hWohPWWn9s-HTqd15A$kR zb*5~SDh=zpYwz%#fN>>)j(ns90mT<Dg||d0u|`MT?=C27Evs^#&Z(6xmZW!VfE@5q zw{z;yGmcf!9%{e9cYwxc&pz*FQ=(_y%#N9!oOQOHYWW$$kha|$D0t}A^NAdam!C~P z+M$^(iZj>`g;ZFO0@*U#iQWMLhA<c`W9a)Hq%qE@F$lHIO1F62a;|U+WNu^AbTASe zACHg|Pc0%}j|0ZndzAcHkytC}AQ!;;`HkOD2nh+vbJO0s(DT}yF}|L2@7|gx7`M#c zi)wF0$W|}Lx6^2o2S3Ims~JS&8x!yH1AY^Q>*$Rs?Oj<ZfG|xpB$H08mp9_~gXJ~Z z&*g@VjEpRrP)H=QnpvHmwq=^5o;(Vs>0gHp{{aYR#<9LVIEXHB%fEWqNIf`~CKcIq z=7D;=ohr?zpGW5vEY%=V8wDz8o%hxscm=_;Dz>Kq#q2_wf}ZV$@(A+g3r@<aKcC0j zt&b`E%H#9b3K+~(X`(Um3zwUdtad7~qmaf9jc9Bj)}m}yqNAf*e(nndoyy9}yR8~b zCUc3)HK;eT`;+}(s0KUsJNZuOX#ks?oSZd1J^l6@{-ZGhp(nfp0x`(N(T(M9G9Y#q zsMGeRj``Z#+b?lAE-7<ev{$cW2m}JPz1{aMosedM_3*ggHGDlOshEFGxM#}U`6zrP z2qpits;Vk6QYkS*J32WfC7VK_U`TooF<=M6I0lPOiH*g~Ei5<%N*X4SNHRJ)I+dHO z?4q&En4moLD~-8?YDBp7iSV0q)JtAo1wL1<$SGe~6048ma5&T_Pfo}=4jAf#l4n9) zQ%g%r6=r8=mo_$rE@musZvXg!-QaTfY#h|gKjOyT6^8>x&s-!Ta6ykVL3HfgYQu48 z&(<n6quZ1B1wTI@2GZ6ZO)i@V&!!mCU{vo-o`%>b63lW{*LT{%A3Qa=nm|Cd(i8A_ zMjs5P3(Yll_m!`%iq)(q>w@K=2lWOteexO05)U;+gxPR2H};s{R5w~9PO)O4+<4mT zS1os-_rVH%>x_>b|Hx5H+8a-ZAflP6U1tmFXs+D&NpBClQE?Sp`}VBYdRLMiYCF?h zX|^j#5!{Vw(Zd8rxmJtKu=|nX&i#Y`^T};$6q%U>gWwd{;e$pY-5eUv2a^8=2uQuD diff --git a/Telegram/Resources/icons/chat/input_smile@3x.png b/Telegram/Resources/icons/chat/input_smile@3x.png deleted file mode 100644 index 5af6ecc1deac19f766acfc9c50cf7e73ace046da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2569 zcmcguS5y;P7EK^x05L!yFSJJ#1SAHC3{jCHB}gdJO9Vp4hf)PJbOZuY6qE;{Dj?`X zFhqIu8amQsC_$um2pEcy641dlYt4Ml-+b(|&$)M>vhK%OH^KDQO+Fq`9smHqhtt<J zKVjl8fVocWH8;#ZPY4)bep4Gj84zDP33T0UaURCT0QnOi3;@P^0zkhcClEaW0Kl0K z1aO`(@Yh*B$A4~#`JDgte+j|!R$l=C?f{&wmL(Cm>2U3_kg*WOfgWJhK6=B{NXSG< zKq>~yi5_l|8vA6bq+)5NjMd^4U|MSND~{-zZs}U`cVJIJ)C3jFnwvTgv+_#lRw~2C zb@gG8xaqHyh_1aLD;pbn>86E&=Pnc{^yr7~!gK16^9Yb^1QJZs_L~Pw|Ndgc>D*O+ zkRZh5E|ov`Dn~-Rtcor8miVLgwlvX8Alauk=I3yHuxRK_#P(JZ7cVVADwRkyk0H&r z3+St<mHh8AV*042&lPt{(~&lBu}QF!Q@pUh4t7^c*M{GeUbJ}arrFNk{8GKKw>B)x zIywkoeOt3&E%rcz7-J}v%a@;vO5=S5chW=;7)&O;)TVqa9wIq5_(FZSktguRZ-Lqm zE&M&V>$BPivXpN!ryK2>BaZ8AZuKw<#4t{!r>L)wr!GaW^yj=u5x-U#`Qr#DXz`7E zJlc5N2Qk!-Anq*ogv>OBAJWSmvAe&OL8|6B!#w$iShB>ax}b@`i0A~ldPLYG6Jtaz zvpmhiLQ_ODqG6&rvU#G)+m0F#XU|$4k_tLL3}WqXFO<a}t&bvEEC#)7<qUS!%p_0M zsz2}g6^5rG{(~+b1X5aTNPgR-aYIn%G~{>7FV(&_nh{6f`usxk$_D7r``XYwA5-_E z-IWwf>^jP>YBfvw&I1Vug>-f`dVv2?iV8D!v^;^(K=e0d!liSs1?c5GoTA`V(mw{H zP96e0wCde<$H1neYYJ$+G?FNc{MMBs*_ghvK3W=bu(MPg!y`srg6^N0DQ!h{`GkY@ zP+QN~^Bug64>oT<KsZCIc0o;asti5GNv$g^21chi4D0CVY%ESp0KaUVGvU|if6+4v z^$O{oJ}Vpt>3S+*CsLc?whwUJ0ZAyfyOoy^?bK<f&x#|q50BZ^m~vbYkP)dl3k`b( zLR%S?RXU<*aHZ}v)#vvTVtj+m(`c#d;LcRXX|Qm%Hm`?6b#T9mODT=&5d?CzCkqC8 ztZ((Prfj#+<qITuxT=AMBOh)5vQGHb7KVO$<C1O59FoODf+K(MSn<(rru<$k?|Xq+ z`@X1(+!cEH!sE(DwFM1L8quVvX~kvaF?wIy+f0h^xU%)3YeY2Ku=N6bZwVf8iNO`k z92r+}QRt=IR8*v8uIN6ob@rQC@Y)=d;vyrK6PJnGg2_Ez3bZ`OPMdRI#kkO>mGJig z>^6<<@U}QUna*3a{tJ2-Qsu5u#ZxCy1s8vA;rdc9v#xP&Lgn4`;k|{fl<O%Lr@N9x zUOm_tFZeoHo7O+l0%KpDekW9%mPkr$w2Ra!cfE({D+;t><f?e0mC<Je7H2uQB>bj9 zk}RaTtV=<*rxQQJE@f#UYPDm0xj!clq)FUD$p2wskr%R|m7Bt<_#^Le&xIDmST*5e z1!hQJ)}LFZyQ91FiC91qd!Sz7yTiiu8A@7WLTmo<Vc~Z?w;m<cP>GG!lT){jXEvbd zb*x{Cx4e-S=#F(yx>)_70X3N9cNo7|4&P4S%X|2F=iepdO`JbPgqyL;KvKPf7Ze?v z0wrWp@~%I8sumAz+NC6g)At9e8SVC7WK(#}M_5quX2H+7w$nDvQir`}&(sN=F0jZU z)os$rv@H{E6FaOzW3DKqhq}*X&1$|DIHYO$*d`}z#w#gYoC&KrA9P-Z42bbLW-P0Y zjR+SJGh3d#e0A!g{Blz7Y7F0uPA=t7SbGAoHkKul+!sPd*vP3AxD+bMA%EQFPu8W5 z#3o+!jD9Z@XPCM~&NfAx!T%K2yvNr`hLQc6kLttZ4&lO3lGf1ocN*5wCFID(mDe(s zMnYWf9fJl+8S?kJpO?G3IXT_foTxNx3};(6hVCh_#kvv&ck7D!gmR3kM^jwU+8w&i z{I9N8UHTG=H+#wJ5KuSpLjB9|bt(|m1{}V7D^GQFo9CX-UrxC{J`a)r&u;hfWU*jw zGh-uf&vN+9ep?$>f993p`)MSQF#hepeb9HRZgfr3rP>`{9VUebCmOJnUecpu-hjE@ zt_;4US9tXEjJZF0f$`4P@Sl%OgNrAhgTJpSDzp?QL?>kiuG}rO10J6&(~M{E=dB5I zxZQU)&B42`t*~7)q2D*S>pa&nYXg%l4WQr9-Vf&66AnM7BPFE&mfYW2O44|+kx6Kd zXe3m5Wnmoef191nC)D^A5Gp*LV37O71kyk+S&_jFl>rGH4VvV1@L@-9^ttln?Vo+F z=ukH@;5o;>;yc~vlbD)_fX@7^f?<m(`PL2%#P@#Y?^;im-aX|-xMhGpr~bM@??Tl2 zJC4arto0V&XJsJokiEyuAit>Yo}8(FSgwZ@Fb^=3Q9L-qu#a26SRl?+9_KO*Jd{(t zLbU!azsH<`L>P_RN{9blynNe~ijo3~tE?^dq<xl-GX3zZEs0)goP)v2<{5Ha)lvW} z1aQaW(q!CYB~%0T9c%owPFg71#qgHoGH06U5J`<by)x=RXR?TLosV|OUdW{V&m+OJ z2t3UB;;{g}4(Pj?>i(y}QoiL9;l5cCdVbLk4>NX_fVn?{1ovOg_P~Stx9vOY(dqEB zpAh<&O7Hu_#^WHt=M6<vyRoQF6POgLSOtakG#&Oz@Sgg3zm>FWR?|x9snmTX!_L0< zk2H9F=d1?TwT()wcLB7nL~UlwL$qnkPsBM1cMRsj5oOq_1ejZDlrQx|QKT%nEQ>F$ zMox<>>d4}$Tz4+X+lsOYi#ZpZlrfRrS4mP-!Q(Ko6$5v@ePU91@x4yAP*?iMVTSl( zD<<qO8qWvxI6bAWLF|lU!B&ZY>qmPIJ1Ty=un=8TmEjGeWrPK`Dk)1BRZnd`0ZdVW zc4I!A)&0`r98azhGP~pFbnY7XoS~H*hkm}jSICzzbp9tI<R5N50&1#ipI!Tn1#1oj z_@fWdcPiyT6|McgM&RYH+|fe*KAtPh=yREp0oF;{O32Bh&RD02K0JztR_3=W2K_%J a>`T6>K}LZ??IeDE!*JMJx)g1v=zjr~GSYMa diff --git a/Telegram/Resources/icons/chat/input_smile_face.png b/Telegram/Resources/icons/chat/input_smile_face.png new file mode 100644 index 0000000000000000000000000000000000000000..044bb0c44d08019a597504dc636e70cddad92d8a GIT binary patch literal 453 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<Ev49!D1}Ut1mbM0{OyASRF(iWX z?evYj%?1Ll`hsg31VrArI(r`#(>b8Cfk8oqi>qzH0%wQ%je%Oi)_&_ay56ZI&#X_c zx4kWNhk?O$cg`LC*Kaa=c!SD2IE@^&7_)5WJF0o8I;?$ee1c^q%Zji6YuP^AwCCP- z_?9)p#Ps{!h`PY~t1XsO6st@5CNFz7>(~-qy)LG2wd^-nCtkV|bNPpw%RhBP!K&pG zx9wP^l`)I$kb%uwVHU<KrM#(;^S1cM&*9rK<G`!f>!rt4XFivlta4DJD`3V^i@w5H z%bX`$Zu^}1>CKrvTHkE=+Y>E%_Ehye?%4PK|G5PwZ!P$iWp?G>ezZwv$2#qAwf&LP z7QWsUQhZ{2M1UIWOR>pe+jP>Jr!Cl$=@Za>EGVG~jX7QY2g8pQydsLJamAn@^mO%e JS?83{1OUAKrIr8y literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_smile_face@2x.png b/Telegram/Resources/icons/chat/input_smile_face@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6386c056dc9262d7e8a61c867da68bc125e2427 GIT binary patch literal 894 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-euz(rC1}QXr;N#E0!1Th?#WAFU z@$IeseTIo5Y#*+Rhj3~2daW!Hcj|CbS#+f<>=ECc#yCa2gRv{7{&UgT()6I_y|(m8 zD>LKtzDTo5wV7*d&wW03CZFGVlVCE>P1$IN#(Ku$j#^HJ081_gmIXl$lE+z?0yG!& zBr`Ft5D7SD*vJsVrSW)%1A`XpltLc`hN(=aEYt)VycknuggG3RHW>ABlB(@TIOF4w zJ6?YOowhgbe%bdMSNCjNV7b23^!)DnkAJ`3ZMyjQSytS0A(Q7@*FE}q|9G)_w)*>v z@54UtngCMPY}fzYL3{bldu$dmd^aoYSKFG|zOD?bKGXVh_w(O<wm(i+zd74*=l9HN z&s}$I%S_iNv!1P7EB)o?iSP^m4v4ut$kl(Sp#Hm4l0oag7Q>&_4M&qM>WFb~j?weo z86y_oc6!H}=*1ygouAk9RNVXd=3v~t%~5OL$uH?Yt{kc*djGhF2v>=P%)3hNm)6pU zw(gNTe%|xe$(_tQ*q2}K+;vyab@4@cL#2rvTW{yydjI|YraWEy<2);$+ctcynyWYc zv^mH3(>Z2~<JP<XteF?IQbbjV^VHL#D=$kjtmba2+ujzfts-OpdLR3R*n-VBmwf(t zCg6|Qy6f6oqjc@XCDYFTW{^$V8nyIn+T-hQ?rp#QcE!bv7S6}zZ@)8H9Z50rY(Cg< z=j@T}Vqt5kUX8hazxN&p_-*%U^_*6dtx;!hSZ_L+wpnfR$r%wkWxsbA%|CxO#mMmS z#~V4@qhJ4%yzS0d$JY5q=Yy}yv!lBhPk5xTGMw5IVa_1gxrV`Erub_%1?7pN3>u%4 z?lPpPY+_<ajawt#(Bl%uu;5I0Df5JhC%71<RBp^;FcOMnY%tQl#_Qmwq|FdOhGGBN XlWVO_gikx%0i}IUS3j3^P6<r_(dciO literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/chat/input_smile_face@3x.png b/Telegram/Resources/icons/chat/input_smile_face@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..faac7bc86f748aa9a9398f1db1869d187bba7e37 GIT binary patch literal 1356 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ASip>6gA{T+3Vp}Gz_QKL#WAFU z@$KBbUVj1vj=o=ae}2c@1xpjsA2Z+RY;EneYuUSYY1a=nF|oP@Yg-k%S|d2jlAdy} zXl-58y1?Lnb+cgM<22*lKh=vZ#HYQvH+$yX;<}kCyR{<<&vn)pG*|_&UUiL(=Myma zvC!cF3(q@-9X4eKjLp^zdi}CK2OjV>oK9vJHrT=Z;M0wRgI4-XSwVF4?C7|-vg_Bc zzx(>t^j6#T#5n6uCswXqI<@d&z1fVa3W1`!Iz3P2Gwom3%=&a<=GL}3-?p=w&v;;M z+WGc-OvL%DFgeT3*Ct&4_l@EA<xl2kmZ&G%u8ypHawf`I?sxdUNxypM3a;Dv_wV0& zu|MCwJ!4~I`xoOMbwWA)((ga-Ehn2tmY0{8)_O(zbNO|9M*E+NY_pu)z|YVBx@!8i zc_MSfCU2WJWwTE4%=O{>J~$<R+VS|Ta{5KP+P(D~H*8U3QFwUH{Ep1e*Xdh$*B`E# zS(KHX89L|WY#sf1iyhg0;+mze9J{-RnT4lLLI4pvPm34q-FtWKx^=tS+u6VW_#t7- zU%Xc8-{NV}(b3&S$A2E4b9LdrJ1<zpg<hXs|Fieug9i&%o<Fd0TGm#r>gsCKpMM!c zJFe|mr?%~=L)M~%kFlZ53O;Y%zc**y8MN}mwQFHX?>@{sdnG18hwp*IvllNs+}zxZ z4ogT$O`1D*uFH<ZNe%xWzqEQO@^|u?&!0Ez=<0fghlgvu_;B^vp+ik~?%)6K6ZpBN zw$|1(yfHRvy;-&Foy(V%)6>&U1?v3w?AdeT+qZ3J&YwU1<jE2qetz%x`1w9QJ}1tc z>B-5<Th_#D>F@8qbocJv^Ey-GHnUrlcs0}%6@8kWc{)BmCdP%`)pPgeiOi?f9wbz} zSoQev<7v*l!f~;&Cm%dWP*$5(DZjgc`N_F+evy%p6Tg4|KEwO+?epjTZ{E1kaZaes z+T89%4s#!4Pk+Dr^;2mt@7=j`;>wkf9KJ(h%mqAEWo2Py<>ia*nKDGa9Y5~=<zrT1 zVW5?jmB50!?1sMw_ix{R{p{JZJ72!cG&eJQ=H~9cIQK`kcYV2REpKIY^=mzS{ki|e z&e+=8{!C`cKfrX%d1>&G_{k;vW_Bti7_TU4720sl+&GXWhi&S*0H$j{)6}{e&Mvql z>UF^C{uvj+4U!?doH!$%&z`BlWY+8zuEF@a##q?(Kw7{{F4cs&`Du$dZt!TuE@WBr o`K*r!<7~&J+EW@z2bbY1>LZre{%PTn%mS5Up00i_>zopr00wJb^Z)<= literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/history_action_edit.png b/Telegram/Resources/icons/history_action_edit.png deleted file mode 100644 index 27b1e77a918e59bc1a2055bcd57a9799c5372bee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 445 zcmV;u0Yd(XP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0004mNkl<ZNQt$U zJ+6X46ot=RU+mn1wXKD{iH*I)V55agU<D?IHJG>nH)125g_VgtrHL_tsOJ^FnFq?x zpy4LB050ddoI3+307Q);b&7ul5P9C}I1Wyy6Fkp*yQYJmrsFt_qKG;?&m#beNV2Y) zsJ?kL8l}WvAs+*>Ixm6bcbZInemou*ij!m*hQ$C8VWZJVrA_A~8OJdvlZib*cW)ye zMbWExF+hvk*b>JvUDvf+MM>X3w51&Ir7iD(r0=%2+-|o#Te1PVXgZx*iR;b_#F=_& zJRT1KK&R6|v)M#m-0yeHW;1Ly8;A&$Qn|IM84QO*_WOMT;BvVv?c6f|57z57&*w9H zy&eH@u~@tf)WY+8z;3rA`P}N=ZkGV)`+lb9l>zN`o0rSQbQ^}Dc@p3EIiJrnH=#Aa zb=}N0u4j@IuLAhdlO7BPX6o1L6^FwC`~4oRR?B>$%2rEO7=Tg=tJMnI?G`~0;CMWm nA`vl5xN0c;`BYcb{#Wq}%RU(QrQn)>00000NkvXXu0mjf$xz9D diff --git a/Telegram/Resources/icons/history_action_edit@2x.png b/Telegram/Resources/icons/history_action_edit@2x.png deleted file mode 100644 index 13571d1b258174f243ca849684d1c46ae530c92d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1011 zcmV<P0}T9$P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm000BJNkl<ZXo2ln zJxlCB6g`>Qr0`eRSP7C82A4$(|AOrzf`3J96dOSsOR==Ew6n3%6=Nl8vn+~XC509m zH{RF03Hi=UHoJMkyAWm#!*I?$=ib~z4FKU62WuL@xbdGr)6ob4##n5%EDM*Hm+1HV z=ytoA`^4|8plM!=F=C97VHgB}Ow**7mlqNd1OausT>?P5u7`UWW5hYnI&-=N0EFNd z78YnQ7*H}sqY<sIuct`loHrfNx~|8-o2HowKB~mV#zu?;W2{*P1o*|pMS6XG%>`dX zqA>!pna?62?9{jdrfJgi^RqN~5z(5EA|R?=_*SgqzVKu6itb6Swry7eFF_(Lb+mN} z?{Ut<OWaGgxvW)WUdHofF>w6p#U51Ud<RoXkh#=H^CT0bSQ)z@vo9NC?YWmZem% zMKK%>snh9{6R2XFByr(+*L7<F@47D8wk=o0|B~mE;H5^1sC{HXPs;P2=Y`;fvQi3E z_58T4G8(SyhMpIAq!p-Q8{4)gVjJaQuIW}e91cV9;^CMxLgW)rv5o3%QwDx{dAX=y z<t&t!=}ETAXyz5b=eN-&TV+APR##wJt+J$G%Lz==^PcC?%F0U2HqnW<_8PElJN5!T zPPnd1?RGoWI21Ckra-&ho=B?{;1z9?Paul~0NUT*uLZp4d9hZReB#%pfH+JLFbsoE zPEN+od~!UfSh(d12+v1f7a3!8e0*F9d{(PWw(Qz#;Qs!e_V)HtMXsuZ=XtcWv{VSb zHU&hs=jP_f_kAKF+TPxd?ctnDlSsD8qA^AB37kzp$hfw)76Y=gvlAmJLn5zL7Llk; zfup0N6riXQ0z@8(tX5ezrqo7&F-Etyx0%4Uwzf)1yuH0CgRe~i05m&0OCKK}xz;FB zqNr6)MF9Y`xw%>BZK8Bd=(<h^2M5vxoA&{&76ONdhouCfc1mVX<#n`n1vux_>-D4v z5E1R}?$XT6Ol*!X>hoovF2^P?KR-`jUtdz-ecz|Y$4BaPI!azof)`&xDuAaJ5@A^u z48us?1_1cJkB5f`IF5s}voko3gQuq__`VNK(+~s!IOhn00IgOFqtPgL75g+z!_UtT z0EG8cB1K?zbv5?)-Q69oudi`+b%pcubM*Ute13k$_UO7^2|VXK1kV`5_xHCFcmUAw z*GD2wPfzjw{*H@_3pkF0UayzR3S$hKrh$kGLHoWB!!S}ACn9K?1^}@$IOobFQUrcm h{QpmV)A8#G`~d}~!uw$HEdT%j002ovPDHLkV1f<j-4*}< diff --git a/Telegram/Resources/icons/history_action_edit@3x.png b/Telegram/Resources/icons/history_action_edit@3x.png deleted file mode 100644 index 25e820122192efc5daf4d933fa162c82ffe3f0bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NB%P)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz>PbXFRCwC#nmek(P!NX2Tm-?l7Oug@ zEFiiLdsm_sf{pv|og$@079)xl79uv9+<^-szA}jxGr9j)ScEA4nJ;H1ha?gJ0002^ zKDk_OI2`VFyX|&67z{F*OdL{GDwV}z;omSGk149|RE0ueHk<hmP_}eBt=Pc%eD1$s zGMT7!^3lMjbyMbKwOX}p+h?H6$yTf7v(e;aDwXPXyZmfaw|qYT>emc8!7i7J>$-+v zG@DJ%`L$Y&K9|d-#443aCDSzT4t6>n&+|A%+iW&M*FeW{*6Ven(YQUHOeUF5(sxC7 zVH%q5^rLLOUN10Mzu%YMg-NQrx&=Bp8jZ5qtb#7QTCFV0Vme97J>8!Q=D<)+(n2q* zR`2IPrjul#tdWW{2L?Jx*F}?oOebmPDwoU33}iZ~(!i%q3e6VQz(@A`eZ5}S^eoJT zsx9lj`ouu3U6}v4rLPMIqPPpgKo|%EVc^FGird_Uf$wT1YeN?X!ax`()WGR<8bbr= zzCss<fzc=yi!n5ip#Ky%1OGUm&#^M_%JF!Nm4TlJlm41Xv|zN}$lB%nN-)s|Mh-@) zbiC*kDwqNTBLvf7VAx=447_qWos=5*6Ie0N^WL1`I8M9WJ{%4S00000fPc%c00RIn W(=22Wh`ql60000<MNUMnLSTY!mIp-u diff --git a/Telegram/Resources/icons/history_action_forward.png b/Telegram/Resources/icons/history_action_forward.png deleted file mode 100644 index b48285c295d39b733e00268b18250ca09c96fab1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmV-70m%M|P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0002~Nkl<ZNQvc@ zF^YyT5QXR0Ml8fWy`%})q_Vft0|<6rz*~3>OR@F@cD5eDR<P3|AiS;oVc8YpZUPJY zV45&*KA2%9FCqe2lw-5_KLrFqK#rqtdkhdlP~Z3Dd7ichPa;VY0Khno2*Xgv^D_Vd z%<~M!m^}ahEXxAU`ECB|qI6xS)lbej#c}-UZQC}=vW%u_x}U*sbsPusJil*|37zMO zs;cY(E|q0z4=@Y^eBZYNXxkR9>we!NrIc3Osc9NBdmyF!QoOEfbBiW~co$Ymp(qNo zn|uNGeMg$6=5LV@0_(a$N~!C424IXKiX!d&_bsxezxQQ6)d%EX-|dGOmfQdU002ov JPDHLkV1j1uhDQJZ diff --git a/Telegram/Resources/icons/history_action_forward@2x.png b/Telegram/Resources/icons/history_action_forward@2x.png deleted file mode 100644 index f3eae397ea13b7130bdd80ea83d58e06e96e1b9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 600 zcmV-e0;m0nP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0006XNkl<ZXo2mR zKa1)>6vfYmR|Kn|g@u^f#`OLn*a_)GLB-N4U}q(^`3Pd=2hh?|Foh7(307KISSTVE zS_Bk@aJSmWzV)GNj2QzSxXqB9oFB~0oyi9Pf{(?q4)Cbn0Ic-}V68X6ZzM?)0)fC& zygdO(rBWCUhX{p2u6uhng%XJb48wpZimv*4x&~sg7);ZIBuTD$dcKGwkqAbk5oB3* z#gEA@BnScqg8>SK!oyz7wo#tv(d+e~s_H{NO#1-GacH;O&@|1thl_a}tJ!R#)9HZY z?vL~5=Rl*;a3$1tWipwY&s3{bgu`JJi$xp`hr9ggd$E>jniLEM>1VDh^;!T#M6=n9 zL{aoF01?q@wW4@D?q2{RqWykPvMl=-fQaaLJW@WN_b&huk!{<gs_K6MKE5daZb|f- ze_3|B9pdpg*6Z~@<1f+3ZfUt(BArfOg#Xe@Poe2_O3`T4pKHJ{3=#yvpSw`M-zT2u z{n-aPoepsv=g&^8Y1%#Kpi2O^i|2W&*XuWf+qO-$TFuEfo(lkg%H{Ia@Tb#>N~Mw` z?~D6@*Rm`Nxm*sqt~+|4DZqBSg`y}JkH?NZm;o#n3uLod%;)ome3%?1CX)%0$>hV~ z0R{lNu0xh(Y&IKL{5%EdcDqm%1(s#G=II$gtJOlKQo;FrcHR5wec%@dGRJjsq4Ar1 maiO`UHvntB0a)t|z*?VlC%C|0+Cjqr0000<MNUMnLSTZTPZ^8= diff --git a/Telegram/Resources/icons/history_action_forward@3x.png b/Telegram/Resources/icons/history_action_forward@3x.png deleted file mode 100644 index 06145c061ab524c4d0352cc807c7c0164f950e9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 504 zcmV<U0SEqxP)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUznn^@KRCwC#+ApiZU=+vk=|GeP!3B%O z<cbz;TiT*gY_qy3MqhwNS)AGGnroU|)1t|wL4!eIVIac%bGSEOE*CE1ew@!6kgv$& zcFrRRj4{R-V~l-_=a+uJj~PaW!(k$k01P9J<0O+wz%Vi%k3~@g3?q}tB$Y}5hLP!X znog$y!^mtllOzc+j4T$5Y&HuRMwZKEE|&ugBkT1#pU(q^k?nR{C=>w0$ZoeQ7K?yk z<Zw8YN~M4MU^<$fALDd7mCNOKf1~elKA)@ADqt81f<RSOz%UYqp|0zIVdQ$f*6Ve^ zFmk)y8jS{E7>T0DG)=%Ta=+i(?KWT-c|0DSP6sgTTd&vqsUqHkl)nwR;__4McDsOw zhh<rS$9=2S0{nw+Hk<rN6_bZx!!Q6}TQp5Wdu_Q~F11<>a5PY<RM3Vw$Kz2^6tpST ze!mACzhMqcH=7OMh<mkK$+C>Ll<<8YaE&yd&jA;Ip66vU8MIZc>$-r;b<FD!qtOWP u0*h_ifLD$NgMq*pV~jDz7-PSC2`~VO{7}5$Xpigw0000<MNUMnLSTYpAKRk< diff --git a/Telegram/Resources/icons/history_action_reply.png b/Telegram/Resources/icons/history_action_reply.png deleted file mode 100644 index 53f921e0f9ef667180bbc84b209d928f106664ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 446 zcmV;v0YUzWP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0004nNkl<ZNQvc@ zudBjP6vw}AWf;UFD9a8FGYA(1QA}%sRj~OFESX=kn5{NN1dB$Ce}P!6qH#ro3j@dZ zg~uMsrp_PZh4+P1F6W%jz2}~DZwvqtp`<92{}X^DNk}9T(Et=h!FW7|uIn!YfS)e% zSu&aAa=9c)mdoWppOEHXR8^Jh^_reO3drSh-0gO})H?tfz?WC6QmKT+Vu4I1b5+A) zv4~o&hC-o$bUF>&wsAZjpSy#4y-wS<8CKivmc3q&nx;K2QM=vd`FswY{8Fy#ayT3^ zl}ZHzTCLVgiGxm*&1U_8cs$P8Z1xeze!mYo2`I}l2ZO;!AoKbBcAIv)-J5}q<M41e z&~coHkVd0%dk##~^aThZu6z_lp%8+dPKTS##^=9Ut?o~9v)R1bsjlmHTd`89a5Ngx zb=}{Xi{T6oG7N*K(<v0dvwS|!$z(#3?DzZk0RSk3pk-O10iIQ>RZgeV#{dA-G>xjN oKK};EG668=QKHh{f0^&<8#_n(9Dd1JU;qFB07*qoM6N<$g2rjhMgRZ+ diff --git a/Telegram/Resources/icons/history_action_reply@2x.png b/Telegram/Resources/icons/history_action_reply@2x.png deleted file mode 100644 index 84fbad64700ed14843b8ed1df88433795deb56f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 864 zcmV-m1E2hfP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0009gNkl<ZXo2mR zzf0?E7>4hYb0VRlL<=I8cBz662E<8(F5=)KLPg^26c88xfg=_dU8E3DT%3x7gWysq zh|qQt1VM!iDToLbiV*yPqTJu`(T{5BXObq(InWE9B`wc;Uk!bq<oyAF_=^#O4$v%Z zfuLy%1Wj82079V<6h-l(m1CL~2#3SS<#L#uob)1(W113(#bU_k^O%^J@G6gETni|Q zf?~0V;o)H~^Ek$pKq8UA>FFsVk%-rM9OGDEetsU=Y!<RC`;o^njs!GK!@<D;BuVls zk7H~Lq*5v9x(-43vqs0S@4g@i0(N(Iv9z?*iad__3E+354@0344i68Zs_M6mL{UUE z8pYt?AbNUw005R{f%Nxv;|ChN(De59;`sQusXhMr`5Bj&m$<mNK%r1Tp-_Nf7_R#@ zf?q2Zi}CvUntpJ1cSl{<IXyj1Q55Uec2S=%jE;`-_V(77@LwsH%iP@DWGoi*Mj(+$ z@ZsU1CE;s%dwZj<>kNm(?gf&`Bwt=$S{}ZZN~OYNGU;4E(=>j3d;}W4mbJAt`vL<4 z1FTdkL1(Tbole`o10y3Nyt%mvI&&Qx8yoij1JP)dS65d-XRbq4Rr|Y;EX&O2^Fe3+ zgk@Qb$K&?rfFwygIywqE_b0O1tbG9h6a;~LdwW6W{>0qeoPB|6rBW%!3q3wQvQ#S3 zG))?YLDMu@E|;4YD3wZ-B*~co04^^t+nN!ptE-I`AOMP@$ap-?+1XjHudlOMEIOV& znx;7y06<k$`TF|$zs(mH7oAQ}_Vx8~Wo3nDXJ@tq48x!x2+jonaB6CbmSuS=P+MbT zW8B~0|MrKPnVE4X0D$A;<GjDW_eG%gu54{>HGD41WHRmr0B~q%h^A@!CQz;Z{(f$6 zZ`X-DJw36rv(vpmwfXt^xvv7%ibNvRb)E0;@Bgj!WlMF}xT~v+xm?b7fods=!jqE| zlH|_LPV)i)*wN9!OeWJ3fod%*EHIbLc_RS)JGIm4bjt()u)Dk4JArD+vfMI(+G=>L q^0)A3ofEpCEf6$qfuLy%1kFGCtxuAmha+?V0000<MNUMnLSTXlDwy8@ diff --git a/Telegram/Resources/icons/history_action_reply@3x.png b/Telegram/Resources/icons/history_action_reply@3x.png deleted file mode 100644 index c6a3db9507a794414a65de0d83c83e9112221de3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 994 zcmV<810DQ{P)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#gh@m}RCwC#nLkK+Q5460W?7bD{v!K> zkr7COgQY_y4mm^(QAE^M(%@)F*3eL+VALKuxJ3{IQ8pDgG$l<%e?$??f(kMzD9YQR zAHVQNn$PF+d7s?R;G*91zW2R%&iS4z5D*9i0)apv5D0}wC={-)u9ljr%F4>k%}s<* zNvV>{<$k{(eNj59+S=OA&d$(3N<@`Tr;Ej6D1}l`H83#n^z?-Ohxe*17R%e)8~Opi zRV^(oC6h^%4!%kfi9{Zc2W5trs%mO#d_Eug75=GeY-|h!0w^0iQ`Ono84iaL4fv$0 zudnan;Q`SDlWQ)^`irr#vF+_`sZ>gH#q#oUgTY`l8ns%jTCG+pl@f`BqNwNRXD)?N zzkhAB*`Q&EwmttyQ1pVq;Kjwo<>e)`V8_SD=jZ28Gm7IuP$7AIeB9mLbvPVwZ@Km$ zoDqpcR#sNJySq8^Al!3ua$>bwtE#FB>p_ae?d|RS{5)&&y1Ke<Zf+1Z!1FmVF~Ncd zNhA^oKeo2E8X6i{_8^!LjYgT|mEPXoOdbRuUS3{CMn-bhHak0uzqZ%c*U{0@oVCr& z%)Gz9<FBp1zdvVfkb>Z!m}`T6rK6)GXKh16L(r{at_><}b#-;l+Ds->JRZkf+xq%C zlkBh8>+kRHG1q1`o0(|SXf)T?*EkEiy1Ig9oryM;N)-x)aMrfCxX7?0X>M-Dp(oA+ z<bq7y7L`?7eSN*p=YzM7=3YRHaCdhHNg1jlG@tMZB}6P1!&efCL?)B{`9dTTvC_D^ zTrThdOoRcowY57tJCH4D4I3z0Yr;;qx3{a+YA8zZJ{t@MrBca27~C|lTCk4{HwHF) zZ*Q-lQ>xb1)~Tr}aPGMl(uIYEyl#3$MFpLCLH<t{Y~uL%IK+adnvSqoEC$z>->|N% z%<Xn_HdmC(<#Tg$!C)|}w#muKyytZkwYIj#<-k-Z6b=p!9vmF}5){%KJHeKhm${z= zgKym5-%lG9a&JpZ3p-&lS-3)v277vX4i681#&tLxti?!c;kcQ6f}1`)Jxz^!bacdK zfS(qQ%Ogm;-Tr+D{_*jlP$<|80B?3`<>7b~-_+E!v9a+t^z`&J%d4kaxS|{y4G#}T zqfxKd%l^{e7Oq$aVs&+OPN%a_r=DQp1SJVrI01n`AP@)y0)ap%3ZDWD03%s%JjQ&j Qwg3PC07*qoM6N<$f_?7O&;S4c diff --git a/Telegram/Resources/icons/send_control_emoji.png b/Telegram/Resources/icons/send_control_emoji.png deleted file mode 100644 index 14059672779e549fced695a6e766158f351af620..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 251 zcmV<X00jSuP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002ONkl<ZIE~#> zISzv`5S&;R6%94t;vf8gCs5G9b1cMBP{1$TP$5J+kyk?DNN6Y$Wu%c;JL_F*)(-&0 z4X8Vp@rMnPB!RV7&Lgfgt{lga(=^dq%WeKj+r1#mGAzr2JkL*4K50Mn50p|!(-g)S z1VL~rbh)xDQ4|HV);mqi&|0%?TRJpN!zhaA`@Yb{bzO^ARTVwY+Zy?DGz>%beJ_RY zFbsp#95%FHm+KzKF}tqgJkLTGyWC;2@XK2O@C0HGsw{`A|JDEi002ovPDHLkV1m-C BWu5>4 diff --git a/Telegram/Resources/icons/send_control_emoji@2x.png b/Telegram/Resources/icons/send_control_emoji@2x.png deleted file mode 100644 index 15ffcdd5f9e5ad0a70b251a2776f7b8ee173c53a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 528 zcmV+r0`L8aP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80005kNkl<ZScUCX zPs;&e5TE5h$z_#`T8`wV97M^{#aVmu4IF$4-+^)z;Yhjo0Lo30QW9wuvL)ruyv<YZ z_U^m?*o(K`otk#%X?`=mXP#+>0|0~%itCffNgM(W0f)ffCcyJNv|24#uh;BLww2hy zppn*UH9Visijr;SZwF&KiZ`1L`2BvxmSq`2p^)iq-o^aw9_aV`Dtj;(SO&C%F?=7M z=ds)Eq9jS!>-8`Y2w3*OyBKF3I|OtIq|<5G@AphInT+k=`Fx&D#KYkLrBVqvj??v1 zb*0<w#!jb$B#*}f$Kx?ZA`#2(|E<Yn5|76te#?A5M~}y&=c@(R>vaWqy<TxR9AY>e zemQwG8pZ8)i_hm%iPLB_jQq8Sf*_zQ%L@2%x!`0n!Bi?`nLLq5Xp&Pbiup1D(qgfw z3Ua^S*>IudlFeq(=kuY<<znjfdNCG@VYOPt)9Iwb(Lcp9A#Hq{a=Fa<;HQvHNmi>B zd)L`)hKt1lce@?lZa0-LC9hN}R#h`EX`xVHYxP~BvWuelO3zp~6}?}x+0;}+PsoRS zP2Sx8E)WC(L{WrXE(h^=9D>0hJC`I0rqd~mMk8pq+pt_NU!E1tGWzEP0N@KEpne4Q SwK)L*0000<MNUMnLSTZ4j{L0v diff --git a/Telegram/Resources/icons/send_control_emoji@3x.png b/Telegram/Resources/icons/send_control_emoji@3x.png deleted file mode 100644 index c3fd4cc403a9361dcecac3b5cd0a71d8ea1c1fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 722 zcmV;@0xkWCP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0007+Nkl<ZXo2lm zJBuPg5U#reg+&AfABl>X2?izxW~PF_KoAo{6Gc-uG*V2}%*fQh)IbDtH53IEAE2P% z3m<jVz`?pZo;_pV%?k5D!O-3Hef4$E^i0nW06=(Q{Cwr&K{fCVpc+sbssW{;8c-Ul z0i~fDP#Tg9Sglqll}a!kkA)}}i$a@R@Zv+3Fc=Kj?RIgq+1x~@(?O%r$i(q6c=0i> zKsucUyWRe!-QjRRDwUEvYrOcF4>Y}A4{SD@Xdf-V+wEo+<;lmq0v3zqZl9d{mKPth zfyH8Rw{JF^F$?nMV>VE$)nxVk4?d=+opuJXU4z=>>_Qy**hQ^YE0)V;9FNCB&^5J5 zEuJTz`ddY5s0NhA%NzIw0Pl|mX*3#$$KwzRg@o7qa=H8`#kgE9NG6k@)oP*N?=$B; z+^Q0Z1nzb_B&^qKj7Fofx6XSJi^YVk^m4i2WHP}_CWCKpZ_mF6bUK|t{1N;89_@BJ zx8jLwyWQSsOs7-ycs$P=pzrq6>4cxrZnwD^Ag<4#6}MO{&l{k~=ks3#2+d~m>Eek) z42430mE-Xsy<U&Q;ZRhl)oQWM)J;$h<%)r{NRyiZ3cufv$Kz2{a5NfWHk)NGp2kv+ z7)XP(BuobTtcAm2yk4)O!i0XmkGWh9eLf$WOeWN7wL;J}wNW39xd-AV4T-BMZRsPC z2%gX9yG6(aaT2$T=Scd47YGD!KA&?dez8~xoN{WBWq`tLHe;ny`Rgt92$V}4#3i>5 z*#`cE*XzY<wTg$sfw^!ROF5MLtUk8`jnQa?U@!=QKma@*4>+Ap(ChU=-)6Ic!C(NL zP6rx|2Gr|ySglq(WZC}<eOIXG0i~fDP#UTMMF9Z60Up9RfYGcQ>;M1&07*qoM6N<$ Ef<~xLO#lD@ diff --git a/Telegram/Resources/icons/send_control_record_active.png b/Telegram/Resources/icons/send_control_record_active.png deleted file mode 100644 index af937a2014591dd16c3b53a729f7c251298a8c6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 869 zcmV-r1DgDaP)<h;3K|Lk000e1NJLTq000sI000;W1^@s6_f@Si0004SX+uL$X=7sm z04R}lk-ba9Kp4iKwo0X~bWlOWAww017K(y6Y8|>1DYb%Cm*kqXLXw6{iWFDD#i8IJ zy7)EtALuH$DhPrih&cEc__auh?=^)MY45naKc3w4a`znI&*;ps`;vfRIy{$7Nvnl5 zDfo^M%_v3?mYHoWWV0Fc96yiniTaQby?XEKI>JTOW`J*ic$Qh*A)Y7BY&n+TJ>p?j zmW#wE#BojsB)%28;_;i{lE+_yc|NxY_(e~t)WJ#zvobe`r-);QUS+NGh?_)Ib7h(8 z7_D*G$RUjsBq*q26FM9usG5{ncVDvUTSA3AjJ_6X;N&QwOx`%DRqCoz)ks%q>Zpm_ z|L19IGt*x8NDAnGb=}ViAh-`S>aP2H>bi|HAn*cQn{9uEa+>=lz24Sh$3S!kxV&j= z$^me-3yeNnGMAfDfR<m=fR77cumB9*1EF;<tJZ&<KLPn|dZIhv@CcZs{n3p-nigl< z2dx{$00009a7bBm000XT000XT0n*)m`~Uy}n@L1LR5%g6R56aiKn&cR5Dk0-(I7xT z%R_ieen0~cfG1GUB04(6Gt!elLnOerGs1f$Y_cbvbe0wydpsEvFOe}u?D?~ORWWor z9kbbNOqOLlh>@!^C@P$8w=3uKS;~eSYGwTEdyxc5`*d<V9uLQ*F>1divg458`FuJe z4X)R#mnc$M);qIJF7^dcgtnqp{@4l&kt!}o<a@d{zxyDOf<wywk4Tk-Mhq$ffs*N% zKHos{a5%6wolboiQ>CWdC%SmeW|PaN3u%>nm@Z_y-Dd56zq5y<9gjyU8I4Bt`~9BY zBAr5wce@>Py<U(0bUM+KNF~l@GbZCWHffp`M#6Z=p$1c|_97=%31!}HHyI2Dl!{x9 z3wpU+co6H&@AvxxEDXKiAQ6J~dd)(TBr+ThwfN!jAYfqVd6E1OA+g<V3CQzYmdm9~ zCKGA3T0Dr6Lk$M(8%dD}h1F_BVBH5WC6Z4f0LJ67EEWr~Z-lFX^+1dqY912t{vTPO vGpmeo)lw=jctsQ#^1LE-p!s}WS9Y~;^VPRZN|E~R00000NkvXXu0mjfH3*0K diff --git a/Telegram/Resources/icons/send_control_record_active@2x.png b/Telegram/Resources/icons/send_control_record_active@2x.png deleted file mode 100644 index f854549ac5675770634baba8a770f581d514ab09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1396 zcmV-)1&jKLP)<h;3K|Lk000e1NJLTq001Na001xu1^@s6c}Lh40004SX+uL$X=7sm z04R}lk-ba9Kp4iKwo0X~bWlOWAww017K(y6Y8|>1DYb%Cm*kqXLXw6{iWFDD#i8IJ zy7)EtALuH$DhPrih&cEc__auh?=^)MY45naKc3w4a`znI&*;ps`;vfRIy{$7Nvnl5 zDfo^M%_v3?mYHoWWV0Fc96yiniTaQby?XEKI>JTOW`J*ic$Qh*A)Y7BY&n+TJ>p?j zmW#wE#BojsB)%28;_;i{lE+_yc|NxY_(e~t)WJ#zvobe`r-);QUS+NGh?_)Ib7h(8 z7_D*G$RUjsBq*q26FM9usG5{ncVDvUTSA3AjJ_6X;N&QwOx`%DRqCoz)ks%q>Zpm_ z|L19IGt*x8NDAnGb=}ViAh-`S>aP2H>bi|HAn*cQn{9uEa+>=lz24Sh$3S!kxV&j= z$^me-3yeNnGMAfDfR<m=fR77cumB9*1EF;<tJZ&<KLPn|dZIhv@CcZs{n3p-nigl< z2dx{$00009a7bBm000XT000XT0n*)m`~Uz0s!2paR9Fe!Siwp&F%V9F{t8NqiWiT1 z(iae+DA<dF;!`O49-b_H0>z`Wdhr3gRA_H{@FsfjBq%5ztnHzo`SV2+liBQUx~VAt zz_OXi%s1c8WW#22j4>{*Un75h4U<nIf3p%=US4Jk3kz&<agoi<%}JQ^^K;g0Hd(9H zVyCC4S)pai&^E7YY;26z>vf(cwOWmjj*fa^lH7ho5VW$g!f$VH)41q)_#%cROw;`c z;c5FJqgWvZRqcb6A0f1W%KQ5}H92*(c%X#D0*)l|C8Gj?;X}&nwa42;C6~)lQ=;y| za0Pe~!mAxBz8H8+lrmA;?KUeE3W+{xldrF@tXM3jxiK$7VjWmO(EDu}0DNM+T4u5N zW+f9LpC`@AI};(5jJ8tQL+(L@T!}+T1`+y>)&&uARVdUpa6+L~8#tjKtx)I!c99RN zP*Quck_{r{dO~GOBCC8{_irQQO6FsegpQAoS-0Dj*WuxzPh6^xPs>hDPFSzkW6#ge z?BL))V{-9nAmTry)9LV`p&{$*PJDi->CAWJ5E=D_9HIys4>>R|EA;a6qS=j%jIfD` z39VyradBbkSrnYvOhJy};bCpUHy1<_y1u^F6Bx|S&RRMSf1yG#W@ctA$eWuRsY^oC znSO5KBpNCdgXS<faC)k^yu8%C7DVLOby|IUd*e8Lm=|nRT*n~fFvU1fGA??_#X2(Q zxi}?Qq;S|9)Iky0rXaUiyDHyD1uB(_>BG;?&bZjzl#{#<;wTNwMx$ZnLT=RwEDfow zt*u#3P`0+VOh-8$A0OvOM@JD~YCxM@M`&PDL2kuE6M|uHZ_o0@o-{u{Z#wHS*4@s| zj(l49;lh){7I#Vo7W_=v-{04mpQOGvtLf=!PB+1)r>9s_##2$yhz>HqG6lJili++2 z!o=q0rWqs0*ipB)w<Bwc08wD6W6dCzRtCVx<-o8B!J%5MMo7Vcg<dL^Tp&&xe9=ai zAcuntY*9i<qyT<(b;WmgcX_#7=980?JfF|Y3uD+Y2cI}eAQMjrq&7A-BKuQZ5+6JE zo0vA4)pA!zRc>l(D)RREB&R-jn}DTo6Yj}`RN(shI>!d~`1nY3#p>`y3}O{d+TH8# zhXAoe>@)h8@Y2$fH76E+(68Z4giHpaJf`aP%f$*Hiv0W_wjWjhKSKBS_qw7F4-a}A zGVp!_M3RW_>gp;Nr1;(49mm+^(~m89QHNB*75fj0ES}>VmiDLs0000<MNUMnLSTaP C%$i;R diff --git a/Telegram/Resources/icons/send_control_record_active@3x.png b/Telegram/Resources/icons/send_control_record_active@3x.png deleted file mode 100644 index 1406aadc58d9a042e1f744cd535945995dc909da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1877 zcmV-b2demqP)<h;3K|Lk000e1NJLTq001@s002k`1^@s6y=?|;0004SX+uL$X=7sm z04R}lk-ba9Kp4iKwo0X~bWlOWAww017K(y6Y8|>1DYb%Cm*kqXLXw6{iWFDD#i8IJ zy7)EtALuH$DhPrih&cEc__auh?=^)MY45naKc3w4a`znI&*;ps`;vfRIy{$7Nvnl5 zDfo^M%_v3?mYHoWWV0Fc96yiniTaQby?XEKI>JTOW`J*ic$Qh*A)Y7BY&n+TJ>p?j zmW#wE#BojsB)%28;_;i{lE+_yc|NxY_(e~t)WJ#zvobe`r-);QUS+NGh?_)Ib7h(8 z7_D*G$RUjsBq*q26FM9usG5{ncVDvUTSA3AjJ_6X;N&QwOx`%DRqCoz)ks%q>Zpm_ z|L19IGt*x8NDAnGb=}ViAh-`S>aP2H>bi|HAn*cQn{9uEa+>=lz24Sh$3S!kxV&j= z$^me-3yeNnGMAfDfR<m=fR77cumB9*1EF;<tJZ&<KLPn|dZIhv@CcZs{n3p-nigl< z2dx{$00009a7bBm000XT000XT0n*)m`~Uz2i%CR5RA>e5T01W_K^We{=Nbx>Dhjto z5D5~ON}&>=!{5-+DqJHGq9YL!*MvqPL0l3Qg`gG`h(zO(m~Wo_z8$k?XLe_2ch5PI zGs%hBci#7TpJ!%vcFb<fFbqO}zpiBbIz|5l{aRsvq8hP^iVCB<yW8mO>@;d>YK)SS z5;N8F^Rsbvb!F`B?HOBJTgL6}ZB!J@dkptkD8Kghc5--lNK^}a5ffH^4ksKzxw*My zZf=fze}AiLVt$B0EHF4?LF>VV6&DwilamuhRJQ^QV1*<un6Ui(d~$YnrmMvf2P|NQ zATF4&g@pwNxu6DMCRG@2dYya9OE@q?SWxsw?C9voi=%csumg^op0L{5T5Zy*fykjN zPJ8Fk)6+w9Mq7g9&=%I(+8V7Yir&`Nrkz7uSXEV(c2d>A%F0SrSBGDWS}>1&myeGR zBRf0WcbuPq&(BXICnv{G2=mnzMi&4E5@jV814aKudn)=*mDq^1u*CLv@N7vLu|(XT zX<@-9M`96@GGe+*xSy^`iZDL}b$!#qbbC$|m!68EUQx8Ar=ns<rHos`Mhv?R*^YxA z-h|#E2@@7Na}U7CUP{F0bz2GD+Tw9kuYc``rG&8bh^fv)>c>V52Q$;t(*|D5y}Z1b z^{5YPeDHL*s_->WTc@U`=xS^)qobqJlUTc_+Lg{ZKUE~;puqQEARQ?OJJ@8v^!Ux; zAGQHGWcd~N{u3taP5SOw2!4NmAGQt$9&(6;fs?2}gXPceG2C0CQd(LnG8@;|*Frl@ zD75PsZ*FdcVKPonPfyIB2|GAAU`D<oJIbc7d3ZNg4^x;dhl~?_QO|fVVf6hkGxC+D zrY8Q9)i*XaSe>tAWH3nHtW{rMUl+#7IK^u)=VfDKqiEWai;D|3Z)p}=Jy|dQz)4h~ zf#di?_Wk``<WJ}aD0~2`<8`WR(GdZ`m;Z@;V1rLbM+eKpku|x9;=rBvqxBzQySuxh z7&10C79Yf}=jZ2zF+q&L<azORa1j-7=ly8?N7%r?fGCE%y}c3HvY*J$A@B_dxQGh4 z^IB}e%F4<JeJ3S~Ba@Sp@qz3bqQm&s0~oRn1Zi*(6>tW3sW3Fm%*=>l$=BBxX=rGO z4`$bSd3k|9Idc1OrLL~d<RBsk&T*n}{WBF&r%S0Q*0j-wGAS%96oD`s7L?=TV<|>_ z1AJu*g~hF)pnxE^tTZ<_v;0;J{c9J7>xqd883x(j-X@uunIa&YjXp%l(9jUs-`^(> z4-e)P$CN=GHnhP<l%KaD7jnu9xbps7-6^cRyj*&dBEizql9D(si7dIR9ikQ&7iF4E zj^HZE;}izR{{DU`AR0C|HwmVQ1T?T6*mQT}$^jf*@{5HbU}R*(6_{>PNqv328@N^* zFoEq>z|BPz+T_B}w!FOThHtJ^_@}R^sK_gkXFD)}g%#MXfRh(*PhkMq+1YW+W467% zzM4Cf>gsBhq&6SKU`GKAuOirC*swUk?jvS7uB@zh<z(%bmzU<s+Sk`d@SzuW3|U!O z<_~q)(1sNazN%$)byX%pWLFagfy2YYsvJW1LvE{+75}OW10Yr+*?ui_H53rJJXSxM zV1L3O4$qG9@$o2h*f5d9<vAr2WZC@-v!+5zOADEuot18!Z7dZpusZ=Ju&q3rfAxh~ z3DK*AgM;SkDf?&DO&4OY@&Y5k2-!M9`|CF<N?Pc@=mtL2q?=Qtx3^d5x3;!soSvQ< zIIuZCKNtE0F-9VcCqzezzhw0&UPLf(S_$t+iAPO=dRkZt41zBT=@IkQT`Axk`H0b@ z0Ef)S$47@P3WM#zMiYd3-PP4)o^x0a=)1c+0$bR5;_)JXjzuvNDwux(`xVK|g-!D5 P00000NkvXXu0mjf+4Xmx diff --git a/Telegram/Resources/icons/send_control_save.png b/Telegram/Resources/icons/send_control_save.png deleted file mode 100644 index 199ab2b0c0f84399f1a7c9728b21680f71bcb55f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmV-s0h#`ZP)<h;3K|Lk000e1NJLTq000sI000dL1^@s6q)t8=0003kNkl<ZD3P6# zA+Lf^7>18992;)RW^oi6BhaW+lbb(*e_*rt2WS+Ei481f8pjI7%|M}Gu}C5i+-&a+ zcYeEGotMkEo$Z|Oeb2MA^L~smhQ4dHT4B50LKH=?zL3pkgGWVC)DPr&9zpK+JC@7k z|3HJm06`EC<aWDZKA%7IZV*uv5r!c_+O~z`I6plMf*^c^NRov8eov65X<*y-vu`*Y zf^&}bdi@q6%QBKAAxK@<uq^A<8^<x3cDo&9Ste0Y6dVo*(l3__48wT;^2K6-wr$CL zJRYHG8ph)>vMi(i`Fw`1>z}@W!8A=&RYjMs*DH#mpmSN4n9XK?1@gU7RTZ3b{I1jK zgz0qJCE4eQecvZYp68fMCSB+3wduMp(lo_rH0nI8Uk=KYxtuMPF#rGn07*qoM6N<$ Ef<k|u$^ZZW diff --git a/Telegram/Resources/icons/send_control_save@2x.png b/Telegram/Resources/icons/send_control_save@2x.png deleted file mode 100644 index 5412d12966f85c012e5fb9226b7a8d628e85ca26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687 zcmV;g0#N;lP)<h;3K|Lk000e1NJLTq001Na000^Y1^@s6-*Em#0007ZNkl<ZNQuSP zJxFp<7zgm@a!E9k>yU#HSO|%OHYh?M4lYd&4i3?rTS!AwG&zW~HQgcHLKnf0AQBw3 z%?7omB1%L98!qG!<^4CvTfXH{%bOiIaKHW!zx#2{Jw!zKtG=<ZfkYw!l}gor{BJN@ zTU&U2eFXsIayiV-&i)-J6beC>W%Rc~p#YsuH+|6F-X2)(<m6<!phzUb#I#y1*zNY| zfH;oB!NCC&bANvii^VdXZ;0o49334oF*i3iSXx@@&-@-nrBdPe_?U^gy1K%`!oug= z?_ktwHPY!c6LWEK0fWIXD62e1tJUJ{?2L&yKR<_FuV>|z#R!6cQmMqm<nwvV&CLy~ z@v$>AGcyrpetsV1a+!(AWHQibG^6VDHmp`FZf|c92n2qFF&d4yyu4&$lF8)fe-<0G zxVVUVy$%47Wf|dc7#zoqjWL-_xW2w-V&d^Qc%C1xo*)RQR4QMJq*AF-?;1pem6a9T z-Q6)UhlhvXx;e^bv&>@EY87U)nHjd(Y-l!{%zQK&{T_x0w6?Z}hldAd(RRBHuh;t} z=yW>KZnv4a{r&x)4i3Wd@-k|*8ngJz%L}%*x8Fl<w;Pfq4ZK#CW$f(iOfp(~8-gI< z^z@X80sv7I;q&>>>2wC3?{>Qg27{AeK0rjhc}Nt+p{QX`adUHXQq2FLe|>j%7u{}m zY|QiXGyHzP5}3gtB7)!V$J5i(NKB{G!TS2TQW!Rfh~RKI&}cN67)g@g@pzQP3<nYQ zX7NE2=i}oeTrQW=n9(32f=;JHE|+`9v|25!uCD$DW;_s4Zz&p!#ZW93``5pU_ct9Y VTB!ss@#g>l002ovPDHLkV1oQyMrQy3 diff --git a/Telegram/Resources/icons/send_control_save@3x.png b/Telegram/Resources/icons/send_control_save@3x.png deleted file mode 100644 index e4961c0e2e451c3ddf57b00af94d6e06ff9c7c4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 933 zcmV;W16urvP)<h;3K|Lk000e1NJLTq001@s001Wl1^@s6M9;h9000AQNkl<ZXo1z) zPfIFc6bA5PwXY<TT$q7@NRSvLgd{{#5=jfAB5)D75L&pZz^#jDW2+!qwdyOh=pz)f z3L#-YN+zViBqy6vpSy^AZ{5r}{>iz%a6m<$^Zv#ehW8yJB6M2~3=H7n;sQROuip3c zpJ*dxG#YVvc?kee6a{|2zx!b(lL>+#;Ny6He#YYBV)w$#W;3p@uTg8Ls)}GR*nKdI z#e!5SRTK3Q7Yc>C2WGWekxr-UqJEFFv$O7i*=#n5qR2+|_4WP#FuUE3OeVuZot~be zx3~A-4E+)Rmc!vdHk)OkPEJnH)6-KcM}Nr2ayp%mB#DJOIy!>EVE8Uef6VW4xm?KQ zax7FbnXHHUe_(F68?r33PzMJGoJ{>u9*+n4e4d3$Bof?QeNkSo7llHBg^I`Hjb!VG z^7(uy7K<#@?(S}*`TC&zem@jN(F9eiRm5VkW_olU6$k`SDwSBMXf)bvpU$F!!5~ys z)kIY)6+|MDR(gH@U}k0p=jZ1b92{&rDijK_P~~zN;c&Rset+N0&CTKI=?MTJ2m*$O zhuel)UtdSLTxOv*Ha6PP^Zmur($bgnK9x$rVzIQ`BpeR2P&L+6t-)4TSHC?)L{Y@p z*jUpIB9RCxl?n@0V-?(dagOHg?JXP*N5kRKXcX0IRTEVzl@JI7+J_<n9UmWSHU@zE z`+H1IPO^hzu^0=bvD4Ii84Ly-9v-rAvMghIdRjAdXJ?0%S1cB__6A$K#)t@sM1nP$ zLZN`!+1YOc<MB8<$Lsa}6zVUGh_JV}$C{L)C|Fon_!7oFIQe`Y9*^fIQJ-N%gzfEZ z*0rIkDppoj{`_u#f1ibtWf^X_`=?P~VMK(jtu5Al=k4tco12?RCX*~wE|-JL<>~~g zHjId{wzh`X*H_KSa{gTakR%CCr?XS2?_orQ<>h6(yu7d`UT<Wx*{1G;KL#Tr%+JrW z7lXA%CX<2PZtpZo8%FfLe1Cj=Y#1eqB5XFB9w;7+h%hxZg@=a+HY%M?!)mqah2q1A z2on<%tlcC@lG?H==opNMFg`wBcT@B3?ha<NS#MP1Fe1X}=xEKobx{;yGMV%SH4h^q zjEszYdKz+ba|5H%s5ho{7!hG;Xb6HJ;OgoM{r&xVLq63n;cnIZBCel$00000NkvXX Hu0mjf3u3er diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 8b1f357ad..25ae77ad8 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -74,7 +74,7 @@ stickersFeaturedUnreadBg: msgFileInBg; stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; stickersFeaturedUnreadTop: 7px; -stickersFeaturedInstalled: icon {{ "send_control_save", lightButtonFg }}; +stickersFeaturedInstalled: icon {{ "chat/input_save", lightButtonFg }}; stickersMaxHeight: 320px; stickersPadding: margins(19px, 13px, 19px, 13px); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 7b7e0475f..f2b7ef7a9 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -279,14 +279,14 @@ historyContactStatusBlock: FlatButton(historyContactStatusButton) { } historyContactStatusMinSkip: 16px; -historySendIcon: icon {{ "send_control_send", historySendIconFg }}; -historySendIconOver: icon {{ "send_control_send", historySendIconFgOver }}; +historySendIcon: icon {{ "chat/input_send", historySendIconFg }}; +historySendIconOver: icon {{ "chat/input_send", historySendIconFgOver }}; historySendIconPosition: point(10px, 11px); historySendSize: size(44px, 46px); -historyScheduleIcon: icon {{ "send_control_schedule", historyComposeAreaBg }}; +historyScheduleIcon: icon {{ "chat/input_schedule", historyComposeAreaBg }}; historyScheduleIconPosition: point(7px, 8px); -historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(2px, 7px) }}; -historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(2px, 7px) }}; +historyEditSaveIcon: icon {{ "chat/input_save", historySendIconFg }}; +historyEditSaveIconOver: icon {{ "chat/input_save", historySendIconFgOver }}; historyAttach: IconButton(defaultIconButton) { width: 44px; @@ -303,8 +303,8 @@ historyAttach: IconButton(defaultIconButton) { } historyAttachEmoji: IconButton(historyAttach) { - icon: icon {{ "send_control_emoji", historyComposeIconFg }}; - iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; + icon: icon {{ "chat/input_smile_face", historyComposeIconFg }}; + iconOver: icon {{ "chat/input_smile_face", historyComposeIconFgOver }}; } historyMessagesTTL: IconButton(historyAttach) { icon: icon {{ "chat/input_autodelete_1d", historyComposeIconFg }}; @@ -313,7 +313,7 @@ historyMessagesTTL: IconButton(historyAttach) { historyMessagesTTL2Icon: icon {{ "chat/input_autodelete_7d", historyComposeIconFg }}; historyMessagesTTL2IconOver: icon {{ "chat/input_autodelete_7d", historyComposeIconFgOver }}; historyAttachEmojiFgActive: windowActiveTextFg; -historyAttachEmojiActive: icon {{ "send_control_emoji", historyAttachEmojiFgActive }}; +historyAttachEmojiActive: icon {{ "chat/input_smile_face", historyAttachEmojiFgActive }}; historyAttachEmojiTooltipDelta: 4px; historyEmojiCircle: size(20px, 20px); historyEmojiCirclePeriod: 1500; @@ -355,7 +355,7 @@ historyRecordVoiceShowDuration: 120; historyRecordVoiceDuration: 120; historyRecordVoice: icon {{ "chat/input_record", historyRecordVoiceFg }}; historyRecordVoiceOver: icon {{ "chat/input_record", historyRecordVoiceFgOver }}; -historyRecordVoiceActive: icon {{ "send_control_record_active", historyRecordVoiceFgActiveIcon }}; +historyRecordVoiceActive: icon {{ "chat/input_record_filled", historyRecordVoiceFgActiveIcon }}; historyRecordSendIconPosition: point(2px, 0px); historyRecordVoiceRippleBgActive: lightButtonBgOver; historyRecordSignalRadius: 5px; @@ -425,8 +425,8 @@ historySilentToggle: IconButton(historyBotKeyboardShow) { historySilentToggleCrossLine: CrossLineAnimation { fg: historyComposeIconFg; icon: icon {{ "chat/input_silent", historyComposeIconFg }}; - startPosition: point(5px, 3px); - endPosition: point(21px, 18px); + startPosition: point(13px, 11px); + endPosition: point(29px, 26px); stroke: 2px; } @@ -435,10 +435,10 @@ historyReplyNameFg: windowActiveTextFg; historyReplyHeight: 49px; historyReplyTop: 8px; historyReplyBottom: 6px; -historyReplyIconPosition: point(13px, 13px); -historyReplyIcon: icon {{ "history_action_reply", historyReplyIconFg }}; -historyForwardIcon: icon {{ "history_action_forward", historyReplyIconFg }}; -historyEditIcon: icon {{ "history_action_edit", historyReplyIconFg }}; +historyReplyIconPosition: point(5px, 5px); +historyReplyIcon: icon {{ "chat/input_reply", historyReplyIconFg }}; +historyForwardIcon: icon {{ "chat/input_forward", historyReplyIconFg }}; +historyEditIcon: icon {{ "chat/input_edit", historyReplyIconFg }}; historyReplyCancel: IconButton { width: 49px; height: 49px; diff --git a/Telegram/SourceFiles/ui/controls/send_button.cpp b/Telegram/SourceFiles/ui/controls/send_button.cpp index d1c3c3080..3e2420fe9 100644 --- a/Telegram/SourceFiles/ui/controls/send_button.cpp +++ b/Telegram/SourceFiles/ui/controls/send_button.cpp @@ -127,7 +127,7 @@ void SendButton::paintSave(Painter &p, bool over) { const auto &saveIcon = over ? st::historyEditSaveIconOver : st::historyEditSaveIcon; - saveIcon.paint(p, st::historySendIconPosition, width()); + saveIcon.paintInCenter(p, rect()); } void SendButton::paintCancel(Painter &p, bool over) { From 56b15b26bb89e39c6e488fdf1b55ff2d32cd11d1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 17:32:45 +0400 Subject: [PATCH 359/396] Add convert to gigagroup button. --- Telegram/Resources/langs/lang.strings | 11 +++ .../boxes/peers/edit_peer_permissions_box.cpp | 99 +++++++++++++++++++ .../boxes/peers/edit_peer_permissions_box.h | 1 + 3 files changed, 111 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e1c0d72d2..626b3652a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2014,6 +2014,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time."; "lng_slowmode_too_long" = "Sorry, this text is too long to send as one message.\n\nSlow mode is enabled. You can't send more than one message at a time."; +"lng_rights_gigagroup_title" = "Broadcast group"; +"lng_rights_gigagroup_convert" = "Convert to Broadcast Group"; +"lng_rights_gigagroup_about" = "Broadcast groups can have over 200,000 members, but only admins can send messages in them."; +"lng_gigagroup_convert_title" = "Broadcast Groups"; +"lng_gigagroup_convert_feature1" = "No limit on the number of members."; +"lng_gigagroup_convert_feature2" = "Only admins can send messages."; +"lng_gigagroup_convert_feature3" = "Can't be turned back into a regular group."; +"lng_gigagroup_convert_sure" = "Convert"; +"lng_gigagroup_warning_title" = "Are you sure?"; +"lng_gigagroup_warning" = "Regular members of the group (non-admins) will **irrevocably** lose their right to post messages in the group.\n\nThis action **can't** be undone."; + "lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_post" = "Post messages"; "lng_rights_channel_edit" = "Edit messages of others"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 6684a390f..5a4bbacdc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "ui/wrap/vertical_layout.h" +#include "ui/layers/generic_box.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" @@ -22,7 +23,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "window/window_session_controller.h" +#include "main/main_session.h" #include "mainwindow.h" +#include "apiwrap.h" #include "app.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -31,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kSlowmodeValues = 7; +constexpr auto kSuggestGigagroupThreshold = 199000; int SlowmodeDelayByIndex(int index) { Expects(index >= 0 && index < kSlowmodeValues); @@ -369,6 +373,14 @@ void EditPeerPermissionsBox::prepare() { inner->add(std::move(checkboxes)); const auto getSlowmodeSeconds = addSlowmodeSlider(inner); + + if (const auto channel = _peer->asChannel()) { + if (channel->amCreator() + && channel->membersCount() >= kSuggestGigagroupThreshold) { + addSuggestGigagroup(inner); + } + } + addBannedButtons(inner); _value = [=, rights = getRestrictions]() -> Result { @@ -520,6 +532,93 @@ void EditPeerPermissionsBox::addSlowmodeLabels( } } +void EditPeerPermissionsBox::addSuggestGigagroup( + not_null<Ui::VerticalLayout*> container) { + container->add( + object_ptr<Ui::FlatLabel>( + container, + tr::lng_rights_gigagroup_title(), + st::rightsHeaderLabel), + st::rightsHeaderMargin); + const auto channel = _peer->asChannel(); + const auto converting = std::make_shared<bool>(); + const auto convertSure = [=] { + if (*converting) { + return; + } + *converting = true; + channel->session().api().request(MTPchannels_ConvertToGigagroup( + channel->inputChannel + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + Ui::hideLayer(); + }).fail([=](const RPCError &error) { + *converting = false; + }).send(); + }; + const auto convertWarn = [=] { + if (*converting) { + return; + } + getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_gigagroup_warning_title()); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_gigagroup_warning(), + st::defaultFlatLabel), + style::margins( + st::boxRowPadding.left(), + st::boxLittleSkip, + st::boxRowPadding.right(), + st::boxLittleSkip)); + box->addButton(tr::lng_gigagroup_convert_sure(), convertSure); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + }; + const auto convert = [=] { + if (*converting) { + return; + } + getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_gigagroup_convert_title()); + const auto addFeature = [&](rpl::producer<QString> text) { + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + std::move(text), + st::defaultFlatLabel), + style::margins( + st::boxRowPadding.left(), + st::boxLittleSkip, + st::boxRowPadding.right(), + st::boxLittleSkip)); + }; + addFeature(tr::lng_gigagroup_convert_feature1()); + addFeature(tr::lng_gigagroup_convert_feature2()); + addFeature(tr::lng_gigagroup_convert_feature3()); + box->addButton(tr::lng_gigagroup_convert_sure(), convertWarn); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + }; + container->add(EditPeerInfoBox::CreateButton( + container, + tr::lng_rights_gigagroup_convert(), + rpl::single(QString()), + convert, + st::peerPermissionsButton)); + + container->add( + object_ptr<Ui::DividerLabel>( + container, + object_ptr<Ui::FlatLabel>( + container, + tr::lng_rights_gigagroup_about(), + st::boxDividerLabel), + st::proxyAboutPadding), + style::margins(0, st::infoProfileSkip, 0, st::infoProfileSkip)); +} + void EditPeerPermissionsBox::addBannedButtons( not_null<Ui::VerticalLayout*> container) { if (const auto chat = _peer->asChat()) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index b7dfd4ed0..a817ac3da 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -39,6 +39,7 @@ protected: private: Fn<int()> addSlowmodeSlider(not_null<Ui::VerticalLayout*> container); void addSlowmodeLabels(not_null<Ui::VerticalLayout*> container); + void addSuggestGigagroup(not_null<Ui::VerticalLayout*> container); void addBannedButtons(not_null<Ui::VerticalLayout*> container); const not_null<Window::SessionNavigation*> _navigation; From 8188ab3033f87b7f6a12aa239a63fda2858fa856 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 19:07:41 +0400 Subject: [PATCH 360/396] Adapt UI for gigagroups. --- .../boxes/peers/edit_peer_info_box.cpp | 34 ++++++++++++++----- .../boxes/peers/edit_peer_permissions_box.cpp | 21 ++++++------ Telegram/SourceFiles/data/data_channel.cpp | 1 + Telegram/SourceFiles/data/data_channel.h | 4 +++ Telegram/SourceFiles/data/data_peer.cpp | 8 +++-- Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/info/info.style | 4 +++ 7 files changed, 52 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index d5924277f..9abba406c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -328,7 +328,7 @@ private: object_ptr<Ui::RpWidget> createStickersEdit(); bool canEditInformation() const; - void refreshHistoryVisibility(anim::type animated = anim::type::normal); + void refreshHistoryVisibility(); void showEditPeerTypeBox( std::optional<rpl::producer<QString>> error = {}); void showEditLinkedChatBox(); @@ -631,7 +631,7 @@ bool Controller::canEditInformation() const { return false; } -void Controller::refreshHistoryVisibility(anim::type animated) { +void Controller::refreshHistoryVisibility() { if (!_controls.historyVisibilityWrap) { return; } @@ -639,7 +639,7 @@ void Controller::refreshHistoryVisibility(anim::type animated) { (_privacySavedValue != Privacy::HasUsername && !_channelHasLocationOriginalValue && (!_linkedChatSavedValue || !*_linkedChatSavedValue)), - animated); + anim::type::instant); } void Controller::showEditPeerTypeBox( @@ -880,7 +880,7 @@ void Controller::fillHistoryVisibilityButton() { updateHistoryVisibility->fire_copy(*_historyVisibilitySavedValue); - refreshHistoryVisibility(anim::type::instant); + refreshHistoryVisibility(); } void Controller::fillSetMessagesTTLButton() { @@ -965,7 +965,7 @@ void Controller::fillManageSection() { }(); const auto canViewKicked = [&] { return isChannel - ? (!channel->isMegagroup()) + ? (channel->isBroadcast() || channel->isGigagroup()) : false; }(); const auto hasRecentActions = [&] { @@ -1015,7 +1015,7 @@ void Controller::fillManageSection() { } if (canEditPreHistoryHidden || canEditSignatures - || canEditInviteLinks + //|| canEditInviteLinks || canViewOrEditLinkedChat || canEditUsername || canSetMessagesTTL) { @@ -1038,7 +1038,10 @@ void Controller::fillManageSection() { [=] { ShowEditPermissions(_navigation, _peer); }, st::infoIconPermissions); } - if (canEditInviteLinks) { + if (canEditInviteLinks + && (canEditUsername + || !_peer->isChannel() + || !_peer->asChannel()->hasUsername())) { auto count = Info::Profile::MigratedOrMeValue( _peer ) | rpl::map([=](not_null<PeerData*> peer) { @@ -1053,8 +1056,13 @@ void Controller::fillManageSection() { }) | rpl::flatten_latest( ) | rpl::start_spawning(_controls.buttonsLayout->lifetime()); + const auto wrap = _controls.buttonsLayout->add( + object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( + _controls.buttonsLayout, + object_ptr<Ui::VerticalLayout>( + _controls.buttonsLayout))); AddButtonWithCount( - _controls.buttonsLayout, + wrap->entity(), tr::lng_manage_peer_invite_links(), rpl::duplicate(count) | ToPositiveNumberString(), [=] { Ui::show( @@ -1062,6 +1070,16 @@ void Controller::fillManageSection() { Ui::LayerOption::KeepOther); }, st::infoIconInviteLinks); + + if (_privacySavedValue) { + _privacyTypeUpdates.events_starting_with_copy( + *_privacySavedValue + ) | rpl::start_with_next([=](Privacy flag) { + wrap->toggle( + flag != Privacy::HasUsername, + anim::type::instant); + }, wrap->lifetime()); + } } if (canViewAdmins) { AddButtonWithCount( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 5a4bbacdc..76bb802dc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/box_content_divider.h" +#include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_values.h" @@ -34,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kSlowmodeValues = 7; -constexpr auto kSuggestGigagroupThreshold = 199000; +constexpr auto kSuggestGigagroupThreshold = 1; AssertIsDebug(199000); int SlowmodeDelayByIndex(int index) { Expects(index >= 0 && index < kSlowmodeValues); @@ -551,7 +552,7 @@ void EditPeerPermissionsBox::addSuggestGigagroup( channel->inputChannel )).done([=](const MTPUpdates &result) { channel->session().api().applyUpdates(result); - Ui::hideLayer(); + Ui::hideSettingsAndLayer(); }).fail([=](const RPCError &error) { *converting = false; }).send(); @@ -565,13 +566,9 @@ void EditPeerPermissionsBox::addSuggestGigagroup( box->addRow( object_ptr<Ui::FlatLabel>( box, - tr::lng_gigagroup_warning(), - st::defaultFlatLabel), - style::margins( - st::boxRowPadding.left(), - st::boxLittleSkip, - st::boxRowPadding.right(), - st::boxLittleSkip)); + tr::lng_gigagroup_warning( + ) | Ui::Text::ToRichLangValue(), + st::infoAboutGigagroup)); box->addButton(tr::lng_gigagroup_convert_sure(), convertSure); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); })); @@ -583,11 +580,13 @@ void EditPeerPermissionsBox::addSuggestGigagroup( getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_gigagroup_convert_title()); const auto addFeature = [&](rpl::producer<QString> text) { + using namespace rpl::mappers; + const auto prefix = QString::fromUtf8("\xE2\x80\xA2 "); box->addRow( object_ptr<Ui::FlatLabel>( box, - std::move(text), - st::defaultFlatLabel), + std::move(text) | rpl::map(prefix + _1), + st::infoAboutGigagroup), style::margins( st::boxRowPadding.left(), st::boxLittleSkip, diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 051e78c9c..9ea1086d8 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -454,6 +454,7 @@ bool ChannelData::canEditInformation() const { bool ChannelData::canEditPermissions() const { return isMegagroup() + && !isGigagroup() && ((adminRights() & AdminRight::f_ban_users) || amCreator()); } diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 5c248072b..cf0c98dc7 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -96,6 +96,7 @@ public: | MTPDchannel::Flag::f_scam | MTPDchannel::Flag::f_fake | MTPDchannel::Flag::f_megagroup + | MTPDchannel::Flag::f_gigagroup | MTPDchannel::Flag::f_restricted | MTPDchannel::Flag::f_signatures | MTPDchannel::Flag::f_username @@ -233,6 +234,9 @@ public: [[nodiscard]] bool isBroadcast() const { return flags() & MTPDchannel::Flag::f_broadcast; } + [[nodiscard]] bool isGigagroup() const { + return flags() & MTPDchannel::Flag::f_gigagroup; + } [[nodiscard]] bool hasUsername() const { return flags() & MTPDchannel::Flag::f_username; } diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 0f43b1100..bad21450b 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -757,11 +757,15 @@ bool PeerData::isFake() const { } bool PeerData::isMegagroup() const { - return isChannel() ? asChannel()->isMegagroup() : false; + return isChannel() && asChannel()->isMegagroup(); } bool PeerData::isBroadcast() const { - return isChannel() ? asChannel()->isBroadcast() : false; + return isChannel() && asChannel()->isBroadcast(); +} + +bool PeerData::isGigagroup() const { + return isChannel() && asChannel()->isGigagroup(); } bool PeerData::isRepliesChat() const { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 26025bc11..2df51e6ff 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -156,6 +156,7 @@ public: [[nodiscard]] bool isFake() const; [[nodiscard]] bool isMegagroup() const; [[nodiscard]] bool isBroadcast() const; + [[nodiscard]] bool isGigagroup() const; [[nodiscard]] bool isRepliesChat() const; [[nodiscard]] bool sharedMediaInfo() const { return isSelf() || isRepliesChat(); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index ec076f378..6a24cf9c4 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -937,3 +937,7 @@ inviteLinkRevokedIcon: icon {{ "info/edit/links_revoked", mediaviewFileExtFg }}; inviteLinkThreeDotsSkip: 12px; inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 4px); inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px); + +infoAboutGigagroup: FlatLabel(defaultFlatLabel) { + minWidth: 274px; +} From 05488022c7b0ff4bf74c31319b0c167cfa08c745 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 12 Feb 2021 19:15:17 +0400 Subject: [PATCH 361/396] Use ttl_period from service messages as well. --- Telegram/SourceFiles/history/history_item.cpp | 39 ++++++++++++++++++- Telegram/SourceFiles/history/history_item.h | 13 +++++-- .../SourceFiles/history/history_message.cpp | 28 ++----------- .../SourceFiles/history/history_message.h | 8 ---- .../SourceFiles/history/history_service.cpp | 2 + .../SourceFiles/history/history_widget.cpp | 4 +- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index e823d29f1..1498221ec 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -809,6 +809,41 @@ bool HistoryItem::canUpdateDate() const { return isScheduled(); } +void HistoryItem::applyTTL(const MTPDmessage &data) { + if (const auto period = data.vttl_period()) { + if (period->v > 0) { + applyTTL(data.vdate().v + period->v); + } + } +} + +void HistoryItem::applyTTL(const MTPDmessageService &data) { + if (const auto period = data.vttl_period()) { + if (period->v > 0) { + applyTTL(data.vdate().v + period->v); + } + } +} + +void HistoryItem::applyTTL(TimeId destroyAt) { + const auto previousDestroyAt = std::exchange(_ttlDestroyAt, destroyAt); + if (previousDestroyAt) { + history()->owner().unregisterMessageTTL(previousDestroyAt, this); + } + if (!_ttlDestroyAt) { + return; + } else if (base::unixtime::now() >= _ttlDestroyAt) { + const auto session = &history()->session(); + crl::on_main(session, [session, id = fullId()]{ + if (const auto item = session->data().message(id)) { + item->destroy(); + } + }); + } else { + history()->owner().registerMessageTTL(_ttlDestroyAt, this); + } +} + void HistoryItem::sendFailed() { Expects(_clientFlags & MTPDmessage_ClientFlag::f_sending); Expects(!(_clientFlags & MTPDmessage_ClientFlag::f_failed)); @@ -957,7 +992,9 @@ void HistoryItem::drawInDialog( p.restoreTextPalette(); } -HistoryItem::~HistoryItem() = default; +HistoryItem::~HistoryItem() { + applyTTL(0); +} QDateTime ItemDateTime(not_null<const HistoryItem*> item) { return base::unixtime::parse(item->date()); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 778d08165..9ff851a6d 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -79,9 +79,6 @@ public: [[nodiscard]] virtual bool notificationReady() const { return true; } - [[nodiscard]] virtual TimeId ttlDestroyAt() const { - return 0; - } [[nodiscard]] PeerData *specialNotificationPeer() const; [[nodiscard]] UserData *viaBot() const; @@ -399,6 +396,10 @@ public: void updateDate(TimeId newDate); [[nodiscard]] bool canUpdateDate() const; + [[nodiscard]] TimeId ttlDestroyAt() const { + return _ttlDestroyAt; + } + virtual ~HistoryItem(); MsgId id; @@ -427,6 +428,10 @@ protected: void setGroupId(MessageGroupId groupId); + void applyTTL(const MTPDmessage &data); + void applyTTL(const MTPDmessageService &data); + void applyTTL(TimeId destroyAt); + Ui::Text::String _text = { st::msgMinWidth }; int _textWidth = -1; int _textHeight = 0; @@ -440,7 +445,9 @@ protected: std::unique_ptr<Data::Media> _media; private: + TimeId _date = 0; + TimeId _ttlDestroyAt = 0; HistoryView::Element *_mainView = nullptr; friend class HistoryView::Element; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 1a0b7e555..42f4fc544 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -525,11 +525,7 @@ HistoryMessage::HistoryMessage( MessageGroupId::FromRaw(history->peer->id, groupedId->v)); } - if (const auto period = data.vttl_period()) { - if (period->v > 0) { - applyTTL(data.vdate().v + period->v); - } - } + applyTTL(data); } HistoryMessage::HistoryMessage( @@ -566,6 +562,8 @@ HistoryMessage::HistoryMessage( }, [](const auto &) { Unexpected("Service message action type in HistoryMessage."); }); + + applyTTL(data); } HistoryMessage::HistoryMessage( @@ -1403,25 +1401,6 @@ void HistoryMessage::applyEdition(const MTPDmessageService &message) { } } -void HistoryMessage::applyTTL(TimeId destroyAt) { - const auto previousDestroyAt = std::exchange(_ttlDestroyAt, destroyAt); - if (previousDestroyAt) { - history()->owner().unregisterMessageTTL(previousDestroyAt, this); - } - if (!_ttlDestroyAt) { - return; - } else if (base::unixtime::now() >= _ttlDestroyAt) { - const auto session = &history()->session(); - crl::on_main(session, [session, id = fullId()]{ - if (const auto item = session->data().message(id)) { - item->destroy(); - } - }); - } else { - history()->owner().registerMessageTTL(_ttlDestroyAt, this); - } -} - void HistoryMessage::updateSentContent( const TextWithEntities &textWithEntities, const MTPMessageMedia *media) { @@ -1927,7 +1906,6 @@ std::unique_ptr<HistoryView::Element> HistoryMessage::createView( } HistoryMessage::~HistoryMessage() { - applyTTL(0); _media.reset(); clearSavedMedia(); if (auto reply = Get<HistoryMessageReply>()) { diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 2c832c797..36e3ac653 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -194,10 +194,6 @@ public: const MTPDupdateShortSentMessage &data, bool wasAlready) override; - [[nodiscard]] TimeId ttlDestroyAt() const override { - return _ttlDestroyAt; - } - // dynamic_cast optimization. [[nodiscard]] HistoryMessage *toHistoryMessage() override { return this; @@ -250,8 +246,6 @@ private: const TextWithEntities &textWithEntities) const; void reapplyText(); - void applyTTL(TimeId destroyAt); - [[nodiscard]] bool checkRepliesPts(const MTPMessageReplies &data) const; QString _timeText; @@ -259,8 +253,6 @@ private: mutable int32 _fromNameVersion = 0; - TimeId _ttlDestroyAt = 0; - friend class HistoryView::Element; friend class HistoryView::Message; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 8f7f733fe..a4b0988c8 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -749,6 +749,7 @@ HistoryService::HistoryService( data.vdate().v, data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) { createFromMtp(data); + applyTTL(data); } HistoryService::HistoryService( @@ -763,6 +764,7 @@ HistoryService::HistoryService( data.vdate().v, data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) { createFromMtp(data); + applyTTL(data); } HistoryService::HistoryService( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 60725698b..5190581a6 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3716,8 +3716,8 @@ bool HistoryWidget::isJoinChannel() const { bool HistoryWidget::isMuteUnmute() const { return _peer - && ((_peer->isBroadcast() - && !_peer->asChannel()->canPublish()) + && ((_peer->isBroadcast() && !_peer->asChannel()->canPublish()) + || (_peer->isGigagroup() && !_peer->asChannel()->canWrite()) || _peer->isRepliesChat()); } From 781e7a2e7932a3c69118ebf5bd9e58a3ea498c81 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 14:31:04 +0400 Subject: [PATCH 362/396] Update API scheme, simplify auto-delete. --- Telegram/Resources/langs/lang.strings | 29 +++--- Telegram/Resources/tl/api.tl | 17 ++-- Telegram/SourceFiles/api/api_updates.cpp | 6 +- Telegram/SourceFiles/data/data_channel.cpp | 4 +- Telegram/SourceFiles/data/data_chat.cpp | 4 +- Telegram/SourceFiles/data/data_peer.cpp | 31 +----- Telegram/SourceFiles/data/data_peer.h | 16 +-- Telegram/SourceFiles/data/data_user.cpp | 4 +- .../export/data/export_data_types.cpp | 2 + .../SourceFiles/history/history_service.cpp | 27 +++++ .../view/controls/history_view_ttl_button.cpp | 41 +++----- .../ui/boxes/auto_delete_settings.cpp | 98 ++++--------------- .../ui/boxes/auto_delete_settings.h | 8 +- 13 files changed, 95 insertions(+), 192 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 626b3652a..0b9db29c9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -977,23 +977,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_history_visibility_hidden_legacy" = "New members won't see more than 100 previous messages."; "lng_manage_messages_ttl_title" = "Auto-delete messages"; -"lng_manage_messages_ttl_never" = "Never"; -"lng_manage_messages_ttl_after1" = "After 24 hours"; -"lng_manage_messages_ttl_after2" = "After 7 days"; -"lng_manage_messages_ttl_about" = "Turning on this setting will make auto-delete messages from this group after the selected period."; -"lng_manage_messages_ttl_about_channel" = "Turning on this setting will make auto-delete messages from this channel after the selected period."; +"lng_manage_messages_ttl_never" = "Off"; +"lng_manage_messages_ttl_after1" = "24 hours"; +"lng_manage_messages_ttl_after2" = "7 days"; "lng_ttl_edit_title" = "Auto-delete messages in this chat"; -"lng_ttl_edit_about" = "Automatically delete new messages sent in this chat after a certain period of time."; -"lng_ttl_edit_about_other" = "{user} has set messages to auto-delete in {duration} for both of you."; -"lng_ttl_edit_about_you" = "You have set messages to auto-delete in {duration} for both you and {user}."; -"lng_ttl_edit_about_you_only" = "You have set messages to auto-delete in {duration} only for yourself."; -"lng_ttl_also_checkbox" = "Also delete for {user}"; -"lng_ttl_about_tooltip_on_title" = "Auto-delete On – {duration}"; -"lng_ttl_about_tooltip" = "Messages in this chat will auto-delete in {duration}."; -"lng_ttl_about_tooltip_no_longer" = "{user} has set messages to auto-delete in {duration}. You can't make this interval longer."; -"lng_ttl_about_tooltip_no_cancel" = "{user} has set messages to auto-delete in {duration}. You can't cancel this."; -"lng_ttl_about_tooltip_off" = "Auto-delete is now Off."; +"lng_ttl_edit_about" = "Automatically delete new messages after a certain period of time for you and {user}."; +"lng_ttl_edit_about_group" = "Automatically delete new messages sent in this chat after a certain period of time."; +"lng_ttl_edit_about_channel" = "Automatically delete new messages sent in this channel after a certain period of time."; +"lng_ttl_edit_save" = "Confirm"; +"lng_ttl_about_tooltip" = "New messages in this chat will be automatically deleted in {duration}."; +"lng_ttl_about_tooltip_channel" = "New messages in this chat will be automatically deleted in {duration}."; +"lng_ttl_about_tooltip_off" = "Auto-delete is now disabled."; "lng_ttl_about_duration1" = "24 hours"; "lng_ttl_about_duration2" = "7 days"; @@ -1117,6 +1112,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_changed_title" = "{from} changed group name to «{title}»"; "lng_action_changed_title_channel" = "Channel name was changed to «{title}»"; "lng_action_created_chat" = "{from} created group «{title}»"; +"lng_action_ttl_changed" = "{from} has set messages to auto-delete in {duration}"; +"lng_action_ttl_changed_channel" = "New messages will auto-delete in {duration}"; +"lng_action_ttl_removed" = "{from} has set messages not to auto-delete"; +"lng_action_ttl_removed_channel" = "New messages will not auto-delete"; "lng_action_created_channel" = "Channel created"; "lng_action_pinned_message" = "{from} pinned «{text}»"; "lng_action_pinned_media" = "{from} pinned {media}"; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 99c92a4fb..c9370f742 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -127,8 +127,8 @@ chatForbidden#7328bdb id:int title:string = Chat; channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#e22542a0 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl:flags.14?PeerHistoryTTL = ChatFull; -channelFull#7c62b528 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl:flags.24?PeerHistoryTTL pending_suggestions:flags.25?Vector<string> = ChatFull; +chatFull#f06c4018 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int = ChatFull; +channelFull#2548c037 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -184,6 +184,7 @@ messageActionContactSignUp#f3f25f76 = MessageAction; messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = MessageAction; messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction; messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<int> = MessageAction; +messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction; dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -231,7 +232,7 @@ inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; inputReportReasonFake#f5ddd6e7 = ReportReason; -userFull#a54475a7 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl:flags.14?PeerHistoryTTL = UserFull; +userFull#139a9a77 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -358,7 +359,6 @@ updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update; updateDialogFilterOrder#a5d72105 order:Vector<int> = Update; updateDialogFilters#3504914f = Update; updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update; -updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update; updateChannelMessageForwards#6e8a84df channel_id:int id:int forwards:int = Update; updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update; updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update; @@ -369,7 +369,9 @@ updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int updateChat#1330a196 chat_id:int = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update; updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update; -updatePeerHistoryTTL#1265be8 flags:# peer:Peer ttl:flags.0?PeerHistoryTTL = Update; +updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; +updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update; +updateBotStopped#30ec6ebc user_id:int stopped:Bool qts:int = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -1221,9 +1223,6 @@ messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector<int> = messages.AffectedFoundMessages; -peerHistoryTTLPM#cf622d96 flags:# my_oneside:flags.0?true my_ttl_period:flags.1?int peer_ttl_period:flags.2?int = PeerHistoryTTL; -peerHistoryTTL#3e11cee9 ttl_period:int = PeerHistoryTTL; - chatInviteImporter#1e3e6680 user_id:int date:int = ChatInviteImporter; messages.exportedChatInvites#bdc62dcc count:int invites:Vector<ExportedChatInvite> users:Vector<User> = messages.ExportedChatInvites; @@ -1499,7 +1498,7 @@ messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:Input messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; -messages.setHistoryTTL#cccb4721 flags:# pm_oneside:flags.0?true peer:InputPeer period:int = Updates; +messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index d4a950d4b..3fefdab20 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1824,11 +1824,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { const auto &d = update.c_updatePeerHistoryTTL(); const auto peerId = peerFromMTP(d.vpeer()); if (const auto peer = session().data().peerLoaded(peerId)) { - if (const auto ttl = d.vttl()) { - peer->applyMessagesTTL(*ttl); - } else { - peer->setMessagesTTL(0, 0, false); - } + peer->setMessagesTTL(d.vttl_period().value_or_empty()); } } break; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 9ea1086d8..880945e0b 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -750,9 +750,7 @@ void ApplyChannelUpdate( channel->clearGroupCall(); } - if (const auto ttl = update.vttl()) { - channel->applyMessagesTTL(*ttl); - } + channel->setMessagesTTL(update.vttl_period().value_or_empty()); channel->setFullFlags(update.vflags().v); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index bb1385b62..2da1d3c4a 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -377,9 +377,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) { chat->clearGroupCall(); } - if (const auto ttl = update.vttl()) { - chat->applyMessagesTTL(*ttl); - } + chat->setMessagesTTL(update.vttl_period().value_or_empty()); if (const auto info = update.vbot_info()) { for (const auto &item : info->v) { item.match([&](const MTPDbotInfo &data) { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index bad21450b..e3cd50b2c 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -950,41 +950,18 @@ void PeerData::setLoadedStatus(LoadedStatus status) { } TimeId PeerData::messagesTTL() const { - return (_ttlMyPeriod && _ttlPeerPeriod) - ? std::min(_ttlMyPeriod, _ttlPeerPeriod) - : std::max(_ttlMyPeriod, _ttlPeerPeriod); + return _ttlPeriod; } -void PeerData::setMessagesTTL( - TimeId myPeriod, - TimeId peerPeriod, - bool oneSide) { - if (_ttlMyPeriod != myPeriod - || _ttlPeerPeriod != peerPeriod - || _ttlOneSide != oneSide) { - _ttlMyPeriod = myPeriod; - _ttlPeerPeriod = peerPeriod; - _ttlOneSide = oneSide; +void PeerData::setMessagesTTL(TimeId period) { + if (_ttlPeriod != period) { + _ttlPeriod = period; session().changes().peerUpdated( this, Data::PeerUpdate::Flag::MessagesTTL); } } -void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { - ttl.match([&](const MTPDpeerHistoryTTL &data) { - setMessagesTTL( - data.vttl_period().v, - 0, - false); - }, [&](const MTPDpeerHistoryTTLPM &data) { - setMessagesTTL( - data.vmy_ttl_period().value_or_empty(), - data.vpeer_ttl_period().value_or_empty(), - data.is_my_oneside()); - }); -} - namespace Data { std::vector<ChatRestrictions> ListOfRestrictions() { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 2df51e6ff..43921e8f6 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -382,18 +382,8 @@ public: } void setLoadedStatus(LoadedStatus status); - [[nodiscard]] TimeId myMessagesTTL() const { - return _ttlMyPeriod; - } - [[nodiscard]] TimeId peerMessagesTTL() const { - return _ttlPeerPeriod; - } - [[nodiscard]] bool oneSideTTL() const { - return _ttlOneSide; - } [[nodiscard]] TimeId messagesTTL() const; - void setMessagesTTL(TimeId myPeriod, TimeId peerPeriod, bool oneSide); - void applyMessagesTTL(const MTPPeerHistoryTTL &ttl); + void setMessagesTTL(TimeId period); [[nodiscard]] Data::GroupCall *groupCall() const; @@ -439,9 +429,7 @@ private: crl::time _lastFullUpdate = 0; - TimeId _ttlMyPeriod = 0; - TimeId _ttlPeerPeriod = 0; - bool _ttlOneSide = false; + TimeId _ttlPeriod = 0; bool _hasPinnedMessages = false; Settings _settings = { kSettingsUnknown }; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 86114b894..03fb7530e 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -256,9 +256,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { MTP_inputNotifyPeer(user->input), update.vnotify_settings()); - if (const auto ttl = update.vttl()) { - user->applyMessagesTTL(*ttl); - } + user->setMessagesTTL(update.vttl_period().value_or_empty()); if (const auto info = update.vbot_info()) { user->setBotInfo(*info); } else { diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index af4b0f6cf..050752f20 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1128,6 +1128,8 @@ ServiceAction ParseServiceAction( content.userIds.push_back(user.v); } result.content = content; + }, [&](const MTPDmessageActionSetMessagesTTL &data) { + // #TODO ttl }, [](const MTPDmessageActionEmpty &data) {}); return result; } diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index a4b0988c8..ea60aa0f7 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -371,6 +371,31 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return prepareInvitedToCallText(action.vusers().v, linkCallId); }; + auto prepareSetMessagesTTL = [this](const MTPDmessageActionSetMessagesTTL &action) { + auto result = PreparedText{}; + const auto period = action.vperiod().v; + const auto duration = (period == 5) AssertIsDebug() + ? u"5 seconds"_q AssertIsDebug() + : (period < 3 * 86400) + ? tr::lng_ttl_about_duration1(tr::now) + : tr::lng_ttl_about_duration2(tr::now); + if (isPost()) { + if (!period) { + result.text = tr::lng_action_ttl_removed_channel(tr::now); + } else { + result.text = tr::lng_action_ttl_changed_channel(tr::now, lt_duration, duration); + } + } else { + result.links.push_back(fromLink()); + if (!period) { + result.text = tr::lng_action_ttl_removed(tr::now, lt_from, fromLinkText()); + } else { + result.text = tr::lng_action_ttl_changed(tr::now, lt_from, fromLinkText(), lt_duration, duration); + } + } + return result; + }; + const auto messageText = action.match([&]( const MTPDmessageActionChatAddUser &data) { return prepareChatAddUserText(data); @@ -424,6 +449,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return prepareGroupCall(data); }, [&](const MTPDmessageActionInviteToGroupCall &data) { return prepareInviteToGroupCall(data); + }, [&](const MTPDmessageActionSetMessagesTTL &data) { + return prepareSetMessagesTTL(data); }, [](const MTPDmessageActionEmpty &) { return PreparedText{ tr::lng_message_empty(tr::now) }; }); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp index 4990955f3..987638687 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -40,23 +40,11 @@ void ShowAutoDeleteToast(not_null<PeerData*> peer) { : (period < 3 * 86400) ? tr::lng_ttl_about_duration1(tr::now) : tr::lng_ttl_about_duration2(tr::now); - auto rich = Ui::Text::Bold( - tr::lng_ttl_about_tooltip_on_title(tr::now, lt_duration, duration) - ).append('\n'); - - const auto myPeriod = peer->myMessagesTTL(); - rich.append((period == myPeriod) - ? tr::lng_ttl_about_tooltip(tr::now, lt_duration, duration) - : (myPeriod - ? tr::lng_ttl_about_tooltip_no_longer - : tr::lng_ttl_about_tooltip_no_cancel)( - tr::now, - lt_user, - peer->shortName(), - lt_duration, - duration)); + const auto text = peer->isBroadcast() + ? tr::lng_ttl_about_tooltip_channel(tr::now, lt_duration, duration) + : tr::lng_ttl_about_tooltip(tr::now, lt_duration, duration); Ui::ShowMultilineToast({ - .text = std::move(rich), + .text = { text }, .duration = kToastDuration, }); } @@ -66,27 +54,20 @@ void AutoDeleteSettingsBox( not_null<PeerData*> peer) { struct State { TimeId savingPeriod = 0; - bool savingOneSide = false; mtpRequestId savingRequestId = 0; QPointer<Ui::GenericBox> weak; }; const auto state = std::make_shared<State>(State{ .weak = box.get() }); - auto callback = [=](TimeId period, bool oneSide) { + auto callback = [=](TimeId period) { auto &api = peer->session().api(); if (state->savingRequestId) { - if (period == state->savingPeriod - && oneSide == state->savingOneSide) { + if (period == state->savingPeriod) { return; } api.request(state->savingRequestId).cancel(); } state->savingPeriod = period; - state->savingOneSide = oneSide; - using Flag = MTPmessages_SetHistoryTTL::Flag; state->savingRequestId = api.request(MTPmessages_SetHistoryTTL( - MTP_flags((oneSide && peer->isUser()) - ? Flag::f_pm_oneside - : Flag(0)), peer->input, MTP_int(period) )).done([=](const MTPUpdates &result) { @@ -101,12 +82,12 @@ void AutoDeleteSettingsBox( }; Ui::AutoDeleteSettingsBox( box, - peer->myMessagesTTL(), - peer->peerMessagesTTL(), - peer->oneSideTTL(), + peer->messagesTTL(), (peer->isUser() - ? std::make_optional(peer->shortName()) - : std::nullopt), + ? tr::lng_ttl_edit_about(lt_user, rpl::single(peer->shortName())) + : peer->isBroadcast() + ? tr::lng_ttl_edit_about_channel() + : tr::lng_ttl_edit_about_group()), std::move(callback)); } diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp index c2234ad97..aae2e19cb 100644 --- a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp @@ -186,60 +186,53 @@ object_ptr<Ui::RpWidget> CreateSliderForTTL( void AutoDeleteSettingsBox( not_null<Ui::GenericBox*> box, - TimeId ttlMyPeriod, - TimeId ttlPeerPeriod, - bool ttlOneSide, - std::optional<QString> userFirstName, - Fn<void(TimeId, bool)> callback) { + TimeId ttlPeriod, + rpl::producer<QString> about, + Fn<void(TimeId)> callback) { box->setTitle(tr::lng_manage_messages_ttl_title()); box->setWidth(st::boxWideWidth); struct State { - TimeId my = 0; - bool oneSide = false; - rpl::event_stream<rpl::producer<QString>> aboutTexts; - Fn<void()> update; + TimeId period = 0; }; const auto state = box->lifetime().make_state<State>(State{ - .my = ttlMyPeriod, - .oneSide = ttlOneSide, + .period = ttlPeriod, }); const auto options = std::vector<QString>{ + tr::lng_manage_messages_ttl_never(tr::now), u"5 seconds"_q, AssertIsDebug() tr::lng_manage_messages_ttl_after1(tr::now), tr::lng_manage_messages_ttl_after2(tr::now), - tr::lng_manage_messages_ttl_never(tr::now), }; const auto periodToIndex = [&](TimeId period) { return !period - ? 3 + ? 0 : (period == 5) AssertIsDebug() - ? 0 AssertIsDebug() + ? 1 AssertIsDebug() : (period < 3 * 86400) - ? 1 - : 2; + ? 2 + : 3; }; const auto indexToPeriod = [&](int index) { return !index - ? 5 AssertIsDebug() + ? 0 : (index == 1) AssertIsDebug() - ? 86400 + ? 5 AssertIsDebug() : (index == 2) - ? 7 * 86400 - : 0; + ? 86400 + : 7 * 86400; }; const auto sliderCallback = [=](int index) { - state->my = indexToPeriod(index); - state->update(); + state->period = indexToPeriod(index); }; const auto slider = box->addRow( CreateSliderForTTL( box, options | ranges::to_vector, - periodToIndex(ttlPeerPeriod), - periodToIndex(ttlMyPeriod), + options.size() - 1, + periodToIndex(ttlPeriod), sliderCallback), { st::boxRowPadding.left(), @@ -247,72 +240,21 @@ void AutoDeleteSettingsBox( st::boxRowPadding.right(), st::boxMediumSkip }); - const auto bothSides = userFirstName - ? box->addRow( - object_ptr<Ui::Checkbox>( - box, - tr::lng_ttl_also_checkbox(tr::now, lt_user, *userFirstName), - !ttlOneSide), - { - st::boxRowPadding.left(), - 0, - st::boxRowPadding.right(), - st::boxMediumSkip }) - : nullptr; - const auto description = box->addRow( object_ptr<Ui::DividerLabel>( box, object_ptr<Ui::FlatLabel>( box, - state->aboutTexts.events() | rpl::flatten_latest(), + std::move(about), st::boxDividerLabel), st::ttlDividerLabelPadding), style::margins()); - if (bothSides) { - bothSides->checkedChanges( - ) | rpl::start_with_next([=](bool checked) { - state->oneSide = !checked; - state->update(); - }, bothSides->lifetime()); - } - - state->update = [=] { - const auto his = ttlPeerPeriod; - const auto wrap = [](TimeId period) { - Expects(period > 0); - - return (period == 5) AssertIsDebug() - ? rpl::single(u"5 seconds"_q) AssertIsDebug() - : (period < 3 * 86400) - ? tr::lng_ttl_about_duration1() - : tr::lng_ttl_about_duration2(); - }; - state->aboutTexts.fire(((!state->my && !his) || !userFirstName) - ? tr::lng_ttl_edit_about() - : (his > 0 && (!state->my || his < state->my)) - ? tr::lng_ttl_edit_about_other( - lt_user, - rpl::single(*userFirstName), - lt_duration, - wrap(his)) - : state->oneSide - ? tr::lng_ttl_edit_about_you_only(lt_duration, wrap(state->my)) - : tr::lng_ttl_edit_about_you( - lt_duration, - wrap(state->my), - lt_user, - rpl::single(*userFirstName))); - }; - state->update(); - box->addButton(tr::lng_settings_save(), [=] { - const auto period = state->my; - const auto oneSide = state->oneSide; + const auto period = state->period; box->closeBox(); - callback(period, oneSide); + callback(period); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h index 0422fa446..0c7e72322 100644 --- a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h @@ -13,10 +13,8 @@ namespace Ui { void AutoDeleteSettingsBox( not_null<Ui::GenericBox*> box, - TimeId ttlMyPeriod, - TimeId ttlPeerPeriod, - bool ttlOneSide, - std::optional<QString> userFirstName, - Fn<void(TimeId, bool)> callback); + TimeId ttlPeriod, + rpl::producer<QString> about, + Fn<void(TimeId)> callback); } // namespace Ui From 14d525dade070a65f6eff3b02d0f621abe8f550f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 14:57:07 +0400 Subject: [PATCH 363/396] Add 'Clear History' to channels as auto-delete entry point. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/boxes/confirm_box.cpp | 27 ++++++++++++++----- .../SourceFiles/window/window_peer_menu.cpp | 3 ++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0b9db29c9..8a3f24305 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1073,6 +1073,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sure_leave_group" = "Are you sure you want to leave this group?"; "lng_sure_delete_group" = "Are you sure, you want to delete this group? All members will be removed and all messages will be lost."; "lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone."; +"lng_no_clear_history_channel" = "In channels you can enable auto-delete for messages."; +"lng_no_clear_history_group" = "In public groups you can enable auto-delete for messages."; "lng_message_empty" = "Empty Message"; "lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the latest version in Settings, or install it from {link}"; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 5ae466802..5d5982b4a 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -583,9 +583,20 @@ void DeleteMessagesBox::prepare() { auto deleteText = lifetime().make_state<rpl::variable<QString>>(); *deleteText = tr::lng_box_delete(); auto deleteStyle = &st::defaultBoxButton; + auto canDelete = true; if (const auto peer = _wipeHistoryPeer) { if (_wipeHistoryJustClear) { - details.text = peer->isSelf() + const auto isChannel = peer->isBroadcast(); + const auto isPublicGroup = peer->isMegagroup() + && peer->asChannel()->isPublic(); + if (isChannel || isPublicGroup) { + canDelete = false; + } + details.text = isChannel + ? tr::lng_no_clear_history_channel(tr::now) + : isPublicGroup + ? tr::lng_no_clear_history_group(tr::now) + : peer->isSelf() ? tr::lng_sure_delete_saved_messages(tr::now) : peer->isUser() ? tr::lng_sure_delete_history(tr::now, lt_contact, peer->name) @@ -671,11 +682,15 @@ void DeleteMessagesBox::prepare() { }); } - addButton( - deleteText->value(), - [=] { deleteAndClear(); }, - *deleteStyle); - addButton(tr::lng_cancel(), [=] { closeBox(); }); + if (canDelete) { + addButton( + deleteText->value(), + [=] { deleteAndClear(); }, + *deleteStyle); + addButton(tr::lng_cancel(), [=] { closeBox(); }); + } else { + addButton(tr::lng_about_done(), [=] { closeBox(); }); + } auto fullHeight = st::boxPadding.top() + _text->height() + st::boxPadding.bottom(); if (_moderateFrom) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index f9f1b70a9..84e8e173e 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -582,7 +582,8 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) { } } if (channel->amIn()) { - if (isGroup && !channel->isPublic()) { + if ((isGroup && !channel->isPublic()) + || channel->canDeleteMessages()) { _addAction( tr::lng_profile_clear_history(tr::now), ClearHistoryHandler(channel)); From 4896509ddf1bb473d1753a91a8bc377ab9b95bc7 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 14:58:01 +0400 Subject: [PATCH 364/396] Allow clear history for everyone in small supergroups. --- Telegram/SourceFiles/boxes/confirm_box.cpp | 2 -- Telegram/SourceFiles/data/data_histories.cpp | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 5d5982b4a..b2174236e 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -748,8 +748,6 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const tr::now, lt_user, user->firstName); - } else if (_wipeHistoryJustClear) { - return std::nullopt; } else { result.checkbox = tr::lng_delete_for_everyone_check(tr::now); } diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 5c7ccff83..47f388fd5 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -595,7 +595,7 @@ void Histories::deleteAllMessages( }; const auto chat = peer->asChat(); const auto channel = peer->asChannel(); - if (revoke && channel && channel->canDelete()) { + if (!justClear && revoke && channel && channel->canDelete()) { return session().api().request(MTPchannels_DeleteChannel( channel->inputChannel )).done([=](const MTPUpdates &result) { @@ -606,8 +606,11 @@ void Histories::deleteAllMessages( // } }).send(); } else if (channel) { + const auto flags = revoke + ? MTPchannels_DeleteHistory::Flag::f_for_everyone + : MTPchannels_DeleteHistory::Flag(0); return session().api().request(MTPchannels_DeleteHistory( - MTP_flags(0), + MTP_flags(flags), channel->inputChannel, MTP_int(deleteTillId) )).done([=](const MTPBool &result) { From 5b6503bfed0ea408feb6c40adb1cd778bf73a43d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 14:58:22 +0400 Subject: [PATCH 365/396] Fix auto-delete button when switching between chats. --- Telegram/SourceFiles/history/history_widget.cpp | 2 +- .../history/view/controls/history_view_ttl_button.cpp | 3 ++- .../history/view/controls/history_view_ttl_button.h | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 5190581a6..98f4a50ed 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5417,7 +5417,7 @@ void HistoryWidget::checkMessagesTTL() { updateControlsGeometry(); updateControlsVisibility(); } - } else if (!_ttlInfo) { + } else if (!_ttlInfo || _ttlInfo->peer() != _peer) { _ttlInfo = std::make_unique<HistoryView::Controls::TTLButton>( this, _peer); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp index 987638687..841fffb48 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -92,7 +92,8 @@ void AutoDeleteSettingsBox( } TTLButton::TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer) -: _button(parent, st::historyMessagesTTL) { +: _peer(peer) +, _button(parent, st::historyMessagesTTL) { _button.setClickedCallback([=] { const auto canEdit = peer->isUser() || (peer->isChat() diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h index f1dc37f22..a0b38156b 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h @@ -23,6 +23,10 @@ class TTLButton final { public: TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer); + [[nodiscard]] not_null<PeerData*> peer() const { + return _peer; + } + void show(); void hide(); void move(int x, int y); @@ -30,6 +34,7 @@ public: [[nodiscard]] int width() const; private: + const not_null<PeerData*> _peer; Ui::IconButton _button; }; From d5fe57100a5e554f0046e7d71351bd2c781d550e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 15:25:23 +0400 Subject: [PATCH 366/396] Add tooltip after converting to gigagroup. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 8a3f24305..a2b3736dd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2025,6 +2025,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gigagroup_convert_sure" = "Convert"; "lng_gigagroup_warning_title" = "Are you sure?"; "lng_gigagroup_warning" = "Regular members of the group (non-admins) will **irrevocably** lose their right to post messages in the group.\n\nThis action **can't** be undone."; +"lng_gigagroup_done" = "Your group can now have more than 200,000 members."; "lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_post" = "Post messages"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 76bb802dc..dd83992d3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kSlowmodeValues = 7; -constexpr auto kSuggestGigagroupThreshold = 1; AssertIsDebug(199000); +constexpr auto kSuggestGigagroupThreshold = 199000; int SlowmodeDelayByIndex(int index) { Expects(index >= 0 && index < kSlowmodeValues); @@ -553,6 +553,7 @@ void EditPeerPermissionsBox::addSuggestGigagroup( )).done([=](const MTPUpdates &result) { channel->session().api().applyUpdates(result); Ui::hideSettingsAndLayer(); + Ui::Toast::Show(tr::lng_gigagroup_done(tr::now)); }).fail([=](const RPCError &error) { *converting = false; }).send(); From 45dcadfff425468e878bc44ee03a64e251a0a3ca Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 16:18:54 +0400 Subject: [PATCH 367/396] Auto-login to domains from app config. --- Telegram/SourceFiles/core/ui_integration.cpp | 30 +++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index fec3bdd66..a5cad2d3b 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -18,9 +18,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "main/main_account.h" #include "main/main_session.h" +#include "main/main_app_config.h" #include "mainwindow.h" namespace Core { +namespace { + +QString UrlWithAutoLoginToken(const QString &url) { + const auto &config = Core::App().activeAccount().appConfig(); + const auto token = config.get<QString>("autologin_token", {}); + const auto domains = config.get<std::vector<QString>>( + "autologin_domains", + {}); + if (domains.empty() + || token.isEmpty() + || !url.startsWith("https://", Qt::CaseInsensitive)) { + return url; + } + auto parsed = QUrl(url); + if (!parsed.isValid()) { + return url; + } else if (!ranges::contains(domains, parsed.host().toLower())) { + return url; + } + const auto added = "autologin_token=" + token; + parsed.setQuery(parsed.hasQuery() + ? (parsed.query() + '&' + added) + : added); + return QString::fromUtf8(parsed.toEncoded()); +} + +} // namespace void UiIntegration::postponeCall(FnMut<void()> &&callable) { Sandbox::Instance().postponeCall(std::move(callable)); @@ -151,7 +179,7 @@ bool UiIntegration::handleUrlClick( return true; } - File::OpenUrl(url); + File::OpenUrl(UrlWithAutoLoginToken(url)); return true; } From 467449ac1304217a1e009d8a7bd017be03761fad Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Feb 2021 21:37:22 +0400 Subject: [PATCH 368/396] When reporting peer allow to select messages first. --- Telegram/CMakeLists.txt | 2 - Telegram/Resources/langs/lang.strings | 6 + Telegram/SourceFiles/boxes/report_box.cpp | 229 ------------------ Telegram/SourceFiles/boxes/report_box.h | 74 ------ Telegram/SourceFiles/data/data_types.h | 5 +- .../history/history_inner_widget.cpp | 53 ++-- .../history/history_inner_widget.h | 6 + .../SourceFiles/history/history_widget.cpp | 148 ++++++++++- Telegram/SourceFiles/history/history_widget.h | 14 ++ .../view/history_view_context_menu.cpp | 111 ++++++++- .../history/view/history_view_context_menu.h | 14 +- .../view/history_view_top_bar_widget.cpp | 123 ++++++++-- .../view/history_view_top_bar_widget.h | 13 + Telegram/SourceFiles/info/info.style | 7 + .../info/profile/info_profile_actions.cpp | 7 +- .../profile/info_profile_inner_widget.cpp | 1 - Telegram/SourceFiles/mainwidget.cpp | 12 + Telegram/SourceFiles/mainwidget.h | 7 + Telegram/SourceFiles/ui/boxes/report_box.cpp | 93 +++++++ Telegram/SourceFiles/ui/boxes/report_box.h | 39 +++ .../SourceFiles/window/window_peer_menu.cpp | 22 +- .../SourceFiles/window/window_peer_menu.h | 5 + .../window/window_session_controller.cpp | 11 + .../window/window_session_controller.h | 7 + Telegram/cmake/td_ui.cmake | 2 + 25 files changed, 639 insertions(+), 372 deletions(-) delete mode 100644 Telegram/SourceFiles/boxes/report_box.cpp delete mode 100644 Telegram/SourceFiles/boxes/report_box.h create mode 100644 Telegram/SourceFiles/ui/boxes/report_box.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/report_box.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 235e68261..c5ba8fdfc 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -240,8 +240,6 @@ PRIVATE boxes/photo_crop_box.h boxes/rate_call_box.cpp boxes/rate_call_box.h - boxes/report_box.cpp - boxes/report_box.h boxes/self_destruction_box.cpp boxes/self_destruction_box.h boxes/send_files_box.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a2b3736dd..baf02adfc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -996,6 +996,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_report_group_title" = "Report group"; "lng_report_bot_title" = "Report bot"; "lng_report_message_title" = "Report message"; +"lng_report_select_messages" = "Select messages"; +"lng_report_messages_none" = "Select Messages"; +"lng_report_messages_count#one" = "Report {count} Message"; +"lng_report_messages_count#other" = "Report {count} Messages"; +"lng_report_details_about" = "Please enter any additional details relevant to your report."; +"lng_report_details" = "Additional Details"; "lng_report_reason_spam" = "Spam"; "lng_report_reason_fake" = "Fake Account"; "lng_report_reason_violence" = "Violence"; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp deleted file mode 100644 index 81487ac66..000000000 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* -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 "boxes/report_box.h" - -#include "lang/lang_keys.h" -#include "data/data_peer.h" -#include "data/data_session.h" -#include "main/main_session.h" -#include "boxes/confirm_box.h" -#include "history/history_item.h" -#include "ui/widgets/checkbox.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/input_fields.h" -#include "ui/toast/toast.h" -#include "mainwindow.h" -#include "core/core_settings.h" -#include "core/application.h" -#include "window/window_session_controller.h" -#include "window/window_peer_menu.h" -#include "styles/style_layers.h" -#include "styles/style_boxes.h" -#include "styles/style_profile.h" - -namespace { - -constexpr auto kReportReasonLengthMax = 200; - -} // namespace - -ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer) -: _peer(peer) -, _api(&_peer->session().mtp()) { -} - -ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids) -: _peer(peer) -, _api(&_peer->session().mtp()) -, _ids(std::move(ids)) { -} - -void ReportBox::prepare() { - setTitle([&] { - if (_ids) { - return tr::lng_report_message_title(); - } else if (_peer->isUser()) { - return tr::lng_report_bot_title(); - } else if (_peer->isMegagroup()) { - return tr::lng_report_group_title(); - } else { - return tr::lng_report_title(); - } - }()); - - addButton(tr::lng_report_button(), [=] { report(); }); - addButton(tr::lng_cancel(), [=] { closeBox(); }); - - _reasonGroup = std::make_shared<Ui::RadioenumGroup<Reason>>( - Reason::Spam); - const auto createButton = [&]( - object_ptr<Ui::Radioenum<Reason>> &button, - Reason reason, - const QString &text) { - button.create( - this, - _reasonGroup, - reason, - text, - st::defaultBoxCheckbox); - }; - createButton(_reasonSpam, Reason::Spam, tr::lng_report_reason_spam(tr::now)); - createButton(_reasonFake, Reason::Fake, tr::lng_report_reason_fake(tr::now)); - createButton(_reasonViolence, Reason::Violence, tr::lng_report_reason_violence(tr::now)); - createButton(_reasonChildAbuse, Reason::ChildAbuse, tr::lng_report_reason_child_abuse(tr::now)); - createButton(_reasonPornography, Reason::Pornography, tr::lng_report_reason_pornography(tr::now)); - createButton(_reasonOther, Reason::Other, tr::lng_report_reason_other(tr::now)); - _reasonGroup->setChangedCallback([=](Reason value) { - reasonChanged(value); - }); - - updateMaxHeight(); -} - -void ReportBox::resizeEvent(QResizeEvent *e) { - BoxContent::resizeEvent(e); - - _reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _reasonSpam->getMargins().top()); - _reasonFake->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->bottomNoMargins() + st::boxOptionListSkip); - _reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonFake->bottomNoMargins() + st::boxOptionListSkip); - _reasonChildAbuse->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip); - _reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonChildAbuse->bottomNoMargins() + st::boxOptionListSkip); - _reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->bottomNoMargins() + st::boxOptionListSkip); - - if (_reasonOtherText) { - _reasonOtherText->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() - st::defaultInputField.textMargins.left(), _reasonOther->bottomNoMargins() + st::newGroupDescriptionPadding.top()); - } -} - -void ReportBox::reasonChanged(Reason reason) { - if (reason == Reason::Other) { - if (!_reasonOtherText) { - _reasonOtherText.create( - this, - st::profileReportReasonOther, - Ui::InputField::Mode::MultiLine, - tr::lng_report_reason_description()); - _reasonOtherText->show(); - _reasonOtherText->setSubmitSettings(Core::App().settings().sendSubmitWay()); - _reasonOtherText->setMaxLength(kReportReasonLengthMax); - _reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height()); - - updateMaxHeight(); - connect(_reasonOtherText, &Ui::InputField::resized, [=] { reasonResized(); }); - connect(_reasonOtherText, &Ui::InputField::submitted, [=] { report(); }); - connect(_reasonOtherText, &Ui::InputField::cancelled, [=] { closeBox(); }); - } - _reasonOtherText->setFocusFast(); - } else if (_reasonOtherText) { - _reasonOtherText.destroy(); - updateMaxHeight(); - } -} - -void ReportBox::setInnerFocus() { - if (_reasonOtherText) { - _reasonOtherText->setFocusFast(); - } else { - setFocus(); - } -} - -void ReportBox::reasonResized() { - updateMaxHeight(); - update(); -} - -void ReportBox::report() { - if (_requestId) { - return; - } - - const auto text = _reasonOtherText - ? _reasonOtherText->getLastText().trimmed() - : QString(); - if (_reasonOtherText && text.isEmpty()) { - _reasonOtherText->showError(); - return; - } - - const auto reason = [&] { - switch (_reasonGroup->value()) { - case Reason::Spam: return MTP_inputReportReasonSpam(); - case Reason::Fake: return MTP_inputReportReasonFake(); - case Reason::Violence: return MTP_inputReportReasonViolence(); - case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse(); - case Reason::Pornography: return MTP_inputReportReasonPornography(); - case Reason::Other: return MTP_inputReportReasonOther(); - } - Unexpected("Bad reason group value."); - }(); - if (_ids) { - auto ids = QVector<MTPint>(); - for (const auto &fullId : *_ids) { - ids.push_back(MTP_int(fullId.msg)); - } - _requestId = _api.request(MTPmessages_Report( - _peer->input, - MTP_vector<MTPint>(ids), - reason, - MTP_string(text) - )).done([=](const MTPBool &result) { - reportDone(result); - }).fail([=](const RPCError &error) { - reportFail(error); - }).send(); - } else { - _requestId = _api.request(MTPaccount_ReportPeer( - _peer->input, - reason, - MTP_string(text) - )).done([=](const MTPBool &result) { - reportDone(result); - }).fail([=](const RPCError &error) { - reportFail(error); - }).send(); - } -} - -void ReportBox::reportDone(const MTPBool &result) { - _requestId = 0; - Ui::Toast::Show(tr::lng_report_thanks(tr::now)); - closeBox(); -} - -void ReportBox::reportFail(const RPCError &error) { - _requestId = 0; - if (_reasonOtherText) { - _reasonOtherText->showError(); - } -} - -void ReportBox::updateMaxHeight() { - const auto buttonsCount = 6; - auto newHeight = st::boxOptionListPadding.top() + _reasonSpam->getMargins().top() + buttonsCount * _reasonSpam->heightNoMargins() + (buttonsCount - 1) * st::boxOptionListSkip + _reasonSpam->getMargins().bottom() + st::boxOptionListPadding.bottom(); - - if (_reasonOtherText) { - newHeight += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom(); - } - setDimensions(st::boxWidth, newHeight); -} - -void BlockSenderFromRepliesBox( - not_null<Ui::GenericBox*> box, - not_null<Window::SessionController*> controller, - FullMsgId id) { - const auto item = controller->session().data().message(id); - Assert(item != nullptr); - - PeerMenuBlockUserBox( - box, - &controller->window(), - item->senderOriginal(), - true, - Window::ClearReply{ id }); -} diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h deleted file mode 100644 index ad637aae3..000000000 --- a/Telegram/SourceFiles/boxes/report_box.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -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 "boxes/abstract_box.h" -#include "ui/layers/generic_box.h" -#include "mtproto/sender.h" - -namespace Window { -class SessionController; -} // namespace Window - -namespace Ui { -template <typename Enum> -class RadioenumGroup; -template <typename Enum> -class Radioenum; -class InputField; -} // namespace Ui - -class ReportBox final : public Ui::BoxContent { -public: - ReportBox(QWidget*, not_null<PeerData*> peer); - ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids); - -protected: - void prepare() override; - void setInnerFocus() override; - - void resizeEvent(QResizeEvent *e) override; - -private: - enum class Reason { - Spam, - Fake, - Violence, - ChildAbuse, - Pornography, - Other, - }; - void reasonChanged(Reason reason); - void reasonResized(); - void updateMaxHeight(); - void report(); - - void reportDone(const MTPBool &result); - void reportFail(const RPCError &error); - - const not_null<PeerData*> _peer; - MTP::Sender _api; - std::optional<MessageIdsList> _ids; - - std::shared_ptr<Ui::RadioenumGroup<Reason>> _reasonGroup; - object_ptr<Ui::Radioenum<Reason>> _reasonSpam = { nullptr }; - object_ptr<Ui::Radioenum<Reason>> _reasonFake = { nullptr }; - object_ptr<Ui::Radioenum<Reason>> _reasonViolence = { nullptr }; - object_ptr<Ui::Radioenum<Reason>> _reasonChildAbuse = { nullptr }; - object_ptr<Ui::Radioenum<Reason>> _reasonPornography = { nullptr }; - object_ptr<Ui::Radioenum<Reason>> _reasonOther = { nullptr }; - object_ptr<Ui::InputField> _reasonOtherText = { nullptr }; - - mtpRequestId _requestId = 0; - -}; - -void BlockSenderFromRepliesBox( - not_null<Ui::GenericBox*> box, - not_null<Window::SessionController*> controller, - FullMsgId id); diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 8089daad4..03bca3ea8 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -185,8 +185,9 @@ constexpr auto EndClientMsgId = MsgId(-0x40000000); constexpr auto ShowAtTheEndMsgId = MsgId(-0x40000000); constexpr auto SwitchAtTopMsgId = MsgId(-0x3FFFFFFF); constexpr auto ShowAtProfileMsgId = MsgId(-0x3FFFFFFE); -constexpr auto ShowAndStartBotMsgId = MsgId(-0x3FFFFFD); -constexpr auto ShowAtGameShareMsgId = MsgId(-0x3FFFFFC); +constexpr auto ShowAndStartBotMsgId = MsgId(-0x3FFFFFFD); +constexpr auto ShowAtGameShareMsgId = MsgId(-0x3FFFFFFC); +constexpr auto ShowForChooseMessagesMsgId = MsgId(-0x3FFFFFFB); constexpr auto ServerMaxMsgId = MsgId(0x3FFFFFFF); constexpr auto ShowAtUnreadMsgId = MsgId(0); constexpr inline bool IsClientMsgId(MsgId id) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index fb88bd32e..fd3661625 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -26,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/toast/toast.h" #include "ui/text/text_options.h" +#include "ui/boxes/report_box.h" +#include "ui/layers/generic_box.h" #include "ui/controls/delete_message_context_action.h" #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" @@ -35,7 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/notifications_manager.h" #include "boxes/confirm_box.h" -#include "boxes/report_box.h" #include "boxes/sticker_set_box.h" #include "chat_helpers/message_field.h" #include "history/history_widget.h" @@ -1058,15 +1059,13 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; - } else if (!_selected.empty()) { - if (_selected.cbegin()->second == FullSelection) { - if (_dragStateItem - && _selected.find(_dragStateItem) != _selected.cend() - && App::hoveredItem()) { - _mouseAction = MouseAction::PrepareDrag; // start items drag - } else if (!_pressWasInactive) { - _mouseAction = MouseAction::PrepareSelect; // start items select - } + } else if (inSelectionMode()) { + if (_dragStateItem + && _selected.find(_dragStateItem) != _selected.cend() + && App::hoveredItem()) { + _mouseAction = MouseAction::PrepareDrag; // start items drag + } else if (!_pressWasInactive) { + _mouseAction = MouseAction::PrepareSelect; // start items select } } if (_mouseAction == MouseAction::None && mouseActionView) { @@ -1319,7 +1318,10 @@ void HistoryInner::mouseActionFinish( } else if (_mouseActionItem) { // if we are in selecting items mode perhaps we want to // toggle selection instead of activating the pressed link - if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { + if (_mouseAction == MouseAction::PrepareDrag + && !_pressWasInactive + && inSelectionMode() + && button != Qt::RightButton) { if (const auto view = _mouseActionItem->mainView()) { if (const auto media = view->media()) { if (media->toggleSelectionByHandlerClick(activated)) { @@ -1357,8 +1359,7 @@ void HistoryInner::mouseActionFinish( } if ((_mouseAction == MouseAction::PrepareSelect) && !_pressWasInactive - && !_selected.empty() - && (_selected.cbegin()->second == FullSelection)) { + && inSelectionMode()) { changeSelectionAsGroup( &_selected, _mouseActionItem, @@ -1375,8 +1376,7 @@ void HistoryInner::mouseActionFinish( } else if ((i == _selected.cend()) && !_dragStateItem->serviceMsg() && (_dragStateItem->id > 0) - && !_selected.empty() - && _selected.cbegin()->second == FullSelection) { + && inSelectionMode()) { if (_selected.size() < MaxSelectedItems) { _selected.emplace(_dragStateItem, FullSelection); repaintItem(_mouseActionItem); @@ -2519,6 +2519,8 @@ bool HistoryInner::inSelectionMode() const { && _dragSelFrom && _dragSelTo) { return true; + } else if (_chooseForReportReason.has_value()) { + return true; } return false; } @@ -2993,6 +2995,14 @@ int HistoryInner::historyDrawTop() const { return (top >= 0) ? (top + _historySkipHeight) : -1; } +void HistoryInner::setChooseReportReason(Ui::ReportReason reason) { + _chooseForReportReason = reason; +} + +void HistoryInner::clearChooseReportReason() { + _chooseForReportReason = std::nullopt; +} + // -1 if should not be visible, -2 if bad history() int HistoryInner::itemTop(const HistoryItem *item) const { if (!item) { @@ -3226,25 +3236,24 @@ void HistoryInner::deleteAsGroup(FullMsgId itemId) { } void HistoryInner::reportItem(FullMsgId itemId) { - Ui::show(Box<ReportBox>(_peer, MessageIdsList(1, itemId))); + HistoryView::ShowReportItemsBox(_peer, { 1, itemId }); } void HistoryInner::reportAsGroup(FullMsgId itemId) { if (const auto item = session().data().message(itemId)) { const auto group = session().data().groups().find(item); - if (!group) { - return reportItem(itemId); - } - Ui::show(Box<ReportBox>( + HistoryView::ShowReportItemsBox( _peer, - session().data().itemsToIds(group->items))); + (group + ? session().data().itemsToIds(group->items) + : MessageIdsList{ 1, itemId })); } } void HistoryInner::blockSenderItem(FullMsgId itemId) { if (const auto item = session().data().message(itemId)) { Ui::show(Box( - BlockSenderFromRepliesBox, + Window::BlockSenderFromRepliesBox, _controller, itemId)); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index ab9ba5034..82a898642 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -35,6 +35,7 @@ class SessionController; namespace Ui { class PopupMenu; +enum class ReportReason; } // namespace Ui class HistoryWidget; @@ -111,6 +112,9 @@ public: int historyTop() const; int historyDrawTop() const; + void setChooseReportReason(Ui::ReportReason reason); + void clearChooseReportReason(); + // -1 if should not be visible, -2 if bad history() int itemTop(const HistoryItem *item) const; int itemTop(const Element *view) const; @@ -312,6 +316,7 @@ private: void deleteAsGroup(FullMsgId itemId); void reportItem(FullMsgId itemId); void reportAsGroup(FullMsgId itemId); + void reportItems(MessageIdsList ids); void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); @@ -350,6 +355,7 @@ private: style::cursor _cursor = style::cur_default; SelectedItems _selected; + std::optional<Ui::ReportReason> _chooseForReportReason; base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed; base::flat_map< diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 98f4a50ed..9574b6696 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -73,6 +73,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_webpage_preview.h" #include "history/view/history_view_top_bar_widget.h" #include "history/view/history_view_contact_status.h" +#include "history/view/history_view_context_menu.h" #include "history/view/history_view_pinned_tracker.h" #include "history/view/history_view_pinned_section.h" #include "history/view/history_view_pinned_bar.h" @@ -102,6 +103,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "apiwrap.h" #include "base/qthelp_regex.h" +#include "ui/boxes/report_box.h" #include "ui/chat/pinned_bar.h" #include "ui/chat/group_call_bar.h" #include "ui/widgets/popup_menu.h" @@ -111,6 +113,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session_settings.h" #include "window/themes/window_theme.h" #include "window/notifications_manager.h" +#include "window/window_controller.h" #include "window/window_session_controller.h" #include "window/window_slide_animation.h" #include "window/window_peer_menu.h" @@ -196,6 +199,7 @@ HistoryWidget::HistoryWidget( this, tr::lng_channel_mute(tr::now).toUpper(), st::historyComposeButton) +, _reportMessages(this, QString(), st::historyComposeButton) , _attachToggle(this, st::historyAttach) , _tabbedSelectorToggle(this, st::historyAttachEmoji) , _botKeyboardShow(this, st::historyBotKeyboardShow) @@ -242,6 +246,7 @@ HistoryWidget::HistoryWidget( _botStart->addClickHandler([=] { sendBotStartCommand(); }); _joinChannel->addClickHandler([=] { joinChannel(); }); _muteUnmute->addClickHandler([=] { toggleMuteUnmute(); }); + _reportMessages->addClickHandler([=] { reportSelectedMessages(); }); connect( _field, &Ui::InputField::submitted, @@ -373,6 +378,7 @@ HistoryWidget::HistoryWidget( _botStart->hide(); _joinChannel->hide(); _muteUnmute->hide(); + _reportMessages->hide(); initVoiceRecordBar(); @@ -687,6 +693,10 @@ HistoryWidget::HistoryWidget( ) | rpl::start_with_next([=] { clearSelected(); }, _topBar->lifetime()); + _topBar->cancelChooseForReportRequest( + ) | rpl::start_with_next([=] { + setChooseReportMessagesDetails({}, nullptr); + }, _topBar->lifetime()); session().api().sendActions( ) | rpl::filter([=](const Api::SendAction &action) { @@ -1743,6 +1753,9 @@ void HistoryWidget::showHistory( if (startBot) { showAtMsgId = ShowAtTheEndMsgId; } + if (showAtMsgId != ShowForChooseMessagesMsgId) { + _chooseForReport = nullptr; + } clearHighlightMessages(); hideInfoTooltip(anim::type::instant); @@ -1753,7 +1766,17 @@ void HistoryWidget::showHistory( if (showAtMsgId == ShowAtUnreadMsgId && insideJumpToEndInsteadOfToUnread()) { showAtMsgId = ShowAtTheEndMsgId; + } else if (showAtMsgId == ShowForChooseMessagesMsgId) { + if (_chooseForReport) { + _chooseForReport->active = true; + _list->setChooseReportReason(_chooseForReport->reason); + updateControlsVisibility(); + updateControlsGeometry(); + updateTopBarChooseForReport(); + } + return; } + _list->clearChooseReportReason(); if (!IsServerMsgId(showAtMsgId) && !IsServerMsgId(-showAtMsgId)) { // To end or to unread. @@ -1918,6 +1941,12 @@ void HistoryWidget::showHistory( refreshTopBarActiveChat(); updateTopBarSelection(); + if (showAtMsgId == ShowForChooseMessagesMsgId) { + showAtMsgId = ShowAtUnreadMsgId; + if (_chooseForReport) { + _chooseForReport->active = true; + } + } if (_channel) { updateNotifyControls(); @@ -1944,6 +1973,11 @@ void HistoryWidget::showHistory( object_ptr<HistoryInner>(this, _scroll, controller(), _history)); _list->show(); + if (_chooseForReport && _chooseForReport->active) { + _list->setChooseReportReason(_chooseForReport->reason); + } + updateTopBarChooseForReport(); + _updateHistoryItems.cancel(); setupPinnedTracker(); @@ -2168,8 +2202,18 @@ void HistoryWidget::updateControlsVisibility() { if (_contactStatus) { _contactStatus->show(); } - if (!editingMessage() && (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart())) { - if (isBlocked()) { + if (!editingMessage() && (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart() || isReportMessages())) { + if (isReportMessages()) { + _unblock->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); + _botStart->hide(); + if (_reportMessages->isHidden()) { + _reportMessages->clearState(); + _reportMessages->show(); + } + } else if (isBlocked()) { + _reportMessages->hide(); _joinChannel->hide(); _muteUnmute->hide(); _botStart->hide(); @@ -2178,6 +2222,7 @@ void HistoryWidget::updateControlsVisibility() { _unblock->show(); } } else if (isJoinChannel()) { + _reportMessages->hide(); _unblock->hide(); _muteUnmute->hide(); _botStart->hide(); @@ -2186,6 +2231,7 @@ void HistoryWidget::updateControlsVisibility() { _joinChannel->show(); } } else if (isMuteUnmute()) { + _reportMessages->hide(); _unblock->hide(); _joinChannel->hide(); _botStart->hide(); @@ -2194,6 +2240,7 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute->show(); } } else if (isBotStart()) { + _reportMessages->hide(); _unblock->hide(); _joinChannel->hide(); _muteUnmute->hide(); @@ -2244,6 +2291,7 @@ void HistoryWidget::updateControlsVisibility() { _botStart->hide(); _joinChannel->hide(); _muteUnmute->hide(); + _reportMessages->hide(); _send->show(); updateSendButtonType(); @@ -2303,6 +2351,7 @@ void HistoryWidget::updateControlsVisibility() { _botStart->hide(); _joinChannel->hide(); _muteUnmute->hide(); + _reportMessages->hide(); _attachToggle->hide(); if (_silent) { _silent->hide(); @@ -3268,6 +3317,24 @@ void HistoryWidget::toggleMuteUnmute() { session().data().updateNotifySettings(_peer, muteForSeconds); } +void HistoryWidget::reportSelectedMessages() { + if (!_list || !_chooseForReport || !_list->getSelectionState().count) { + return; + } + const auto ids = _list->getSelectedItems(); + const auto peer = _peer; + const auto reason = _chooseForReport->reason; + const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); + const auto send = [=](const QString &text) { + HistoryView::SendReport(peer, reason, text, ids); + controller()->clearChooseReportMessages(); + if (*box) { + (*box)->closeBox(); + } + }; + *box = controller()->window().show(Box(Ui::ReportDetailsBox, send)); +} + History *HistoryWidget::history() const { return _history; } @@ -3706,6 +3773,10 @@ bool HistoryWidget::isBotStart() const { return false; } +bool HistoryWidget::isReportMessages() const { + return _peer && _chooseForReport && _chooseForReport->active; +} + bool HistoryWidget::isBlocked() const { return _peer && _peer->isUser() && _peer->asUser()->isBlocked(); } @@ -3993,7 +4064,7 @@ void HistoryWidget::moveFieldControls() { // _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send -// (_botStart|_unblock|_joinChannel|_muteUnmute) +// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages) auto buttonsBottom = bottom - _attachToggle->height(); auto left = st::historySendRight; @@ -4037,8 +4108,8 @@ void HistoryWidget::moveFieldControls() { _botStart->setGeometry(fullWidthButtonRect); _unblock->setGeometry(fullWidthButtonRect); _joinChannel->setGeometry(fullWidthButtonRect); - _muteUnmute->setGeometry(fullWidthButtonRect); + _reportMessages->setGeometry(fullWidthButtonRect); } void HistoryWidget::updateFieldSize() { @@ -4406,13 +4477,19 @@ void HistoryWidget::handleHistoryChange(not_null<const History*> history) { const auto botStart = isBotStart(); const auto joinChannel = isJoinChannel(); const auto muteUnmute = isMuteUnmute(); + const auto reportMessages = isReportMessages(); const auto update = false - || (_unblock->isHidden() == unblock) - || (!unblock && _botStart->isHidden() == botStart) - || (!unblock + || (_reportMessages->isHidden() == reportMessages) + || (!reportMessages && _unblock->isHidden() == unblock) + || (!reportMessages + && !unblock + && _botStart->isHidden() == botStart) + || (!reportMessages + && !unblock && !botStart && _joinChannel->isHidden() == joinChannel) - || (!unblock + || (!reportMessages + && !unblock && !botStart && !joinChannel && _muteUnmute->isHidden() == muteUnmute); @@ -4667,7 +4744,7 @@ void HistoryWidget::updateHistoryGeometry( if (_contactStatus) { newScrollHeight -= _contactStatus->height(); } - if (!editingMessage() && (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute())) { + if (!editingMessage() && (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute() || isReportMessages())) { newScrollHeight -= _unblock->height(); } else { if (editingMessage() || _canSendMessages) { @@ -5427,6 +5504,28 @@ void HistoryWidget::checkMessagesTTL() { } } +void HistoryWidget::setChooseReportMessagesDetails( + Ui::ReportReason reason, + Fn<void(MessageIdsList)> callback) { + if (!callback) { + const auto refresh = _chooseForReport && _chooseForReport->active; + _chooseForReport = nullptr; + if (_list) { + _list->clearChooseReportReason(); + } + if (refresh) { + updateControlsVisibility(); + updateControlsGeometry(); + updateTopBarChooseForReport(); + } + } else { + _chooseForReport = std::make_unique<ChooseMessagesForReport>( + ChooseMessagesForReport{ + .reason = reason, + .callback = std::move(callback) }); + } +} + void HistoryWidget::refreshPinnedBarButton(bool many) { const auto close = !many; auto button = object_ptr<Ui::IconButton>( @@ -6209,6 +6308,18 @@ MessageIdsList HistoryWidget::getSelectedItems() const { return _list ? _list->getSelectedItems() : MessageIdsList(); } +void HistoryWidget::updateTopBarChooseForReport() { + if (_chooseForReport && _chooseForReport->active) { + _topBar->showChooseMessagesForReport( + _chooseForReport->reason); + } else { + _topBar->clearChooseMessagesForReport(); + } + updateTopBarSelection(); + updateControlsVisibility(); + updateControlsGeometry(); +} + void HistoryWidget::updateTopBarSelection() { if (!_list) { _topBar->showSelected(HistoryView::TopBarWidget::SelectedState {}); @@ -6216,8 +6327,25 @@ void HistoryWidget::updateTopBarSelection() { } auto selectedState = _list->getSelectionState(); - _nonEmptySelection = (selectedState.count > 0) || selectedState.textSelected; + _nonEmptySelection = (selectedState.count > 0) + || selectedState.textSelected; _topBar->showSelected(selectedState); + + const auto transparent = Qt::WA_TransparentForMouseEvents; + if (selectedState.count == 0) { + _reportMessages->clearState(); + _reportMessages->setAttribute(transparent); + _reportMessages->setColorOverride(st::windowSubTextFg->c); + } else if (_reportMessages->testAttribute(transparent)) { + _reportMessages->setAttribute(transparent, false); + _reportMessages->setColorOverride(std::nullopt); + } + _reportMessages->setText(Ui::Text::Upper(selectedState.count + ? tr::lng_report_messages_count( + tr::now, + lt_count, + selectedState.count) + : tr::lng_report_messages_none(tr::now))); updateControlsVisibility(); updateHistoryGeometry(); if (!Ui::isLayerShown() && !Core::App().passcodeLocked()) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 13ccd8f69..49c093912 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -68,6 +68,7 @@ class PinnedBar; class GroupCallBar; struct PreparedList; class SendFilesWay; +enum class ReportReason; namespace Toast { class Instance; } // namespace Toast @@ -141,6 +142,7 @@ public: bool isItemCompletelyHidden(HistoryItem *item) const; void updateTopBarSelection(); + void updateTopBarChooseForReport(); void loadMessages(); void loadMessagesDown(); @@ -230,6 +232,9 @@ public: void applyDraft( FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear); void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false); + void setChooseReportMessagesDetails( + Ui::ReportReason reason, + Fn<void(MessageIdsList)> callback); void clearAllLoadRequests(); void clearDelayedShowAtRequest(); void clearDelayedShowAt(); @@ -313,6 +318,11 @@ private: ScrollChangeType type; int value; }; + struct ChooseMessagesForReport { + Ui::ReportReason reason = {}; + Fn<void(MessageIdsList)> callback; + bool active = false; + }; enum class TextUpdateEvent { SaveDraft = (1 << 0), SendTyping = (1 << 1), @@ -377,6 +387,7 @@ private: [[nodiscard]] int computeMaxFieldHeight() const; void toggleMuteUnmute(); + void reportSelectedMessages(); void toggleKeyboard(bool manual = true); void startBotCommand(); void hidePinnedMessage(); @@ -572,6 +583,7 @@ private: bool isBlocked() const; bool isJoinChannel() const; bool isMuteUnmute() const; + bool isReportMessages() const; bool updateCmdStartShown(); void updateSendButtonType(); bool showRecordButton() const; @@ -687,6 +699,7 @@ private: object_ptr<Ui::FlatButton> _botStart; object_ptr<Ui::FlatButton> _joinChannel; object_ptr<Ui::FlatButton> _muteUnmute; + object_ptr<Ui::FlatButton> _reportMessages; object_ptr<Ui::IconButton> _attachToggle; object_ptr<Ui::EmojiButton> _tabbedSelectorToggle; object_ptr<Ui::IconButton> _botKeyboardShow; @@ -740,6 +753,7 @@ private: base::Timer _saveCloudDraftTimer; base::weak_ptr<Ui::Toast::Instance> _topToast; + std::unique_ptr<ChooseMessagesForReport> _chooseForReport; object_ptr<Ui::PlainShadow> _topShadow; bool _inGrab = false; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index bf2c7743a..433d803ef 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -24,11 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/toast/toast.h" #include "ui/controls/delete_message_context_action.h" +#include "ui/boxes/report_box.h" #include "ui/ui_utility.h" #include "chat_helpers/send_context_menu.h" #include "boxes/confirm_box.h" #include "boxes/sticker_set_box.h" -#include "boxes/report_box.h" #include "data/data_photo.h" #include "data/data_photo_media.h" #include "data/data_document.h" @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "base/platform/base_platform_info.h" #include "window/window_peer_menu.h" +#include "window/window_controller.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" #include "core/application.h" @@ -782,13 +783,12 @@ void AddReportAction( const auto itemId = item->fullId(); const auto callback = crl::guard(controller, [=] { if (const auto item = owner->message(itemId)) { - const auto peer = item->history()->peer; const auto group = owner->groups().find(item); - Ui::show(Box<ReportBox>( - peer, + ShowReportItemsBox( + item->history()->peer, (group ? owner->itemsToIds(group->items) - : MessageIdsList(1, itemId)))); + : MessageIdsList{ 1, itemId })); } }); menu->addAction(tr::lng_context_report_msg(tr::now), callback); @@ -1051,4 +1051,105 @@ void AddPollActions( } } +void ShowReportItemsBox(not_null<PeerData*> peer, MessageIdsList ids) { + const auto chosen = [=](Ui::ReportReason reason) { + Ui::show(Box(Ui::ReportDetailsBox, [=](const QString &text) { + SendReport(peer, reason, text, ids); + Ui::hideLayer(); + })); + }; + Ui::show(Box( + Ui::ReportReasonBox, + Ui::ReportSource::Message, + chosen)); +} + +void ShowReportPeerBox( + not_null<Window::SessionController*> window, + not_null<PeerData*> peer) { + struct State { + QPointer<Ui::GenericBox> reasonBox; + QPointer<Ui::GenericBox> detailsBox; + MessageIdsList ids; + }; + const auto state = std::make_shared<State>(); + const auto chosen = [=](Ui::ReportReason reason) { + const auto send = [=](const QString &text) { + window->clearChooseReportMessages(); + SendReport(peer, reason, text, std::move(state->ids)); + if (const auto strong = state->reasonBox.data()) { + strong->closeBox(); + } + if (const auto strong = state->detailsBox.data()) { + strong->closeBox(); + } + }; + if (reason == Ui::ReportReason::Fake + || reason == Ui::ReportReason::Other) { + state->ids = {}; + state->detailsBox = window->window().show( + Box(Ui::ReportDetailsBox, send)); + return; + } + window->showChooseReportMessages(peer, reason, [=]( + MessageIdsList ids) { + state->ids = std::move(ids); + state->detailsBox = window->window().show( + Box(Ui::ReportDetailsBox, send)); + }); + }; + state->reasonBox = window->window().show(Box( + Ui::ReportReasonBox, + (peer->isBroadcast() + ? Ui::ReportSource::Channel + : peer->isUser() + ? Ui::ReportSource::Bot + : Ui::ReportSource::Group), + chosen)); +} + +void SendReport( + not_null<PeerData*> peer, + Ui::ReportReason reason, + const QString &comment, + MessageIdsList ids) { + const auto apiReason = [&] { + using Reason = Ui::ReportReason; + switch (reason) { + case Reason::Spam: return MTP_inputReportReasonSpam(); + case Reason::Fake: return MTP_inputReportReasonFake(); + case Reason::Violence: return MTP_inputReportReasonViolence(); + case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse(); + case Reason::Pornography: return MTP_inputReportReasonPornography(); + case Reason::Other: return MTP_inputReportReasonOther(); + } + Unexpected("Bad reason group value."); + }(); + if (ids.empty()) { + peer->session().api().request(MTPaccount_ReportPeer( + peer->input, + apiReason, + MTP_string(comment) + )).done([=](const MTPBool &result) { + Ui::Toast::Show(tr::lng_report_thanks(tr::now)); + }).fail([=](const RPCError &error) { + }).send(); + } else { + auto apiIds = QVector<MTPint>(); + apiIds.reserve(ids.size()); + for (const auto &fullId : ids) { + apiIds.push_back(MTP_int(fullId.msg)); + } + peer->session().api().request(MTPmessages_Report( + peer->input, + MTP_vector<MTPint>(apiIds), + apiReason, + MTP_string(comment) + )).done([=](const MTPBool &result) { + Ui::Toast::Show(tr::lng_report_thanks(tr::now)); + }).fail([=](const RPCError &error) { + }).send(); + } +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h index be6f160dc..e9d7f2418 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.h +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -11,10 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class PopupMenu; +enum class ReportReason; } // namespace Ui namespace Window { class SessionNavigation; +class SessionController; } // namespace Main namespace HistoryView { @@ -55,4 +57,14 @@ void AddPollActions( not_null<HistoryItem*> item, Context context); -} // namespace +void ShowReportItemsBox(not_null<PeerData*> peer, MessageIdsList ids); +void ShowReportPeerBox( + not_null<Window::SessionController*> window, + not_null<PeerData*> peer); +void SendReport( + not_null<PeerData*> peer, + Ui::ReportReason reason, + const QString &comment, + MessageIdsList ids = {}); + +} // namespace HistoryView 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 1a654ee20..25f5b2c53 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/dropdown_menu.h" #include "ui/effects/radial_animation.h" #include "ui/toasts/common_toasts.h" +#include "ui/boxes/report_box.h" // Ui::ReportReason #include "ui/special_buttons.h" #include "ui/unread_badge.h" #include "ui/ui_utility.h" @@ -63,6 +64,7 @@ TopBarWidget::TopBarWidget( , _sendNow(this, tr::lng_selected_send_now(), st::defaultActiveButton) , _delete(this, tr::lng_selected_delete(), st::defaultActiveButton) , _back(this, st::historyTopBarBack) +, _cancelChoose(this, st::topBarCloseChoose) , _call(this, st::topBarCall) , _groupCall(this, st::topBarGroupCall) , _search(this, st::topBarSearch) @@ -90,6 +92,8 @@ TopBarWidget::TopBarWidget( _menuToggle->setClickedCallback([=] { showMenu(); }); _infoToggle->setClickedCallback([=] { toggleInfoSection(); }); _back->addClickHandler([=] { backClicked(); }); + _cancelChoose->setClickedCallback( + [=] { _cancelChooseForReport.fire({}); }); rpl::combine( _controller->activeChatValue(), @@ -224,6 +228,34 @@ void TopBarWidget::groupCall() { } } +void TopBarWidget::showChooseMessagesForReport(Ui::ReportReason reason) { + setChooseForReportReason(reason); +} + +void TopBarWidget::clearChooseMessagesForReport() { + setChooseForReportReason(std::nullopt); +} + +void TopBarWidget::setChooseForReportReason( + std::optional<Ui::ReportReason> reason) { + if (_chooseForReportReason == reason) { + return; + } + const auto wasNoReason = !_chooseForReportReason; + _chooseForReportReason = reason; + const auto nowNoReason = !_chooseForReportReason; + updateControlsVisibility(); + updateControlsGeometry(); + update(); + if (wasNoReason != nowNoReason && _selectedCount > 0) { + toggleSelectedControls(false); + finishAnimating(); + } + setCursor((nowNoReason && !_selectedCount) + ? style::cur_pointer + : style::cur_default); +} + void TopBarWidget::showMenu() { if (!_activeChat.key || _menu) { return; @@ -318,8 +350,8 @@ void TopBarWidget::paintEvent(QPaintEvent *e) { } Painter p(this); - auto hasSelected = (_selectedCount > 0); - auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.)); + auto selectedButtonsTop = countSelectedButtonsTop( + _selectedShown.value(showSelectedActions() ? 1. : 0.)); p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBg); if (selectedButtonsTop < 0) { @@ -337,6 +369,34 @@ void TopBarWidget::paintTopBar(Painter &p) { auto statustop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height; auto availableWidth = width() - _rightTaken - nameleft; + if (_chooseForReportReason) { + const auto text = [&] { + using Reason = Ui::ReportReason; + switch (*_chooseForReportReason) { + case Reason::Spam: return tr::lng_report_reason_spam(tr::now); + case Reason::Violence: + return tr::lng_report_reason_violence(tr::now); + case Reason::ChildAbuse: + return tr::lng_report_reason_child_abuse(tr::now); + case Reason::Pornography: + return tr::lng_report_reason_pornography(tr::now); + } + Unexpected("reason in TopBarWidget::paintTopBar."); + }(); + p.setPen(st::dialogsNameFg); + p.setFont(st::semiboldFont); + p.drawTextLeft(nameleft, nametop, width(), text); + + p.setFont(st::dialogsTextFont); + p.setPen(st::historyStatusFg); + p.drawTextLeft( + nameleft, + statustop, + width(), + tr::lng_report_select_messages(tr::now)); + return; + } + const auto history = _activeChat.key.history(); const auto folder = _activeChat.key.folder(); if (folder @@ -476,7 +536,8 @@ QRect TopBarWidget::getMembersShowAreaGeometry() const { void TopBarWidget::mousePressEvent(QMouseEvent *e) { auto handleClick = (e->button() == Qt::LeftButton) && (e->pos().y() < st::topBarHeight) - && (!_selectedCount); + && !_selectedCount + && !_chooseForReportReason; if (handleClick) { if (_animatingMode && _back->rect().contains(e->pos())) { backClicked(); @@ -610,14 +671,16 @@ void TopBarWidget::updateSearchVisibility() { const auto historyMode = (_activeChat.section == Section::History); const auto smallDialogsColumn = _activeChat.key.folder() && (width() < _back->width() + _search->width()); - _search->setVisible(historyMode && !smallDialogsColumn); + _search->setVisible(historyMode + && !smallDialogsColumn + && !_chooseForReportReason); } void TopBarWidget::updateControlsGeometry() { if (!_activeChat.key) { return; } - auto hasSelected = (_selectedCount > 0); + auto hasSelected = showSelectedActions(); auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.)); auto otherButtonsTop = selectedButtonsTop + st::topBarHeight; auto buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth); @@ -648,7 +711,11 @@ void TopBarWidget::updateControlsGeometry() { _delete->moveToLeft(buttonsLeft, selectedButtonsTop); _clear->moveToRight(st::topBarActionSkip, selectedButtonsTop); - if (_back->isHidden()) { + if (!_cancelChoose->isHidden()) { + _leftTaken = 0; + _cancelChoose->moveToLeft(_leftTaken, otherButtonsTop); + _leftTaken += _cancelChoose->width(); + } else if (_back->isHidden()) { _leftTaken = st::topBarArrowPadding.right(); } else { const auto smallDialogsColumn = _activeChat.key.folder() @@ -714,12 +781,13 @@ void TopBarWidget::updateControlsVisibility() { auto backVisible = Adaptive::OneColumn() || !_controller->content()->stackIsEmpty() || _activeChat.key.folder(); - _back->setVisible(backVisible); + _back->setVisible(backVisible && !_chooseForReportReason); + _cancelChoose->setVisible(_chooseForReportReason.has_value()); if (_info) { - _info->setVisible(Adaptive::OneColumn()); + _info->setVisible(Adaptive::OneColumn() && !_chooseForReportReason); } if (_unreadBadge) { - _unreadBadge->show(); + _unreadBadge->setVisible(!_chooseForReportReason); } const auto section = _activeChat.section; const auto historyMode = (section == Section::History); @@ -730,11 +798,12 @@ void TopBarWidget::updateControlsVisibility() { ? hasPollsMenu : historyMode); updateSearchVisibility(); - _menuToggle->setVisible(hasMenu); + _menuToggle->setVisible(hasMenu && !_chooseForReportReason); _infoToggle->setVisible(historyMode && !_activeChat.key.folder() && !Adaptive::OneColumn() - && _controller->canShowThirdSection()); + && _controller->canShowThirdSection() + && !_chooseForReportReason); const auto callsEnabled = [&] { if (const auto peer = _activeChat.key.peer()) { if (const auto user = peer->asUser()) { @@ -743,7 +812,9 @@ void TopBarWidget::updateControlsVisibility() { } return false; }(); - _call->setVisible(historyMode && callsEnabled); + _call->setVisible(historyMode + && callsEnabled + && !_chooseForReportReason); const auto groupCallsEnabled = [&] { if (const auto peer = _activeChat.key.peer()) { if (peer->canManageGroupCall()) { @@ -755,10 +826,12 @@ void TopBarWidget::updateControlsVisibility() { } return false; }(); - _groupCall->setVisible(historyMode && groupCallsEnabled); + _groupCall->setVisible(historyMode + && groupCallsEnabled + && !_chooseForReportReason); if (_membersShowArea) { - _membersShowArea->show(); + _membersShowArea->setVisible(!_chooseForReportReason); } updateControlsGeometry(); } @@ -824,21 +897,29 @@ void TopBarWidget::showSelected(SelectedState state) { _canSendNow = canSendNow; updateControlsVisibility(); } - if (wasSelected != hasSelected) { + if (wasSelected != hasSelected && !_chooseForReportReason) { setCursor(hasSelected ? style::cur_default : style::cur_pointer); updateMembersShowArea(); - _selectedShown.start( - [this] { selectedShowCallback(); }, - hasSelected ? 0. : 1., - hasSelected ? 1. : 0., - st::slideWrapDuration, - anim::easeOutCirc); + toggleSelectedControls(hasSelected); } else { updateControlsGeometry(); } } +void TopBarWidget::toggleSelectedControls(bool shown) { + _selectedShown.start( + [this] { selectedShowCallback(); }, + shown ? 0. : 1., + shown ? 1. : 0., + st::slideWrapDuration, + anim::easeOutCirc); +} + +bool TopBarWidget::showSelectedActions() const { + return (_selectedCount > 0) && !_chooseForReportReason; +} + void TopBarWidget::selectedShowCallback() { updateControlsGeometry(); 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 0cde0340a..ae6d0453c 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -24,6 +24,7 @@ class IconButton; class DropdownMenu; class UnreadBadge; class InfiniteRadialAnimation; +enum class ReportReason; } // namespace Ui namespace Window { @@ -66,6 +67,9 @@ public: SendActionPainter *sendAction); void setCustomTitle(const QString &title); + void showChooseMessagesForReport(Ui::ReportReason reason); + void clearChooseMessagesForReport(); + rpl::producer<> forwardSelectionRequest() const { return _forwardSelection.events(); } @@ -78,6 +82,9 @@ public: rpl::producer<> clearSelectionRequest() const { return _clearSelection.events(); } + rpl::producer<> cancelChooseForReportRequest() const { + return _cancelChooseForReport.events(); + } protected: void paintEvent(QPaintEvent *e) override; @@ -126,6 +133,9 @@ private: void refreshUnreadBadge(); void updateUnreadBadge(); + void setChooseForReportReason(std::optional<Ui::ReportReason> reason); + void toggleSelectedControls(bool shown); + [[nodiscard]] bool showSelectedActions() const; const not_null<Window::SessionController*> _controller; ActiveChat _activeChat; @@ -143,6 +153,7 @@ private: object_ptr<Ui::RoundButton> _forward, _sendNow, _delete; object_ptr<Ui::IconButton> _back; + object_ptr<Ui::IconButton> _cancelChoose; object_ptr<Ui::UnreadBadge> _unreadBadge = { nullptr }; object_ptr<Ui::AbstractButton> _info = { nullptr }; @@ -164,6 +175,7 @@ private: std::unique_ptr<Ui::InfiniteRadialAnimation> _connecting; SendActionPainter *_sendAction = nullptr; + std::optional<Ui::ReportReason> _chooseForReportReason; base::Timer _onlineUpdater; @@ -171,6 +183,7 @@ private: rpl::event_stream<> _sendNowSelection; rpl::event_stream<> _deleteSelection; rpl::event_stream<> _clearSelection; + rpl::event_stream<> _cancelChooseForReport; }; diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 6a24cf9c4..a8684372b 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -749,6 +749,13 @@ topBarSearch: IconButton { color: windowBgOver; } } +topBarCloseChoose: IconButton(topBarSearch) { + width: 56px; + icon: icon {{ "info_close", boxTitleCloseFg }}; + iconOver: icon {{ "info_close", boxTitleCloseFgOver }}; + iconPosition: point(10px, -1px); + rippleAreaPosition: point(7px, 7px); +} topBarSkip: -5px; topBarCallSkip: -1px; topBarMenuToggle: IconButton(topBarSearch) { diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index f9ddf2910..fc08c38a4 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/box_content_divider.h" +#include "ui/boxes/report_box.h" #include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper @@ -29,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/add_contact_box.h" -#include "boxes/report_box.h" #include "boxes/peers/edit_contact_box.h" #include "lang/lang_keys.h" #include "info/info_controller.h" @@ -586,11 +586,14 @@ void ActionsFiller::addBotCommandActions(not_null<UserData*> user) { void ActionsFiller::addReportAction() { const auto peer = _peer; + const auto report = [=] { + + }; AddActionButton( _wrap, tr::lng_profile_report(), rpl::single(true), - [=] { Ui::show(Box<ReportBox>(peer)); }, + report, st::infoBlockButton); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index cf72e9904..a0f0c42ad 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" -#include "boxes/report_box.h" #include "mainwidget.h" #include "main/main_session.h" #include "apiwrap.h" diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 50c8614bf..0798670d5 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1414,6 +1414,18 @@ void MainWidget::ctrlEnterSubmitUpdated() { _history->updateFieldSubmitSettings(); } +void MainWidget::showChooseReportMessages( + not_null<PeerData*> peer, + Ui::ReportReason reason, + Fn<void(MessageIdsList)> done) { + _history->setChooseReportMessagesDetails(reason, std::move(done)); + ui_showPeerHistory(peer->id, SectionShow(), ShowForChooseMessagesMsgId); +} + +void MainWidget::clearChooseReportMessages() { + _history->setChooseReportMessagesDetails({}, nullptr); +} + void MainWidget::ui_showPeerHistory( PeerId peerId, const SectionShow ¶ms, diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 79b25a047..a3dc81401 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -65,6 +65,7 @@ namespace Ui { class ResizeArea; class PlainShadow; class DropdownMenu; +enum class ReportReason; template <typename Widget> class SlideWrap; } // namespace Ui @@ -210,6 +211,12 @@ public: void searchInChat(Dialogs::Key chat); + void showChooseReportMessages( + not_null<PeerData*> peer, + Ui::ReportReason reason, + Fn<void(MessageIdsList)> done); + void clearChooseReportMessages(); + void ui_showPeerHistory( PeerId peer, const SectionShow ¶ms, diff --git a/Telegram/SourceFiles/ui/boxes/report_box.cpp b/Telegram/SourceFiles/ui/boxes/report_box.cpp new file mode 100644 index 000000000..539d6a97d --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/report_box.cpp @@ -0,0 +1,93 @@ +/* +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 "ui/boxes/report_box.h" + +#include "lang/lang_keys.h" +#include "ui/layers/generic_box.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" +#include "ui/toast/toast.h" +#include "styles/style_layers.h" +#include "styles/style_boxes.h" +#include "styles/style_profile.h" + +namespace Ui { +namespace { + +constexpr auto kReportReasonLengthMax = 512; + +using Source = ReportSource; +using Reason = ReportReason; + +} // namespace + +void ReportReasonBox( + not_null<GenericBox*> box, + ReportSource source, + Fn<void(Reason)> done) { + box->setTitle([&] { + switch (source) { + case Source::Message: return tr::lng_report_message_title(); + case Source::Channel: return tr::lng_report_title(); + case Source::Group: return tr::lng_report_group_title(); + case Source::Bot: return tr::lng_report_bot_title(); + } + Unexpected("'source' in ReportReasonBox."); + }()); + const auto add = [&](Reason reason, tr::phrase<> text) { + const auto layout = box->verticalLayout(); + const auto button = layout->add( + object_ptr<Ui::SettingsButton>(layout, text())); + button->setClickedCallback([=] { + done(reason); + }); + }; + add(Reason::Spam, tr::lng_report_reason_spam); + if (source != Source::Message) { + add(Reason::Fake, tr::lng_report_reason_fake); + } + add(Reason::Violence, tr::lng_report_reason_violence); + add(Reason::ChildAbuse, tr::lng_report_reason_child_abuse); + add(Reason::Pornography, tr::lng_report_reason_pornography); + add(Reason::Other, tr::lng_report_reason_other); + + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +void ReportDetailsBox( + not_null<GenericBox*> box, + Fn<void(QString)> done) { + box->addRow( + object_ptr<Ui::FlatLabel>( + box, // #TODO reports + tr::lng_report_details_about(), + st::boxLabel), + { + st::boxRowPadding.left(), + st::boxPadding.top(), + st::boxRowPadding.right(), + st::boxPadding.bottom() }); + const auto details = box->addRow( + object_ptr<Ui::InputField>( + box, + st::newGroupDescription, + Ui::InputField::Mode::MultiLine, + tr::lng_report_details(), + QString())); + details->setMaxLength(kReportReasonLengthMax); + box->setFocusCallback([=] { + details->setFocusFast(); + }); + box->addButton(tr::lng_report_button(), [=] { + const auto text = details->getLastText(); + done(text); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/report_box.h b/Telegram/SourceFiles/ui/boxes/report_box.h new file mode 100644 index 000000000..613011654 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/report_box.h @@ -0,0 +1,39 @@ +/* +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 + +namespace Ui { + +class GenericBox; + +enum class ReportSource { + Message, + Channel, + Group, + Bot, +}; + +enum class ReportReason { + Spam, + Fake, + Violence, + ChildAbuse, + Pornography, + Other, +}; + +void ReportReasonBox( + not_null<GenericBox*> box, + ReportSource source, + Fn<void(ReportReason)> done); + +void ReportDetailsBox( + not_null<GenericBox*> box, + Fn<void(QString)> done); + +} // namespace Ui diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 84e8e173e..95e891f7a 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -11,10 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "boxes/mute_settings_box.h" #include "boxes/add_contact_box.h" -#include "boxes/report_box.h" #include "boxes/create_poll_box.h" #include "boxes/peers/add_participants_box.h" #include "boxes/peers/edit_contact_box.h" +#include "ui/boxes/report_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/widgets/labels.h" @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_message.h" // GetErrorTextForSending. +#include "history/view/history_view_context_menu.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "support/support_helper.h" @@ -604,8 +605,8 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) { const auto needReport = !channel->amCreator() && (!isGroup || channel->isPublic()); if (needReport) { - _addAction(tr::lng_profile_report(tr::now), [channel] { - Ui::show(Box<ReportBox>(channel)); + _addAction(tr::lng_profile_report(tr::now), [=] { + HistoryView::ShowReportPeerBox(navigation, channel); }); } } @@ -974,6 +975,21 @@ void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user) { }); } +void BlockSenderFromRepliesBox( + not_null<Ui::GenericBox*> box, + not_null<SessionController*> controller, + FullMsgId id) { + const auto item = controller->session().data().message(id); + Assert(item != nullptr); + + PeerMenuBlockUserBox( + box, + &controller->window(), + item->senderOriginal(), + true, + Window::ClearReply{ id }); +} + QPointer<Ui::RpWidget> ShowForwardMessagesBox( not_null<Window::SessionNavigation*> navigation, MessageIdsList &&items, diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 939f52a39..fbec5bd76 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -86,6 +86,11 @@ void PeerMenuBlockUserBox( std::variant<v::null_t, ClearChat, ClearReply> suggestClear); void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user); +void BlockSenderFromRepliesBox( + not_null<Ui::GenericBox*> box, + not_null<Window::SessionController*> controller, + FullMsgId id); + void ToggleHistoryArchived(not_null<History*> history, bool archived); Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer); Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 82da3e84f..fbcb5be6d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1073,6 +1073,17 @@ void SessionController::clearPassportForm() { _passportForm = nullptr; } +void SessionController::showChooseReportMessages( + not_null<PeerData*> peer, + Ui::ReportReason reason, + Fn<void(MessageIdsList)> done) { + content()->showChooseReportMessages(peer, reason, std::move(done)); +} + +void SessionController::clearChooseReportMessages() { + content()->clearChooseReportMessages(); +} + void SessionController::updateColumnLayout() { content()->updateColumnLayout(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index d973fe3da..282106b15 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -45,6 +45,7 @@ class FormController; namespace Ui { class LayerWidget; +enum class ReportReason; } // namespace Ui namespace Window { @@ -332,6 +333,12 @@ public: void showPassportForm(const Passport::FormRequest &request); void clearPassportForm(); + void showChooseReportMessages( + not_null<PeerData*> peer, + Ui::ReportReason reason, + Fn<void(MessageIdsList)> done); + void clearChooseReportMessages(); + base::Variable<bool> &dialogsListFocused() { return _dialogsListFocused; } diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 60acbe619..5897b79c4 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -72,6 +72,8 @@ PRIVATE ui/boxes/choose_date_time.h ui/boxes/edit_invite_link.cpp ui/boxes/edit_invite_link.h + ui/boxes/report_box.cpp + ui/boxes/report_box.h ui/chat/attach/attach_album_thumbnail.cpp ui/chat/attach/attach_album_thumbnail.h ui/chat/attach/attach_album_preview.cpp From 48821af4759293d33fb6b6e7904c923e14a919d7 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 17:50:41 +0400 Subject: [PATCH 369/396] Toggle selection by comments button click. --- .../history/history_inner_widget.cpp | 6 ++--- .../SourceFiles/history/history_widget.cpp | 27 ++++++++++++------- .../history/view/history_view_element.cpp | 5 ++++ .../history/view/history_view_element.h | 2 ++ .../history/view/history_view_list_widget.cpp | 14 +++------- .../history/view/history_view_message.cpp | 12 +++++++++ .../history/view/history_view_message.h | 2 ++ Telegram/SourceFiles/mainwidget.cpp | 5 +++- Telegram/SourceFiles/ui/boxes/report_box.cpp | 15 ++++++----- 9 files changed, 57 insertions(+), 31 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index fd3661625..a1c2f73fa 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1323,10 +1323,8 @@ void HistoryInner::mouseActionFinish( && inSelectionMode() && button != Qt::RightButton) { if (const auto view = _mouseActionItem->mainView()) { - if (const auto media = view->media()) { - if (media->toggleSelectionByHandlerClick(activated)) { - activated = nullptr; - } + if (view->toggleSelectionByHandlerClick(activated)) { + activated = nullptr; } } } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9574b6696..b35e0880d 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1753,9 +1753,6 @@ void HistoryWidget::showHistory( if (startBot) { showAtMsgId = ShowAtTheEndMsgId; } - if (showAtMsgId != ShowForChooseMessagesMsgId) { - _chooseForReport = nullptr; - } clearHighlightMessages(); hideInfoTooltip(anim::type::instant); @@ -1768,6 +1765,7 @@ void HistoryWidget::showHistory( showAtMsgId = ShowAtTheEndMsgId; } else if (showAtMsgId == ShowForChooseMessagesMsgId) { if (_chooseForReport) { + clearSelected(); _chooseForReport->active = true; _list->setChooseReportReason(_chooseForReport->reason); updateControlsVisibility(); @@ -1776,7 +1774,6 @@ void HistoryWidget::showHistory( } return; } - _list->clearChooseReportReason(); if (!IsServerMsgId(showAtMsgId) && !IsServerMsgId(-showAtMsgId)) { // To end or to unread. @@ -1939,14 +1936,16 @@ void HistoryWidget::showHistory( } _history->setFakeUnreadWhileOpened(true); - refreshTopBarActiveChat(); - updateTopBarSelection(); - if (showAtMsgId == ShowForChooseMessagesMsgId) { - showAtMsgId = ShowAtUnreadMsgId; + if (_showAtMsgId == ShowForChooseMessagesMsgId) { + _showAtMsgId = ShowAtUnreadMsgId; if (_chooseForReport) { _chooseForReport->active = true; } + } else { + _chooseForReport = nullptr; } + refreshTopBarActiveChat(); + updateTopBarSelection(); if (_channel) { updateNotifyControls(); @@ -2034,6 +2033,7 @@ void HistoryWidget::showHistory( unreadCountUpdated(); // set _historyDown badge. showAboutTopPromotion(); } else { + _chooseForReport = nullptr; refreshTopBarActiveChat(); updateTopBarSelection(); checkMessagesTTL(); @@ -3325,9 +3325,13 @@ void HistoryWidget::reportSelectedMessages() { const auto peer = _peer; const auto reason = _chooseForReport->reason; const auto box = std::make_shared<QPointer<Ui::GenericBox>>(); + const auto weak = Ui::MakeWeak(_list.data()); const auto send = [=](const QString &text) { + if (weak) { + clearSelected(); + controller()->clearChooseReportMessages(); + } HistoryView::SendReport(peer, reason, text, ids); - controller()->clearChooseReportMessages(); if (*box) { (*box)->closeBox(); } @@ -5514,6 +5518,7 @@ void HistoryWidget::setChooseReportMessagesDetails( _list->clearChooseReportReason(); } if (refresh) { + clearSelected(); updateControlsVisibility(); updateControlsGeometry(); updateTopBarChooseForReport(); @@ -6260,7 +6265,9 @@ void HistoryWidget::confirmDeleteSelected() { } void HistoryWidget::escape() { - if (_nonEmptySelection && _list) { + if (_chooseForReport) { + controller()->clearChooseReportMessages(); + } else if (_nonEmptySelection && _list) { clearSelected(); } else if (_isInlineBot) { cancelInlineBot(); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 3d4d0303f..c84818a53 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -736,6 +736,11 @@ HistoryMessageReply *Element::displayedReply() const { return nullptr; } +bool Element::toggleSelectionByHandlerClick( + const ClickHandlerPtr &handler) const { + return false; +} + bool Element::hasVisibleText() const { return false; } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 54b4b4ad4..a9b414875 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -292,6 +292,8 @@ public: virtual void applyGroupAdminChanges( const base::flat_set<UserId> &changes) { } + [[nodiscard]] virtual bool toggleSelectionByHandlerClick( + const ClickHandlerPtr &handler) const; struct VerticalRepaintRange { int top = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 08c77e2ae..d8d8c8e2b 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2184,16 +2184,10 @@ void ListWidget::mouseActionFinish( repaintItem(pressState.itemId); const auto toggleByHandler = [&](const ClickHandlerPtr &handler) { - if (_overElement) { - // If we are in selecting items mode perhaps we want to - // toggle selection instead of activating the pressed link. - if (const auto media = _overElement->media()) { - if (media->toggleSelectionByHandlerClick(handler)) { - return true; - } - } - } - return false; + // If we are in selecting items mode perhaps we want to + // toggle selection instead of activating the pressed link. + return _overElement + && _overElement->toggleSelectionByHandlerClick(handler); }; auto activated = ClickHandler::unpressed(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index ea03bb705..687deade6 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2059,6 +2059,18 @@ HistoryMessageReply *Message::displayedReply() const { return nullptr; } +bool Message::toggleSelectionByHandlerClick( + const ClickHandlerPtr &handler) const { + if (_comments && _comments->link == handler) { + return true; + } else if (const auto media = this->media()) { + if (media->toggleSelectionByHandlerClick(handler)) { + return true; + } + } + return false; +} + bool Message::displayPinIcon() const { return data()->isPinned() && !isPinnedContext(); } diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 132c3420f..0d61e61d7 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -105,6 +105,8 @@ public: bool displayEditedBadge() const override; TimeId displayedEditDate() const override; HistoryMessageReply *displayedReply() const override; + bool toggleSelectionByHandlerClick( + const ClickHandlerPtr &handler) const override; int infoWidth() const override; VerticalRepaintRange verticalRepaintRange() const override; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 0798670d5..d4dc84d4b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1419,7 +1419,10 @@ void MainWidget::showChooseReportMessages( Ui::ReportReason reason, Fn<void(MessageIdsList)> done) { _history->setChooseReportMessagesDetails(reason, std::move(done)); - ui_showPeerHistory(peer->id, SectionShow(), ShowForChooseMessagesMsgId); + ui_showPeerHistory( + peer->id, + SectionShow::Way::Forward, + ShowForChooseMessagesMsgId); } void MainWidget::clearChooseReportMessages() { diff --git a/Telegram/SourceFiles/ui/boxes/report_box.cpp b/Telegram/SourceFiles/ui/boxes/report_box.cpp index 539d6a97d..32f2dec74 100644 --- a/Telegram/SourceFiles/ui/boxes/report_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/report_box.cpp @@ -42,7 +42,7 @@ void ReportReasonBox( const auto add = [&](Reason reason, tr::phrase<> text) { const auto layout = box->verticalLayout(); const auto button = layout->add( - object_ptr<Ui::SettingsButton>(layout, text())); + object_ptr<SettingsButton>(layout, text())); button->setClickedCallback([=] { done(reason); }); @@ -63,7 +63,7 @@ void ReportDetailsBox( not_null<GenericBox*> box, Fn<void(QString)> done) { box->addRow( - object_ptr<Ui::FlatLabel>( + object_ptr<FlatLabel>( box, // #TODO reports tr::lng_report_details_about(), st::boxLabel), @@ -73,20 +73,23 @@ void ReportDetailsBox( st::boxRowPadding.right(), st::boxPadding.bottom() }); const auto details = box->addRow( - object_ptr<Ui::InputField>( + object_ptr<InputField>( box, st::newGroupDescription, - Ui::InputField::Mode::MultiLine, + InputField::Mode::MultiLine, tr::lng_report_details(), QString())); details->setMaxLength(kReportReasonLengthMax); box->setFocusCallback([=] { details->setFocusFast(); }); - box->addButton(tr::lng_report_button(), [=] { + + const auto submit = [=] { const auto text = details->getLastText(); done(text); - }); + }; + QObject::connect(details, &InputField::submitted, submit); + box->addButton(tr::lng_report_button(), submit); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } From 5f030bc0c830f0030579135f6c5b4740d7b52bf4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 20:16:42 +0400 Subject: [PATCH 370/396] Remove debug-only ttl for messages. --- Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/boxes/confirm_box.cpp | 6 ++++-- .../SourceFiles/history/history_service.cpp | 4 ++-- .../ui/boxes/auto_delete_settings.cpp | 17 ++++++++--------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index baf02adfc..0cc9d7fc0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -981,7 +981,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_messages_ttl_after1" = "24 hours"; "lng_manage_messages_ttl_after2" = "7 days"; -"lng_ttl_edit_title" = "Auto-delete messages in this chat"; "lng_ttl_edit_about" = "Automatically delete new messages after a certain period of time for you and {user}."; "lng_ttl_edit_about_group" = "Automatically delete new messages sent in this chat after a certain period of time."; "lng_ttl_edit_about_channel" = "Automatically delete new messages sent in this channel after a certain period of time."; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index b2174236e..f2b12f4bd 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -666,8 +666,10 @@ void DeleteMessagesBox::prepare() { if (_wipeHistoryJustClear && _wipeHistoryPeer && (_wipeHistoryPeer->isUser() - || _wipeHistoryPeer->isMegagroup() - || _wipeHistoryPeer->isChat())) { + || (_wipeHistoryPeer->isChat() + && _wipeHistoryPeer->asChat()->canDeleteMessages()) + || (_wipeHistoryPeer->isChannel() + && _wipeHistoryPeer->asChannel()->canDeleteMessages()))) { _wipeHistoryPeer->updateFull(); _autoDeleteSettings.create( this, diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index ea60aa0f7..2261bac1e 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -374,8 +374,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto prepareSetMessagesTTL = [this](const MTPDmessageActionSetMessagesTTL &action) { auto result = PreparedText{}; const auto period = action.vperiod().v; - const auto duration = (period == 5) AssertIsDebug() - ? u"5 seconds"_q AssertIsDebug() + const auto duration = (period == 5) + ? u"5 seconds"_q : (period < 3 * 86400) ? tr::lng_ttl_about_duration1(tr::now) : tr::lng_ttl_about_duration2(tr::now); diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp index aae2e19cb..4ab3bca2f 100644 --- a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp @@ -190,7 +190,6 @@ void AutoDeleteSettingsBox( rpl::producer<QString> about, Fn<void(TimeId)> callback) { box->setTitle(tr::lng_manage_messages_ttl_title()); - box->setWidth(st::boxWideWidth); struct State { TimeId period = 0; @@ -202,25 +201,25 @@ void AutoDeleteSettingsBox( const auto options = std::vector<QString>{ tr::lng_manage_messages_ttl_never(tr::now), - u"5 seconds"_q, AssertIsDebug() + //u"5 seconds"_q, AssertIsDebug() tr::lng_manage_messages_ttl_after1(tr::now), tr::lng_manage_messages_ttl_after2(tr::now), }; const auto periodToIndex = [&](TimeId period) { return !period ? 0 - : (period == 5) AssertIsDebug() - ? 1 AssertIsDebug() + //: (period == 5) AssertIsDebug() + //? 1 AssertIsDebug() : (period < 3 * 86400) - ? 2 - : 3; + ? 1 + : 2; }; const auto indexToPeriod = [&](int index) { return !index ? 0 - : (index == 1) AssertIsDebug() - ? 5 AssertIsDebug() - : (index == 2) + //: (index == 1) AssertIsDebug() + //? 5 AssertIsDebug() + : (index == 1) ? 86400 : 7 * 86400; }; From d67a5dc5109e76593ec224e3b3a37a224abbc49b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 20:26:58 +0400 Subject: [PATCH 371/396] Remove messages ttl edit from Manage Channel. --- .../boxes/peers/edit_peer_info_box.cpp | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 9abba406c..4c81c2797 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -29,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_changes.h" #include "history/admin_log/history_admin_log_section.h" -#include "history/view/controls/history_view_ttl_button.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "mainwidget.h" @@ -337,7 +336,6 @@ private: //void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); - void fillSetMessagesTTLButton(); void fillManageSection(); void submitTitle(); @@ -883,35 +881,6 @@ void Controller::fillHistoryVisibilityButton() { refreshHistoryVisibility(); } -void Controller::fillSetMessagesTTLButton() { - Expects(_controls.buttonsLayout != nullptr); - - auto label = _peer->session().changes().peerFlagsValue( - _peer, - Data::PeerUpdate::Flag::MessagesTTL - ) | rpl::map([=] { - const auto period = _peer->messagesTTL(); - return !period - ? tr::lng_manage_messages_ttl_never() - : (period == 5) // for debugging - ? rpl::single<QString>("5 seconds") // for debugging - : (period < 3 * 86400) - ? tr::lng_manage_messages_ttl_after1() - : tr::lng_manage_messages_ttl_after2(); - }) | rpl::flatten_latest(); - - const auto buttonCallback = [=] { - Ui::show( - Box(HistoryView::Controls::AutoDeleteSettingsBox, _peer), - Ui::LayerOption::KeepOther); - }; - AddButtonWithText( - _controls.buttonsLayout, - tr::lng_manage_messages_ttl_title(), - std::move(label), - buttonCallback); -} - void Controller::fillManageSection() { Expects(_controls.buttonsLayout != nullptr); @@ -935,13 +904,6 @@ void Controller::fillManageSection() { ? channel->canEditPreHistoryHidden() : chat->canEditPreHistoryHidden(); }(); - const auto canSetMessagesTTL = [&] { - // Leave this entry point only for channels for now. - // Groups and users have their entry point in 'Clear History' box. - return isChannel - && !channel->isMegagroup() - && channel->canDeleteMessages(); - }(); const auto canEditPermissions = [&] { return isChannel @@ -1007,9 +969,6 @@ void Controller::fillManageSection() { if (canEditPreHistoryHidden) { fillHistoryVisibilityButton(); } - if (canSetMessagesTTL) { - fillSetMessagesTTLButton(); - } if (canEditSignatures) { fillSignaturesButton(); } @@ -1017,8 +976,7 @@ void Controller::fillManageSection() { || canEditSignatures //|| canEditInviteLinks || canViewOrEditLinkedChat - || canEditUsername - || canSetMessagesTTL) { + || canEditUsername) { AddSkip( _controls.buttonsLayout, st::editPeerTopButtonsLayoutSkip, From 707d090802b2aa68ec4f0ec612250ba7a80ff39e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 20:37:51 +0400 Subject: [PATCH 372/396] Don't allow to edit/revoke bot links. --- .../boxes/peers/edit_peer_invite_link.cpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index fa624b79e..e7f3d1f1b 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -170,12 +170,14 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); - result->addAction( - tr::lng_group_invite_context_edit(tr::now), - editLink); - result->addAction( - tr::lng_group_invite_context_revoke(tr::now), - revokeLink); + if (!admin->isBot()) { + result->addAction( + tr::lng_group_invite_context_edit(tr::now), + editLink); + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + } } return result; }; @@ -255,7 +257,9 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { ) | rpl::start_with_next([=](const LinkData &data) { const auto now = base::unixtime::now(); const auto expired = IsExpiredLink(data, now); - reactivateWrap->toggle(!revoked && expired, anim::type::instant); + reactivateWrap->toggle( + !revoked && expired && !admin->isBot(), + anim::type::instant); copyShareWrap->toggle(!revoked && !expired, anim::type::instant); const auto timeExpired = (data.expireDate > 0) @@ -591,9 +595,11 @@ void AddPermanentLinkBlock( result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); - result->addAction( - tr::lng_group_invite_context_revoke(tr::now), - revokeLink); + if (!admin->isBot()) { + result->addAction( + tr::lng_group_invite_context_revoke(tr::now), + revokeLink); + } return result; }; const auto label = container->lifetime().make_state<Ui::InviteLinkLabel>( From ce5739048a8b6f7a63a3d490ca2236466e0c37c4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Feb 2021 20:34:55 +0400 Subject: [PATCH 373/396] Fix build on Xcode. --- Telegram/SourceFiles/calls/calls_group_call.cpp | 6 +++--- Telegram/SourceFiles/data/data_peer.cpp | 4 ++-- Telegram/SourceFiles/history/history_widget.cpp | 8 ++++---- .../ui/controls/delete_message_context_action.cpp | 1 - Telegram/SourceFiles/window/window_session_controller.cpp | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 14bbe001d..37a7c1967 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -252,7 +252,7 @@ void GroupCall::start() { hangup(); if (error.type() == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q) { Ui::ShowMultilineToast({ - .text = tr::lng_group_call_no_anonymous(tr::now), + .text = { tr::lng_group_call_no_anonymous(tr::now) }, }); } }).send(); @@ -368,13 +368,13 @@ void GroupCall::rejoin() { hangup(); Ui::ShowMultilineToast({ - .text = (type == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q + .text = { type == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q ? tr::lng_group_call_no_anonymous(tr::now) : type == u"GROUPCALL_PARTICIPANTS_TOO_MUCH"_q ? tr::lng_group_call_too_many(tr::now) : type == u"GROUPCALL_FORBIDDEN"_q ? tr::lng_group_not_accessible(tr::now) - : Lang::Hard::ServerError()), + : Lang::Hard::ServerError() }, }); }).send(); }); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index e3cd50b2c..7b5aef4fe 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -110,9 +110,9 @@ void PeerClickHandler::onClick(ClickContext context) const { && (!currentPeer->isChannel() || currentPeer->asChannel()->linkedChat() != clickedChannel)) { Ui::ShowMultilineToast({ - .text = (_peer->isMegagroup() + .text = { _peer->isMegagroup() ? tr::lng_group_not_accessible(tr::now) - : tr::lng_channel_not_accessible(tr::now)), + : tr::lng_channel_not_accessible(tr::now) }, }); } else { window->showPeerHistory( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index b35e0880d..e504f0d82 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2487,9 +2487,9 @@ void HistoryWidget::messagesFailed(const RPCError &error, int requestId) { auto was = _peer; controller()->showBackFromStack(); Ui::ShowMultilineToast({ - .text = ((was && was->isMegagroup()) + .text = { (was && was->isMegagroup()) ? tr::lng_group_not_accessible(tr::now) - : tr::lng_channel_not_accessible(tr::now)), + : tr::lng_channel_not_accessible(tr::now) }, }); return; } @@ -5594,8 +5594,8 @@ void HistoryWidget::setupGroupCallTracker() { const auto channel = peer->asChannel(); if (channel && channel->amAnonymous()) { Ui::ShowMultilineToast({ - .text = tr::lng_group_call_no_anonymous(tr::now), - }); + .text = { tr::lng_group_call_no_anonymous(tr::now) }, + }); return; } else if (peer->groupCall()) { controller()->startOrJoinGroupCall(peer); diff --git a/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp index bda1c0436..63e5e3ac4 100644 --- a/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp +++ b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp @@ -48,7 +48,6 @@ private: const TimeId _destroyAt = 0; const Fn<void()> _destroyByTimerCallback; const crl::time _startedAt = 0; - crl::time _lastCheckAt = 0; base::Timer _refreshTimer; Text::String _text; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index fbcb5be6d..ae360797e 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -940,7 +940,7 @@ void SessionController::startOrJoinGroupCall( const auto channel = peer->asChannel(); if (channel && channel->amAnonymous()) { Ui::ShowMultilineToast({ - .text = tr::lng_group_call_no_anonymous(tr::now), + .text = { tr::lng_group_call_no_anonymous(tr::now) }, }); return; } From 433866f2c58fe576e495c26f32c374becd067f0f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 17 Feb 2021 22:11:03 +0400 Subject: [PATCH 374/396] Closed alpha version 2.5.9.1. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 2 +- Telegram/build/version | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index ee001697c..59548d219 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="2.5.9.0" /> + Version="2.5.9.1" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 369fcaf7f..8b2cd0dd5 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,9,0 - PRODUCTVERSION 2,5,9,0 + FILEVERSION 2,5,9,1 + PRODUCTVERSION 2,5,9,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.9.0" + VALUE "FileVersion", "2.5.9.1" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.9.0" + VALUE "ProductVersion", "2.5.9.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index cc346bd9e..74007b791 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,9,0 - PRODUCTVERSION 2,5,9,0 + FILEVERSION 2,5,9,1 + PRODUCTVERSION 2,5,9,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.9.0" + VALUE "FileVersion", "2.5.9.1" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.9.0" + VALUE "ProductVersion", "2.5.9.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index fd2ad5779..16b953451 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/const_string.h" -#define TDESKTOP_REQUESTED_ALPHA_VERSION (0ULL) +#define TDESKTOP_REQUESTED_ALPHA_VERSION (2005009001ULL) #ifdef TDESKTOP_ALLOW_CLOSED_ALPHA #define TDESKTOP_ALPHA_VERSION TDESKTOP_REQUESTED_ALPHA_VERSION diff --git a/Telegram/build/version b/Telegram/build/version index 1513201f9..7d949483b 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,5 +3,5 @@ AppVersionStrMajor 2.5 AppVersionStrSmall 2.5.9 AppVersionStr 2.5.9 BetaChannel 0 -AlphaVersion 0 -AppVersionOriginal 2.5.9 +AlphaVersion 2005009001 +AppVersionOriginal 2.5.9.1 From 018232680f43ce19b47133e45645759599a15bf7 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 18 Feb 2021 19:25:17 +0400 Subject: [PATCH 375/396] Add support for admins without additional rights. --- Telegram/Resources/tl/api.tl | 3 ++- Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp | 2 +- Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index c9370f742..1a6e75208 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -370,6 +370,7 @@ updateChat#1330a196 chat_id:int = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update; updateGroupCall#a45eb99b chat_id:int call:GroupCall = Update; updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; +updateChatParticipant#609a6ed4 flags:# chat_id:int date:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant qts:int = Update; updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update; updateBotStopped#30ec6ebc user_id:int stopped:Bool qts:int = Update; @@ -1058,7 +1059,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines; statsURL#47a971e0 url:string = StatsURL; -chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true = ChatAdminRights; +chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights; chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 1ea91afc5..522c0bcec 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -329,7 +329,7 @@ void EditAdminBox::prepare() { if (!_saveCallback) { return; } - const auto newFlags = value() + const auto newFlags = (value() | ChatAdminRight::f_other) & ((!channel || channel->amCreator()) ? ~Flags(0) : channel->adminRights()); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index dd83992d3..6918fe0d4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -773,7 +773,7 @@ EditFlagsControl<MTPDchatBannedRights::Flags> CreateEditRestrictions( EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights( QWidget *parent, - rpl::producer<QString> header, + rpl::producer<QString> header, MTPDchatAdminRights::Flags rights, std::map<MTPDchatAdminRights::Flags, QString> disabledMessages, bool isGroup, From 0a678ae8bdf512038e2bc1293ed66462c3ea30b8 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 14:18:13 +0400 Subject: [PATCH 376/396] No 'Clear History' for everyone in groups. --- Telegram/Resources/tl/api.tl | 2 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 2 ++ Telegram/SourceFiles/data/data_histories.cpp | 4 ---- Telegram/SourceFiles/window/window_peer_menu.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 1a6e75208..01457d6b9 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1568,7 +1568,7 @@ channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_right channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool; -channels.deleteHistory#bda41f3f flags:# for_everyone:flags.0?true channel:InputChannel max_id:int = Bool; +channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; channels.getLeftChannels#8341ecc0 offset:int = messages.Chats; channels.getGroupsForDiscussion#f5dad378 = messages.Chats; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index f2b12f4bd..3828af9e4 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -750,6 +750,8 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const tr::now, lt_user, user->firstName); + } else if (_wipeHistoryJustClear) { + return std::nullopt; } else { result.checkbox = tr::lng_delete_for_everyone_check(tr::now); } diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 47f388fd5..6c9eacc2a 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -606,11 +606,7 @@ void Histories::deleteAllMessages( // } }).send(); } else if (channel) { - const auto flags = revoke - ? MTPchannels_DeleteHistory::Flag::f_for_everyone - : MTPchannels_DeleteHistory::Flag(0); return session().api().request(MTPchannels_DeleteHistory( - MTP_flags(flags), channel->inputChannel, MTP_int(deleteTillId) )).done([=](const MTPBool &result) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 95e891f7a..4efeba6a1 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -583,16 +583,16 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) { } } if (channel->amIn()) { + auto text = isGroup + ? tr::lng_profile_leave_group(tr::now) + : tr::lng_profile_leave_channel(tr::now); + _addAction(text, DeleteAndLeaveHandler(channel)); if ((isGroup && !channel->isPublic()) || channel->canDeleteMessages()) { _addAction( tr::lng_profile_clear_history(tr::now), ClearHistoryHandler(channel)); } - auto text = isGroup - ? tr::lng_profile_leave_group(tr::now) - : tr::lng_profile_leave_channel(tr::now); - _addAction(text, DeleteAndLeaveHandler(channel)); } else { auto text = isGroup ? tr::lng_profile_join_group(tr::now) From f9f52302bbe3101f4b9e6a5edde81a1b8da0a9af Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 15:54:27 +0400 Subject: [PATCH 377/396] Ignore accents in contacts list sorting. --- Telegram/SourceFiles/data/data_folder.cpp | 7 ++++++- Telegram/SourceFiles/data/data_folder.h | 2 ++ Telegram/SourceFiles/data/data_session.cpp | 8 ++++++++ Telegram/SourceFiles/data/data_session.h | 2 ++ Telegram/SourceFiles/dialogs/dialogs_entry.h | 1 + Telegram/SourceFiles/dialogs/dialogs_list.cpp | 8 +++----- Telegram/SourceFiles/history/history.cpp | 9 +++++++++ Telegram/SourceFiles/history/history.h | 5 +++++ 8 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index ef9e83a17..a87eeea06 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -50,7 +50,8 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id) &owner->session(), FilterId(), owner->session().serverConfig().pinnedDialogsInFolderMax.value()) -, _name(tr::lng_archived_name(tr::now)) { +, _name(tr::lng_archived_name(tr::now)) +, _chatListNameSortKey(owner->nameSortKey(_name)) { indexNameParts(); session().changes().peerUpdates( @@ -408,4 +409,8 @@ const base::flat_set<QChar> &Folder::chatListFirstLetters() const { return _nameFirstLetters; } +const QString &Folder::chatListNameSortKey() const { + return _chatListNameSortKey; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_folder.h b/Telegram/SourceFiles/data/data_folder.h index a2442c70b..ab0c59b95 100644 --- a/Telegram/SourceFiles/data/data_folder.h +++ b/Telegram/SourceFiles/data/data_folder.h @@ -58,6 +58,7 @@ public: bool chatListMessageKnown() const override; void requestChatListMessage() override; const QString &chatListName() const override; + const QString &chatListNameSortKey() const override; const base::flat_set<QString> &chatListNameWords() const override; const base::flat_set<QChar> &chatListFirstLetters() const override; @@ -102,6 +103,7 @@ private: QString _name; base::flat_set<QString> _nameWords; base::flat_set<QChar> _nameFirstLetters; + QString _chatListNameSortKey; std::vector<not_null<History*>> _lastHistories; HistoryItem *_chatListMessage = nullptr; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index e3843b692..c72ad3603 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1156,6 +1156,10 @@ void Session::forgetPassportCredentials() { _passportCredentials = nullptr; } +QString Session::nameSortKey(const QString &name) const { + return TextUtilities::RemoveAccents(name).toLower(); +} + void Session::setupMigrationViewer() { session().changes().peerUpdates( PeerUpdate::Flag::Migration @@ -1203,9 +1207,13 @@ void Session::setupPeerNameViewer() { session().changes().realtimeNameUpdates( ) | rpl::start_with_next([=](const NameUpdate &update) { const auto peer = update.peer; + if (const auto history = historyLoaded(peer)) { + history->refreshChatListNameSortKey(); + } const auto &oldLetters = update.oldFirstLetters; _contactsNoChatsList.peerNameChanged(peer, oldLetters); _contactsList.peerNameChanged(peer, oldLetters); + }, _lifetime); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 6914c33d0..1a307076d 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -78,6 +78,8 @@ public: return *_session; } + [[nodiscard]] QString nameSortKey(const QString &name) const; + [[nodiscard]] Groups &groups() { return _groups; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 297313d78..672d7d505 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -155,6 +155,7 @@ public: virtual bool chatListMessageKnown() const = 0; virtual void requestChatListMessage() = 0; virtual const QString &chatListName() const = 0; + virtual const QString &chatListNameSortKey() const = 0; virtual const base::flat_set<QString> &chatListNameWords() const = 0; virtual const base::flat_set<QChar> &chatListFirstLetters() const = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp index 51d8f6734..f325f91ca 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -62,20 +62,18 @@ not_null<Row*> List::addByName(Key key) { void List::adjustByName(not_null<Row*> row) { Expects(row->pos() >= 0 && row->pos() < _rows.size()); - const auto &name = row->entry()->chatListName(); + const auto &key = row->entry()->chatListNameSortKey(); const auto index = row->pos(); const auto i = _rows.begin() + index; const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) { - const auto &greater = row->entry()->chatListName(); - return greater.compare(name, Qt::CaseInsensitive) >= 0; + return row->entry()->chatListNameSortKey().compare(key) >= 0; }); if (before != i + 1) { rotate(i, i + 1, before); } else if (i != _rows.begin()) { const auto from = std::make_reverse_iterator(i); const auto after = std::find_if(from, _rows.rend(), [&](Row *row) { - const auto &less = row->entry()->chatListName(); - return less.compare(name, Qt::CaseInsensitive) <= 0; + return row->entry()->chatListNameSortKey().compare(key) <= 0; }).base(); if (after != i) { rotate(after, i, i + 1); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 8728fe610..64d10d8f4 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -62,6 +62,7 @@ History::History(not_null<Data::Session*> owner, PeerId peerId) , peer(owner->peer(peerId)) , cloudDraftTextCache(st::dialogsTextWidthMin) , _mute(owner->notifyIsMuted(peer)) +, _chatListNameSortKey(owner->nameSortKey(peer->name)) , _sendActionPainter(this) { if (const auto user = peer->asUser()) { if (user->isBot()) { @@ -2019,6 +2020,14 @@ const QString &History::chatListName() const { return peer->name; } +const QString &History::chatListNameSortKey() const { + return _chatListNameSortKey; +} + +void History::refreshChatListNameSortKey() { + _chatListNameSortKey = owner().nameSortKey(peer->name); +} + const base::flat_set<QString> &History::chatListNameWords() const { return peer->nameWords(); } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index b715e7733..2dfc0837e 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -365,6 +365,7 @@ public: bool chatListMessageKnown() const override; void requestChatListMessage() override; const QString &chatListName() const override; + const QString &chatListNameSortKey() const override; const base::flat_set<QString> &chatListNameWords() const override; const base::flat_set<QChar> &chatListFirstLetters() const override; void loadUserpic() override; @@ -375,6 +376,8 @@ public: int y, int size) const override; + void refreshChatListNameSortKey(); + void setFakeChatListMessageFrom(const MTPmessages_Messages &data); void checkChatListMessageRemoved(not_null<HistoryItem*> item); @@ -566,6 +569,8 @@ private: // be a migrate message, but _chatListMessage should be the one before. std::optional<HistoryItem*> _chatListMessage; + QString _chatListNameSortKey; + bool _unreadMark = false; bool _fakeUnreadWhileOpened = false; From 47d2ecf62964a210d371dc68c558d1282ed7e450 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 17:53:11 +0400 Subject: [PATCH 378/396] Fix a possible crash in Clear History. --- Telegram/SourceFiles/history/history_message.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 42f4fc544..8588c20f9 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1391,12 +1391,15 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) { void HistoryMessage::applyEdition(const MTPDmessageService &message) { if (message.vaction().type() == mtpc_messageActionHistoryClear) { + const auto wasGrouped = history()->owner().groups().isGrouped(this); setReplyMarkup(nullptr); refreshMedia(nullptr); setEmptyText(); setViewsCount(-1); setForwardsCount(-1); - + if (wasGrouped) { + history()->owner().groups().unregisterMessage(this); + } finishEditionToEmpty(); } } From 221b89611742a6e3f5b9c9de846f88c9834ccfa1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 17:53:20 +0400 Subject: [PATCH 379/396] Suggest converting to gigagroup. --- Telegram/Resources/langs/lang.strings | 3 + .../boxes/peers/edit_peer_permissions_box.cpp | 124 +++++++++--------- .../boxes/peers/edit_peer_permissions_box.h | 3 + Telegram/SourceFiles/data/data_channel.cpp | 10 ++ Telegram/SourceFiles/data/data_groups.cpp | 4 +- Telegram/SourceFiles/data/data_session.cpp | 14 ++ Telegram/SourceFiles/data/data_session.h | 5 + .../SourceFiles/history/history_widget.cpp | 32 +++++ Telegram/SourceFiles/history/history_widget.h | 2 + 9 files changed, 134 insertions(+), 63 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0cc9d7fc0..a533e34dd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2031,6 +2031,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gigagroup_warning_title" = "Are you sure?"; "lng_gigagroup_warning" = "Regular members of the group (non-admins) will **irrevocably** lose their right to post messages in the group.\n\nThis action **can't** be undone."; "lng_gigagroup_done" = "Your group can now have more than 200,000 members."; +"lng_gigagroup_suggest_title" = "Limit reached"; +"lng_gigagroup_suggest_text" = "Your group has reached a limit of **200,000** members.\n\nYou can increase this limit by converting the group to a **broadcast group** where only admins can post. Interested?"; +"lng_gigagroup_suggest_more" = "Learn more"; "lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_post" = "Post messages"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 6918fe0d4..a1570e1c6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -314,6 +314,68 @@ ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup) { return result; } +Fn<void()> AboutGigagroupCallback(not_null<ChannelData*> channel) { + const auto converting = std::make_shared<bool>(); + const auto convertSure = [=] { + if (*converting) { + return; + } + *converting = true; + channel->session().api().request(MTPchannels_ConvertToGigagroup( + channel->inputChannel + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + Ui::hideSettingsAndLayer(); + Ui::Toast::Show(tr::lng_gigagroup_done(tr::now)); + }).fail([=](const RPCError &error) { + *converting = false; + }).send(); + }; + const auto convertWarn = [=] { + if (*converting) { + return; + } + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_gigagroup_warning_title()); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_gigagroup_warning( + ) | Ui::Text::ToRichLangValue(), + st::infoAboutGigagroup)); + box->addButton(tr::lng_gigagroup_convert_sure(), convertSure); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + }), Ui::LayerOption::KeepOther); + }; + return [=] { + if (*converting) { + return; + } + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_gigagroup_convert_title()); + const auto addFeature = [&](rpl::producer<QString> text) { + using namespace rpl::mappers; + const auto prefix = QString::fromUtf8("\xE2\x80\xA2 "); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + std::move(text) | rpl::map(prefix + _1), + st::infoAboutGigagroup), + style::margins( + st::boxRowPadding.left(), + st::boxLittleSkip, + st::boxRowPadding.right(), + st::boxLittleSkip)); + }; + addFeature(tr::lng_gigagroup_convert_feature1()); + addFeature(tr::lng_gigagroup_convert_feature2()); + addFeature(tr::lng_gigagroup_convert_feature3()); + box->addButton(tr::lng_gigagroup_convert_sure(), convertWarn); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + }), Ui::LayerOption::KeepOther); + }; +} + EditPeerPermissionsBox::EditPeerPermissionsBox( QWidget*, not_null<Window::SessionNavigation*> navigation, @@ -541,71 +603,11 @@ void EditPeerPermissionsBox::addSuggestGigagroup( tr::lng_rights_gigagroup_title(), st::rightsHeaderLabel), st::rightsHeaderMargin); - const auto channel = _peer->asChannel(); - const auto converting = std::make_shared<bool>(); - const auto convertSure = [=] { - if (*converting) { - return; - } - *converting = true; - channel->session().api().request(MTPchannels_ConvertToGigagroup( - channel->inputChannel - )).done([=](const MTPUpdates &result) { - channel->session().api().applyUpdates(result); - Ui::hideSettingsAndLayer(); - Ui::Toast::Show(tr::lng_gigagroup_done(tr::now)); - }).fail([=](const RPCError &error) { - *converting = false; - }).send(); - }; - const auto convertWarn = [=] { - if (*converting) { - return; - } - getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) { - box->setTitle(tr::lng_gigagroup_warning_title()); - box->addRow( - object_ptr<Ui::FlatLabel>( - box, - tr::lng_gigagroup_warning( - ) | Ui::Text::ToRichLangValue(), - st::infoAboutGigagroup)); - box->addButton(tr::lng_gigagroup_convert_sure(), convertSure); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); - })); - }; - const auto convert = [=] { - if (*converting) { - return; - } - getDelegate()->show(Box([=](not_null<Ui::GenericBox*> box) { - box->setTitle(tr::lng_gigagroup_convert_title()); - const auto addFeature = [&](rpl::producer<QString> text) { - using namespace rpl::mappers; - const auto prefix = QString::fromUtf8("\xE2\x80\xA2 "); - box->addRow( - object_ptr<Ui::FlatLabel>( - box, - std::move(text) | rpl::map(prefix + _1), - st::infoAboutGigagroup), - style::margins( - st::boxRowPadding.left(), - st::boxLittleSkip, - st::boxRowPadding.right(), - st::boxLittleSkip)); - }; - addFeature(tr::lng_gigagroup_convert_feature1()); - addFeature(tr::lng_gigagroup_convert_feature2()); - addFeature(tr::lng_gigagroup_convert_feature3()); - box->addButton(tr::lng_gigagroup_convert_sure(), convertWarn); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); - })); - }; container->add(EditPeerInfoBox::CreateButton( container, tr::lng_rights_gigagroup_convert(), rpl::single(QString()), - convert, + AboutGigagroupCallback(_peer->asChannel()), st::peerPermissionsButton)); container->add( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index a817ac3da..ee4dfc0df 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -49,6 +49,9 @@ private: }; +[[nodiscard]] Fn<void()> AboutGigagroupCallback( + not_null<ChannelData*> channel); + template <typename Flags> struct EditFlagsControl { object_ptr<Ui::RpWidget> widget; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 880945e0b..c09430116 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -739,6 +739,16 @@ void ApplyChannelUpdate( const MTPDchannelFull &update) { const auto session = &channel->session(); + if (channel->isMegagroup()) { + const auto suggestions = update.vpending_suggestions().value_or_empty(); + channel->owner().setSuggestToGigagroup( + channel, + ranges::contains( + suggestions, + "convert_to_gigagroup"_q, + &MTPstring::v)); + } + channel->setAvailableMinId(update.vavailable_min_id().value_or_empty()); auto canViewAdmins = channel->canViewAdmins(); auto canViewMembers = channel->canViewMembers(); diff --git a/Telegram/SourceFiles/data/data_groups.cpp b/Telegram/SourceFiles/data/data_groups.cpp index 965566994..650a4f889 100644 --- a/Telegram/SourceFiles/data/data_groups.cpp +++ b/Telegram/SourceFiles/data/data_groups.cpp @@ -65,8 +65,8 @@ void Groups::unregisterMessage(not_null<const HistoryItem*> item) { } void Groups::refreshMessage( - not_null<HistoryItem*> item, - bool justRefreshViews) { + not_null<HistoryItem*> item, + bool justRefreshViews) { if (!isGrouped(item)) { unregisterMessage(item); return; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index c72ad3603..8d844e64e 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2049,6 +2049,20 @@ MsgId Session::nextLocalMessageId() { return _localMessageIdCounter++; } +void Session::setSuggestToGigagroup( + not_null<ChannelData*> group, + bool suggest) { + if (suggest) { + _suggestToGigagroup.emplace(group); + } else { + _suggestToGigagroup.remove(group); + } +} + +bool Session::suggestToGigagroup(not_null<ChannelData*> group) const { + return _suggestToGigagroup.contains(group); +} + HistoryItem *Session::message(ChannelId channelId, MsgId itemId) const { if (!itemId) { return nullptr; diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 1a307076d..3e0418093 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -327,6 +327,10 @@ public: const Dialogs::Key &key1, const Dialogs::Key &key2); + void setSuggestToGigagroup(not_null<ChannelData*> group, bool suggest); + [[nodiscard]] bool suggestToGigagroup( + not_null<ChannelData*> group) const; + void registerMessage(not_null<HistoryItem*> item); void unregisterMessage(not_null<HistoryItem*> item); @@ -930,6 +934,7 @@ private: rpl::event_stream<not_null<WebPageData*>> _webpageUpdates; rpl::event_stream<not_null<ChannelData*>> _channelDifferenceTooLong; + base::flat_set<not_null<ChannelData*>> _suggestToGigagroup; base::flat_multi_map<TimeId, not_null<PollData*>> _pollsClosings; base::Timer _pollsClosingTimer; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e504f0d82..87a68e485 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/send_files_box.h" #include "boxes/share_box.h" #include "boxes/edit_caption_box.h" +#include "boxes/peers/edit_peer_permissions_box.h" // ShowAboutGigagroup. #include "core/file_utilities.h" #include "ui/toast/toast.h" #include "ui/toasts/common_toasts.h" @@ -3446,6 +3447,36 @@ void HistoryWidget::doneShow() { checkHistoryActivation(); controller()->widget()->setInnerFocus(); _preserveScrollTop = false; + checkSuggestToGigagroup(); +} + +void HistoryWidget::checkSuggestToGigagroup() { + const auto group = _peer ? _peer->asMegagroup() : nullptr; + if (!group || !group->owner().suggestToGigagroup(group)) { + return; + } + InvokeQueued(_list, [=] { + if (!Ui::isLayerShown()) { + group->owner().setSuggestToGigagroup(group, false); + group->session().api().request(MTPhelp_DismissSuggestion( + group->input, + MTP_string("convert_to_gigagroup") + )).send(); + Ui::show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_gigagroup_suggest_title()); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_gigagroup_suggest_text( + ) | Ui::Text::ToRichLangValue(), + st::infoAboutGigagroup)); + box->addButton( + tr::lng_gigagroup_suggest_more(), + AboutGigagroupCallback(group)); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); + } + }); } void HistoryWidget::finishAnimating() { @@ -6183,6 +6214,7 @@ void HistoryWidget::fullPeerUpdated(PeerData *peer) { _list->updateBotInfo(); handlePeerUpdate(); + checkSuggestToGigagroup(); } if (updateCmdStartShown()) { refresh = true; diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 49c093912..65df86b0e 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -330,6 +330,8 @@ private: using TextUpdateEvents = base::flags<TextUpdateEvent>; friend inline constexpr bool is_flag_type(TextUpdateEvent) { return true; }; + void checkSuggestToGigagroup(); + void initTabbedSelector(); void initVoiceRecordBar(); void refreshTabbedPanel(); From 3d85ca2f844f7051c54b8d39bc9107f4dae3d705 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 18:58:07 +0400 Subject: [PATCH 380/396] Disable ttl for self and notifications. --- Telegram/SourceFiles/boxes/confirm_box.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 3828af9e4..cc10c6bf6 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -665,7 +665,9 @@ void DeleteMessagesBox::prepare() { if (_wipeHistoryJustClear && _wipeHistoryPeer - && (_wipeHistoryPeer->isUser() + && ((_wipeHistoryPeer->isUser() + && !_wipeHistoryPeer->isSelf() + && !_wipeHistoryPeer->isNotificationsUser()) || (_wipeHistoryPeer->isChat() && _wipeHistoryPeer->asChat()->canDeleteMessages()) || (_wipeHistoryPeer->isChannel() From aa46ec8bc14afe1726d8cc4d500c47349315c81c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Feb 2021 14:35:21 +0400 Subject: [PATCH 381/396] Load photo thumbnail only if no inline thumbnail found. --- .../SourceFiles/history/view/media/history_view_photo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index b86d4fdef..e72f121d6 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -109,7 +109,8 @@ void Photo::ensureDataMediaCreated() const { void Photo::dataMediaCreated() const { Expects(_dataMedia != nullptr); - if (!_dataMedia->image(PhotoSize::Large) + if (_data->inlineThumbnailBytes().isEmpty() + && !_dataMedia->image(PhotoSize::Large) && !_dataMedia->image(PhotoSize::Thumbnail)) { _dataMedia->wanted(PhotoSize::Small, _realParent->fullId()); } @@ -376,10 +377,10 @@ void Photo::paintUserpicFrame( if (const auto large = _dataMedia->image(PhotoSize::Large)) { return large->pixCircled(_pixw, _pixh); } else if (const auto thumbnail = _dataMedia->image( - PhotoSize::Thumbnail)) { + PhotoSize::Thumbnail)) { return thumbnail->pixBlurredCircled(_pixw, _pixh); } else if (const auto small = _dataMedia->image( - PhotoSize::Small)) { + PhotoSize::Small)) { return small->pixBlurredCircled(_pixw, _pixh); } else if (const auto blurred = _dataMedia->thumbnailInline()) { return blurred->pixBlurredCircled(_pixw, _pixh); From 7dabcf5a32811975ce8b6b87ea4be4a09068678d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Feb 2021 12:53:25 +0400 Subject: [PATCH 382/396] Fix build on Linux. --- Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 017a1ae84..87c23d840 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -189,7 +189,8 @@ private: left / 86400)); } else { const auto time = base::unixtime::parse(link.expireDate).time(); - add(time.toString(Qt::SystemLocaleLongDate)); + add(time.toString(QLocale::system().dateTimeFormat( + QLocale::LongFormat))); } } return result; From b5a2b0fb98edfbda6e2855026f59e7e3fbedae87 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Feb 2021 17:24:03 +0400 Subject: [PATCH 383/396] Detect file mime from content as well on Linux. --- Telegram/SourceFiles/data/data_document.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 6f0bcf47f..946a452a1 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_specific.h" #include "platform/platform_file_utilities.h" #include "base/platform/base_platform_info.h" +#include "base/platform/base_platform_file_utilities.h" #include "history/history.h" #include "history/history_item.h" #include "history/view/media/history_view_gif.h" @@ -1714,10 +1715,16 @@ bool IsIpRevealingName(const QString &filepath) { const auto list = joined.split(' '); return base::flat_set<QString>(list.begin(), list.end()); }(); + static const auto kMimeTypes = [] { + const auto joined = u"text/html image/svg+xml"_q; + const auto list = joined.split(' '); + return base::flat_set<QString>(list.begin(), list.end()); + }(); return ranges::binary_search( kExtensions, - FileExtension(filepath).toLower()); + FileExtension(filepath).toLower() + ) || base::Platform::IsNonExtensionMimeFrom(filepath, kMimeTypes); } base::binary_guard ReadImageAsync( From 4928066be7a84c44a6c92bffc4d57fa249c6865c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 12:45:57 +0400 Subject: [PATCH 384/396] Fix animated userpics playback in media viewer. Regression was introduced in 0ecd4d3b40. I hope it fixes #10288. --- .../media/view/media_view_overlay_widget.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 7607cd2ff..604449dd3 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2495,19 +2495,21 @@ bool OverlayWidget::initStreaming(bool continueStreaming) { void OverlayWidget::startStreamingPlayer() { Expects(_streamed != nullptr); - if (!_streamed->instance.player().paused() - && !_streamed->instance.player().finished() - && !_streamed->instance.player().failed()) { + const auto &player = _streamed->instance.player(); + if (player.playing()) { if (!_streamed->withSound) { return; } _pip = nullptr; + } else if (!player.paused() && !player.finished() && !player.failed()) { + _pip = nullptr; } else if (_pip && _streamed->withSound) { return; } const auto position = _document - ? _document->session().settings().mediaLastPlaybackPosition(_document->id) + ? _document->session().settings().mediaLastPlaybackPosition( + _document->id) : _photo ? _photo->videoStartPosition() : 0; From 673fc63680bd88bc16186dd42cbc0d46cca99176 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 23 Feb 2021 02:20:18 +0400 Subject: [PATCH 385/396] Check DESKTOP_APP_DISABLE_X11_INTEGRATION on actions --- .github/workflows/linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ce1109d86..ba7fd18a6 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -62,6 +62,7 @@ jobs: defines: - "" - "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" + - "DESKTOP_APP_DISABLE_X11_INTEGRATION" - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" - "DESKTOP_APP_DISABLE_GTK_INTEGRATION" From 60a1e548acc7b7ea31992d0afce356b357b5ea56 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 23 Feb 2021 02:20:42 +0400 Subject: [PATCH 386/396] Use LZO compression in snap --- snap/snapcraft.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 0268b33b7..b0c7b1ae7 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -5,6 +5,7 @@ icon: Telegram/Resources/art/icon512@2x.png base: core20 grade: stable confinement: strict +compression: lzo architectures: - build-on: amd64 From bb119ca967051b75cc71e7df4318ada72745a813 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 23 Feb 2021 02:23:29 +0400 Subject: [PATCH 387/396] Run docker in interactive mode for colored output and Ctrl+C --- docs/building-cmake.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/building-cmake.md b/docs/building-cmake.md index d39d7bd9f..f518c5dcb 100644 --- a/docs/building-cmake.md +++ b/docs/building-cmake.md @@ -18,7 +18,7 @@ Go to the `tdesktop` directory and run Make sure that you're still in the `tdesktop` directory and run (using [your **api_id** and **api_hash**](#obtain-your-api-credentials)) - docker run --rm \ + docker run --rm -it \ -v $PWD:/usr/src/tdesktop \ tdesktop:centos_env \ /usr/src/tdesktop/Telegram/build/docker/centos_env/build.sh \ @@ -29,7 +29,7 @@ Make sure that you're still in the `tdesktop` directory and run (using [your **a Or, to create a debug build, run (also using [your **api_id** and **api_hash**](#obtain-your-api-credentials)) - docker run --rm \ + docker run --rm -it \ -v $PWD:/usr/src/tdesktop \ -e DEBUG=1 \ tdesktop:centos_env \ From 1a3253ae8b537a756c3bd06a5f5ceef471548d52 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 23 Feb 2021 04:55:14 +0400 Subject: [PATCH 388/396] Implement three items tray menu on Linux --- Telegram/SourceFiles/core/application.cpp | 11 ------ Telegram/SourceFiles/mainwindow.cpp | 39 ++++++++----------- Telegram/SourceFiles/mainwindow.h | 2 +- .../platform/linux/main_window_linux.cpp | 7 +--- .../platform/mac/main_window_mac.mm | 27 ++++++++----- Telegram/SourceFiles/window/main_window.cpp | 4 -- Telegram/SourceFiles/window/main_window.h | 2 +- .../window/notifications_manager.cpp | 1 - 8 files changed, 36 insertions(+), 57 deletions(-) diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 3b5d6fb98..c1eea20a1 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -137,17 +137,6 @@ Application::Application(not_null<Launcher*> launcher) UpdateChecker().setMtproto(session); } }, _lifetime); - - _domain->activeValue( - ) | rpl::filter(rpl::mappers::_1 != nullptr - ) | rpl::take(1) | rpl::start_with_next([=] { - if (_window) { - // Global::DesktopNotify is used in updateTrayMenu. - // This should be called when user settings are read. - // Right now after they are read the startMtp() is called. - _window->widget()->updateTrayMenu(); - } - }, _lifetime); } Application::~Application() { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 39c2a4fd0..9bad8c5f5 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -138,18 +138,17 @@ void MainWindow::createTrayIconMenu() { trayIconMenu->deleteOnHide(false); #else // Q_OS_WIN trayIconMenu = new QMenu(this); + + connect(trayIconMenu, &QMenu::aboutToShow, [=] { + updateIsActive(); + updateTrayMenu(); + }); #endif // else for Q_OS_WIN auto notificationActionText = Core::App().settings().desktopNotify() ? tr::lng_disable_notifications_from_tray(tr::now) : tr::lng_enable_notifications_from_tray(tr::now); - if (Platform::IsLinux() && !Platform::IsWayland()) { - trayIconMenu->addAction(tr::lng_open_from_tray(tr::now), [=] { - showFromTray(); - }); - } - const auto showLifetime = std::make_shared<rpl::lifetime>(); trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), [=] { if (_activeForTrayIconAction) { minimizeToTray(); @@ -205,7 +204,6 @@ void MainWindow::finishFirstShow() { applyInitialWorkMode(); createGlobalMenu(); firstShadowsUpdate(); - updateTrayMenu(); windowDeactivateEvents( ) | rpl::start_with_next([=] { @@ -644,24 +642,19 @@ bool MainWindow::eventFilter(QObject *object, QEvent *e) { return Platform::MainWindow::eventFilter(object, e); } -void MainWindow::updateTrayMenu(bool force) { - if (!trayIconMenu || (Platform::IsWindows() && !force)) return; +void MainWindow::updateTrayMenu() { + if (!trayIconMenu) return; auto actions = trayIconMenu->actions(); - if (Platform::IsLinux() && !Platform::IsWayland()) { - const auto minimizeAction = actions.at(1); - minimizeAction->setEnabled(isVisible()); - } else { - const auto active = isActiveForTrayMenu(); - if (_activeForTrayIconAction != active) { - _activeForTrayIconAction = active; - const auto toggleAction = actions.at(0); - toggleAction->setText(_activeForTrayIconAction - ? tr::lng_minimize_to_tray(tr::now) - : tr::lng_open_from_tray(tr::now)); - } + const auto active = isActiveForTrayMenu(); + if (_activeForTrayIconAction != active) { + _activeForTrayIconAction = active; + const auto toggleAction = actions.at(0); + toggleAction->setText(_activeForTrayIconAction + ? tr::lng_minimize_to_tray(tr::now) + : tr::lng_open_from_tray(tr::now)); } - auto notificationAction = actions.at(Platform::IsLinux() && !Platform::IsWayland() ? 2 : 1); + auto notificationAction = actions.at(1); auto notificationActionText = Core::App().settings().desktopNotify() ? tr::lng_disable_notifications_from_tray(tr::now) : tr::lng_enable_notifications_from_tray(tr::now); @@ -691,7 +684,7 @@ void MainWindow::handleTrayIconActication( return; } if (reason == QSystemTrayIcon::Context) { - updateTrayMenu(true); + updateTrayMenu(); base::call_delayed(1, this, [=] { psShowTrayMenu(); }); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index f47d01689..4cecda0cc 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -79,7 +79,7 @@ public: } void showMainMenu(); - void updateTrayMenu(bool force = false) override; + void updateTrayMenu() override; void fixOrder() override; void showSpecialLayer( diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 23b036f35..8353d703b 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -628,11 +628,6 @@ void MainWindow::psShowTrayMenu() { } void MainWindow::psTrayMenuUpdated() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - if (_sniTrayIcon && trayIconMenu) { - _sniTrayIcon->setContextMenu(trayIconMenu); - } -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -692,7 +687,6 @@ void MainWindow::attachToSNITrayIcon() { handleTrayIconActication(QSystemTrayIcon::MiddleClick); }); }); - updateTrayMenu(); } void MainWindow::sniSignalEmitted( @@ -804,6 +798,7 @@ void MainWindow::psSetupTrayIcon() { this); _sniTrayIcon->setTitle(AppName.utf16()); + _sniTrayIcon->setContextMenu(trayIconMenu); setSNITrayIcon(counter, muted); attachToSNITrayIcon(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 6adde8c14..d583fe442 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -525,6 +525,18 @@ void MainWindow::stateChangedHook(Qt::WindowState state) { void MainWindow::handleActiveChangedHook() { InvokeQueued(this, [this] { _private->updateNativeTitle(); }); + + // On macOS just remove trayIcon menu if the window is not active. + // So we will activate the window on click instead of showing the menu. + if (isActiveForTrayMenu()) { + if (trayIcon + && trayIconMenu + && trayIcon->contextMenu() != trayIconMenu) { + trayIcon->setContextMenu(trayIconMenu); + } + } else if (trayIcon) { + trayIcon->setContextMenu(nullptr); + } } void MainWindow::initHook() { @@ -555,16 +567,6 @@ void MainWindow::psShowTrayMenu() { } void MainWindow::psTrayMenuUpdated() { - // On macOS just remove trayIcon menu if the window is not active. - // So we will activate the window on click instead of showing the menu. - if (isActive()) { - if (trayIcon && trayIconMenu - && trayIcon->contextMenu() != trayIconMenu) { - trayIcon->setContextMenu(trayIconMenu); - } - } else if (trayIcon) { - trayIcon->setContextMenu(nullptr); - } } void MainWindow::psSetupTrayIcon() { @@ -573,6 +575,11 @@ void MainWindow::psSetupTrayIcon() { trayIcon->setIcon(generateIconForTray( Core::App().unreadBadge(), Core::App().unreadBadgeMuted())); + if (isActiveForTrayMenu()) { + trayIcon->setContextMenu(trayIconMenu); + } else { + trayIcon->setContextMenu(nullptr); + } attachToTrayIcon(trayIcon); } else { updateIconCounters(); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index ccd0d047a..154f030e4 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -280,7 +280,6 @@ void MainWindow::handleActiveChanged() { Core::App().checkMediaViewActivation(); } base::call_delayed(1, this, [this] { - updateTrayMenu(); handleActiveChangedHook(); }); } @@ -300,7 +299,6 @@ void MainWindow::handleVisibleChanged(bool visible) { void MainWindow::showFromTray() { base::call_delayed(1, this, [this] { - updateTrayMenu(); updateGlobalMenu(); }); activate(); @@ -556,7 +554,6 @@ void MainWindow::attachToTrayIcon(not_null<QSystemTrayIcon*> icon) { handleTrayIconActication(reason); }); }); - App::wnd()->updateTrayMenu(); } void MainWindow::paintEvent(QPaintEvent *e) { @@ -694,7 +691,6 @@ bool MainWindow::minimizeToTray() { closeWithoutDestroy(); controller().updateIsActiveBlur(); - updateTrayMenu(); updateGlobalMenu(); showTrayTooltip(); return true; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index a6ab3749e..811752a65 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -90,7 +90,7 @@ public: // Returns how much could the window get extended. int tryToExtendWidthBy(int addToWidth); - virtual void updateTrayMenu(bool force = false) { + virtual void updateTrayMenu() { } virtual void fixOrder() { } diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 324aad458..55e8031e6 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -54,7 +54,6 @@ System::System() , _waitForAllGroupedTimer([=] { showGrouped(); }) { subscribe(settingsChanged(), [=](ChangeType type) { if (type == ChangeType::DesktopEnabled) { - App::wnd()->updateTrayMenu(); clearAll(); } else if (type == ChangeType::ViewParams) { updateAll(); From 27681db7f691a2646c5ede5492b18a9ca0374b37 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 17:16:29 +0400 Subject: [PATCH 389/396] Show better error on invite peek discussion group open. --- Telegram/SourceFiles/window/window_peer_menu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 4efeba6a1..7382ae434 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -552,6 +552,11 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) { if (channel->isBroadcast()) { if (const auto chat = channel->linkedChat()) { _addAction(tr::lng_profile_view_discussion(tr::now), [=] { + if (channel->invitePeekExpires()) { + Ui::Toast::Show( + tr::lng_channel_invite_private(tr::now)); + return; + } navigation->showPeerHistory( chat, Window::SectionShow::Way::Forward); From 16b4959e71e88e12e8ad25fc7a2a7367287e818d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 18:47:43 +0400 Subject: [PATCH 390/396] Add invite link QR code generation. --- Telegram/Resources/langs/lang.strings | 4 + .../boxes/peers/edit_peer_invite_link.cpp | 131 ++++++++++++++++++ .../boxes/peers/edit_peer_invite_link.h | 1 + .../boxes/peers/edit_peer_invite_links.cpp | 3 + Telegram/SourceFiles/info/info.style | 5 + Telegram/SourceFiles/intro/intro_qr.cpp | 36 ++--- Telegram/SourceFiles/intro/intro_qr.h | 2 + 7 files changed, 164 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a533e34dd..d5ca2f5b9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1232,6 +1232,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; "lng_group_invite_context_edit" = "Edit"; +"lng_group_invite_context_qr" = "Get QR Code"; "lng_group_invite_context_revoke" = "Revoke"; "lng_group_invite_context_delete" = "Delete"; "lng_group_invite_context_delete_all" = "Delete all"; @@ -1261,6 +1262,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_used_about" = "This link reached its usage limit."; "lng_group_invite_can_join_via_link#one" = "{count} person can join via this link."; "lng_group_invite_can_join_via_link#other" = "{count} people can join via this link."; +"lng_group_invite_qr_title" = "Invite by QR Code"; +"lng_group_invite_qr_about" = "Everyone on Telegram can scan this code to join your group."; +"lng_group_invite_qr_copied" = "QR Code copied to clipboard."; "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index e7f3d1f1b..38dc102e3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -38,17 +38,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "settings/settings_common.h" #include "mtproto/sender.h" +#include "qr/qr_generate.h" +#include "intro/intro_qr.h" // TelegramLogoImage #include "styles/style_boxes.h" #include "styles/style_layers.h" // st::boxDividerLabel. #include "styles/style_info.h" #include "styles/style_settings.h" #include <QtGui/QGuiApplication> +#include <QtCore/QMimeData> namespace { constexpr auto kFirstPage = 20; constexpr auto kPerPage = 100; +constexpr auto kShareQrSize = 768; +constexpr auto kShareQrPadding = 16; using LinkData = Api::InviteLink; @@ -113,6 +118,105 @@ private: return updated.link.isEmpty() || (!revoked && updated.revoked); } +QImage QrExact(const Qr::Data &data, int pixel) { + const auto image = [](int size) { + auto result = QImage( + size, + size, + QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + { + QPainter p(&result); + const auto skip = size / 12; + const auto logoSize = size - 2 * skip; + p.drawImage( + skip, + skip, + Intro::details::TelegramLogoImage().scaled( + logoSize, + logoSize, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation)); + } + return result; + }; + return Qr::ReplaceCenter( + Qr::Generate(data, pixel, st::windowFg->c), + image(Qr::ReplaceSize(data, pixel))); +} + +QImage Qr(const Qr::Data &data, int pixel, int max = 0) { + Expects(data.size > 0); + + if (max > 0 && data.size * pixel > max) { + pixel = std::max(max / data.size, 1); + } + return QrExact(data, pixel * style::DevicePixelRatio()); +} + +QImage Qr(const QString &text, int pixel, int max) { + return Qr(Qr::Encode(text), pixel, max); +} + +QImage QrForShare(const QString &text) { + const auto data = Qr::Encode(text); + const auto size = (kShareQrSize - 2 * kShareQrPadding); + const auto image = QrExact(data, size / data.size); + auto result = QImage( + kShareQrPadding * 2 + image.width(), + kShareQrPadding * 2 + image.height(), + QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::white); + { + auto p = QPainter(&result); + p.drawImage(kShareQrPadding, kShareQrPadding, image); + } + return result; +} + +void QrBox( + not_null<Ui::GenericBox*> box, + const QString &link, + Fn<void(QImage)> share) { + box->setTitle(tr::lng_group_invite_qr_title()); + + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); + + const auto qr = Qr( + link, + st::inviteLinkQrPixel, + st::boxWidth - st::boxRowPadding.left() - st::boxRowPadding.right()); + const auto size = qr.width() / style::DevicePixelRatio(); + const auto height = st::inviteLinkQrSkip * 2 + size; + const auto container = box->addRow( + object_ptr<Ui::BoxContentDivider>(box, height), + st::inviteLinkQrMargin); + const auto button = Ui::CreateChild<Ui::AbstractButton>(container); + button->resize(size, size); + button->paintRequest( + ) | rpl::start_with_next([=] { + QPainter(button).drawImage(QRect(0, 0, size, size), qr); + }, button->lifetime()); + container->widthValue( + ) | rpl::start_with_next([=](int width) { + button->move((width - size) / 2, st::inviteLinkQrSkip); + }, button->lifetime()); + button->setClickedCallback([=] { + share(QrForShare(link)); + }); + + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_group_invite_qr_about(), + st::boxLabel), + st::inviteLinkQrValuePadding); + + box->addButton( + tr::lng_group_invite_context_copy(), + [=] { share(QrForShare(link)); }); +} + Controller::Controller( not_null<PeerData*> peer, not_null<UserData*> admin, @@ -147,6 +251,9 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { const auto shareLink = crl::guard(weak, [=] { ShareInviteLinkBox(_peer, link); }); + const auto getLinkQr = crl::guard(weak, [=] { + InviteLinkQrBox(link); + }); const auto revokeLink = crl::guard(weak, [=] { RevokeLink(_peer, admin, link); }); @@ -170,6 +277,9 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) { result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); + result->addAction( + tr::lng_group_invite_context_qr(tr::now), + getLinkQr); if (!admin->isBot()) { result->addAction( tr::lng_group_invite_context_edit(tr::now), @@ -473,12 +583,15 @@ SingleRowController::SingleRowController( void SingleRowController::prepare() { auto row = std::make_unique<PeerListRow>(_peer); + const auto raw = row.get(); std::move( _status ) | rpl::start_with_next([=](const QString &status) { raw->setCustomStatus(status); + delegate()->peerListUpdateRow(raw); }, _lifetime); + delegate()->peerListAppendRow(std::move(row)); delegate()->peerListRefreshRows(); } @@ -561,6 +674,11 @@ void AddPermanentLinkBlock( ShareInviteLinkBox(peer, current.link); } }); + const auto getLinkQr = crl::guard(weak, [=] { + if (const auto current = value->current(); !current.link.isEmpty()) { + InviteLinkQrBox(current.link); + } + }); const auto revokeLink = crl::guard(weak, [=] { const auto box = std::make_shared<QPointer<ConfirmBox>>(); const auto done = crl::guard(weak, [=] { @@ -595,6 +713,9 @@ void AddPermanentLinkBlock( result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); + result->addAction( + tr::lng_group_invite_context_qr(tr::now), + getLinkQr); if (!admin->isBot()) { result->addAction( tr::lng_group_invite_context_revoke(tr::now), @@ -790,6 +911,16 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) { Ui::LayerOption::KeepOther); } +void InviteLinkQrBox(const QString &link) { + Ui::show(Box(QrBox, link, [=](const QImage &image) { + auto mime = std::make_unique<QMimeData>(); + mime->setImageData(image); + QGuiApplication::clipboard()->setMimeData(mime.release()); + + Ui::Toast::Show(tr::lng_group_invite_qr_copied(tr::now)); + })); +} + void EditLink( not_null<PeerData*> peer, const Api::InviteLink &data) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index c8825e10f..0d97c60be 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -34,6 +34,7 @@ void AddPermanentLinkBlock( void CopyInviteLink(const QString &link); void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link); +void InviteLinkQrBox(const QString &link); void RevokeLink( not_null<PeerData*> peer, not_null<UserData*> admin, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 87c23d840..dc78ef8af 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -572,6 +572,9 @@ base::unique_qptr<Ui::PopupMenu> LinksController::createRowContextMenu( result->addAction(tr::lng_group_invite_context_share(tr::now), [=] { ShareInviteLinkBox(_peer, link); }); + result->addAction(tr::lng_group_invite_context_qr(tr::now), [=] { + InviteLinkQrBox(link); + }); result->addAction(tr::lng_group_invite_context_edit(tr::now), [=] { EditLink(_peer, data); }); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index a8684372b..f1076b875 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -945,6 +945,11 @@ inviteLinkThreeDotsSkip: 12px; inviteLinkRevokedTitlePadding: margins(22px, 16px, 10px, 4px); inviteLinkLimitMargin: margins(22px, 8px, 22px, 8px); +inviteLinkQrPixel: 8px; +inviteLinkQrSkip: 24px; +inviteLinkQrMargin: margins(0px, 0px, 0px, 13px); +inviteLinkQrValuePadding: margins(22px, 0px, 22px, 12px); + infoAboutGigagroup: FlatLabel(defaultFlatLabel) { minWidth: 274px; } diff --git a/Telegram/SourceFiles/intro/intro_qr.cpp b/Telegram/SourceFiles/intro/intro_qr.cpp index 380889263..c5bed139a 100644 --- a/Telegram/SourceFiles/intro/intro_qr.cpp +++ b/Telegram/SourceFiles/intro/intro_qr.cpp @@ -32,24 +32,6 @@ namespace Intro { namespace details { namespace { -[[nodiscard]] QImage TelegramLogoImage() { - const auto size = QSize(st::introQrCenterSize, st::introQrCenterSize); - auto result = QImage( - size * style::DevicePixelRatio(), - QImage::Format_ARGB32_Premultiplied); - result.fill(Qt::transparent); - result.setDevicePixelRatio(style::DevicePixelRatio()); - { - auto p = QPainter(&result); - auto hq = PainterHighQualityEnabler(p); - p.setBrush(st::activeButtonBg); - p.setPen(Qt::NoPen); - p.drawEllipse(QRect(QPoint(), size)); - st::introQrPlane.paintInCenter(p, QRect(QPoint(), size)); - } - return result; -} - [[nodiscard]] QImage TelegramQrExact(const Qr::Data &data, int pixel) { return Qr::Generate(data, pixel, st::windowFg->c); } @@ -430,5 +412,23 @@ void QrWidget::cancelled() { api().request(base::take(_requestId)).cancel(); } +QImage TelegramLogoImage() { + const auto size = QSize(st::introQrCenterSize, st::introQrCenterSize); + auto result = QImage( + size * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + result.setDevicePixelRatio(style::DevicePixelRatio()); + { + auto p = QPainter(&result); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(st::activeButtonBg); + p.setPen(Qt::NoPen); + p.drawEllipse(QRect(QPoint(), size)); + st::introQrPlane.paintInCenter(p, QRect(QPoint(), size)); + } + return result; +} + } // namespace details } // namespace Intro diff --git a/Telegram/SourceFiles/intro/intro_qr.h b/Telegram/SourceFiles/intro/intro_qr.h index 87b9cd800..563a4a4a8 100644 --- a/Telegram/SourceFiles/intro/intro_qr.h +++ b/Telegram/SourceFiles/intro/intro_qr.h @@ -59,5 +59,7 @@ private: }; +[[nodiscard]] QImage TelegramLogoImage(); + } // namespace details } // namespace Intro From 61af510b7dae348b483e20e414a09f7e7511abe0 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 18:55:14 +0400 Subject: [PATCH 391/396] Move Copy QR button to the left. --- Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 38dc102e3..423448ea5 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -212,7 +212,7 @@ void QrBox( st::boxLabel), st::inviteLinkQrValuePadding); - box->addButton( + box->addLeftButton( tr::lng_group_invite_context_copy(), [=] { share(QrForShare(link)); }); } From 975fcb0c1a354039456ac710b4ad6243963d9ca2 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 19:12:22 +0400 Subject: [PATCH 392/396] Show channel post authors in Saved Messages. --- Telegram/SourceFiles/history/history_message.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 8588c20f9..b246098c4 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1045,6 +1045,9 @@ void HistoryMessage::createComponents(const CreateConfig &config) { if (savedFrom && savedFrom->isChannel()) { mask |= HistoryMessageSigned::Bit(); } + } else if ((_history->peer->isSelf() || _history->peer->isRepliesChat()) + && !config.authorOriginal.isEmpty()) { + mask |= HistoryMessageSigned::Bit(); } if (config.editDate != TimeId(0)) { mask |= HistoryMessageEdited::Bit(); From 522a71f7dc8480ce0bfc42846c06b32c47800b50 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 19:18:09 +0400 Subject: [PATCH 393/396] Improve auto-delete settings phrase. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/confirm_box.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d5ca2f5b9..d35edf2f8 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1725,6 +1725,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_delete_for_me_hint#one" = "This will delete it just for you."; "lng_delete_for_me_hint#other" = "This will delete them just for you."; "lng_edit_auto_delete_settings" = "Edit Auto-Delete Settings"; +"lng_enable_auto_delete" = "Enable Auto-Delete"; "lng_selected_unsend_about_user_one" = "You can also delete the message you sent from {user}'s inbox by checking \"Unsend my messages\"."; "lng_selected_unsend_about_user#one" = "You can also delete the {count} message you sent from {user}'s inbox by checking \"Unsend my messages\"."; "lng_selected_unsend_about_user#other" = "You can also delete the {count} messages you sent from {user}'s inbox by checking \"Unsend my messages\"."; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index cc10c6bf6..9c90309a4 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -675,7 +675,9 @@ void DeleteMessagesBox::prepare() { _wipeHistoryPeer->updateFull(); _autoDeleteSettings.create( this, - tr::lng_edit_auto_delete_settings(tr::now), + (_wipeHistoryPeer->messagesTTL() + ? tr::lng_edit_auto_delete_settings(tr::now) + : tr::lng_enable_auto_delete(tr::now)), st::boxLinkButton); _autoDeleteSettings->setClickedCallback([=] { getDelegate()->show( From 4875f0b003af9215876a57c13d42554cade9dcd9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 20:44:37 +0400 Subject: [PATCH 394/396] Improve auto-delete service messages. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/history/history_service.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d35edf2f8..4cdcaa5ad 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1120,8 +1120,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_changed_title_channel" = "Channel name was changed to «{title}»"; "lng_action_created_chat" = "{from} created group «{title}»"; "lng_action_ttl_changed" = "{from} has set messages to auto-delete in {duration}"; +"lng_action_ttl_changed_you" = "You set messages to auto-delete in {duration}"; "lng_action_ttl_changed_channel" = "New messages will auto-delete in {duration}"; "lng_action_ttl_removed" = "{from} has set messages not to auto-delete"; +"lng_action_ttl_removed_you" = "You disabled the auto-delete timer"; "lng_action_ttl_removed_channel" = "New messages will not auto-delete"; "lng_action_created_channel" = "Channel created"; "lng_action_pinned_message" = "{from} pinned «{text}»"; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 2261bac1e..98876178e 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -385,6 +385,12 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { } else { result.text = tr::lng_action_ttl_changed_channel(tr::now, lt_duration, duration); } + } else if (_from->isSelf()) { + if (!period) { + result.text = tr::lng_action_ttl_removed_you(tr::now); + } else { + result.text = tr::lng_action_ttl_changed_you(tr::now, lt_duration, duration); + } } else { result.links.push_back(fromLink()); if (!period) { From 2054b73c3cb50750e6a4e0b6b27b3a9979a5ee56 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 20:46:35 +0400 Subject: [PATCH 395/396] Apply date edition in service messages. --- Telegram/SourceFiles/history/history_item.cpp | 8 ++++++++ Telegram/SourceFiles/history/history_item.h | 1 + Telegram/SourceFiles/history/history_service.cpp | 1 + 3 files changed, 10 insertions(+) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 1498221ec..7c35d37ef 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -195,6 +195,14 @@ TimeId HistoryItem::NewMessageDate(TimeId scheduled) { return scheduled ? scheduled : base::unixtime::now(); } +void HistoryItem::applyServiceDateEdition(const MTPDmessageService &data) { + const auto date = data.vdate().v; + if (_date == date) { + return; + } + _date = date; +} + void HistoryItem::finishEdition(int oldKeyboardTop) { _history->owner().requestItemViewRefresh(this); invalidateChatListEntry(); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 9ff851a6d..960bb0f1d 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -416,6 +416,7 @@ protected: virtual void markMediaAsReadHook() { } + void applyServiceDateEdition(const MTPDmessageService &data); void finishEdition(int oldKeyboardTop); void finishEditionToEmpty(); diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 98876178e..15bced85c 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -1045,6 +1045,7 @@ void HistoryService::applyEdition(const MTPDmessageService &message) { UpdateComponents(0); createFromMtp(message); + applyServiceDateEdition(message); if (message.vaction().type() == mtpc_messageActionHistoryClear) { removeMedia(); From 740ffb3c6426d62ac1a54e68d5a13f91479baf9a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Feb 2021 19:39:30 +0400 Subject: [PATCH 396/396] Version 2.6. - Set messages to auto-delete for everyone 24 hours or 7 days after sending. - Control auto-delete settings in any of your chats, as well as in groups and channels where you are an admin. - To enable auto-delete, right click on the chat in the chat list > Clear History > Enable Auto-Delete. - Create invite links that work for a limited time or a limited number of uses. - See which users joined using your, or your admins', invite links. - Turn any invite link into a QR code users can scan with their phone cameras. - To manage invite links, click ... > Manage Group/Channel > Invite Links. - Convert groups that have reached 200,000 members into unlimited Broadcast Groups. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 12 ++++++------ changelog.txt | 11 +++++++++++ 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 59548d219..3816c8599 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="2.5.9.1" /> + Version="2.6.0.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 8b2cd0dd5..5c6f539bb 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,9,1 - PRODUCTVERSION 2,5,9,1 + FILEVERSION 2,6,0,0 + PRODUCTVERSION 2,6,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.5.9.1" + VALUE "FileVersion", "2.6.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.9.1" + VALUE "ProductVersion", "2.6.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 74007b791..7f933960f 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,5,9,1 - PRODUCTVERSION 2,5,9,1 + FILEVERSION 2,6,0,0 + PRODUCTVERSION 2,6,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.5.9.1" + VALUE "FileVersion", "2.6.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.5.9.1" + VALUE "ProductVersion", "2.6.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 16b953451..c244d62f8 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/const_string.h" -#define TDESKTOP_REQUESTED_ALPHA_VERSION (2005009001ULL) +#define TDESKTOP_REQUESTED_ALPHA_VERSION (0ULL) #ifdef TDESKTOP_ALLOW_CLOSED_ALPHA #define TDESKTOP_ALPHA_VERSION TDESKTOP_REQUESTED_ALPHA_VERSION @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 2005009; -constexpr auto AppVersionStr = "2.5.9"; +constexpr auto AppVersion = 2006000; +constexpr auto AppVersionStr = "2.6"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 7d949483b..73aab998c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 2005009 -AppVersionStrMajor 2.5 -AppVersionStrSmall 2.5.9 -AppVersionStr 2.5.9 +AppVersion 2006000 +AppVersionStrMajor 2.6 +AppVersionStrSmall 2.6 +AppVersionStr 2.6.0 BetaChannel 0 -AlphaVersion 2005009001 -AppVersionOriginal 2.5.9.1 +AlphaVersion 0 +AppVersionOriginal 2.6 diff --git a/changelog.txt b/changelog.txt index 4122791cb..8e0d7ba4b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,14 @@ +2.6 (23.02.21) + +- Set messages to auto-delete for everyone 24 hours or 7 days after sending. +- Control auto-delete settings in any of your chats, as well as in groups and channels where you are an admin. +- To enable auto-delete, right click on the chat in the chat list > Clear History > Enable Auto-Delete. +- Create invite links that work for a limited time or a limited number of uses. +- See which users joined using your, or your admins', invite links. +- Turn any invite link into a QR code users can scan with their phone cameras. +- To manage invite links, click ... > Manage Group/Channel > Invite Links. +- Convert groups that have reached 200,000 members into unlimited Broadcast Groups. + 2.5.9 (17.02.21) - Add 'Invite via Link' button to Add Members box.