diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4c8916d70..81d54f23d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -625,6 +625,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_power_ui" = "Interface animations"; "lng_settings_power_auto" = "Save Power on Low Battery"; "lng_settings_power_auto_about" = "Automatically disable all animations when your laptop is in a battery saving mode."; +"lng_settings_power_turn_off" = "Please turn off Save Power on Low Battery to change these settings."; "lng_settings_cloud_password_on" = "On"; "lng_settings_cloud_password_off" = "Off"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index a32758489..394c43c79 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -42,6 +42,7 @@ namespace { constexpr auto kSlowmodeValues = 7; constexpr auto kSuggestGigagroupThreshold = 199000; +constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000); [[nodiscard]] auto Dependencies(PowerSaving::Flags) -> std::vector> { @@ -453,8 +454,38 @@ template struct State final { std::map> checkViews; rpl::event_stream<> anyChanges; + rpl::variable forceDisabledMessage; + rpl::variable forceDisabled; + base::flat_map realCheckedValues; + base::weak_ptr toast; }; const auto state = container->lifetime().make_state(); + if (descriptor.forceDisabledMessage) { + state->forceDisabledMessage = std::move( + descriptor.forceDisabledMessage); + state->forceDisabled = state->forceDisabledMessage.value( + ) | rpl::map([=](const QString &message) { + return !message.isEmpty(); + }); + + state->forceDisabled.value( + ) | rpl::start_with_next([=](bool disabled) { + if (disabled) { + for (const auto &[flags, checkView] : state->checkViews) { + checkView->setChecked(false, anim::type::normal); + } + } else { + for (const auto &[flags, checkView] : state->checkViews) { + if (const auto i = state->realCheckedValues.find(flags) + ; i != state->realCheckedValues.end()) { + checkView->setChecked( + i->second, + anim::type::normal); + } + } + } + }, container->lifetime()); + } const auto &st = descriptor.st ? *descriptor.st : st::rightsButton; const auto value = [=] { @@ -492,7 +523,9 @@ template const auto locked = (lockedIt != end(descriptor.disabledMessages)) ? std::make_optional(lockedIt->second) : std::nullopt; - const auto toggled = ((checked & flags) != 0); + const auto realChecked = (checked & flags) != 0; + state->realCheckedValues.emplace(flags, realChecked); + const auto toggled = realChecked && !state->forceDisabled.current(); const auto checkView = [&]() -> not_null { if (isInner) { @@ -559,15 +592,30 @@ template state->checkViews.emplace(flags, checkView); checkView->checkedChanges( ) | rpl::start_with_next([=](bool checked) { - if (locked.has_value()) { - if (checked != toggled) { - Ui::ShowMultilineToast({ + if (checked && state->forceDisabled.current()) { + if (!state->toast) { + state->toast = Ui::ShowMultilineToast({ .parentOverride = container, - .text = { *locked }, + .text = { state->forceDisabledMessage.current() }, + .duration = kForceDisableTooltipDuration, }); + } + checkView->setChecked(false, anim::type::instant); + } else if (locked.has_value()) { + if (checked != toggled) { + if (!state->toast) { + state->toast = Ui::ShowMultilineToast({ + .parentOverride = container, + .text = { *locked }, + .duration = kForceDisableTooltipDuration, + }); + } checkView->setChecked(toggled, anim::type::instant); } } else { + if (!state->forceDisabled.current()) { + state->realCheckedValues[flags] = checked; + } InvokeQueued(container, [=] { applyDependencies(checkView); state->anyChanges.fire({}); @@ -1141,12 +1189,15 @@ ChatAdminRights AdminRightsForOwnershipTransfer( EditFlagsControl CreateEditPowerSaving( QWidget *parent, - PowerSaving::Flags flags) { + PowerSaving::Flags flags, + rpl::producer forceDisabledMessage) { auto widget = object_ptr(parent); + auto descriptor = Settings::PowerSavingLabels(); + descriptor.forceDisabledMessage = std::move(forceDisabledMessage); auto result = CreateEditFlags( widget.data(), flags, - Settings::PowerSavingLabels()); + std::move(descriptor)); result.widget = std::move(widget); return result; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index 0cc1d0ed8..3e18d7b6a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -74,6 +74,7 @@ struct EditFlagsDescriptor { std::vector> labels; base::flat_map disabledMessages; const style::SettingsButton *st = nullptr; + rpl::producer forceDisabledMessage; }; using RestrictionLabel = EditFlagsLabel; @@ -109,5 +110,6 @@ using AdminRightLabel = EditFlagsLabel; [[nodiscard]] auto CreateEditPowerSaving( QWidget *parent, - PowerSaving::Flags flags + PowerSaving::Flags flags, + rpl::producer forceDisabledMessage ) -> EditFlagsControl; diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index 091cdf538..c66805f43 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -499,7 +499,7 @@ void TopBar::initBlobsUnder( using namespace rpl::mappers; auto hideBlobs = rpl::combine( - PowerSaving::Value(PowerSaving::kCalls), + PowerSaving::OnValue(PowerSaving::kCalls), Core::App().appDeactivatedValue(), group->instanceStateValue() ) | rpl::map(_1 || _2 || _3 == GroupCall::InstanceState::Disconnected); diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index 7dd2e8365..10e1123cd 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -235,7 +235,7 @@ Members::Controller::Controller( }, _lifetime); rpl::combine( - PowerSaving::Value(PowerSaving::kCalls), + PowerSaving::OnValue(PowerSaving::kCalls), Core::App().appDeactivatedValue() ) | rpl::start_with_next([=](bool disabled, bool deactivated) { const auto hide = disabled || deactivated; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index fb00ef4a0..b6f6b3cc3 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -14,10 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_channel.h" #include "data/data_download_manager.h" -#include "base/timer.h" +#include "base/battery_saving.h" #include "base/event_filter.h" #include "base/concurrent_timer.h" #include "base/qt_signal_producer.h" +#include "base/timer.h" #include "base/unixtime.h" #include "core/core_settings.h" #include "core/update_checker.h" @@ -78,6 +79,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/effects/spoiler_mess.h" #include "ui/cached_round_corners.h" +#include "ui/power_saving.h" #include "storage/serialize_common.h" #include "storage/storage_domain.h" #include "storage/storage_databases.h" @@ -143,6 +145,7 @@ Application::Application(not_null launcher) , _launcher(launcher) , _private(std::make_unique()) , _platformIntegration(Platform::Integration::Create()) +, _batterySaving(std::make_unique()) , _databases(std::make_unique()) , _animationsManager(std::make_unique()) , _clearEmojiImageLoaderTimer([=] { clearEmojiSourceImages(); }) @@ -279,6 +282,13 @@ void Application::run() { _mediaControlsManager = std::make_unique(); } + rpl::combine( + _batterySaving->value(), + settings().ignoreBatterySavingValue() + ) | rpl::start_with_next([=](bool saving, bool ignore) { + PowerSaving::SetForceAll(saving && !ignore); + }, _lifetime); + style::ShortAnimationPlaying( ) | rpl::start_with_next([=](bool playing) { if (playing) { @@ -409,14 +419,14 @@ void Application::showOpenGLCrashNotification() { const auto enable = [=] { Ui::GL::ForceDisable(false); Ui::GL::CrashCheckFinish(); - Core::App().settings().setDisableOpenGL(false); + settings().setDisableOpenGL(false); Local::writeSettings(); Restart(); }; const auto keepDisabled = [=] { Ui::GL::ForceDisable(true); Ui::GL::CrashCheckFinish(); - Core::App().settings().setDisableOpenGL(true); + settings().setDisableOpenGL(true); Local::writeSettings(); }; _lastActivePrimaryWindow->show(Ui::MakeConfirmBox({ @@ -658,6 +668,10 @@ Settings &Application::settings() { return _private->settings; } +const Settings &Application::settings() const { + return _private->settings; +} + void Application::saveSettingsDelayed(crl::time delay) { if (_saveSettingsTimer) { _saveSettingsTimer->callOnce(delay); @@ -670,7 +684,7 @@ void Application::saveSettings() { bool Application::canReadDefaultDownloadPath(bool always) const { if (KSandbox::isInside() - && (always || Core::App().settings().downloadPath().isEmpty())) { + && (always || settings().downloadPath().isEmpty())) { const auto path = QStandardPaths::writableLocation( QStandardPaths::DownloadLocation); return base::CanReadDirectory(path); @@ -679,7 +693,7 @@ bool Application::canReadDefaultDownloadPath(bool always) const { } bool Application::canSaveFileWithoutAskingForPath() const { - return !Core::App().settings().askDownloadPath(); + return !settings().askDownloadPath(); } MTP::Config &Application::fallbackProductionConfig() const { diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index a69809375..963275e74 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; +namespace base { +class BatterySaving; +} // namespace base + namespace Platform { class Integration; } // namespace Platform @@ -127,15 +131,14 @@ public: Application &operator=(const Application &other) = delete; ~Application(); + void run(); + [[nodiscard]] Launcher &launcher() const { return *_launcher; } [[nodiscard]] Platform::Integration &platformIntegration() const { return *_platformIntegration; } - - void run(); - [[nodiscard]] Ui::Animations::Manager &animationManager() const { return *_animationsManager; } @@ -150,6 +153,9 @@ public: [[nodiscard]] Tray &tray() const { return *_tray; } + [[nodiscard]] base::BatterySaving &batterySaving() const { + return *_batterySaving; + } // Windows interface. bool hasActiveWindow(not_null session) const; @@ -191,6 +197,7 @@ public: void startSettingsAndBackground(); [[nodiscard]] Settings &settings(); + [[nodiscard]] const Settings &settings() const; void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay); void saveSettings(); @@ -373,6 +380,7 @@ private: struct Private; const std::unique_ptr _private; const std::unique_ptr _platformIntegration; + const std::unique_ptr _batterySaving; const std::unique_ptr _databases; const std::unique_ptr _animationsManager; diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 79e560f8e..442102968 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -197,7 +197,8 @@ QByteArray Settings::serialize() const { + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) * 3 - + Serialize::bytearraySize(mediaViewPosition); + + Serialize::bytearraySize(mediaViewPosition) + + sizeof(qint32); auto result = QByteArray(); result.reserve(size); @@ -329,7 +330,8 @@ QByteArray Settings::serialize() const { << qint32(_windowTitleContent.current().hideChatName ? 1 : 0) << qint32(_windowTitleContent.current().hideAccountName ? 1 : 0) << qint32(_windowTitleContent.current().hideTotalUnread ? 1 : 0) - << mediaViewPosition; + << mediaViewPosition + << qint32(_ignoreBatterySaving.current() ? 1 : 0); } return result; } @@ -433,6 +435,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 hideAccountName = _windowTitleContent.current().hideAccountName ? 1 : 0; qint32 hideTotalUnread = _windowTitleContent.current().hideTotalUnread ? 1 : 0; QByteArray mediaViewPosition; + qint32 ignoreBatterySaving = _ignoreBatterySaving.current() ? 1 : 0; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -661,6 +664,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> mediaViewPosition; } + if (!stream.atEnd()) { + stream >> ignoreBatterySaving; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -857,6 +863,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _mediaViewPosition = { .maximized = 2 }; } } + _ignoreBatterySaving = (ignoreBatterySaving == 1); } QString Settings::getSoundPath(const QString &key) const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index b65fd3118..eb3df53fd 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -777,6 +777,15 @@ public: void setMediaViewPosition(const WindowPosition &position) { _mediaViewPosition = position; } + [[nodiscard]] bool ignoreBatterySaving() const { + return _ignoreBatterySaving.current(); + } + [[nodiscard]] rpl::producer ignoreBatterySavingValue() const { + return _ignoreBatterySaving.value(); + } + void setIgnoreBatterySavingValue(bool value) { + _ignoreBatterySaving = value; + } [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static float64 DefaultDialogsWidthRatio(); @@ -900,6 +909,7 @@ private: rpl::event_stream<> _skipTranslationLanguagesChanges; bool _rememberedDeleteMessageOnlyForYou = false; WindowPosition _mediaViewPosition = { .maximized = 2 }; + rpl::variable _ignoreBatterySaving = false; bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream _tabbedReplacedWithInfoValue; // per-window diff --git a/Telegram/SourceFiles/settings/settings_power_saving.cpp b/Telegram/SourceFiles/settings/settings_power_saving.cpp index bd75ebbf7..dd4561aed 100644 --- a/Telegram/SourceFiles/settings/settings_power_saving.cpp +++ b/Telegram/SourceFiles/settings/settings_power_saving.cpp @@ -7,11 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_power_saving.h" +#include "base/battery_saving.h" #include "boxes/peers/edit_peer_permissions_box.h" #include "core/application.h" +#include "core/core_settings.h" #include "lang/lang_keys.h" #include "settings/settings_common.h" #include "ui/layers/generic_box.h" +#include "ui/toasts/common_toasts.h" #include "ui/widgets/buttons.h" #include "ui/power_saving.h" #include "styles/style_menu_icons.h" @@ -19,6 +22,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_settings.h" namespace Settings { +namespace { + +constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000); + +} // namespace void PowerSavingBox(not_null box) { box->setStyle(st::layerBox); @@ -26,40 +34,90 @@ void PowerSavingBox(not_null box) { box->setWidth(st::boxWideWidth); const auto container = box->verticalLayout(); + const auto ignore = Core::App().settings().ignoreBatterySaving(); + const auto batterySaving = Core::App().batterySaving().enabled(); // Force top shadow visibility. box->setPinnedToTopContent( object_ptr(box, st::lineWidth)); - AddSubsectionTitle( + const auto subtitle = AddSubsectionTitle( container, tr::lng_settings_power_subtitle(), st::powerSavingSubtitlePadding); + struct State { + rpl::variable forceDisabledMessage; + }; + const auto state = container->lifetime().make_state(); + state->forceDisabledMessage = (batterySaving.value_or(false) && !ignore) + ? tr::lng_settings_power_turn_off(tr::now) + : QString(); + auto [checkboxes, getResult, changes] = CreateEditPowerSaving( box, - PowerSaving::kAll & ~PowerSaving::Current()); + PowerSaving::kAll & ~PowerSaving::Current(), + state->forceDisabledMessage.value()); + const auto controlsRaw = checkboxes.data(); box->addRow(std::move(checkboxes), {}); auto automatic = (Ui::SettingsButton*)nullptr; - const auto hasBattery = true; - const auto automaticEnabled = true; - if (hasBattery) { + if (batterySaving.has_value()) { AddSkip(container); AddDivider(container); AddSkip(container); - AddButton( + automatic = AddButton( container, tr::lng_settings_power_auto(), st::powerSavingButtonNoIcon - )->toggleOn(rpl::single(automaticEnabled)); + )->toggleOn(rpl::single(!ignore)); AddSkip(container); AddDividerText(container, tr::lng_settings_power_auto_about()); + + state->forceDisabledMessage = rpl::combine( + automatic->toggledValue(), + Core::App().batterySaving().value() + ) | rpl::map([=](bool dontIgnore, bool saving) { + return (saving && dontIgnore) + ? tr::lng_settings_power_turn_off() + : rpl::single(QString()); + }) | rpl::flatten_latest(); + + const auto disabler = Ui::CreateChild(container.get()); + disabler->setClickedCallback([=] { + Ui::ShowMultilineToast({ + .parentOverride = container, + .text = tr::lng_settings_power_turn_off(tr::now), + .duration = kForceDisableTooltipDuration, + }); + }); + disabler->paintRequest() | rpl::start_with_next([=](QRect clip) { + auto color = st::boxBg->c; + color.setAlpha(96); + QPainter(disabler).fillRect(clip, color); + }, disabler->lifetime()); + rpl::combine( + subtitle->geometryValue(), + controlsRaw->geometryValue() + ) | rpl::start_with_next([=](QRect subtitle, QRect controls) { + disabler->setGeometry(subtitle.united(controls)); + }, disabler->lifetime()); + disabler->showOn(state->forceDisabledMessage.value( + ) | rpl::map([=](const QString &value) { + return !value.isEmpty(); + })); } box->addButton(tr::lng_settings_save(), [=, collect = getResult] { - Set(PowerSaving::kAll & ~collect()); + const auto ignore = automatic + ? !automatic->toggled() + : Core::App().settings().ignoreBatterySaving(); + const auto batterySaving = Core::App().batterySaving().enabled(); + if (ignore || !batterySaving.value_or(false)) { + Set(PowerSaving::kAll & ~collect()); + } + Core::App().settings().setIgnoreBatterySavingValue(ignore); Core::App().saveSettingsDelayed(); box->closeBox(); }); diff --git a/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp index ffa961ea2..a88f2074d 100644 --- a/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp +++ b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp @@ -117,7 +117,7 @@ GroupCallUserpics::GroupCallUserpics( }); rpl::combine( - PowerSaving::Value(PowerSaving::kCalls), + PowerSaving::OnValue(PowerSaving::kCalls), std::move(hideBlobs) ) | rpl::start_with_next([=](bool disabled, bool deactivated) { const auto hide = disabled || deactivated; diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index 61b33edd2..64be6e5c2 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -517,7 +517,7 @@ CallMuteButton::CallMuteButton( parent, _st->active.bgSize, rpl::combine( - PowerSaving::Value(PowerSaving::kCalls), + PowerSaving::OnValue(PowerSaving::kCalls), std::move(hideBlobs), _state.value( ) | rpl::map([](const CallMuteButtonState &state) { diff --git a/Telegram/SourceFiles/ui/power_saving.cpp b/Telegram/SourceFiles/ui/power_saving.cpp index 32d8b81fc..a401aca8a 100644 --- a/Telegram/SourceFiles/ui/power_saving.cpp +++ b/Telegram/SourceFiles/ui/power_saving.cpp @@ -12,16 +12,32 @@ namespace { Flags Data/* = {}*/; rpl::event_stream<> Events; +bool AllForced/* = false*/; } // namespace +void Set(Flags flags) { + if (const auto diff = Data ^ flags) { + Data = flags; + if (!AllForced) { + if (diff & kAnimations) { + anim::SetDisabled(On(kAnimations)); + } + Events.fire({}); + } + } +} + Flags Current() { return Data; } -void Set(Flags flags) { - if (const auto diff = Data ^ flags) { - Data = flags; +void SetForceAll(bool force) { + if (AllForced == force) { + return; + } + AllForced = force; + if (const auto diff = Data ^ kAll) { if (diff & kAnimations) { anim::SetDisabled(On(kAnimations)); } @@ -29,18 +45,12 @@ void Set(Flags flags) { } } -rpl::producer Changes() { - return Events.events() | rpl::map(Current); +bool ForceAll() { + return AllForced; } -rpl::producer Value() { - return rpl::single(Current()) | rpl::then(Changes()); -} - -rpl::producer Value(Flag flag) { - return Value() | rpl::map([=](Flags flags) { - return (flags & flag) != 0; - }) | rpl::distinct_until_changed(); +rpl::producer<> Changes() { + return Events.events(); } } // namespace PowerSaving diff --git a/Telegram/SourceFiles/ui/power_saving.h b/Telegram/SourceFiles/ui/power_saving.h index 003171cbe..c3b065375 100644 --- a/Telegram/SourceFiles/ui/power_saving.h +++ b/Telegram/SourceFiles/ui/power_saving.h @@ -25,14 +25,21 @@ enum Flag : uint32 { inline constexpr bool is_flag_type(Flag) { return true; } using Flags = base::flags; -[[nodiscard]] Flags Current(); -[[nodiscard]] rpl::producer Changes(); -[[nodiscard]] rpl::producer Value(); -[[nodiscard]] rpl::producer Value(Flag flag); void Set(Flags flags); +[[nodiscard]] Flags Current(); + +void SetForceAll(bool force); +[[nodiscard]] bool ForceAll(); + +[[nodiscard]] rpl::producer<> Changes(); [[nodiscard]] inline bool On(Flag flag) { - return Current() & flag; + return ForceAll() || (Current() & flag); +} +[[nodiscard]] inline rpl::producer OnValue(Flag flag) { + return rpl::single(On(flag)) | rpl::then(Changes() | rpl::map([=] { + return On(flag); + })) | rpl::distinct_until_changed(); } } // namespace PowerSaving diff --git a/Telegram/lib_base b/Telegram/lib_base index 2e306a724..17ac5644d 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 2e306a7245de70b6e6943b0bc33892bf0d327320 +Subproject commit 17ac5644d1f5cdaeb79b42cdf931b819cf0dbcf3 diff --git a/Telegram/lib_rpl b/Telegram/lib_rpl index bc7b4cae2..8b1015d1b 160000 --- a/Telegram/lib_rpl +++ b/Telegram/lib_rpl @@ -1 +1 @@ -Subproject commit bc7b4cae2ea69c67a7b501289ffba7c8eddc5d19 +Subproject commit 8b1015d1bd57ef03fcd07a3eeddd3f5a9b688ade