From 1cb1f1cbc12b19ecc4b3ec3a41743c11d8115079 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 18 Jun 2021 16:11:32 +0400 Subject: [PATCH] Add a hint to turn on the camera. --- .../Resources/icons/calls/video_tooltip.png | Bin 0 -> 360 bytes .../icons/calls/video_tooltip@2x.png | Bin 0 -> 622 bytes .../icons/calls/video_tooltip@3x.png | Bin 0 -> 967 bytes Telegram/SourceFiles/calls/calls.style | 13 +- .../calls/group/calls_group_call.cpp | 83 ++++--- .../calls/group/calls_group_call.h | 36 ++- .../calls/group/calls_group_panel.cpp | 207 +++++++++++++++--- .../calls/group/calls_group_panel.h | 31 ++- Telegram/lib_ui | 2 +- 9 files changed, 293 insertions(+), 79 deletions(-) create mode 100644 Telegram/Resources/icons/calls/video_tooltip.png create mode 100644 Telegram/Resources/icons/calls/video_tooltip@2x.png create mode 100644 Telegram/Resources/icons/calls/video_tooltip@3x.png diff --git a/Telegram/Resources/icons/calls/video_tooltip.png b/Telegram/Resources/icons/calls/video_tooltip.png new file mode 100644 index 0000000000000000000000000000000000000000..60ecf2cd7d8d8914d15428dd5fbdcec24d028197 GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K$eE_u2*hFAzL zo%E2mMS-XF;kE>J-R)iqvKa@~`fO^PmLc>wcDH3f(ZMP*d=C>N0FIGAt(b~|{$(WbCfO&IGlrP7`THYOL7w$Bv=_&8L zsvEFw*An|^)%xfa?I#XoF*3jXy|1Lu>d5iy9w+6gA}f5OZp5{cEi)fF(iWX zZJ1$Svw=W~-}iP84cYUKUh^h0b#ien@SQ3DL{#%cyv8M08-*KVscwmu6Qli8r1&)^L%O-E?_HSOb z>c-cq)c)hiylXt1R2F@e?ELYx=wa;j!s&@T#|}LgU3Mj1{n&!Pb?4`wU*27QjbHwU zm&>;N-3=<975Bdze^|=UcYr*v(J>dti%Bt5(9DtYbOsmW(p}?yf8>3wwVVM&I3F z-Qb(NWV@C^U-I4XKhG=ISxn}bD|4WuMj+uqcU{VVh0euQ25c+t!|q-*Ve!$BBnLIk64jFBga`aXau^sKVdr zw%L(ulNP&X=bh?Yq|sBkB}4ia*TfjF&N&e!f*Mh~boE?jEL|zGc#ji{5~I`I3)7aZ z)VQv38z_EMfI(?}yP!*jZJ*}NJ#ii$EJ`2G6&*NUD7Y|oshE<|wEHJGRW7RQEuGz& sQM2={@byojOm~E?_kXC9sAc`b*lxdt-SD&fEKpo~y85}Sb4q9e0HNOatpET3 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/video_tooltip@3x.png b/Telegram/Resources/icons/calls/video_tooltip@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..13a66723a0cd5dd46919060e31be1f91c8daaaad GIT binary patch literal 967 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyy~ySip>6gB0F2&*1?oH}rII49Q@9 z8)>;t$xy)b@DUwNp%?}WMngfBBL`F?-W?Quz%!wNMbN|Jh{qCzi9!NQpT1<*8q4*6 zU*@PA7_?gacIf+?sk=|jRcg8Lz-!GPFP%^F58|2T%N=~rQB%cIx1YqjoitkjS8D@_QdE-51+|5Jy2(9T=?p%i%vhCbT7|5OM6AP zS;p(^>DkZO_k^v!__ZpxJ2H~rYC$*ilV5M#6z2L}zL>Fuo4)w- z&pYCaH_DbAZd2y8W(wGI|9!8*qOb;DccC;UiCY%caVosgeD6}!1lrGcFF0C$V@rvu zE#n%2`9i0!dip19zNzsse|vP4-t!aM)`I=z3qAh$~-f_&geJ&f`TL?XAjFFuY0*mGMf4o8lSLx3Vh;JanhilniG$|R#MC{xuI+S zwV7FxqfL1e%SWzEv)K>zmnCh~_ data; }; +struct GroupCall::VideoTrack { + VideoTrack(bool paused, bool requireARGB32, not_null peer); + + Webrtc::VideoTrack track; + rpl::variable trackSize; + not_null peer; + rpl::lifetime lifetime; + Group::VideoQuality quality = Group::VideoQuality(); + bool shown = false; +}; + +GroupCall::VideoTrack::VideoTrack( + bool paused, + bool requireARGB32, + not_null peer) +: track((paused + ? Webrtc::VideoState::Paused + : Webrtc::VideoState::Active), + requireARGB32) +, peer(peer) { +} + [[nodiscard]] bool IsGroupCallAdmin( not_null peer, not_null participantPeer) { @@ -451,6 +473,21 @@ void GroupCall::MediaChannelDescriptionsTask::cancel() { } } +not_null GroupCall::TrackPeer( + const std::unique_ptr &track) { + return track->peer; +} + +not_null GroupCall::TrackPointer( + const std::unique_ptr &track) { + return &track->track; +} + +rpl::producer GroupCall::TrackSizeValue( + const std::unique_ptr &track) { + return track->trackSize.value(); +} + GroupCall::GroupCall( not_null delegate, Group::JoinInfo info, @@ -1064,43 +1101,39 @@ void GroupCall::markEndpointActive( if (active) { const auto i = _activeVideoTracks.emplace( endpoint, - VideoTrack{ - .track = std::make_unique( - (paused - ? Webrtc::VideoState::Paused - : Webrtc::VideoState::Active), - _requireARGB32), - .peer = endpoint.peer, - }).first; - const auto track = i->second.track.get(); + std::make_unique( + paused, + _requireARGB32, + endpoint.peer)).first; + const auto track = &i->second->track; track->renderNextFrame( ) | rpl::start_with_next([=] { - auto &activeTrack = _activeVideoTracks[endpoint]; + const auto activeTrack = _activeVideoTracks[endpoint].get(); const auto size = track->frameSize(); if (size.isEmpty()) { track->markFrameShown(); - } else if (!activeTrack.shown) { - activeTrack.shown = true; + } else if (!activeTrack->shown) { + activeTrack->shown = true; markTrackShown(endpoint, true); } - activeTrack.trackSize = size; - }, i->second.lifetime); + activeTrack->trackSize = size; + }, i->second->lifetime); const auto size = track->frameSize(); - i->second.trackSize = size; + i->second->trackSize = size; if (!size.isEmpty() || paused) { - i->second.shown = true; + i->second->shown = true; shown = true; } else { track->stateValue( ) | rpl::filter([=](Webrtc::VideoState state) { return (state == Webrtc::VideoState::Paused) - && !_activeVideoTracks[endpoint].shown; + && !_activeVideoTracks[endpoint]->shown; }) | rpl::start_with_next([=] { - _activeVideoTracks[endpoint].shown = true; + _activeVideoTracks[endpoint]->shown = true; markTrackShown(endpoint, true); - }, i->second.lifetime); + }, i->second->lifetime); } addVideoOutput(i->first.id, { track->sink() }); } else { @@ -1144,7 +1177,7 @@ void GroupCall::markTrackPaused(const VideoEndpoint &endpoint, bool paused) { const auto i = _activeVideoTracks.find(endpoint); Assert(i != end(_activeVideoTracks)); - i->second.track->setState(paused + i->second->track.setState(paused ? Webrtc::VideoState::Paused : Webrtc::VideoState::Active); } @@ -2420,13 +2453,13 @@ void GroupCall::updateRequestedVideoChannels() { .ssrcGroups = (params->camera.endpointId == endpointId ? params->camera.ssrcGroups : params->screen.ssrcGroups), - .minQuality = ((video.quality == Group::VideoQuality::Full + .minQuality = ((video->quality == Group::VideoQuality::Full && endpoint.type == VideoEndpointType::Screen) ? Quality::Full : Quality::Thumbnail), - .maxQuality = ((video.quality == Group::VideoQuality::Full) + .maxQuality = ((video->quality == Group::VideoQuality::Full) ? Quality::Full - : (video.quality == Group::VideoQuality::Medium + : (video->quality == Group::VideoQuality::Medium && endpoint.type != VideoEndpointType::Screen) ? Quality::Medium : Quality::Thumbnail), @@ -2911,10 +2944,10 @@ void GroupCall::requestVideoQuality( return; } const auto i = _activeVideoTracks.find(endpoint); - if (i == end(_activeVideoTracks) || i->second.quality == quality) { + if (i == end(_activeVideoTracks) || i->second->quality == quality) { return; } - i->second.quality = quality; + i->second->quality = quality; updateRequestedVideoChannelsDelayed(); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index e6bcae38e..6a4611050 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -98,6 +98,8 @@ struct VideoEndpoint { std::string id; [[nodiscard]] bool empty() const noexcept { + Expects(id.empty() || peer != nullptr); + return id.empty(); } [[nodiscard]] explicit operator bool() const noexcept { @@ -194,6 +196,15 @@ public: using GlobalShortcutManager = base::GlobalShortcutManager; + struct VideoTrack; + + [[nodiscard]] static not_null TrackPeer( + const std::unique_ptr &track); + [[nodiscard]] static not_null TrackPointer( + const std::unique_ptr &track); + [[nodiscard]] static rpl::producer TrackSizeValue( + const std::unique_ptr &track); + GroupCall( not_null delegate, Group::JoinInfo info, @@ -321,27 +332,8 @@ public: -> rpl::producer { return _videoEndpointLarge.value(); } - - struct VideoTrack { - std::unique_ptr track; - rpl::variable trackSize; - PeerData *peer = nullptr; - rpl::lifetime lifetime; - Group::VideoQuality quality = Group::VideoQuality(); - bool shown = false; - - [[nodiscard]] explicit operator bool() const { - return (track != nullptr); - } - [[nodiscard]] bool operator==(const VideoTrack &other) const { - return (track == other.track) && (peer == other.peer); - } - [[nodiscard]] bool operator!=(const VideoTrack &other) const { - return !(*this == other); - } - }; [[nodiscard]] auto activeVideoTracks() const - -> const base::flat_map & { + -> const base::flat_map> & { return _activeVideoTracks; } [[nodiscard]] auto shownVideoTracks() const @@ -625,7 +617,9 @@ private: rpl::event_stream _videoStreamActiveUpdates; rpl::event_stream _videoStreamPausedUpdates; rpl::event_stream _videoStreamShownUpdates; - base::flat_map _activeVideoTracks; + base::flat_map< + VideoEndpoint, + std::unique_ptr> _activeVideoTracks; base::flat_set _shownVideoTracks; rpl::variable _videoEndpointLarge; rpl::variable _videoEndpointPinned = false; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 03b46c3e8..7bd3589b5 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -499,17 +499,26 @@ void Panel::refreshVideoButtons(std::optional overrideWideMode) { &st::groupCallVideoActiveSmall); _video->show(); _video->setClickedCallback([=] { + hideStickedTooltip( + StickedTooltip::Camera, + StickedTooltipHide::Activated); _call->toggleVideo(!_call->isSharingCamera()); }); _video->setColorOverrides( toggleableOverrides(_call->isSharingCameraValue())); _call->isSharingCameraValue( ) | rpl::start_with_next([=](bool sharing) { + if (sharing) { + hideStickedTooltip( + StickedTooltip::Camera, + StickedTooltipHide::Activated); + } _video->setProgress(sharing ? 1. : 0.); }, _video->lifetime()); _call->mutedValue( ) | rpl::start_with_next([=] { updateButtonsGeometry(); + showStickedTooltip(); }, _video->lifetime()); } if (!_screenShare) { @@ -536,6 +545,45 @@ void Panel::refreshVideoButtons(std::optional overrideWideMode) { updateButtonsGeometry(); } +void Panel::hideStickedTooltip(StickedTooltipHide hide) { + if (!_stickedTooltipClose || !_niceTooltipControl) { + return; + } + if (_niceTooltipControl.data() == _video.data()) { + hideStickedTooltip(StickedTooltip::Camera, hide); + } else if (_niceTooltipControl.data() == _mute->outer().get()) { + hideStickedTooltip(StickedTooltip::Microphone, hide); + } +} + +void Panel::hideStickedTooltip( + StickedTooltip type, + StickedTooltipHide hide) { + if (hide != StickedTooltipHide::Unavailable) { + _stickedTooltipsShown |= type; + if (hide == StickedTooltipHide::Discarded) { + // #TODO calls save to settings. + } + } + const auto control = (type == StickedTooltip::Camera) + ? _video.data() + : (type == StickedTooltip::Microphone) + ? _mute->outer().get() + : nullptr; + if (_niceTooltipControl.data() == control) { + hideNiceTooltip(); + } +} + +void Panel::hideNiceTooltip() { + if (!_niceTooltip) { + return; + } + _stickedTooltipClose = nullptr; + _niceTooltip.release()->toggleAnimated(false); + _niceTooltipControl = nullptr; +} + void Panel::initShareAction() { const auto showBoxCallback = [=](object_ptr next) { _layerBg->showBox(std::move(next)); @@ -843,9 +891,9 @@ void Panel::raiseControls() { void Panel::setupVideo(not_null viewport) { const auto setupTile = [=]( const VideoEndpoint &endpoint, - const GroupCall::VideoTrack &track) { + const std::unique_ptr &track) { using namespace rpl::mappers; - const auto row = _members->lookupRow(track.peer); + const auto row = _members->lookupRow(GroupCall::TrackPeer(track)); Assert(row != nullptr); auto pinned = rpl::combine( _call->videoEndpointLargeValue(), @@ -853,8 +901,8 @@ void Panel::setupVideo(not_null viewport) { ) | rpl::map(_1 == endpoint && _2); viewport->add( endpoint, - VideoTileTrack{ track.track.get(), row }, - track.trackSize.value(), + VideoTileTrack{ GroupCall::TrackPointer(track), row }, + GroupCall::TrackSizeValue(track), std::move(pinned)); }; for (const auto &[endpoint, track] : _call->activeVideoTracks()) { @@ -908,18 +956,24 @@ void Panel::toggleWideControls(bool shown) { } _showWideControls = shown; crl::on_main(widget(), [=] { - if (_wideControlsShown == _showWideControls) { - return; - } - _wideControlsShown = _showWideControls; - _wideControlsAnimation.start( - [=] { updateButtonsGeometry(); }, - _wideControlsShown ? 0. : 1., - _wideControlsShown ? 1. : 0., - st::slideWrapDuration); + updateWideControlsVisibility(); }); } +void Panel::updateWideControlsVisibility() { + const auto shown = _showWideControls + || (_stickedTooltipClose != nullptr); + if (_wideControlsShown == shown) { + return; + } + _wideControlsShown = shown; + _wideControlsAnimation.start( + [=] { updateButtonsGeometry(); }, + _wideControlsShown ? 0. : 1., + _wideControlsShown ? 1. : 0., + st::slideWrapDuration); +} + void Panel::subscribeToChanges(not_null real) { const auto validateRecordingMark = [=](bool recording) { if (!recording && _recordingMark) { @@ -988,6 +1042,7 @@ void Panel::subscribeToChanges(not_null real) { _call->isSharingCameraValue() ) | rpl::start_with_next([=] { refreshVideoButtons(); + showStickedTooltip(); }, widget()->lifetime()); rpl::combine( @@ -1373,6 +1428,7 @@ bool Panel::updateMode() { updateButtonsStyles(); refreshControlsBackground(); updateControlsGeometry(); + showStickedTooltip(); return true; } @@ -1557,8 +1613,12 @@ void Panel::trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime) { } void Panel::trackControlOver(not_null control, bool over) { - if (_niceTooltip) { - _niceTooltip.release()->toggleAnimated(false); + if (_stickedTooltipClose) { + if (!over) { + return; + } + } else { + hideNiceTooltip(); } if (over) { Ui::Integration::Instance().registerLeaveSubscription(control); @@ -1569,7 +1629,37 @@ void Panel::trackControlOver(not_null control, bool over) { toggleWideControls(over); } -void Panel::showNiceTooltip(not_null control) { +void Panel::showStickedTooltip() { + static const auto kHasCamera = !Webrtc::GetVideoInputList().empty(); + if (!(_stickedTooltipsShown & StickedTooltip::Camera) + && (_mode.current() == PanelMode::Wide) + && _video + && _call->videoIsWorking() + && !_call->mutedByAdmin() + && kHasCamera) { // Don't recount this every time for now. + showNiceTooltip(_video, NiceTooltipType::Sticked); + return; + } + hideStickedTooltip( + StickedTooltip::Camera, + StickedTooltipHide::Unavailable); + + if (!(_stickedTooltipsShown & StickedTooltip::Microphone) + && (_mode.current() == PanelMode::Wide) + && _mute + && !_call->mutedByAdmin() + && false) { // Check if there is incoming sound. + showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked); + return; + } + hideStickedTooltip( + StickedTooltip::Microphone, + StickedTooltipHide::Unavailable); +} + +void Panel::showNiceTooltip( + not_null control, + NiceTooltipType type) { auto text = [&]() -> rpl::producer { if (control == _screenShare.data()) { if (_call->mutedByAdmin()) { @@ -1597,38 +1687,95 @@ void Panel::showNiceTooltip(not_null control) { }(); if (!text || _wideControlsAnimation.animating() - || !_wideControlsShown) { + || !_wideControlsShown + || _stickedTooltipClose) { return; } + const auto inner = [&]() -> Ui::RpWidget* { + const auto normal = (type == NiceTooltipType::Normal); + auto container = normal + ? nullptr + : Ui::CreateChild(widget().get()); + const auto label = Ui::CreateChild( + (normal ? widget().get() : container), + std::move(text), + st::groupCallNiceTooltipLabel); + if (normal) { + return label; + } + const auto button = Ui::CreateChild( + container, + st::groupCallStickedTooltipClose); + rpl::combine( + label->sizeValue(), + button->sizeValue() + ) | rpl::start_with_next([=](QSize text, QSize close) { + const auto height = std::max(text.height(), close.height()); + container->resize(text.width() + close.width(), height); + label->move(0, (height - text.height()) / 2); + button->move(text.width(), (height - close.height()) / 2); + }, container->lifetime()); + button->setClickedCallback([=] { + hideStickedTooltip(StickedTooltipHide::Discarded); + }); + _stickedTooltipClose = button; + updateWideControlsVisibility(); + return container; + }(); _niceTooltip.create( widget().get(), - object_ptr( - widget().get(), - std::move(text), - st::groupCallNiceTooltipLabel), - st::groupCallNiceTooltip); + object_ptr::fromRaw(inner), + (type == NiceTooltipType::Sticked + ? st::groupCallStickedTooltip + : st::groupCallNiceTooltip)); const auto tooltip = _niceTooltip.data(); const auto weak = QPointer(tooltip); const auto destroy = [=] { delete weak.data(); }; - tooltip->setAttribute(Qt::WA_TransparentForMouseEvents); + if (type != NiceTooltipType::Sticked) { + tooltip->setAttribute(Qt::WA_TransparentForMouseEvents); + } tooltip->setHiddenCallback(destroy); base::qt_signal_producer( control.get(), &QObject::destroyed ) | rpl::start_with_next(destroy, tooltip->lifetime()); - const auto geometry = control->geometry(); + _niceTooltipControl = control; + updateTooltipGeometry(); + tooltip->toggleAnimated(true); +} + +void Panel::updateTooltipGeometry() { + if (!_niceTooltip) { + return; + } else if (!_niceTooltipControl) { + hideNiceTooltip(); + return; + } + const auto geometry = _niceTooltipControl->geometry(); + const auto weak = QPointer(_niceTooltip); const auto countPosition = [=](QSize size) { const auto strong = weak.data(); - if (!strong) { - return QPoint(); - } + const auto wide = (_mode.current() == PanelMode::Wide); const auto top = geometry.y() - - st::groupCallNiceTooltipTop + - (wide ? st::groupCallNiceTooltipTop : 0) - size.height(); const auto middle = geometry.center().x(); + if (!strong) { + return QPoint(); + } else if (!wide) { + return QPoint( + std::max( + std::min( + middle - size.width() / 2, + (widget()->width() + - st::groupCallMembersMargin.right() + - size.width())), + st::groupCallMembersMargin.left()), + top); + } const auto back = _controlsBackgroundWide.data(); if (size.width() >= _viewport->widget()->width()) { return QPoint(_viewport->widget()->x(), top); @@ -1645,8 +1792,7 @@ void Panel::showNiceTooltip(not_null control) { return QPoint(middle - size.width() / 2, top); } }; - tooltip->pointAt(geometry, RectPart::Top, countPosition); - tooltip->toggleAnimated(true); + _niceTooltip->pointAt(geometry, RectPart::Top, countPosition); } void Panel::trackControls(bool track) { @@ -1832,6 +1978,7 @@ void Panel::updateButtonsGeometry() { width, st::groupCallMembersBottomSkip); } + updateTooltipGeometry(); } bool Panel::videoButtonInNarrowMode() const { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 4383b510c..4e2a7cc49 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -84,6 +84,24 @@ private: using State = GroupCall::State; struct ControlsBackgroundNarrow; + enum class NiceTooltipType { + Normal, + Sticked, + }; + enum class StickedTooltip { + Camera = 0x01, + Microphone = 0x02, + }; + friend constexpr inline bool is_flag_type(StickedTooltip) { + return true; + }; + using StickedTooltips = base::flags; + enum class StickedTooltipHide { + Unavailable, + Activated, + Discarded, + }; + std::unique_ptr createWindow(); [[nodiscard]] not_null widget() const; @@ -111,11 +129,18 @@ private: void trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime); void trackControlOver(not_null control, bool over); - void showNiceTooltip(not_null control); + void showNiceTooltip( + not_null control, + NiceTooltipType type = NiceTooltipType::Normal); + void showStickedTooltip(); + void hideStickedTooltip(StickedTooltipHide hide); + void hideStickedTooltip(StickedTooltip type, StickedTooltipHide hide); + void hideNiceTooltip(); bool updateMode(); void updateControlsGeometry(); void updateButtonsGeometry(); + void updateTooltipGeometry(); void updateButtonsStyles(); void updateMembersGeometry(); void refreshControlsBackground(); @@ -127,6 +152,7 @@ private: std::optional overrideWideMode = std::nullopt); void refreshTopButton(); void toggleWideControls(bool shown); + void updateWideControlsVisibility(); [[nodiscard]] bool videoButtonInNarrowMode() const; void endCall(); @@ -202,6 +228,9 @@ private: std::unique_ptr _mute; object_ptr _hangup; object_ptr _niceTooltip = { nullptr }; + QPointer _stickedTooltipClose; + QPointer _niceTooltipControl; + StickedTooltips _stickedTooltipsShown; Fn _callShareLinkCallback; const std::unique_ptr _toasts; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index d8abc6024..825ef11f1 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit d8abc60245236e388206e2021bfeb58e83e504fc +Subproject commit 825ef11f1a5c6b00cd571c24525c84dca431eaa2