diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 237c2bb0b..5cf7685a9 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1173,6 +1173,8 @@ PRIVATE
settings/settings_experimental.h
settings/settings_folders.cpp
settings/settings_folders.h
+ settings/settings_global_ttl.cpp
+ settings/settings_global_ttl.h
settings/settings_information.cpp
settings/settings_information.h
settings/settings_intro.cpp
diff --git a/Telegram/Resources/animations/ttl.tgs b/Telegram/Resources/animations/ttl.tgs
new file mode 100644
index 000000000..14c228a7f
Binary files /dev/null and b/Telegram/Resources/animations/ttl.tgs differ
diff --git a/Telegram/Resources/icons/settings/ttl.png b/Telegram/Resources/icons/settings/ttl.png
new file mode 100644
index 000000000..a8263fe2e
Binary files /dev/null and b/Telegram/Resources/icons/settings/ttl.png differ
diff --git a/Telegram/Resources/icons/settings/ttl@2x.png b/Telegram/Resources/icons/settings/ttl@2x.png
new file mode 100644
index 000000000..01a63ec35
Binary files /dev/null and b/Telegram/Resources/icons/settings/ttl@2x.png differ
diff --git a/Telegram/Resources/icons/settings/ttl@3x.png b/Telegram/Resources/icons/settings/ttl@3x.png
new file mode 100644
index 000000000..a80936938
Binary files /dev/null and b/Telegram/Resources/icons/settings/ttl@3x.png differ
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 4197f473f..5fcb2dd18 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -8,5 +8,6 @@
../../animations/cloud_password/password_input.tgs
../../animations/cloud_password/hint.tgs
../../animations/cloud_password/email.tgs
+ ../../animations/ttl.tgs
diff --git a/Telegram/SourceFiles/menu/menu_ttl.cpp b/Telegram/SourceFiles/menu/menu_ttl.cpp
index 0b2eb509d..ca27d0f63 100644
--- a/Telegram/SourceFiles/menu/menu_ttl.cpp
+++ b/Telegram/SourceFiles/menu/menu_ttl.cpp
@@ -158,10 +158,12 @@ void TTLBoxOld(
} // namespace
void TTLBox(not_null box, Args args) {
- box->addRow(object_ptr(
- box,
- std::move(args.about),
- st::boxLabel));
+ if (args.about) {
+ box->addRow(object_ptr(
+ box,
+ std::move(args.about),
+ st::boxLabel));
+ }
const auto ttls = std::vector{
(86400 * 1),
@@ -188,17 +190,14 @@ void TTLBox(not_null box, Args args) {
const auto pickerTtl = TimePickerBox(box, ttls, phrases, args.startTtl);
Ui::ConfirmBox(box, {
- .confirmed = [=] {
- args.callback(pickerTtl());
- box->getDelegate()->hideLayer();
- },
+ .confirmed = [=] { args.callback(pickerTtl()); },
.confirmText = tr::lng_settings_save(),
.cancelText = tr::lng_cancel(),
});
box->setTitle(tr::lng_manage_messages_ttl_title());
- if (args.startTtl) {
+ if (args.startTtl && !args.hideDisable) {
box->addLeftButton(tr::lng_manage_messages_ttl_disable(), [=] {
args.callback(0);
box->getDelegate()->hideLayer();
diff --git a/Telegram/SourceFiles/menu/menu_ttl.h b/Telegram/SourceFiles/menu/menu_ttl.h
index e38ec8f0c..8d92453ec 100644
--- a/Telegram/SourceFiles/menu/menu_ttl.h
+++ b/Telegram/SourceFiles/menu/menu_ttl.h
@@ -23,6 +23,7 @@ struct Args {
TimeId startTtl;
rpl::producer about;
Fn callback;
+ bool hideDisable = false;
};
void TTLBox(not_null box, Args args);
diff --git a/Telegram/SourceFiles/menu/menu_ttl_validator.cpp b/Telegram/SourceFiles/menu/menu_ttl_validator.cpp
index cd1e70e75..8fc263115 100644
--- a/Telegram/SourceFiles/menu/menu_ttl_validator.cpp
+++ b/Telegram/SourceFiles/menu/menu_ttl_validator.cpp
@@ -92,6 +92,7 @@ Args TTLValidator::createArgs() const {
}).fail([=] {
state->savingRequestId = 0;
}).send();
+ show->hideLayer();
};
auto about = peer->isUser()
? tr::lng_ttl_edit_about(lt_user, rpl::single(peer->shortName()))
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index c0345e362..2fa8252ce 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -93,6 +93,7 @@ settingsIconPin: icon {{ "settings/pin", settingsIconFg }};
settingsIconDownload: icon {{ "settings/download", settingsIconFg }};
settingsIconMention: icon {{ "settings/mention", settingsIconFg }};
settingsIconTopics: icon {{ "settings/topics", settingsIconFg }};
+settingsIconTTL: icon {{ "settings/ttl", settingsIconFg }};
settingsPremiumIconChannelsOff: icon {{ "settings/premium/channels_off", settingsIconFg }};
settingsPremiumIconDouble: icon {{ "settings/premium/double", settingsIconFg }};
diff --git a/Telegram/SourceFiles/settings/settings_global_ttl.cpp b/Telegram/SourceFiles/settings/settings_global_ttl.cpp
new file mode 100644
index 000000000..aef9a8407
--- /dev/null
+++ b/Telegram/SourceFiles/settings/settings_global_ttl.cpp
@@ -0,0 +1,273 @@
+/*
+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 "settings/settings_global_ttl.h"
+
+#include "api/api_self_destruct.h"
+#include "apiwrap.h"
+#include "lang/lang_keys.h"
+#include "lottie/lottie_icon.h"
+#include "main/main_session.h"
+#include "menu/menu_ttl.h"
+#include "settings/settings_common.h"
+#include "ui/boxes/confirm_box.h"
+#include "ui/text/format_values.h"
+#include "ui/text/text_utilities.h"
+#include "ui/toasts/common_toasts.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/checkbox.h"
+#include "ui/widgets/labels.h"
+#include "ui/wrap/vertical_layout.h"
+#include "window/window_session_controller.h"
+#include "styles/style_boxes.h"
+#include "styles/style_layers.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+void SetupTopContent(
+ not_null parent,
+ rpl::producer<> showFinished) {
+ const auto divider = Ui::CreateChild(parent.get());
+ const auto verticalLayout = parent->add(
+ object_ptr(parent.get()));
+
+ auto icon = CreateLottieIcon(
+ verticalLayout,
+ {
+ .name = u"ttl"_q,
+ .sizeOverride = {
+ st::settingsCloudPasswordIconSize,
+ st::settingsCloudPasswordIconSize,
+ },
+ },
+ st::settingsFilterIconPadding);
+ std::move(
+ showFinished
+ ) | rpl::start_with_next([animate = std::move(icon.animate)] {
+ animate(anim::repeat::loop);
+ }, verticalLayout->lifetime());
+ verticalLayout->add(std::move(icon.widget));
+
+ verticalLayout->geometryValue(
+ ) | rpl::start_with_next([=](const QRect &r) {
+ divider->setGeometry(r);
+ }, divider->lifetime());
+
+}
+
+} // namespace
+
+class GlobalTTL : public Section {
+public:
+ GlobalTTL(
+ QWidget *parent,
+ not_null controller);
+
+ [[nodiscard]] rpl::producer title() override;
+ void setupContent();
+
+ void showFinished() override final;
+
+private:
+ void rebuildButtons(TimeId currentTTL) const;
+ void showSure(TimeId ttl, bool rebuild) const;
+
+ void request(TimeId ttl) const;
+
+ const not_null _controller;
+ const std::shared_ptr _group;
+ const std::shared_ptr _show;
+
+ not_null _buttons;
+
+ rpl::event_stream<> _showFinished;
+ rpl::lifetime _requestLifetime;
+
+};
+
+GlobalTTL::GlobalTTL(
+ QWidget *parent,
+ not_null controller)
+: Section(parent)
+, _controller(controller)
+, _group(std::make_shared(0))
+, _show(std::make_shared(controller))
+, _buttons(Ui::CreateChild(this)) {
+ setupContent();
+}
+
+rpl::producer GlobalTTL::title() {
+ return tr::lng_settings_ttl_title();
+}
+
+void GlobalTTL::request(TimeId ttl) const {
+ _controller->session().api().selfDestruct().updateDefaultHistoryTTL(ttl);
+}
+
+void GlobalTTL::showSure(TimeId ttl, bool rebuild) const {
+ const auto ttlText = Ui::FormatTTLAfter(ttl);
+ const auto confirmed = [=] {
+ if (rebuild) {
+ rebuildButtons(ttl);
+ }
+ _group->setChangedCallback([=](int value) {
+ _group->setChangedCallback(nullptr);
+ Ui::ShowMultilineToast({
+ .parentOverride = _show->toastParent(),
+ .text = tr::lng_settings_ttl_after_toast(
+ tr::now,
+ lt_after_duration,
+ { .text = ttlText },
+ Ui::Text::WithEntities)
+ });
+ _show->hideLayer(); // Don't use close().
+ });
+ request(ttl);
+ };
+ if (_group->value()) {
+ confirmed();
+ return;
+ }
+ _show->showBox(Ui::MakeConfirmBox({
+ .text = tr::lng_settings_ttl_after_sure(
+ lt_after_duration,
+ rpl::single(ttlText)),
+ .confirmed = confirmed,
+ .cancelled = [=](Fn &&close) {
+ _group->setChangedCallback(nullptr);
+ close();
+ },
+ .confirmText = tr::lng_sure_enable(),
+ }));
+}
+
+void GlobalTTL::rebuildButtons(TimeId currentTTL) const {
+ auto ttls = std::vector{
+ 0,
+ 3600 * 24,
+ 3600 * 24 * 7,
+ 3600 * 24 * 31,
+ };
+ if (!ranges::contains(ttls, currentTTL)) {
+ ttls.push_back(currentTTL);
+ ranges::sort(ttls);
+ }
+ if (_buttons->count() > ttls.size()) {
+ return;
+ }
+ _buttons->clear();
+ for (const auto &ttl : ttls) {
+ const auto ttlText = Ui::FormatTTLAfter(ttl);
+ const auto button = AddButton(
+ _buttons,
+ (!ttl)
+ ? tr::lng_settings_ttl_after_off()
+ : tr::lng_settings_ttl_after(
+ lt_after_duration,
+ rpl::single(ttlText)),
+ st::settingsButtonNoIcon);
+ button->setClickedCallback([=] {
+ if (_group->value() == ttl) {
+ return;
+ }
+ if (!ttl) {
+ _group->setChangedCallback(nullptr);
+ request(ttl);
+ return;
+ }
+ showSure(ttl, false);
+ });
+ const auto radio = Ui::CreateChild(
+ button.get(),
+ _group,
+ ttl,
+ QString());
+ radio->setAttribute(Qt::WA_TransparentForMouseEvents);
+ radio->show();
+ button->sizeValue(
+ ) | rpl::start_with_next([=] {
+ radio->moveToRight(0, radio->checkRect().top());
+ }, radio->lifetime());
+ }
+ _buttons->resizeToWidth(width());
+}
+
+void GlobalTTL::setupContent() {
+ setFocusPolicy(Qt::StrongFocus);
+ setFocus();
+
+ const auto content = Ui::CreateChild(this);
+
+ SetupTopContent(content, _showFinished.events());
+
+ AddSkip(content);
+ AddSubsectionTitle(content, tr::lng_settings_ttl_after_subtitle());
+
+ content->add(object_ptr::fromRaw(_buttons));
+
+ {
+ const auto &apiTTL = _controller->session().api().selfDestruct();
+ const auto rebuild = [=](TimeId period) {
+ rebuildButtons(period);
+ _group->setValue(period);
+ };
+ rebuild(apiTTL.periodDefaultHistoryTTLCurrent());
+ apiTTL.periodDefaultHistoryTTL(
+ ) | rpl::start_with_next(rebuild, content->lifetime());
+ }
+
+ const auto show = std::make_shared(_controller);
+ AddButton(
+ content,
+ tr::lng_settings_ttl_after_custom(),
+ st::settingsButtonNoIcon)->setClickedCallback([=] {
+ struct Args {
+ std::shared_ptr show;
+ TimeId startTtl;
+ rpl::producer about;
+ Fn callback;
+ };
+
+ show->showBox(Box(TTLMenu::TTLBox, TTLMenu::Args{
+ .show = show,
+ .startTtl = _group->value(),
+ .callback = [=](TimeId ttl) { showSure(ttl, true); },
+ .hideDisable = true,
+ }));
+ });
+
+ AddSkip(content);
+
+ auto footer = object_ptr(
+ content,
+ tr::lng_settings_ttl_after_about(
+ lt_link,
+ tr::lng_settings_ttl_after_about_link(
+ ) | rpl::map([](QString s) { return Ui::Text::Link(s, 1); }),
+ Ui::Text::WithEntities),
+ st::boxDividerLabel);
+ footer->overrideLinkClickHandler([=] {
+ });
+ content->add(object_ptr(
+ content,
+ std::move(footer),
+ st::settingsDividerLabelPadding));
+
+ Ui::ResizeFitChild(this, content);
+}
+
+void GlobalTTL::showFinished() {
+ _showFinished.fire({});
+}
+
+Type GlobalTTLId() {
+ return GlobalTTL::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_global_ttl.h b/Telegram/SourceFiles/settings/settings_global_ttl.h
new file mode 100644
index 000000000..a97c7491b
--- /dev/null
+++ b/Telegram/SourceFiles/settings/settings_global_ttl.h
@@ -0,0 +1,17 @@
+/*
+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 "settings/settings_type.h"
+
+namespace Settings {
+
+Type GlobalTTLId();
+
+} // namespace Settings
+
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
index 7d51d969e..f4c270808 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/cloud_password/settings_cloud_password_start.h"
#include "settings/settings_blocked_peers.h"
#include "settings/settings_common.h"
+#include "settings/settings_global_ttl.h"
#include "settings/settings_local_passcode.h"
#include "settings/settings_premium.h" // Settings::ShowPremium.
#include "settings/settings_privacy_controllers.h"
@@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/core_settings.h"
#include "ui/chat/chat_style.h"
+#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/wrap/vertical_layout.h"
@@ -459,9 +461,6 @@ void SetupCloudPassword(
reloadOnActivation);
session->api().cloudPassword().reload();
-
- AddSkip(container);
- AddDividerText(container, tr::lng_settings_cloud_password_start_about());
}
void SetupSensitiveContent(
@@ -671,6 +670,37 @@ void SetupSessionsList(
});
}
+void SetupGlobalTTLList(
+ not_null controller,
+ not_null container,
+ rpl::producer<> updateTrigger,
+ Fn showOther) {
+ const auto session = &controller->session();
+ auto ttlLabel = rpl::combine(
+ session->api().selfDestruct().periodDefaultHistoryTTL(),
+ tr::lng_settings_ttl_after_off()
+ ) | rpl::map([](int ttl, const QString &none) {
+ return ttl ? Ui::FormatTTL(ttl) : none;
+ });
+ const auto globalTTLButton = AddButtonWithLabel(
+ container,
+ tr::lng_settings_ttl_title(),
+ std::move(ttlLabel),
+ st::settingsButton,
+ { &st::settingsIconTTL, kIconPurple });
+ globalTTLButton->addClickHandler([=] {
+ showOther(GlobalTTLId());
+ });
+ std::move(
+ updateTrigger
+ ) | rpl::start_with_next([=] {
+ session->api().selfDestruct().reload();
+ }, container->lifetime());
+
+ AddSkip(container);
+ AddDividerText(container, tr::lng_settings_ttl_about());
+}
+
void SetupSecurity(
not_null controller,
not_null container,
@@ -691,6 +721,11 @@ void SetupSecurity(
showOther);
SetupLocalPasscode(controller, container, showOther);
SetupCloudPassword(controller, container, showOther);
+ SetupGlobalTTLList(
+ controller,
+ container,
+ rpl::duplicate(updateTrigger),
+ showOther);
}
} // namespace
diff --git a/Telegram/SourceFiles/ui/text/format_values.cpp b/Telegram/SourceFiles/ui/text/format_values.cpp
index 2aaa50e72..450eac08d 100644
--- a/Telegram/SourceFiles/ui/text/format_values.cpp
+++ b/Telegram/SourceFiles/ui/text/format_values.cpp
@@ -407,6 +407,30 @@ QString FormatTTL(float64 ttl) {
: tr::lng_years({}, lt_count, std::round(ttl / (86400 * 365)));
}
+QString FormatTTLAfter(float64 ttl) {
+ return (ttl <= 3600 * 23)
+ ? tr::lng_settings_ttl_after_hours(tr::now, lt_count, int(ttl / 3600))
+ : (ttl <= (86400) * 6)
+ ? tr::lng_settings_ttl_after_days(
+ tr::now,
+ lt_count,
+ int(ttl / (86400)))
+ : (ttl <= (86400 * 7) * 3)
+ ? tr::lng_settings_ttl_after_weeks(
+ tr::now,
+ lt_count,
+ int(ttl / (86400 * 7)))
+ : (ttl <= (86400 * 31) * 11)
+ ? tr::lng_settings_ttl_after_months(
+ tr::now,
+ lt_count,
+ int(ttl / (86400 * 31)))
+ : tr::lng_settings_ttl_after_years(
+ tr::now,
+ lt_count,
+ std::round(ttl / (86400 * 365)));
+}
+
QString FormatTTLTiny(float64 ttl) {
return (ttl <= 3600 * 9)
? tr::lng_hours_tiny(tr::now, lt_count, int(ttl / 3600))
diff --git a/Telegram/SourceFiles/ui/text/format_values.h b/Telegram/SourceFiles/ui/text/format_values.h
index a92e0e441..a9251aabb 100644
--- a/Telegram/SourceFiles/ui/text/format_values.h
+++ b/Telegram/SourceFiles/ui/text/format_values.h
@@ -29,6 +29,7 @@ inline constexpr auto FileStatusSizeFailed = 0xFFFFFFF2LL;
[[nodiscard]] QString FormatImageSizeText(const QSize &size);
[[nodiscard]] QString FormatPhone(const QString &phone);
[[nodiscard]] QString FormatTTL(float64 ttl);
+[[nodiscard]] QString FormatTTLAfter(float64 ttl);
[[nodiscard]] QString FormatTTLTiny(float64 ttl);
[[nodiscard]] QString FormatMuteFor(float64 sec);
[[nodiscard]] QString FormatMuteForTiny(float64 sec);