/* 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_experimental.h" #include "settings/settings_common.h" #include "data/components/passkeys.h" #include "ui/layers/generic_box.h" #include "main/main_session.h" #include "ui/boxes/confirm_box.h" #include "ui/search_field_controller.h" #include "ui/text/text_entity.h" #include "ui/toast/toast.h" #include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/fields/input_field.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/popup_menu.h" #include "ui/vertical_list.h" #include "ui/gl/gl_detection.h" #include "ui/chat/chat_style_radius.h" #include "ui/controls/compose_ai_button_factory.h" #include "base/options.h" #include "boxes/moderate_messages_box.h" #include "core/application.h" #include "core/launcher.h" #include "core/sandbox.h" #include "chat_helpers/tabbed_panel.h" #include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_widget.h" #include "dialogs/ui/dialogs_layout.h" #include "ffmpeg/ffmpeg_utility.h" #include "history/history_item_components.h" #include "history/view/controls/compose_controls_common.h" #include "history/view/history_view_message.h" #include "info/profile/info_profile_actions.h" #include "info/profile/tabs/adapters/info_profile_tab_media.h" #include "info/profile/tabs/info_profile_tabs_host.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "mainwidget.h" #include "media/player/media_player_instance.h" #include "mtproto/session_private.h" #include "webview/webview_embed.h" #include "window/main_window.h" #include "window/window_filters_favorite.h" #include "window/window_peer_menu.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "window/notifications_manager.h" #include "info/info_flexible_scroll.h" #include "chat_helpers/stickers_list_widget.h" #include "styles/style_chat_helpers.h" #include "styles/style_info.h" #include "styles/style_settings.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" #include #include namespace Settings { namespace { const auto kOptionsClipboardPrefix = u"tdesktop-flags:"_q; struct DecodeOptionsResult { bool ok = false; QString json; }; [[nodiscard]] QString EncodeOptionsToText(const QString &json) { const auto flags = QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals; return kOptionsClipboardPrefix + qs(qCompress(json.toLatin1(), 9).toBase64(flags)); } [[nodiscard]] DecodeOptionsResult DecodeOptionsFromText(const QString &text) { auto result = DecodeOptionsResult(); if (!text.startsWith(kOptionsClipboardPrefix)) { return result; } auto encoded = QStringView(text).mid( kOptionsClipboardPrefix.size()).toLatin1(); const auto compressed = QByteArray::fromBase64Encoding( std::move(encoded), QByteArray::Base64UrlEncoding | QByteArray::AbortOnBase64DecodingErrors); if (!compressed || (*compressed).isEmpty()) { return result; } const auto decoded = qUncompress(*compressed); if (decoded.isEmpty()) { return result; } auto error = QJsonParseError(); const auto parsed = QJsonDocument::fromJson(decoded, &error); if ((error.error != QJsonParseError::NoError) || !parsed.isObject()) { return result; } result.ok = true; result.json = QString::fromUtf8(decoded); return result; } void SetupCopyDeepLink( not_null window, not_null button, const QString &id) { const auto link = u"tg://settings/experimental/"_q + id; const auto menu = button->lifetime().make_state>(); button->events( ) | rpl::filter([](not_null e) { return e->type() == QEvent::ContextMenu; }) | rpl::on_next([=](not_null e) { *menu = base::make_unique_q( button, st::popupMenuWithIcons); (*menu)->addAction(u"Copy deep link"_q, [=] { TextUtilities::SetClipboardText({ link }); window->showToast({ .text = { u"Deep link copied to clipboard."_q }, .iconLottie = u"toast/voip_invite"_q, .iconLottieSize = st::toastLottieIconSize, }); }, &st::menuIconCopy); (*menu)->popup(QCursor::pos()); e->accept(); }, button->lifetime()); } [[nodiscard]] not_null AddOptionRow( not_null container, const QString &name, const QString &description, const style::SettingsButton &st) { if (description.isEmpty()) { return container->add(object_ptr