diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 68bab389c..61561be9f 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -64,7 +64,10 @@ jobs: - name: First set up. run: | sudo chown -R `whoami`:admin /usr/local/share - brew install automake ninja pkg-config nasm meson + + brew update + brew upgrade || true + brew install automake meson nasm ninja pkg-config # Disable spotlight. sudo mdutil -a -i off diff --git a/CMakeLists.txt b/CMakeLists.txt index 8678bd78c..6971dbc8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ include(cmake/options.cmake) if (NOT DESKTOP_APP_USE_PACKAGED) if (WIN32) - set(qt_version 5.15.12) + set(qt_version 5.15.13) elseif (APPLE) set(qt_version 6.2.7) endif() @@ -68,6 +68,7 @@ endif() include(cmake/external/qt/package.cmake) set(desktop_app_skip_libs + glibmm variant ) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index ee1f569e4..bcf747d53 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -30,8 +30,9 @@ include(cmake/lib_tgvoip.cmake) include(cmake/lib_tgcalls.cmake) include(cmake/lib_prisma.cmake) include(cmake/td_export.cmake) -include(cmake/td_mtproto.cmake) +include(cmake/td_iv.cmake) include(cmake/td_lang.cmake) +include(cmake/td_mtproto.cmake) include(cmake/td_scheme.cmake) include(cmake/td_ui.cmake) include(cmake/generate_appdata_changelog.cmake) @@ -62,8 +63,9 @@ PRIVATE desktop-app::external_minizip tdesktop::td_export - tdesktop::td_mtproto + tdesktop::td_iv tdesktop::td_lang + tdesktop::td_mtproto tdesktop::td_scheme tdesktop::td_ui desktop-app::lib_webrtc @@ -176,6 +178,8 @@ PRIVATE api/api_chat_filters.h api/api_chat_invite.cpp api/api_chat_invite.h + api/api_chat_links.cpp + api/api_chat_links.h api/api_chat_participants.cpp api/api_chat_participants.h api/api_cloud_password.cpp @@ -184,6 +188,8 @@ PRIVATE api/api_common.h api/api_confirm_phone.cpp api/api_confirm_phone.h + api/api_earn.cpp + api/api_earn.h api/api_editing.cpp api/api_editing.h api/api_global_privacy.cpp @@ -458,8 +464,6 @@ PRIVATE chat_helpers/spellchecker_common.h chat_helpers/stickers_dice_pack.cpp chat_helpers/stickers_dice_pack.h - chat_helpers/stickers_emoji_image_loader.cpp - chat_helpers/stickers_emoji_image_loader.h chat_helpers/stickers_emoji_pack.cpp chat_helpers/stickers_emoji_pack.h chat_helpers/stickers_gift_box_pack.cpp @@ -786,6 +790,8 @@ PRIVATE history/view/media/history_view_media.h history/view/media/history_view_media_common.cpp history/view/media/history_view_media_common.h + history/view/media/history_view_media_generic.cpp + history/view/media/history_view_media_generic.h history/view/media/history_view_media_grouped.cpp history/view/media/history_view_media_grouped.h history/view/media/history_view_media_spoiler.cpp @@ -925,14 +931,18 @@ PRIVATE history/history_view_highlight_manager.h history/history_widget.cpp history/history_widget.h - info/boosts/giveaway/giveaway_list_controllers.cpp - info/boosts/giveaway/giveaway_list_controllers.h - info/boosts/create_giveaway_box.cpp - info/boosts/create_giveaway_box.h - info/boosts/info_boosts_inner_widget.cpp - info/boosts/info_boosts_inner_widget.h - info/boosts/info_boosts_widget.cpp - info/boosts/info_boosts_widget.h + info/channel_statistics/boosts/create_giveaway_box.cpp + info/channel_statistics/boosts/create_giveaway_box.h + info/channel_statistics/boosts/giveaway/giveaway_list_controllers.cpp + info/channel_statistics/boosts/giveaway/giveaway_list_controllers.h + info/channel_statistics/boosts/info_boosts_inner_widget.cpp + info/channel_statistics/boosts/info_boosts_inner_widget.h + info/channel_statistics/boosts/info_boosts_widget.cpp + info/channel_statistics/boosts/info_boosts_widget.h + info/channel_statistics/earn/info_earn_inner_widget.cpp + info/channel_statistics/earn/info_earn_inner_widget.h + info/channel_statistics/earn/info_earn_widget.cpp + info/channel_statistics/earn/info_earn_widget.h info/common_groups/info_common_groups_inner_widget.cpp info/common_groups/info_common_groups_inner_widget.h info/common_groups/info_common_groups_widget.cpp @@ -1063,6 +1073,10 @@ PRIVATE intro/intro_step.h intro/intro_widget.cpp intro/intro_widget.h + iv/iv_delegate_impl.cpp + iv/iv_delegate_impl.h + iv/iv_instance.cpp + iv/iv_instance.h lang/lang_cloud_manager.cpp lang/lang_cloud_manager.h lang/lang_instance.cpp @@ -1197,6 +1211,8 @@ PRIVATE menu/menu_mute.h menu/menu_send.cpp menu/menu_send.h + menu/menu_sponsored.cpp + menu/menu_sponsored.h menu/menu_ttl_validator.cpp menu/menu_ttl_validator.h mtproto/config_loader.cpp @@ -1251,8 +1267,6 @@ PRIVATE payments/payments_checkout_process.h payments/payments_form.cpp payments/payments_form.h - platform/linux/linux_desktop_environment.cpp - platform/linux/linux_desktop_environment.h platform/linux/linux_wayland_integration_dummy.cpp platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h @@ -1359,6 +1373,10 @@ PRIVATE settings/business/settings_away_message.h settings/business/settings_shortcut_messages.cpp settings/business/settings_shortcut_messages.h + settings/business/settings_chat_intro.cpp + settings/business/settings_chat_intro.h + settings/business/settings_chat_links.cpp + settings/business/settings_chat_links.h settings/business/settings_chatbots.cpp settings/business/settings_chatbots.h settings/business/settings_greeting.cpp @@ -1561,8 +1579,6 @@ PRIVATE window/window_session_controller.cpp window/window_session_controller.h window/window_session_controller_link_info.h - window/window_slide_animation.cpp - window/window_slide_animation.h window/window_top_bar_wrap.h window/themes/window_theme.cpp window/themes/window_theme.h @@ -1637,6 +1653,7 @@ PRIVATE qrc/emoji_preview.qrc qrc/telegram/animations.qrc qrc/telegram/export.qrc + qrc/telegram/iv.qrc qrc/telegram/telegram.qrc qrc/telegram/sounds.qrc winrc/Telegram.rc @@ -1744,13 +1761,9 @@ elseif (APPLE) ) endif() else() - target_link_libraries(Telegram - PRIVATE - desktop-app::external_glibmm - ) - include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake) generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml) + generate_dbus(Telegram org.freedesktop. XdgNotifications ${src_loc}/platform/linux/org.freedesktop.Notifications.xml) if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) target_link_libraries(Telegram diff --git a/Telegram/Resources/animations/chat_link.tgs b/Telegram/Resources/animations/chat_link.tgs new file mode 100644 index 000000000..21622df37 Binary files /dev/null and b/Telegram/Resources/animations/chat_link.tgs differ diff --git a/Telegram/Resources/icons/filled_go_to_message.png b/Telegram/Resources/icons/filled_go_to_message.png new file mode 100644 index 000000000..19ad56f87 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message.png differ diff --git a/Telegram/Resources/icons/filled_go_to_message@2x.png b/Telegram/Resources/icons/filled_go_to_message@2x.png new file mode 100644 index 000000000..fdc472452 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message@2x.png differ diff --git a/Telegram/Resources/icons/filled_go_to_message@3x.png b/Telegram/Resources/icons/filled_go_to_message@3x.png new file mode 100644 index 000000000..0a6bde9b8 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message@3x.png differ diff --git a/Telegram/Resources/icons/menu/earn.png b/Telegram/Resources/icons/menu/earn.png new file mode 100644 index 000000000..a922012bd Binary files /dev/null and b/Telegram/Resources/icons/menu/earn.png differ diff --git a/Telegram/Resources/icons/menu/earn@2x.png b/Telegram/Resources/icons/menu/earn@2x.png new file mode 100644 index 000000000..cf8d10d34 Binary files /dev/null and b/Telegram/Resources/icons/menu/earn@2x.png differ diff --git a/Telegram/Resources/icons/menu/earn@3x.png b/Telegram/Resources/icons/menu/earn@3x.png new file mode 100644 index 000000000..8ff7dd69b Binary files /dev/null and b/Telegram/Resources/icons/menu/earn@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro.png b/Telegram/Resources/icons/settings/premium/business/business_intro.png new file mode 100644 index 000000000..0d357a419 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png b/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png new file mode 100644 index 000000000..3e120fa5d Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png b/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png new file mode 100644 index 000000000..878abd236 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links.png b/Telegram/Resources/icons/settings/premium/business/business_links.png new file mode 100644 index 000000000..470bc4f80 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links@2x.png b/Telegram/Resources/icons/settings/premium/business/business_links@2x.png new file mode 100644 index 000000000..42a770604 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links@3x.png b/Telegram/Resources/icons/settings/premium/business/business_links@3x.png new file mode 100644 index 000000000..67b1f55ad Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away.png b/Telegram/Resources/icons/settings/premium/promo/business_away.png new file mode 100644 index 000000000..b18690565 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png new file mode 100644 index 000000000..24813a145 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png new file mode 100644 index 000000000..d473fdbd4 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png new file mode 100644 index 000000000..5e71b3b93 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png new file mode 100644 index 000000000..ba588c6ec Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png new file mode 100644 index 000000000..615d94b27 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png new file mode 100644 index 000000000..5b812d8ec Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png new file mode 100644 index 000000000..6490fb474 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png new file mode 100644 index 000000000..af17dbc53 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting.png new file mode 100644 index 000000000..22d1526c2 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png new file mode 100644 index 000000000..21c575d73 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png new file mode 100644 index 000000000..3bf932cd6 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours.png b/Telegram/Resources/icons/settings/premium/promo/business_hours.png new file mode 100644 index 000000000..ea917bb24 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png new file mode 100644 index 000000000..62411855b Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png new file mode 100644 index 000000000..923950f84 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro.png b/Telegram/Resources/icons/settings/premium/promo/business_intro.png new file mode 100644 index 000000000..57d1c8c82 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png new file mode 100644 index 000000000..4132668cc Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png new file mode 100644 index 000000000..587b4e36e Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location.png b/Telegram/Resources/icons/settings/premium/promo/business_location.png new file mode 100644 index 000000000..074f44184 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png new file mode 100644 index 000000000..abc79b9b9 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png new file mode 100644 index 000000000..1d58a00bc Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png new file mode 100644 index 000000000..91282589d Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png new file mode 100644 index 000000000..90662ccf9 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png new file mode 100644 index 000000000..ebe964f29 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/channel.png b/Telegram/Resources/icons/sponsored/channel.png new file mode 100644 index 000000000..2f66a5911 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel.png differ diff --git a/Telegram/Resources/icons/sponsored/channel@2x.png b/Telegram/Resources/icons/sponsored/channel@2x.png new file mode 100644 index 000000000..765e4cf31 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/channel@3x.png b/Telegram/Resources/icons/sponsored/channel@3x.png new file mode 100644 index 000000000..21a5c1f9e Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about.png b/Telegram/Resources/icons/sponsored/large_about.png new file mode 100644 index 000000000..95fb27be4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@2x.png b/Telegram/Resources/icons/sponsored/large_about@2x.png new file mode 100644 index 000000000..8af8a580f Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@3x.png b/Telegram/Resources/icons/sponsored/large_about@3x.png new file mode 100644 index 000000000..94d8147c2 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn.png b/Telegram/Resources/icons/sponsored/large_earn.png new file mode 100644 index 000000000..c41ac8378 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn@2x.png b/Telegram/Resources/icons/sponsored/large_earn@2x.png new file mode 100644 index 000000000..79232dd2b Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn@3x.png b/Telegram/Resources/icons/sponsored/large_earn@3x.png new file mode 100644 index 000000000..2d722c06c Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about.png b/Telegram/Resources/icons/sponsored/privacy_about.png new file mode 100644 index 000000000..67fc01990 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@2x.png b/Telegram/Resources/icons/sponsored/privacy_about@2x.png new file mode 100644 index 000000000..e63d296dc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@3x.png b/Telegram/Resources/icons/sponsored/privacy_about@3x.png new file mode 100644 index 000000000..3b95f5bc6 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about.png b/Telegram/Resources/icons/sponsored/remove_about.png new file mode 100644 index 000000000..06a33dc3c Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@2x.png b/Telegram/Resources/icons/sponsored/remove_about@2x.png new file mode 100644 index 000000000..996851dbc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@3x.png b/Telegram/Resources/icons/sponsored/remove_about@3x.png new file mode 100644 index 000000000..0863b33e8 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split.png b/Telegram/Resources/icons/sponsored/revenue_split.png new file mode 100644 index 000000000..94390bce4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@2x.png b/Telegram/Resources/icons/sponsored/revenue_split@2x.png new file mode 100644 index 000000000..23a23ff0a Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@3x.png b/Telegram/Resources/icons/sponsored/revenue_split@3x.png new file mode 100644 index 000000000..4086a7714 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals.png b/Telegram/Resources/icons/sponsored/withdrawals.png new file mode 100644 index 000000000..4baf224d7 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals@2x.png b/Telegram/Resources/icons/sponsored/withdrawals@2x.png new file mode 100644 index 000000000..93d2225f1 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals@3x.png b/Telegram/Resources/icons/sponsored/withdrawals@3x.png new file mode 100644 index 000000000..8827877fc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals@3x.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph.png b/Telegram/Resources/icons/statistics/mini_currency_graph.png new file mode 100644 index 000000000..cce4957b3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png new file mode 100644 index 000000000..b48cf0572 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png new file mode 100644 index 000000000..14cdf7ca3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png differ diff --git a/Telegram/Resources/iv_html/highlight.9.12.0.css b/Telegram/Resources/iv_html/highlight.9.12.0.css new file mode 100644 index 000000000..7d8be18d0 --- /dev/null +++ b/Telegram/Resources/iv_html/highlight.9.12.0.css @@ -0,0 +1 @@ +.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} \ No newline at end of file diff --git a/Telegram/Resources/iv_html/highlight.9.12.0.js b/Telegram/Resources/iv_html/highlight.9.12.0.js new file mode 100644 index 000000000..f30a334c9 --- /dev/null +++ b/Telegram/Resources/iv_html/highlight.9.12.0.js @@ -0,0 +1,3 @@ +/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ +!function(e){var t="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):t&&(t.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return t.hljs}))}(function(e){function t(e){return e.replace(/&/g,"&").replace(//g,">")}function r(e){return e.nodeName.toLowerCase()}function a(e,t){var r=e&&e.exec(t);return r&&0===r.index}function n(e){return E.test(e)}function i(e){var t,r,a,i,s=e.className+" ";if(s+=e.parentNode?e.parentNode.className:"",r=M.exec(s))return w(r[1])?r[1]:"no-highlight";for(s=s.split(/\s+/),t=0,a=s.length;a>t;t++)if(i=s[t],n(i)||w(i))return i}function s(e){var t,r={},a=Array.prototype.slice.call(arguments,1);for(t in e)r[t]=e[t];return a.forEach(function(e){for(t in e)r[t]=e[t]}),r}function c(e){var t=[];return function a(e,n){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?n+=i.nodeValue.length:1===i.nodeType&&(t.push({event:"start",offset:n,node:i}),n=a(i,n),r(i).match(/br|hr|img|input/)||t.push({event:"stop",offset:n,node:i}));return n}(e,0),t}function o(e,a,n){function i(){return e.length&&a.length?e[0].offset!==a[0].offset?e[0].offset"}function c(e){u+=""}function o(e){("start"===e.event?s:c)(e.node)}for(var l=0,u="",d=[];e.length||a.length;){var b=i();if(u+=t(n.substring(l,b[0].offset)),l=b[0].offset,b===e){d.reverse().forEach(c);do o(b.splice(0,1)[0]),b=i();while(b===e&&b.length&&b[0].offset===l);d.reverse().forEach(s)}else"start"===b[0].event?d.push(b[0].node):d.pop(),o(b.splice(0,1)[0])}return u+t(n.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(t){return s(e,{v:null},t)})),e.cached_variants||e.eW&&[s(e)]||[e]}function u(e){function t(e){return e&&e.source||e}function r(r,a){return new RegExp(t(r),"m"+(e.cI?"i":"")+(a?"g":""))}function a(n,i){if(!n.compiled){if(n.compiled=!0,n.k=n.k||n.bK,n.k){var s={},c=function(t,r){e.cI&&(r=r.toLowerCase()),r.split(" ").forEach(function(e){var r=e.split("|");s[r[0]]=[t,r[1]?Number(r[1]):1]})};"string"==typeof n.k?c("keyword",n.k):k(n.k).forEach(function(e){c(e,n.k[e])}),n.k=s}n.lR=r(n.l||/\w+/,!0),i&&(n.bK&&(n.b="\\b("+n.bK.split(" ").join("|")+")\\b"),n.b||(n.b=/\B|\b/),n.bR=r(n.b),n.e||n.eW||(n.e=/\B|\b/),n.e&&(n.eR=r(n.e)),n.tE=t(n.e)||"",n.eW&&i.tE&&(n.tE+=(n.e?"|":"")+i.tE)),n.i&&(n.iR=r(n.i)),null==n.r&&(n.r=1),n.c||(n.c=[]),n.c=Array.prototype.concat.apply([],n.c.map(function(e){return l("self"===e?n:e)})),n.c.forEach(function(e){a(e,n)}),n.starts&&a(n.starts,i);var o=n.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([n.tE,n.i]).map(t).filter(Boolean);n.t=o.length?r(o.join("|"),!0):{exec:function(){return null}}}}a(e)}function d(e,r,n,i){function s(e,t){var r,n;for(r=0,n=t.c.length;n>r;r++)if(a(t.c[r].bR,e))return t.c[r]}function c(e,t){if(a(e.eR,t)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?c(e.parent,t):void 0}function o(e,t){return!n&&a(t.iR,e)}function l(e,t){var r=v.cI?t[0].toLowerCase():t[0];return e.k.hasOwnProperty(r)&&e.k[r]}function p(e,t,r,a){var n=a?"":L.classPrefix,i='',i+t+s}function m(){var e,r,a,n;if(!N.k)return t(E);for(n="",r=0,N.lR.lastIndex=0,a=N.lR.exec(E);a;)n+=t(E.substring(r,a.index)),e=l(N,a),e?(M+=e[1],n+=p(e[0],t(a[0]))):n+=t(a[0]),r=N.lR.lastIndex,a=N.lR.exec(E);return n+t(E.substr(r))}function f(){var e="string"==typeof N.sL;if(e&&!x[N.sL])return t(E);var r=e?d(N.sL,E,!0,k[N.sL]):b(E,N.sL.length?N.sL:void 0);return N.r>0&&(M+=r.r),e&&(k[N.sL]=r.top),p(r.language,r.value,!1,!0)}function g(){C+=null!=N.sL?f():m(),E=""}function _(e){C+=e.cN?p(e.cN,"",!0):"",N=Object.create(e,{parent:{value:N}})}function h(e,t){if(E+=e,null==t)return g(),0;var r=s(t,N);if(r)return r.skip?E+=t:(r.eB&&(E+=t),g(),r.rB||r.eB||(E=t)),_(r,t),r.rB?0:t.length;var a=c(N,t);if(a){var n=N;n.skip?E+=t:(n.rE||n.eE||(E+=t),g(),n.eE&&(E=t));do N.cN&&(C+=R),N.skip||(M+=N.r),N=N.parent;while(N!==a.parent);return a.starts&&_(a.starts,""),n.rE?0:t.length}if(o(t,N))throw new Error('Illegal lexeme "'+t+'" for mode "'+(N.cN||"")+'"');return E+=t,t.length||1}var v=w(e);if(!v)throw new Error('Unknown language: "'+e+'"');u(v);var y,N=i||v,k={},C="";for(y=N;y!==v;y=y.parent)y.cN&&(C=p(y.cN,"",!0)+C);var E="",M=0;try{for(var B,S,$=0;;){if(N.t.lastIndex=$,B=N.t.exec(r),!B)break;S=h(r.substring($,B.index),B[0]),$=B.index+S}for(h(r.substr($)),y=N;y.parent;y=y.parent)y.cN&&(C+=R);return{r:M,value:C,language:e,top:N}}catch(A){if(A.message&&-1!==A.message.indexOf("Illegal"))return{r:0,value:t(r)};throw A}}function b(e,r){r=r||L.languages||k(x);var a={r:0,value:t(e)},n=a;return r.filter(w).forEach(function(t){var r=d(t,e,!1);r.language=t,r.r>n.r&&(n=r),r.r>a.r&&(n=a,a=r)}),n.language&&(a.second_best=n),a}function p(e){return L.tabReplace||L.useBR?e.replace(B,function(e,t){return L.useBR&&"\n"===e?"
":L.tabReplace?t.replace(/\t/g,L.tabReplace):""}):e}function m(e,t,r){var a=t?C[t]:r,n=[e.trim()];return e.match(/\bhljs\b/)||n.push("hljs"),-1===e.indexOf(a)&&n.push(a),n.join(" ").trim()}function f(e){var t,r,a,s,l,u=i(e);n(u)||(L.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e,l=t.textContent,a=u?d(u,l,!0):b(l),r=c(t),r.length&&(s=document.createElementNS("http://www.w3.org/1999/xhtml","div"),s.innerHTML=a.value,a.value=o(r,c(s),l)),a.value=p(a.value),e.innerHTML=a.value,e.className=m(e.className,u,a.language),e.result={language:a.language,re:a.r},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.r}))}function g(e){L=s(L,e)}function _(){if(!_.called){_.called=!0;var e=document.querySelectorAll("pre code");N.forEach.call(e,f)}}function h(){addEventListener("DOMContentLoaded",_,!1),addEventListener("load",_,!1)}function v(t,r){var a=x[t]=r(e);a.aliases&&a.aliases.forEach(function(e){C[e]=t})}function y(){return k(x)}function w(e){return e=(e||"").toLowerCase(),x[e]||x[C[e]]}var N=[],k=Object.keys,x={},C={},E=/^(no-?highlight|plain|text)$/i,M=/\blang(?:uage)?-([\w-]+)\b/i,B=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,R="
",L={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=d,e.highlightAuto=b,e.fixMarkup=p,e.highlightBlock=f,e.configure=g,e.initHighlighting=_,e.initHighlightingOnLoad=h,e.registerLanguage=v,e.listLanguages=y,e.getLanguage=w,e.inherit=s,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(t,r,a){var n=e.inherit({cN:"comment",b:t,e:r,c:[]},a||{});return n.c.push(e.PWM),n.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),n},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e.registerLanguage("apache",function(e){var t={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"section",b:""},{cN:"attribute",b:/\w+/,r:0,k:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"meta",b:"\\s\\[",e:"\\]$"},{cN:"variable",b:"[\\$%]\\{",e:"\\}",c:["self",t]},t,e.QSM]}}],i:/\S/}}),e.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},r={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,r,a,t]}}),e.registerLanguage("coffeescript",function(e){var t={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},r="[A-Za-z$_][0-9A-Za-z$_]*",a={cN:"subst",b:/#\{/,e:/}/,k:t},n=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,a]},{b:/"/,e:/"/,c:[e.BE,a]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[a,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{b:"@"+r},{sL:"javascript",eB:!0,eE:!0,v:[{b:"```",e:"```"},{b:"`",e:"`"}]}];a.c=n;var i=e.inherit(e.TM,{b:r}),s="(\\(.*\\))?\\s*\\B[-=]>",c={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:t,c:["self"].concat(n)}]};return{aliases:["coffee","cson","iced"],k:t,i:/\/\*/,c:n.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+r+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,c]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[c]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{b:r+":",e:":",rB:!0,rE:!0,r:0}])}}),e.registerLanguage("cpp",function(e){var t={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[e.BE]},{b:'(u8?|U)?R"',e:'"',c:[e.BE]},{b:"'\\\\?.",e:"'",i:"."}]},a={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},n={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},e.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},e.CLCM,e.CBCM]},i=e.IR+"\\s*\\(",s={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},c=[t,e.CLCM,e.CBCM,a,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:s,i:"",k:s,c:["self",t]},{b:e.IR+"::",k:s},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:s,c:c.concat([{b:/\(/,e:/\)/,k:s,c:c.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+e.IR+"[\\*&\\s]+)+"+i,rB:!0,e:/[{;=]/,eE:!0,k:s,i:/[^\w\s\*&]/,c:[{b:i,rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:s,r:0,c:[e.CLCM,e.CBCM,r,a,t]},e.CLCM,e.CBCM,n]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},e.TM]}]),exports:{preprocessor:n,strings:r,k:s}}}),e.registerLanguage("cs",function(e){var t={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long nameof object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let on orderby partial remove select set value var where yield",literal:"null false true"},r={cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a=e.inherit(r,{i:/\n/}),n={cN:"subst",b:"{",e:"}",k:t},i=e.inherit(n,{i:/\n/}),s={cN:"string",b:/\$"/,e:'"',i:/\n/,c:[{b:"{{"},{b:"}}"},e.BE,i]},c={cN:"string",b:/\$@"/,e:'"',c:[{b:"{{"},{b:"}}"},{b:'""'},n]},o=e.inherit(c,{i:/\n/,c:[{b:"{{"},{b:"}}"},{b:'""'},i]});n.c=[c,s,r,e.ASM,e.QSM,e.CNM,e.CBCM],i.c=[o,s,a,e.ASM,e.QSM,e.CNM,e.inherit(e.CBCM,{i:/\n/})];var l={v:[c,s,r,e.ASM,e.QSM]},u=e.IR+"(<"+e.IR+"(\\s*,\\s*"+e.IR+")*>)?(\\[\\])?";return{aliases:["csharp"],k:t,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"doctag",v:[{b:"///",r:0},{b:""},{b:""}]}]}),e.CLCM,e.CBCM,{cN:"meta",b:"#",e:"$",k:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},l,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[e.inherit(e.TM,{b:"[a-zA-Z](\\.?\\w)*"}),e.CLCM,e.CBCM]},{cN:"meta",b:"^\\s*\\[",eB:!0,e:"\\]",eE:!0,c:[{cN:"meta-string",b:/"/,e:/"/}]},{bK:"new return throw await else",r:0},{cN:"function",b:"("+u+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,r:0,c:[l,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}}),e.registerLanguage("css",function(e){var t="[a-zA-Z-][a-zA-Z0-9_-]*",r={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:t,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,r]}]}}),e.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}}),e.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}}),e.registerLanguage("ini",function(e){var t={cN:"string",c:[e.BE],v:[{b:"'''",e:"'''",r:10},{b:'"""',e:'"""',r:10},{b:'"',e:'"'},{b:"'",e:"'"}]};return{aliases:["toml"],cI:!0,i:/\S/,c:[e.C(";","$"),e.HCM,{cN:"section",b:/^\s*\[+/,e:/\]+/},{b:/^[a-z0-9\[\]_-]+\s*=\s*/,e:"$",rB:!0,c:[{cN:"attr",b:/[a-z0-9\[\]_-]+/},{b:/=/,eW:!0,r:0,c:[{cN:"literal",b:/\bon|off|true|false|yes|no\b/},{cN:"variable",v:[{b:/\$[\w\d"][\w\d_]*/},{b:/\$\{(.*?)}/}]},t,{cN:"number",b:/([\+\-]+)?[\d]+_[\d_]+/},e.NM]}]}]}}),e.registerLanguage("java",function(e){var t="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",r=t+"(<"+t+"(\\s*,\\s*"+t+")*>)?",a="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",i={cN:"number",b:n,r:0};return{aliases:["jsp"],k:a,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+r+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:a,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:a,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},i,{cN:"meta",b:"@[A-Za-z]+"}]}}),e.registerLanguage("javascript",function(e){var t="[A-Za-z$_][0-9A-Za-z$_]*",r={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:r,c:[]},i={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,i,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:r,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,i,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:t+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:t,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+t+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:t},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:r,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:t}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}}),e.registerLanguage("json",function(e){var t={literal:"true false null"},r=[e.QSM,e.CNM],a={e:",",eW:!0,eE:!0,c:r,k:t},n={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(a,{b:/:/})],i:"\\S"},i={b:"\\[",e:"\\]",c:[e.inherit(a)],i:"\\S"};return r.splice(r.length,0,n,i),{c:r,k:t,i:"\\S"}}),e.registerLanguage("makefile",function(e){var t={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@%`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},e.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[r],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[r],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}}),e.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}}),e.registerLanguage("nginx",function(e){var t={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},r={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,t],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[t]},{cN:"regexp",c:[e.BE,t],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},t]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:r}],r:0}],i:"[^\\s\\}]"}}),e.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},r={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},a=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:r,l:a,i:""}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:a,c:[e.UTM]},{b:"\\."+e.UIR,r:0}]}}),e.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},a={b:"->{",e:"}"},n={v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=[e.BE,r,n],s=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),a,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"function",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",eE:!0,r:5,c:[e.TM]},{b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=s,a.c=s,{aliases:["pl","pm"],l:/[\w\.]+/,k:t,c:s}}),e.registerLanguage("php",function(e){var t={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},r={cN:"meta",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,r],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[r]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},r,{cN:"keyword",b:/\$this\b/},t,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",t,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}}),e.registerLanguage("python",function(e){var t={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},r={cN:"meta",b:/^(>>>|\.\.\.) /},a={cN:"subst",b:/\{/,e:/\}/,k:t,i:/#/},n={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[r,a]},{b:/(fr|rf|f)"""/,e:/"""/,c:[r,a]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[a]},{b:/(fr|rf|f)"/,e:/"/,c:[a]},e.ASM,e.QSM]},i={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},s={cN:"params",b:/\(/,e:/\)/,c:["self",r,i,n]};return a.c=[n,i,r],{aliases:["py","gyp"],k:t,i:/(<\/|->|\?)|=>/,c:[r,i,n,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,s,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}}),e.registerLanguage("ruby",function(e){ +var t="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},a={cN:"doctag",b:"@[A-Za-z]+"},n={b:"#<",e:">"},i=[e.C("#","$",{c:[a]}),e.C("^\\=begin","^\\=end",{c:[a],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},c={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},o={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},l=[c,n,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(i)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:t}),o].concat(i)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[c,{b:t}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",k:"unless",c:[n,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(i),r:0}].concat(i);s.c=l,o.c=l;var u="[>?]>",d="[\\w#]+\\(\\w+\\):\\d+:\\d+>",b="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",p=[{b:/^\s*=>/,starts:{e:"$",c:l}},{cN:"meta",b:"^("+u+"|"+d+"|"+b+")",starts:{e:"$",c:l}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:i.concat(p).concat(l)}}),e.registerLanguage("shell",function(e){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}}),e.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*#]/,c:[{bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment",e:/;/,eW:!0,l:/[\w\.]+/,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}}),e}); \ No newline at end of file diff --git a/Telegram/Resources/iv_html/morphdom-umd.min.2.7.2.js b/Telegram/Resources/iv_html/morphdom-umd.min.2.7.2.js new file mode 100644 index 000000000..1e40c7eae --- /dev/null +++ b/Telegram/Resources/iv_html/morphdom-umd.min.2.7.2.js @@ -0,0 +1 @@ +(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global.morphdom=factory())})(this,function(){"use strict";var DOCUMENT_FRAGMENT_NODE=11;function morphAttrs(fromNode,toNode){var toNodeAttrs=toNode.attributes;var attr;var attrName;var attrNamespaceURI;var attrValue;var fromValue;if(toNode.nodeType===DOCUMENT_FRAGMENT_NODE||fromNode.nodeType===DOCUMENT_FRAGMENT_NODE){return}for(var i=toNodeAttrs.length-1;i>=0;i--){attr=toNodeAttrs[i];attrName=attr.name;attrNamespaceURI=attr.namespaceURI;attrValue=attr.value;if(attrNamespaceURI){attrName=attr.localName||attrName;fromValue=fromNode.getAttributeNS(attrNamespaceURI,attrName);if(fromValue!==attrValue){if(attr.prefix==="xmlns"){attrName=attr.name}fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}}else{fromValue=fromNode.getAttribute(attrName);if(fromValue!==attrValue){fromNode.setAttribute(attrName,attrValue)}}}var fromNodeAttrs=fromNode.attributes;for(var d=fromNodeAttrs.length-1;d>=0;d--){attr=fromNodeAttrs[d];attrName=attr.name;attrNamespaceURI=attr.namespaceURI;if(attrNamespaceURI){attrName=attr.localName||attrName;if(!toNode.hasAttributeNS(attrNamespaceURI,attrName)){fromNode.removeAttributeNS(attrNamespaceURI,attrName)}}else{if(!toNode.hasAttribute(attrName)){fromNode.removeAttribute(attrName)}}}}var range;var NS_XHTML="http://www.w3.org/1999/xhtml";var doc=typeof document==="undefined"?undefined:document;var HAS_TEMPLATE_SUPPORT=!!doc&&"content"in doc.createElement("template");var HAS_RANGE_SUPPORT=!!doc&&doc.createRange&&"createContextualFragment"in doc.createRange();function createFragmentFromTemplate(str){var template=doc.createElement("template");template.innerHTML=str;return template.content.childNodes[0]}function createFragmentFromRange(str){if(!range){range=doc.createRange();range.selectNode(doc.body)}var fragment=range.createContextualFragment(str);return fragment.childNodes[0]}function createFragmentFromWrap(str){var fragment=doc.createElement("body");fragment.innerHTML=str;return fragment.childNodes[0]}function toElement(str){str=str.trim();if(HAS_TEMPLATE_SUPPORT){return createFragmentFromTemplate(str)}else if(HAS_RANGE_SUPPORT){return createFragmentFromRange(str)}return createFragmentFromWrap(str)}function compareNodeNames(fromEl,toEl){var fromNodeName=fromEl.nodeName;var toNodeName=toEl.nodeName;var fromCodeStart,toCodeStart;if(fromNodeName===toNodeName){return true}fromCodeStart=fromNodeName.charCodeAt(0);toCodeStart=toNodeName.charCodeAt(0);if(fromCodeStart<=90&&toCodeStart>=97){return fromNodeName===toNodeName.toUpperCase()}else if(toCodeStart<=90&&fromCodeStart>=97){return toNodeName===fromNodeName.toUpperCase()}else{return false}}function createElementNS(name,namespaceURI){return!namespaceURI||namespaceURI===NS_XHTML?doc.createElement(name):doc.createElementNS(namespaceURI,name)}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function syncBooleanAttrProp(fromEl,toEl,name){if(fromEl[name]!==toEl[name]){fromEl[name]=toEl[name];if(fromEl[name]){fromEl.setAttribute(name,"")}else{fromEl.removeAttribute(name)}}}var specialElHandlers={OPTION:function(fromEl,toEl){var parentNode=fromEl.parentNode;if(parentNode){var parentName=parentNode.nodeName.toUpperCase();if(parentName==="OPTGROUP"){parentNode=parentNode.parentNode;parentName=parentNode&&parentNode.nodeName.toUpperCase()}if(parentName==="SELECT"&&!parentNode.hasAttribute("multiple")){if(fromEl.hasAttribute("selected")&&!toEl.selected){fromEl.setAttribute("selected","selected");fromEl.removeAttribute("selected")}parentNode.selectedIndex=-1}}syncBooleanAttrProp(fromEl,toEl,"selected")},INPUT:function(fromEl,toEl){syncBooleanAttrProp(fromEl,toEl,"checked");syncBooleanAttrProp(fromEl,toEl,"disabled");if(fromEl.value!==toEl.value){fromEl.value=toEl.value}if(!toEl.hasAttribute("value")){fromEl.removeAttribute("value")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!==newValue){fromEl.value=newValue}var firstChild=fromEl.firstChild;if(firstChild){var oldValue=firstChild.nodeValue;if(oldValue==newValue||!newValue&&oldValue==fromEl.placeholder){return}firstChild.nodeValue=newValue}},SELECT:function(fromEl,toEl){if(!toEl.hasAttribute("multiple")){var selectedIndex=-1;var i=0;var curChild=fromEl.firstChild;var optgroup;var nodeName;while(curChild){nodeName=curChild.nodeName&&curChild.nodeName.toUpperCase();if(nodeName==="OPTGROUP"){optgroup=curChild;curChild=optgroup.firstChild}else{if(nodeName==="OPTION"){if(curChild.hasAttribute("selected")){selectedIndex=i;break}i++}curChild=curChild.nextSibling;if(!curChild&&optgroup){curChild=optgroup.nextSibling;optgroup=null}}}fromEl.selectedIndex=selectedIndex}}};var ELEMENT_NODE=1;var DOCUMENT_FRAGMENT_NODE$1=11;var TEXT_NODE=3;var COMMENT_NODE=8;function noop(){}function defaultGetNodeKey(node){if(node){return node.getAttribute&&node.getAttribute("id")||node.id}}function morphdomFactory(morphAttrs){return function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"||fromNode.nodeName==="BODY"){var toNodeHtml=toNode;toNode=doc.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}else if(toNode.nodeType===DOCUMENT_FRAGMENT_NODE$1){toNode=toNode.firstElementChild}var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||noop;var skipFromChildren=options.skipFromChildren||noop;var addChild=options.addChild||function(parent,child){return parent.appendChild(child)};var childrenOnly=options.childrenOnly===true;var fromNodesLookup=Object.create(null);var keyedRemovalList=[];function addKeyedRemoval(key){keyedRemovalList.push(key)}function walkDiscardedChildNodes(node,skipKeyedNodes){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){var key=undefined;if(skipKeyedNodes&&(key=getNodeKey(curChild))){addKeyedRemoval(key)}else{onNodeDiscarded(curChild);if(curChild.firstChild){walkDiscardedChildNodes(curChild,skipKeyedNodes)}}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,skipKeyedNodes){if(onBeforeNodeDiscarded(node)===false){return}if(parentNode){parentNode.removeChild(node)}onNodeDiscarded(node);walkDiscardedChildNodes(node,skipKeyedNodes)}function indexTree(node){if(node.nodeType===ELEMENT_NODE||node.nodeType===DOCUMENT_FRAGMENT_NODE$1){var curChild=node.firstChild;while(curChild){var key=getNodeKey(curChild);if(key){fromNodesLookup[key]=curChild}indexTree(curChild);curChild=curChild.nextSibling}}}indexTree(fromNode);function handleNodeAdded(el){onNodeAdded(el);var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var unmatchedFromEl=fromNodesLookup[key];if(unmatchedFromEl&&compareNodeNames(curChild,unmatchedFromEl)){curChild.parentNode.replaceChild(unmatchedFromEl,curChild);morphEl(unmatchedFromEl,curChild)}else{handleNodeAdded(curChild)}}else{handleNodeAdded(curChild)}curChild=nextSibling}}function cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey){while(curFromNodeChild){var fromNextSibling=curFromNodeChild.nextSibling;if(curFromNodeKey=getNodeKey(curFromNodeChild)){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}}function morphEl(fromEl,toEl,childrenOnly){var toElKey=getNodeKey(toEl);if(toElKey){delete fromNodesLookup[toElKey]}if(!childrenOnly){if(onBeforeElUpdated(fromEl,toEl)===false){return}morphAttrs(fromEl,toEl);onElUpdated(fromEl);if(onBeforeElChildrenUpdated(fromEl,toEl)===false){return}}if(fromEl.nodeName!=="TEXTAREA"){morphChildren(fromEl,toEl)}else{specialElHandlers.TEXTAREA(fromEl,toEl)}}function morphChildren(fromEl,toEl){var skipFrom=skipFromChildren(fromEl,toEl);var curToNodeChild=toEl.firstChild;var curFromNodeChild=fromEl.firstChild;var curToNodeKey;var curFromNodeKey;var fromNextSibling;var toNextSibling;var matchingFromEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeKey=getNodeKey(curToNodeChild);while(!skipFrom&&curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;if(curToNodeChild.isSameNode&&curToNodeChild.isSameNode(curFromNodeChild)){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}curFromNodeKey=getNodeKey(curFromNodeChild);var curFromNodeType=curFromNodeChild.nodeType;var isCompatible=undefined;if(curFromNodeType===curToNodeChild.nodeType){if(curFromNodeType===ELEMENT_NODE){if(curToNodeKey){if(curToNodeKey!==curFromNodeKey){if(matchingFromEl=fromNodesLookup[curToNodeKey]){if(fromNextSibling===matchingFromEl){isCompatible=false}else{fromEl.insertBefore(matchingFromEl,curFromNodeChild);if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=matchingFromEl;curFromNodeKey=getNodeKey(curFromNodeChild)}}else{isCompatible=false}}}else if(curFromNodeKey){isCompatible=false}isCompatible=isCompatible!==false&&compareNodeNames(curFromNodeChild,curToNodeChild);if(isCompatible){morphEl(curFromNodeChild,curToNodeChild)}}else if(curFromNodeType===TEXT_NODE||curFromNodeType==COMMENT_NODE){isCompatible=true;if(curFromNodeChild.nodeValue!==curToNodeChild.nodeValue){curFromNodeChild.nodeValue=curToNodeChild.nodeValue}}}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}if(curToNodeKey&&(matchingFromEl=fromNodesLookup[curToNodeKey])&&compareNodeNames(matchingFromEl,curToNodeChild)){if(!skipFrom){addChild(fromEl,matchingFromEl)}morphEl(matchingFromEl,curToNodeChild)}else{var onBeforeNodeAddedResult=onBeforeNodeAdded(curToNodeChild);if(onBeforeNodeAddedResult!==false){if(onBeforeNodeAddedResult){curToNodeChild=onBeforeNodeAddedResult}if(curToNodeChild.actualize){curToNodeChild=curToNodeChild.actualize(fromEl.ownerDocument||doc)}addChild(fromEl,curToNodeChild);handleNodeAdded(curToNodeChild)}}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey);var specialElHandler=specialElHandlers[fromEl.nodeName];if(specialElHandler){specialElHandler(fromEl,toEl)}}var morphedNode=fromNode;var morphedNodeType=morphedNode.nodeType;var toNodeType=toNode.nodeType;if(!childrenOnly){if(morphedNodeType===ELEMENT_NODE){if(toNodeType===ELEMENT_NODE){if(!compareNodeNames(fromNode,toNode)){onNodeDiscarded(fromNode);morphedNode=moveChildren(fromNode,createElementNS(toNode.nodeName,toNode.namespaceURI))}}else{morphedNode=toNode}}else if(morphedNodeType===TEXT_NODE||morphedNodeType===COMMENT_NODE){if(toNodeType===morphedNodeType){if(morphedNode.nodeValue!==toNode.nodeValue){morphedNode.nodeValue=toNode.nodeValue}return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{if(toNode.isSameNode&&toNode.isSameNode(morphedNode)){return}morphEl(morphedNode,toNode,childrenOnly);if(keyedRemovalList){for(var i=0,len=keyedRemovalList.length;i li, +article ol > li { + padding-left: 4px; +} +article.rtl ul > li, +article.rtl ol > li { + padding-right: 4px; + padding-left: 0; +} +/*article ul > li { + position: relative; +} +article ul > li:before { + content: '\2022'; + position: absolute; + display: block; + font-size: 163%; + left: -19px; + top: 1px; +} +article.rtl ul > li:before { + left: auto; + right: -19px; +}*/ +article ul ul, +article ul ol, +article ol ul, +article ol ol { + margin: 0 0 12px; +} + +article table { + width: 100%; + border-collapse: collapse; +} +article table.bordered, +article table.bordered td, +article table.bordered th { + border: 1px solid var(--td-history-to-down-shadow); +} +article table.striped tr:nth-child(odd) td { + background-color: var(--td-box-divider-bg); +} +article table caption { + font-size: 15px; + line-height: 18px; + margin: 4px 0 7px; + text-align: left; + color: var(--td-window-sub-text-fg); +} +article.rtl table caption { + text-align: right; +} +article td, +article th { + font-size: 15px; + line-height: 21px; + padding: 6px 5px 5px; + background-color: var(--td-window-bg); + vertical-align: middle; + font-weight: normal; + text-align: left; +} +article th { + background-color: var(--td-box-divider-bg); +} +article.rtl table td, +article.rtl table th { + text-align: right; +} +article details { + position: relative; + margin: 0 0 12px; + padding: 0 0 1px; +} +article details:before { + content: ''; + display: block; + border-bottom: 1px solid var(--td-history-to-down-shadow); + position: absolute; + left: 18px; + right: 0; + bottom: 0; +} +article.rtl details:before { + right: 18px; + left: 0; +} +article details + details { + margin-top: -12px; +} +article details > details:last-child { + margin-bottom: -1px; +} +article summary { + padding: 10px 18px 10px 42px; + line-height: 25px; + min-height: 25px; +} +article.rtl summary { + padding-left: 18px; + padding-right: 42px; +} +article summary:hover { + cursor: pointer; +} +article summary:focus { + outline: none; +} +article summary::-webkit-details-marker { + display: none; +} +article summary::marker { + content: ''; +} +article summary:before { + content: ''; + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAICAYAAADN5B7xAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH1JREFUeNqEjUEKgCAQRSfrNi1bdZFadJjsMC46SSAIHqjB5mcFqdFfhD3eUyKZtb6ln92O2janmXdvrRu+ZTfAgasu1jAHU4qiHAwc/Ff4oCQKsxxZ0NT33XrxUTjkWvgiXFf3TWkU6Vt+XihH515yFiQRpfLnEMUw3yHAABZNTT9emBrvAAAAAElFTkSuQmCC'); + transition: all .2s ease; + display: inline-block; + position: absolute; + width: 12px; + height: 8px; + left: 18px; + top: 18px; +} +article.rtl summary:before { + right: 18px; + left: auto; +} +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + article summary:before { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAQCAYAAAAMJL+VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPxJREFUeNq8lEESgiAUhgFbZ0epSW28gB2pZbrrSukBHDWto1TrwHih45AiaDOxesLP9w1PBlzXNfrLSNPqkGWV8ysHGMBqv4mAlyFC7MRPk+T51Z0Lh73AAJZgIoRFUR/bEMb4TggJPG9TTIUzxmIuWHWzOCLfQQgwRiedRMBpIsObFvn+NgSTLEE2bCiKm6eDQ0bAkS2v4AjYuPvJcqtEu9DDshaB665zFZzSV6yCfyr5JplLTOA9wZiEg/a+72Qic9nxubMOPijQSZraCK4UjEiezSVYmsBHBSrJAEIJ1wr0knG4kUAt0cONBX2JGXzGi1uG7SNmOt4CDADc4r+K4txg+wAAAABJRU5ErkJggg=='); + background-size: 12px 8px; + } +} +article details[open] > summary:before { + /*transform: rotateZ(-180deg);*/ + transform: scaleY(-1); +} +article li summary { + padding-left: 24px; +} +article li details:before, +article li summary:before { + left: 0; +} + +img, +video, +iframe { + max-width: 100%; + max-height: 480px; + vertical-align: top; +} +video { + width: 100%; +} +audio { + width: 100%; + width: calc(100% - 36px); + margin: 0 18px; + vertical-align: top; +} +img { + font-size: 12px; + line-height: 14px; + color: var(--td-window-sub-text-fg); +} +img.pic { + max-height: none; + font-size: inherit; + vertical-align: middle; + position: relative; + top: -0.1em; +} +img.pic.optional { + opacity: 0.4; +} +body:hover img.pic.optional { + opacity: 1; +} +iframe.autosize { + max-height: none; +} +.iframe-wrap { + max-width: 100%; + vertical-align: top; + display: inline-block; + position: relative; +} +.iframe-wrap iframe { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; +} +figure { + margin: 0 0 16px; + padding: 0; + text-align: center; + position: relative; +} +figure.nowide { + margin-left: 18px; + margin-right: 18px; +} +figure.nowide figcaption { + padding-left: 0; + padding-right: 0; +} +ul figure.nowide, +ol figure.nowide { + margin: 0 0 12px; +} +figure > figure { + margin: 0; +} +figcaption { + font-size: 15px; + color: var(--td-window-sub-text-fg); + padding: 6px 18px 0; + line-height: 19px; + text-align: left; +} +article.rtl figcaption { + text-align: right; +} +ul figcaption, +ol figcaption { + padding-left: 0; + padding-right: 0; +} +figcaption > cite { + font-family: var(--font-sans); + font-size: 12px; + display: block; + line-height: 15px; + padding: 2px 0 0; + font-style: normal; +} +footer { + margin: 12px 18px; + color: var(--td-window-sub-text-fg); +} + +figure.slideshow-wrap { + position: relative; +} +figure.slideshow { + position: absolute; + top: 0px; + white-space: nowrap; + width: 100%; + background: #000; + overflow: hidden; +} +figure.slideshow a { + transition: margin 200ms ease-in-out; +} +figure.slideshow .photo-wrap, +figure.slideshow .video-wrap { + position: static !important; + display: inline-block; + margin: 0; + vertical-align: middle; +} +.slideshow-buttons { + position: absolute; + width: 100%; + bottom: 10px; + white-space: nowrap; + overflow: hidden; + z-index: 5; +} +.slideshow-buttons > fieldset { + padding: 0; + margin: 0; + border: none; + line-height: 0; + overflow: hidden; + overflow-x: auto; + min-width: auto; +} +.slideshow-buttons label { + display: inline-block; + padding: 7px; + cursor: pointer; +} +.slideshow-buttons input { + position: absolute; + left: -10000px; +} +.slideshow-buttons label i { + display: inline-block; + background: #fff; + box-shadow: 0 0 3px rgba(0, 0, 0, .4); + border-radius: 3.5px; + width: 7px; + height: 7px; + opacity: .6; + transition: opacity .3s; +} +.slideshow-buttons input:checked ~ i { + opacity: 1; +} +.slideshow-next, +.slideshow-prev { + position: absolute; + z-index: 4; + top: 0; + width: 25%; + max-width: 128px; + height: 100%; + cursor: pointer; + transition: opacity 200ms ease-in-out; + user-select: none; + opacity: 0.6; +} +.slideshow-next { + right: 0; + background: linear-gradient(to right, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%); +} +.slideshow-prev { + left: 0; + background: linear-gradient(to left, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%); +} +.slideshow-next:hover { + opacity: 1; +} +.slideshow-prev:hover { + opacity: 1; +} +.slideshow-prev svg, +.slideshow-next svg { + fill: none; + top: calc(50% - 12px); + position: absolute; + z-index: 5; + width: 24px; + height: 24px; + pointer-events: none; +} +.slideshow-prev svg { + left: calc(min(50% - 12px, 20px)); +} +.slideshow-next svg { + right: calc(min(50% - 12px, 20px)); +} +.slideshow-prev path, +.slideshow-next path { + stroke-width: 1.4; + stroke: #fff; +} + +figure.collage-wrap { + margin: 0px 12px; +} +figure.collage-wrap figcaption { + padding: 6px 6px 0px; +} +figure.collage { + overflow: hidden; + border-radius: 6px; +} +figure.collage .photo-wrap, +figure.collage .video-wrap { + position: absolute; +} +figure.collage .photo-wrap .photo { + background-size: cover; +} +figure.collage .video-wrap video { + object-fit: cover; + position: absolute; + top: 50%; + left: 50%; + width: auto; + height: auto; + min-width: 100%; + min-height: 100%; + transform: translate(-50%, -50%); +} +figure.collage .video-wrap .video-small, +video[autoplay] { + pointer-events: none; +} + +figure.table-wrap { + overflow: auto; + -webkit-overflow-scrolling: touch; +} +figure.table { + display: table-cell; + padding: 0 18px; +} +article ol figure.table-wrap, +article ul figure.table-wrap { + margin-top: 7px; +} +article ol figure.table, +article ul figure.table { + padding: 0; +} + +figure blockquote.embed-post { + text-align: left; + margin-bottom: 0; +} +article.rtl figure blockquote.embed-post { + text-align: right; +} +blockquote.embed-post address { + margin: 0; + padding: 5px 0 9px; + overflow: hidden; +} +blockquote.embed-post address figure { + width: 50px; + height: 50px; + float: left; + margin: 0 12px 0 0; + background: no-repeat center; + background-size: cover; + border-radius: 50%; +} +article.rtl blockquote.embed-post address figure { + float: right; + margin-left: 12px; + margin-right: 0; +} +blockquote.embed-post address a { + display: inline-block; + padding-top: 2px; + font-size: 17px; + font-weight: 600; + color: var(--td-window-fg); +} +blockquote.embed-post address time { + display: block; + line-height: 19px; +} +blockquote.embed-post p, +blockquote.embed-post blockquote { + margin: 0 0 7px; + clear: left; +} +blockquote.embed-post figcaption { + padding-left: 0; + padding-right: 0; +} +blockquote.embed-post figure.collage { + margin-left: -2px; + margin-right: -2px; +} +blockquote.embed-post footer { + margin: 12px 0 0; + font-style: normal; +} +blockquote.embed-post footer hr { + display: none; +} + +section.embed-post { + display: block; + width: auto; + height: auto; + background: var(--td-box-divider-bg); + margin: 0 18px 12px; + padding: 24px 18px; + text-align: center; +} +section.embed-post strong { + font-size: 21px; + font-weight: normal; + display: block; + color: var(--td-window-sub-text-fg); +} +section.embed-post small { + display: block; + color: var(--td-window-sub-text-fg); +} + + +section.related { + margin: 7px 0 12px; +} +section.related h4 { + font-family: var(--font-sans); + font-size: 17px; + line-height: 26px; + font-weight: 500; + display: block; + padding: 7px 18px; + background: var(--td-box-divider-bg); + margin: 0; + color: var(--td-window-fg); +} +section.related a.related-link { + display: block; + padding: 15px 18px 16px; + background: var(--td-window-bg); + position: relative; + overflow: hidden; +} +section.related a.related-link:after { + content: ''; + display: block; + border-bottom: 1px solid var(--td-history-to-down-shadow); + position: absolute; + left: 18px; + right: 0; + bottom: 0; +} +section.related .related-link-url { + display: block; + font-size: 15px; + line-height: 18px; + padding: 7px 0; + color: var(--td-window-sub-text-fg); + word-break: break-word; +} +section.related .related-link-thumb { + display: inline-block; + float: right; + width: 87px; + height: 87px; + border-radius: 4px; + background: no-repeat center; + background-size: cover; + margin-left: 15px; +} +section.related .related-link-content { + display: block; + margin: -3px 0; +} +section.related .related-link-title { + font-size: 15px; + font-weight: 500; + line-height: 18px; + display: block; + display: -webkit-box; + margin-bottom: 4px; + max-height: 36px; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + white-space: pre-wrap; + color: var(--td-window-fg); +} +section.related .related-link-desc { + font-size: 14px; + line-height: 17px; + display: block; + display: -webkit-box; + max-height: 51px; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + white-space: pre-wrap; + color: var(--td-window-fg); +} +section.related .related-link-source { + font-size: 13px; + line-height: 17px; + display: block; + overflow: hidden; + margin-top: 4px; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--td-window-sub-text-fg); +} + +section.message { + position: absolute; + display: table; + width: 100%; + height: 100%; +} +section.message.static { + position: static; + min-height: 200px; + height: 100vh; +} +section.message > aside { + display: table-cell; + vertical-align: middle; + text-align: center; + color: var(--td-window-sub-text-fg); + font-size: 24px; + pointer-events: none; +} +section.message > aside > cite { + display: block; + font-size: 14px; + padding: 10px 0 0; + font-style: normal; + color: var(--td-window-sub-text-fg); +} + +section.channel { + margin-top: -16px; + margin-bottom: -9px; +} +section.channel:first-child { + margin-top: 0; +} +section.channel > a { + display: block; + background: var(--td-box-divider-bg); +} +section.channel > a > div.join { + color: var(--td-window-active-text-fg); + font-weight: 500; + padding: 7px 18px; + float: right; +} +section.channel.joined > a > div.join { + display: none; +} +section.channel > a > div.join:hover { + text-decoration: underline; +} +section.channel > a > div.join span:before { + content: var(--td-lng-iv-join-channel); +} +section.channel > a > h4 { + font-family: var(--font-sans); + font-size: 17px; + line-height: 26px; + font-weight: 500; + margin: 0; + color: var(--td-window-fg); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 7px 18px; +} + +.pullquote { + text-align: center; + max-width: 420px; + font-size: 19px; + display: block; + margin: 0 auto; +} +.photo-wrap, +.video-wrap { + width: 100%; + margin: 0 auto; + position: relative; + overflow: hidden; +} +.photo-bg, +.video-bg { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + position: absolute; + filter: blur(16px); + width: 100%; + height: 100%; +} +.video-bg, +video { + position: absolute; + top: 0px; +} +.photo { + position: relative; + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.photo, +video { + opacity: 0; + transition: opacity 300ms ease-in-out; +} +.photo.loaded, +video.loaded { + opacity: 1; +} +.video-play-outer { + position: relative; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} +.video-play { + position: relative; + width: 48px; + height: 0; + padding-top: 48px; + max-width: 48px; + max-height: 48px; + background-color: rgba(0, 0, 0, 0.34); + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; +} +.video-play::before { + content: ''; + position: absolute; + margin: -48px -4px 0px 0px; + width: 0; + height: 0; + border-style: solid; + border-width: 10px 0 10px 16px; + border-color: transparent transparent transparent white; +} + +.toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--td-toast-bg); + color: var(--td-toast-fg); + padding: 10px 20px; + border-radius: 6px; + z-index: 9999; + opacity: 0; + animation: fadeIn 200ms linear forwards; +} +.toast.hiding { + opacity: 1; + animation: fadeOut 1000ms linear forwards; +} diff --git a/Telegram/Resources/iv_html/page.js b/Telegram/Resources/iv_html/page.js new file mode 100644 index 000000000..053a6a0ab --- /dev/null +++ b/Telegram/Resources/iv_html/page.js @@ -0,0 +1,657 @@ +var IV = { + notify: function(message) { + if (window.external && window.external.invoke) { + window.external.invoke(JSON.stringify(message)); + } + }, + frameClickHandler: function(e) { + var target = e.target; + var context = ''; + while (target) { + if (target.id == 'menu_page_blocker') { + IV.notify({ event: 'menu_page_blocker_click' }); + IV.menuShown(false); + return; + } + if (target.tagName == 'AUDIO' || target.tagName == 'VIDEO') { + return; + } + if (context === '' + && target.hasAttribute + && target.hasAttribute('data-context')) { + context = String(target.getAttribute('data-context')); + } + if (target.tagName == 'A') { + break; + } + target = target.parentNode; + } + if (!target || !target.hasAttribute('href')) { + return; + } + var base = document.createElement('A'); + base.href = window.location.href; + if (base.origin != target.origin + || base.pathname != target.pathname + || base.search != target.search) { + IV.notify({ + event: 'link_click', + url: target.href, + context: context, + }); + } else if (target.hash.length < 2) { + IV.jumpToHash(''); + } else { + IV.jumpToHash(decodeURIComponent(target.hash.substr(1))); + } + e.preventDefault(); + }, + getElementTop: function (element) { + var top = 0; + while (element && !element.classList.contains('page-scroll')) { + top += element.offsetTop; + element = element.offsetParent; + } + return top; + }, + jumpToHash: function (hash, instant) { + var current = IV.computeCurrentState(); + current.hash = hash; + window.history.replaceState( + current, + '', + 'page' + IV.index + '.html'); + if (hash == '') { + IV.scrollTo(0, instant); + return; + } + + var element = document.getElementsByName(hash)[0]; + if (element) { + IV.scrollTo(IV.getElementTop(element), instant); + } + }, + frameKeyDown: function (e) { + const keyW = (e.key === 'w') + || (e.code === 'KeyW') + || (e.keyCode === 87); + const keyQ = (e.key === 'q') + || (e.code === 'KeyQ') + || (e.keyCode === 81); + const keyM = (e.key === 'm') + || (e.code === 'KeyM') + || (e.keyCode === 77); + if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) { + e.preventDefault(); + IV.notify({ + event: 'keydown', + modifier: e.ctrlKey ? 'ctrl' : 'cmd', + key: keyW ? 'w' : keyQ ? 'q' : 'm', + }); + } else if (e.key === 'Escape' || e.keyCode === 27) { + e.preventDefault(); + if (IV.position) { + window.history.back(); + } else { + IV.notify({ + event: 'keydown', + key: 'escape', + }); + } + } + }, + frameMouseEnter: function (e) { + IV.notify({ event: 'mouseenter' }); + }, + frameMouseUp: function (e) { + IV.notify({ event: 'mouseup' }); + }, + lastScrollTop: 0, + frameScrolled: function (e) { + const was = IV.lastScrollTop; + IV.lastScrollTop = IV.findPageScroll().scrollTop; + IV.updateJumpToTop(was < IV.lastScrollTop); + IV.checkVideos(); + }, + updateJumpToTop: function (scrolledDown) { + if (IV.lastScrollTop < 100) { + document.getElementById('bottom_up').classList.add('hidden'); + } else if (scrolledDown && IV.lastScrollTop > 200) { + document.getElementById('bottom_up').classList.remove('hidden'); + } + }, + updateStyles: function (styles) { + if (IV.styles !== styles) { + IV.styles = styles; + document.getElementsByTagName('html')[0].style = styles; + } + }, + toggleChannelJoined: function (id, joined) { + IV.channelsJoined['channel' + id] = joined; + IV.checkChannelButtons(); + }, + checkChannelButtons: function() { + const channels = document.getElementsByClassName('channel'); + for (var i = 0; i < channels.length; ++i) { + const channel = channels[i]; + const full = String(channel.getAttribute('data-context')); + const value = IV.channelsJoined[full]; + if (value !== undefined) { + channel.classList.toggle('joined', value); + } + } + }, + slideshowSlide: function(el, delta) { + var dir = window.getComputedStyle(el, null).direction || 'ltr'; + var marginProp = dir == 'rtl' ? 'marginRight' : 'marginLeft'; + if (delta) { + var form = el.parentNode.firstChild; + var s = form.s; + const next = +s.value + delta; + s.value = (next == s.length) ? 0 : (next == -1) ? (s.length - 1) : next; + form.nextSibling.firstChild.style[marginProp] = (-100 * s.value) + '%'; + } else { + el.form.nextSibling.firstChild.style[marginProp] = (-100 * el.value) + '%'; + } + return false; + }, + initPreBlocks: function() { + if (!hljs) { + return; + } + var pres = document.getElementsByTagName('pre'); + for (var i = 0; i < pres.length; i++) { + if (pres[i].hasAttribute('data-language')) { + hljs.highlightBlock(pres[i]); + } + } + }, + initEmbedBlocks: function() { + var iframes = document.getElementsByTagName('iframe'); + for (var i = 0; i < iframes.length; i++) { + (function(iframe) { + window.addEventListener('message', function(event) { + if (event.source !== iframe.contentWindow || + event.origin != window.origin) { + return; + } + try { + var data = JSON.parse(event.data); + } catch(e) { + var data = {}; + } + if (data.eventType == 'resize_frame') { + if (data.eventData.height) { + iframe.style.height = data.eventData.height + 'px'; + } + } + }, false); + })(iframes[i]); + } + }, + addRipple: function (button, x, y) { + const ripple = document.createElement('span'); + ripple.classList.add('ripple'); + + const inner = document.createElement('span'); + inner.classList.add('inner'); + x -= button.offsetLeft; + y -= button.offsetTop; + + const mx = button.clientWidth - x; + const my = button.clientHeight - y; + const sq1 = x * x + y * y; + const sq2 = mx * mx + y * y; + const sq3 = x * x + my * my; + const sq4 = mx * mx + my * my; + const radius = Math.sqrt(Math.max(sq1, sq2, sq3, sq4)); + + inner.style.width = inner.style.height = `${2 * radius}px`; + inner.style.left = `${x - radius}px`; + inner.style.top = `${y - radius}px`; + inner.classList.add('inner'); + + ripple.addEventListener('animationend', function (e) { + if (e.animationName === 'fadeOut') { + ripple.remove(); + } + }); + + ripple.appendChild(inner); + button.appendChild(ripple); + }, + stopRipples: function (button) { + const id = button.id ? button.id : button; + button = document.getElementById(id); + const ripples = button.getElementsByClassName('ripple'); + for (var i = 0; i < ripples.length; ++i) { + const ripple = ripples[i]; + if (!ripple.classList.contains('hiding')) { + ripple.classList.add('hiding'); + } + } + }, + init: function () { + var current = IV.computeCurrentState(); + window.history.replaceState(current, '', IV.pageUrl(0)); + IV.jumpToHash(current.hash, true); + + IV.lastScrollTop = window.history.state.scroll; + IV.findPageScroll().onscroll = IV.frameScrolled; + + const buttons = document.getElementsByClassName('fixed_button'); + for (let i = 0; i < buttons.length; ++i) { + const button = buttons[i]; + button.addEventListener('mousedown', function (e) { + IV.addRipple(e.currentTarget, e.clientX, e.clientY); + }); + button.addEventListener('mouseup', function (e) { + const id = e.currentTarget.id; + setTimeout(function () { + IV.stopRipples(id); + }, 0); + }); + button.addEventListener('mouseleave', function (e) { + IV.stopRipples(e.currentTarget); + }); + } + IV.initMedia(); + IV.notify({ event: 'ready' }); + + IV.forceScrollFocus(); + IV.frameScrolled(); + }, + initMedia: function () { + var scroll = IV.findPageScroll(); + const photos = scroll.getElementsByClassName('photo'); + for (let i = 0; i < photos.length; ++i) { + const photo = photos[i]; + if (photo.classList.contains('loaded')) { + continue; + } + + const url = photo.style.backgroundImage; + if (!url || url.length < 7) { + continue; + } + var img = new Image(); + img.onload = function () { + photo.classList.add('loaded'); + } + img.src = url.substr(5, url.length - 7); + if (img.complete) { + photo.classList.add('loaded'); + IV.stopAnimations(photo); + } + } + IV.videos = []; + const videos = scroll.getElementsByClassName('video'); + for (let i = 0; i < videos.length; ++i) { + const element = videos[i]; + IV.videos.push({ + element: element, + src: String(element.getAttribute('data-src')), + autoplay: (element.getAttribute('data-autoplay') == '1'), + loop: (element.getAttribute('data-loop') == '1'), + small: (element.getAttribute('data-small') == '1'), + filled: (element.firstChild + && element.firstChild.tagName == 'VIDEO'), + }); + } + }, + checkVideos: function () { + const visibleTop = IV.lastScrollTop; + const visibleBottom = visibleTop + IV.findPageScroll().offsetHeight; + const videos = IV.videos; + for (let i = 0; i < videos.length; ++i) { + const video = videos[i]; + const element = video.element; + const wrap = element.offsetParent; // video-wrap + const top = IV.getElementTop(wrap); + const bottom = top + wrap.offsetHeight; + if (top < visibleBottom && bottom > visibleTop) { + if (!video.created) { + video.created = new Date(); + video.loaded = false; + element.innerHTML = ''; + var media = element.firstChild; + media.oncontextmenu = function () { return false; }; + media.oncanplay = IV.checkVideos; + media.onloadeddata = IV.checkVideos; + } + } else if (video.created && video.autoplay) { + video.created = false; + element.innerHTML = ''; + } + if (video.created && !video.loaded) { + var media = element.firstChild; + const HAVE_CURRENT_DATA = 2; + if (media && media.readyState >= HAVE_CURRENT_DATA) { + video.loaded = true; + media.classList.add('loaded'); + if ((new Date() - video.created) < 100) { + IV.stopAnimations(media); + } + } + } + } + }, + showTooltip: function (text) { + var toast = document.createElement('div'); + toast.classList.add('toast'); + toast.textContent = text; + document.body.appendChild(toast); + setTimeout(function () { + toast.classList.add('hiding'); + }, 2000); + setTimeout(function () { + document.body.removeChild(toast); + }, 3000); + }, + scrollTo: function (y, instant) { + if (y < 200) { + document.getElementById('bottom_up').classList.add('hidden'); + } + IV.findPageScroll().scrollTo({ + top: y || 0, + behavior: instant ? 'instant' : 'smooth' + }); + }, + computeCurrentState: function () { + var now = IV.findPageScroll(); + return { + position: IV.position, + index: IV.index, + hash: ((!window.history.state + || window.history.state.hash === undefined) + ? window.location.hash.substr(1) + : window.history.state.hash), + scroll: now ? now.scrollTop : 0 + }; + }, + pageUrl: function (index, hash) { + var result = 'page' + index + '.html'; + if (hash) { + result += '#' + hash; + } + return result; + }, + navigateTo: function (index, hash) { + if (!index && !IV.index) { + IV.navigateToDOM(IV.index, hash); + return; + } + IV.pending = [index, hash]; + if (!IV.cache[index]) { + IV.loadPage(index); + } else if (IV.cache[index].dom) { + IV.navigateToDOM(index, hash); + } else if (IV.cache[index].content) { + IV.navigateToLoaded(index, hash); + } + }, + applyUpdatedContent: function (index) { + if (IV.index != index) { + IV.cache[index].contentUpdated = (IV.cache[index].dom !== undefined); + return; + } + var data = JSON.parse(IV.cache[index].content); + var article = function (el) { + return el.getElementsByTagName('article')[0]; + }; + var from = article(IV.findPageScroll()); + var to = article(IV.makeScrolledContent(data.html)); + morphdom(from, to, { + onBeforeElUpdated: function (fromEl, toEl) { + if (fromEl.classList.contains('video') + && toEl.classList.contains('video') + && fromEl.hasAttribute('data-src') + && toEl.hasAttribute('data-src') + && (fromEl.getAttribute('data-src') + == toEl.getAttribute('data-src'))) { + return false; + } else if (fromEl.tagName == 'SECTION' + && fromEl.classList.contains('channel') + && fromEl.hasAttribute('data-context') + && toEl.tagName == 'SECTION' + && toEl.classList.contains('channel') + && toEl.hasAttribute('data-context') + && (String(fromEl.getAttribute('data-context')) + == String(toEl.getAttribute('data-context')))) { + return false; + } else if (fromEl.classList.contains('loaded')) { + toEl.classList.add('loaded'); + } + return !fromEl.isEqualNode(toEl); + } + }); + IV.initMedia(); + eval(data.js); + }, + loadPage: function (index) { + if (!IV.cache[index]) { + IV.cache[index] = {}; + } + IV.cache[index].loading = true; + + let xhr = new XMLHttpRequest(); + xhr.onload = function () { + IV.cache[index].loading = false; + IV.cache[index].content = xhr.responseText; + IV.applyUpdatedContent(index); + if (IV.pending && IV.pending[0] == index) { + IV.navigateToLoaded(index, IV.pending[1]); + } + if (IV.cache[index].reloadPending) { + IV.cache[index].reloadPending = false; + IV.reloadPage(index); + } + } + + xhr.open('GET', 'page' + index + '.json'); + xhr.send(); + }, + reloadPage: function (index) { + if (IV.cache[index] && IV.cache[index].loading) { + IV.cache[index].reloadPending = true; + return; + } + IV.loadPage(index); + }, + + makeScrolledContent: function (html) { + var result = document.createElement('div'); + result.className = 'page-scroll'; + result.tabIndex = '-1'; + result.innerHTML = '
' + + html + + '
'; + result.onscroll = IV.frameScrolled; + return result; + }, + navigateToLoaded: function (index, hash) { + if (IV.cache[index].dom) { + IV.navigateToDOM(index, hash); + } else { + var data = JSON.parse(IV.cache[index].content); + IV.cache[index].dom = IV.makeScrolledContent(data.html); + + IV.navigateToDOM(index, hash); + eval(data.js); + } + }, + navigateToDOM: function (index, hash) { + IV.pending = null; + if (IV.index == index) { + IV.jumpToHash(hash); + IV.forceScrollFocus(); + return; + } + window.history.replaceState( + IV.computeCurrentState(), + '', + IV.pageUrl(IV.index)); + + IV.position = IV.position + 1; + window.history.pushState( + { position: IV.position, index: index, hash: hash }, + '', + IV.pageUrl(index)); + IV.showDOM(index, hash); + }, + findPageScroll: function () { + var all = document.getElementsByClassName('page-scroll'); + for (i = 0; i < all.length; ++i) { + if (!all[i].classList.contains('hidden-left') + && !all[i].classList.contains('hidden-right')) { + return all[i]; + } + } + return null; + }, + showDOM: function (index, hash, scroll) { + IV.pending = null; + if (IV.index != index) { + var initial = !window.history.state + || window.history.state.position === undefined; + var back = initial + || IV.position > window.history.state.position; + IV.position = initial ? 0 : window.history.state.position; + + var now = IV.cache[index].dom; + var was = IV.findPageScroll(); + if (!IV.cache[IV.index]) { + IV.cache[IV.index] = {}; + } + IV.cache[IV.index].dom = was; + was.parentNode.appendChild(now); + if (scroll !== undefined) { + now.scrollTop = scroll; + setTimeout(function () { + // When returning by history.back to an URL with a hash + // for the first time browser forces the scroll to the + // hash instead of the saved scroll position. + // + // This workaround prevents incorrect scroll position. + now.scrollTop = scroll; + }, 0); + } + + now.classList.add(back ? 'hidden-left' : 'hidden-right'); + now.classList.remove(back ? 'hidden-right' : 'hidden-left'); + IV.stopAnimations(now.firstChild); + + if (!was.listening) { + was.listening = true; + was.firstChild.addEventListener('transitionend', function (e) { + if (was.classList.contains('hidden-left') + || was.classList.contains('hidden-right')) { + if (was.parentNode) { + was.parentNode.removeChild(was); + var videos = was.getElementsByClassName('video'); + for (var i = 0; i < videos.length; ++i) { + videos[i].innerHTML = ''; + } + } + } + }); + } + + was.classList.add(back ? 'hidden-right' : 'hidden-left'); + now.classList.remove(back ? 'hidden-left' : 'hidden-right'); + + IV.index = index; + IV.notify({ + event: 'location_change', + index: IV.index, + position: IV.position, + hash: IV.computeCurrentState().hash, + }); + if (IV.cache[index].contentUpdated) { + IV.cache[index].contentUpdated = false; + IV.applyUpdatedContent(index); + } else { + IV.initMedia(); + } + IV.checkChannelButtons(); + if (scroll === undefined) { + IV.jumpToHash(hash, true); + } else { + IV.lastScrollTop = scroll; + IV.updateJumpToTop(true); + } + } else if (scroll !== undefined) { + IV.scrollTo(scroll); + IV.lastScrollTop = scroll; + IV.updateJumpToTop(true); + } else { + IV.jumpToHash(hash); + } + + IV.forceScrollFocus(); + IV.frameScrolled(); + }, + forceScrollFocus: function () { + IV.findPageScroll().focus(); + setTimeout(function () { + // Doesn't work on #hash-ed pages in Windows WebView2 otherwise. + IV.findPageScroll().focus(); + }, 100); + }, + stopAnimations: function (element) { + element.getAnimations().forEach( + (animation) => animation.finish()); + }, + back: function () { + window.history.back(); + }, + menuShown: function (shown) { + var already = document.getElementById('menu_page_blocker'); + if (already && shown) { + return; + } else if (already) { + document.body.removeChild(already); + return; + } else if (!shown) { + return; + } + var blocker = document.createElement('div'); + blocker.id = 'menu_page_blocker'; + document.body.appendChild(blocker); + }, + + videos: {}, + videosPlaying: {}, + + cache: {}, + channelsJoined: {}, + index: 0, + position: 0 +}; + +document.onclick = IV.frameClickHandler; +document.onkeydown = IV.frameKeyDown; +document.onmouseenter = IV.frameMouseEnter; +document.onmouseup = IV.frameMouseUp; +document.onresize = IV.checkVideos; +window.onmessage = IV.postMessageHandler; +window.addEventListener('popstate', function (e) { + if (e.state) { + IV.showDOM(e.state.index, e.state.hash, e.state.scroll); + } +}); +document.addEventListener("DOMContentLoaded", IV.forceScrollFocus); diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0048795c9..d40e1b3b2 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -652,6 +652,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_messages_privacy" = "Messages"; "lng_settings_voices_privacy" = "Voice messages"; "lng_settings_bio_privacy" = "Bio"; +"lng_settings_birthday_privacy" = "Date of Birth"; "lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages."; "lng_settings_privacy_premium_link" = "Telegram Premium"; "lng_settings_passcode_disable" = "Disable Passcode"; @@ -664,6 +665,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_phone_label" = "Phone number"; "lng_settings_username_add" = "Add username"; "lng_settings_username_about" = "Username lets people contact you on Telegram without needing your phone number."; +"lng_settings_birthday_label" = "Date of Birth"; +"lng_settings_birthday_add" = "Add"; +"lng_settings_birthday_about" = "Choose who can see your birthday in {link}."; +"lng_settings_birthday_about_link" = "Settings"; +"lng_settings_birthday_contacts" = "Only your contacts can see your birthday. {link}"; +"lng_settings_birthday_contacts_link" = "Change >"; +"lng_settings_birthday_saved" = "Your date of birth was updated."; +"lng_settings_birthday_reset" = "Remove"; +"lng_settings_channel_label" = "Personal channel"; +"lng_settings_channel_add" = "Add"; +"lng_settings_channel_remove" = "Remove"; +"lng_settings_channel_no_yet" = "You don't have any public channels yet."; +"lng_settings_channel_start" = "Start a Channel"; +"lng_settings_channel_saved" = "Your personal channel was updated."; +"lng_settings_channel_removed" = "Your personal channel was removed."; "lng_settings_add_account_about" = "You can add up to four accounts with different phone numbers."; "lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality."; "lng_settings_advanced" = "Advanced"; @@ -1087,12 +1103,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_everyone" = "Everybody"; "lng_edit_privacy_contacts" = "My contacts"; "lng_edit_privacy_close_friends" = "Close friends"; +"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium"; "lng_edit_privacy_nobody" = "Nobody"; "lng_edit_privacy_premium" = "Premium users"; "lng_edit_privacy_exceptions" = "Add exceptions"; +"lng_edit_privacy_user_types" = "User types"; +"lng_edit_privacy_users_and_groups" = "Users and groups"; +"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers"; "lng_edit_privacy_exceptions_count#one" = "{count} user"; "lng_edit_privacy_exceptions_count#other" = "{count} users"; +"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}"; "lng_edit_privacy_exceptions_add" = "Add users or groups"; "lng_edit_privacy_phone_number_title" = "Phone number privacy"; @@ -1136,6 +1157,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_about_always_title" = "Always allow"; "lng_edit_privacy_about_never_title" = "Never allow"; +"lng_edit_privacy_birthday_title" = "Date of birth privacy"; +"lng_edit_privacy_birthday_header" = "Who can see my date of birth"; +"lng_edit_privacy_birthday_always_empty" = "Always allow"; +"lng_edit_privacy_birthday_never_empty" = "Never allow"; +"lng_edit_privacy_birthday_exceptions" = "These users will or will not be able to see your date of birth regardless of the settings above."; +"lng_edit_privacy_birthday_always_title" = "Always allow"; +"lng_edit_privacy_birthday_never_title" = "Never allow"; +"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}"; +"lng_edit_privacy_birthday_yet_link" = "Add my birthday >"; + "lng_edit_privacy_calls_title" = "Voice calls privacy"; "lng_edit_privacy_calls_header" = "Who can call you"; "lng_edit_privacy_calls_always_empty" = "Always allow"; @@ -1288,6 +1319,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_profile_photo_by_you" = "photo set by you"; "lng_profile_public_photo" = "public photo"; +"lng_invite_upgrade_title" = "Upgrade to Premium"; +"lng_invite_upgrade_group_invite#one" = "{users} only accepts invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_invite#other" = "{users} only accept invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_write#one" = "{users} only accepts messages and invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_write#other" = "{users} only accept messages and invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_invite#one" = "{users} only accepts invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_invite#other" = "{users} only accept invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_write#one" = "{users} only accepts messages and invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_write#other" = "{users} only accept messages and invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_users_few" = "{users} and {last}"; +"lng_invite_upgrade_users_many#one" = "{users} and **{count}** more person"; +"lng_invite_upgrade_users_many#other" = "{users} and **{count}** more people"; +"lng_invite_upgrade_or" = "or"; +"lng_invite_upgrade_via_title" = "Invite via Link"; +"lng_invite_upgrade_via_group_about" = "You can send an invite link to the group in a private message instead."; +"lng_invite_upgrade_via_channel_about" = "You can send an invite link to the channel in a private message instead."; +"lng_invite_status_disabled" = "available only to Premium users"; + "lng_via_link_group_one" = "**{user}** restricts adding them to groups.\nYou can send them an invite link as message instead."; "lng_via_link_group_many#one" = "**{count} user** restricts adding them to groups.\nYou can send them an invite link as message instead."; "lng_via_link_group_many#other" = "**{count} users** restrict adding them to groups.\nYou can send them an invite link as message instead."; @@ -1303,12 +1352,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_via_link_shared_many#one" = "Link shared with **{count} user**."; "lng_via_link_shared_many#other" = "Link shared with **{count} users**."; +"lng_info_personal_channel_label" = "Channel"; "lng_info_mobile_label" = "Mobile"; "lng_info_mobile_context_menu_fragment_about" = "This number is not tied to a SIM card and was acquired on {link}."; "lng_info_mobile_context_menu_fragment_about_link" = "Fragment"; "lng_info_mobile_hidden" = "Hidden"; "lng_info_username_label" = "Username"; "lng_info_usernames_label" = "also"; +"lng_info_birthday_label" = "Date of birth"; +"lng_info_birthday_years#one" = "{date} ({count} year old)"; +"lng_info_birthday_years#other" = "{date} ({count} years old)"; +"lng_info_birthday_today_years#one" = "{date}\n({count} year old)"; +"lng_info_birthday_today_years#other" = "{date}\n({count} years old)"; +"lng_info_birthday_today_label" = "Birthday today"; +"lng_info_birthday_today" = "{emoji} {date}"; "lng_info_bio_label" = "Bio"; "lng_info_link_label" = "Link"; "lng_info_location_label" = "Location"; @@ -1540,6 +1597,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_report_button" = "Report"; "lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon."; +"lng_report_sponsored_hidden" = "Sponsored messages will be hidden."; +"lng_report_sponsored_reported" = "We will review this ad to ensure it matches out {link}."; +"lng_report_sponsored_reported_link" = "Ad Policies and Guidelines"; +"lng_report_sponsored_reported_learn" = "Learn more about our {link}."; + "lng_channel_add_members" = "Add members"; "lng_channel_add_users" = "Add users"; "lng_channel_add_removed" = "Remove user"; @@ -1760,6 +1822,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_giveaway_results_none" = "No winners of the giveaway could be selected."; "lng_action_boost_apply#one" = "{from} boosted the group"; "lng_action_boost_apply#other" = "{from} boosted the group {count} times"; +"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?"; "lng_similar_channels_title" = "Similar channels"; "lng_similar_channels_view_all" = "View all"; @@ -1919,6 +1982,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_forwarded_imported" = "This message was imported from another app. It may not be real."; "lng_signed_author" = "Author: {user}"; "lng_sponsored_message_title" = "Sponsored"; +"lng_sponsored_message_revenue_button" = "what's this?"; "lng_recommended_message_title" = "Recommended"; "lng_edited" = "edited"; "lng_commented" = "commented"; @@ -2185,6 +2249,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_business_about_away_messages" = "Define messages that are automatically sent when you are off."; "lng_business_subtitle_chatbots" = "Chatbots"; "lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions."; +"lng_business_subtitle_chat_intro" = "Custom Intro"; +"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you."; +"lng_business_subtitle_chat_links" = "Links to Chat"; +"lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message."; "lng_location_title" = "Location"; "lng_location_about" = "Display the location of your business on your account."; @@ -2286,8 +2354,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot."; "lng_chatbots_remove" = "Remove Bot"; "lng_chatbots_not_found" = "Chatbot not found."; +"lng_chatbots_not_supported" = "This bot doesn't support Telegram Business yet."; "lng_chatbots_add" = "Add"; "lng_chatbots_info_url" = "https://telegram.org/privacy"; +"lng_chatbot_status_can_reply" = "bot manages this chat"; +"lng_chatbot_status_paused" = "bot stopped"; +"lng_chatbot_status_views" = "bot has access to this chat"; +"lng_chatbot_button_pause" = "Stop"; +"lng_chatbot_button_resume" = "Start"; +"lng_chatbot_menu_manage" = "Manage bot"; +"lng_chatbot_menu_remove" = "Remove bot from this chat"; +"lng_chatbot_menu_revoke" = "Revoke access to this chat"; + +"lng_chat_intro_title" = "Custom Intro"; +"lng_chat_intro_subtitle" = "Customize your intro"; +"lng_chat_intro_default_title" = "No messages here yet..."; +"lng_chat_intro_default_message" = "Send a message or click on the greeting below"; +"lng_chat_intro_enter_title" = "Enter Title"; +"lng_chat_intro_enter_message" = "Enter Message"; +"lng_chat_intro_choose_sticker" = "Choose Sticker"; +"lng_chat_intro_random_sticker" = "Random"; +"lng_chat_intro_about" = "You can customize the message people see before they start a chat with you."; +"lng_chat_intro_reset" = "Reset to Default"; + +"lng_chat_links_title" = "Links to Chat"; +"lng_chat_links_about" = "Give your customers short links that start a chat with you – and suggest the first message from them to you."; +"lng_chat_links_create_link" = "Create a Link to Chat"; +"lng_chat_links_footer" = "You can also use a simple link for a chat with you – {links}"; +"lng_chat_links_footer_both" = "{username} or {link}"; +"lng_chat_links_no_clicks" = "no clicks"; +"lng_chat_links_clicks#one" = "{count} click"; +"lng_chat_links_clicks#other" = "{count} clicks"; +"lng_chat_link_new_title" = "New Link"; +"lng_chat_link_edit_title" = "Edit Link"; +"lng_chat_link_description" = "Add a message that will be entered in the message field for anyone who starts a chat with you using this link."; +"lng_chat_link_placeholder" = "Add Preset Message"; +"lng_chat_link_saved" = "Chat link saved."; +"lng_chat_link_copy" = "Copy"; +"lng_chat_link_share" = "Share"; +"lng_chat_link_rename" = "Rename"; +"lng_chat_link_delete" = "Delete"; +"lng_chat_link_name" = "Link Name (optional)"; +"lng_chat_link_name_about" = "Add a name for this link that only you will see."; +"lng_chat_link_delete_sure" = "Are you sure you want to delete this chat link?"; +"lng_chat_link_qr_title" = "Chat Link QR Code"; +"lng_chat_link_qr_about" = "Everyone on Telegram can scan this code to contact you."; +"lng_chat_link_copied" = "Chat link copied to clipboard."; "lng_boost_channel_button" = "Boost Channel"; "lng_boost_group_button" = "Boost Group"; @@ -2387,6 +2499,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boost_channel_needs_level_reactions#one" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as a reaction."; "lng_boost_channel_needs_level_reactions#other" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as reactions."; +"lng_boost_channel_title_cpm" = "Boost Channel"; +"lng_boost_channel_needs_level_cpm#one" = "Your channel needs to reach **Level {count}** to switch off ads."; +"lng_boost_channel_needs_level_cpm#other" = "Your channel needs to reach **Level {count}** to switch off ads."; + "lng_boost_group_title_emoji" = "Enable emoji pack"; "lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack."; "lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack."; @@ -2593,6 +2709,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_link_also_send" = "You can also {link} to a friend as a gift."; "lng_gift_link_also_send_link" = "send this link"; "lng_gift_link_use" = "Use Link"; +"lng_gift_link_already_title" = "You already have Telegram Premium"; +"lng_gift_link_already_about" = "You can activate this link after {date} or {link} to a friend."; +"lng_gift_link_already_link" = "send the link"; "lng_gift_link_used_title" = "Used Gift Link"; "lng_gift_link_used_about" = "This link was used to activate\na **Telegram Premium** subscription."; "lng_gift_link_used_footer" = "This link was used on {date}."; @@ -2883,6 +3002,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bot_remove_from_side_menu_done" = "Bot removed from the main menu."; "lng_bot_settings" = "Settings"; "lng_bot_open" = "Open Bot"; +"lng_bot_terms" = "Terms of Use"; "lng_bot_reload_page" = "Reload Page"; "lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat."; "lng_bot_add_to_menu_done" = "Bot added to the menu."; @@ -3013,6 +3133,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_delete_msg" = "Delete Message"; "lng_context_auto_delete_in" = "auto-delete in {duration}"; "lng_context_select_msg" = "Select Message"; +"lng_context_select_msg_bulk" = "Select up to this message"; "lng_context_report_msg" = "Report Message"; "lng_context_pin_msg" = "Pin Message"; "lng_context_unpin_msg" = "Unpin Message"; @@ -3511,6 +3632,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_call_cancel" = "Cancel"; "lng_call_microphone_off" = "{user}'s microphone is off"; +"lng_call_battery_level_low" = "{user}'s battery level is low"; "lng_group_call_title" = "Voice Chat"; "lng_group_call_title_channel" = "Live Stream"; @@ -4561,6 +4683,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_view_button_external_link" = "Open link"; "lng_view_button_boost" = "Boost"; "lng_view_button_giftcode" = "Open"; +"lng_view_button_iv" = "Instant View"; "lng_sponsored_hide_ads" = "Hide"; "lng_sponsored_title" = "What are sponsored messages?"; @@ -4568,6 +4691,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together."; "lng_sponsored_info_menu" = "About this ad"; "lng_sponsored_info_submenu" = "Advertiser: {text}"; +"lng_sponsored_menu_revenued_about" = "About These Ads"; +"lng_sponsored_menu_revenued_report" = "Report Ad"; +"lng_sponsored_revenued_subtitle" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:"; +"lng_sponsored_revenued_info1_title" = "Respect Your Privacy"; +"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are abased on the channel in which you see them."; +"lng_sponsored_revenued_info2_title" = "Help the Channel Creator"; +"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed."; +"lng_sponsored_revenued_info3_title" = "Can Be Removed"; +"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?"; +"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}"; "lng_telegram_features_url" = "https://t.me/TelegramTips"; @@ -4863,9 +4998,74 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}"; "lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}"; +"lng_channel_earn_title" = "Monetization"; +"lng_channel_earn_about" = "Telegram shares 50% of the revenue from ads displayed in your channel. {link}"; +"lng_channel_earn_about_link" = "Learn more {emoji}"; +"lng_channel_earn_overview_title" = "Proceeds overview"; +"lng_channel_earn_available" = "Balance available to withdraw"; +"lng_channel_earn_reward" = "Proceeds since last withdrawal"; +"lng_channel_earn_total" = "Total lifetime proceeds"; +"lng_channel_earn_balance_title" = "Available balance"; +"lng_channel_earn_balance_button" = "Withdraw"; +"lng_channel_earn_balance_password_title" = "Two-step verification"; +"lng_channel_earn_balance_password_description" = "Please enter your password to withdraw."; +"lng_channel_earn_balance_about" = "Collect your reward using Fragment, a third-party platform used by the advertiser to pay for the ad. {link}"; +"lng_channel_earn_balance_about_temp" = "In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. {link}"; +"lng_channel_earn_transfer_sure_about1" = "Check the address of the recipient:"; +"lng_channel_earn_transfer_sure_about2" = "This action can not be undone. If the address above is incorrect you will lose your TON."; +"lng_channel_earn_history_title" = "Transaction history"; +"lng_channel_earn_history_in" = "Proceeds from ads"; +"lng_channel_earn_history_in_about" = "Proceeds from ads displayed in"; +"lng_channel_earn_history_out" = "Withdrawal"; +"lng_channel_earn_history_out_failed" = "Not Completed"; +"lng_channel_earn_history_out_about_failed" = "Withdrawal failed"; +"lng_channel_earn_history_out_button" = "View in Blockchain Explorer"; +"lng_channel_earn_history_return" = "Refund"; +"lng_channel_earn_history_return_about" = "Refunded back"; +"lng_channel_earn_history_pending" = "Pending"; +"lng_channel_earn_history_show_more#one" = "Show {count} More Transaction"; +"lng_channel_earn_history_show_more#other" = "Show {count} More Transactions"; +"lng_channel_earn_off" = "Switch Off Ads"; +"lng_channel_earn_off_about" = "You will not be eligible for any rewards if you switch off ads."; +"lng_channel_earn_cpm_min" = "No Ads"; +"lng_channel_earn_cpm#one" = "{emoji} {count} CPM"; +"lng_channel_earn_cpm#other" = "{emoji} {count} CPM"; +"lng_channel_earn_learn_title" = "Earn From Your Channel"; +"lng_channel_earn_learn_in_subtitle" = "Telegram Ads"; +"lng_channel_earn_learn_in_about" = "Telegram can display ads in your channel."; +"lng_channel_earn_learn_split_subtitle" = "50:50 revenue split"; +"lng_channel_earn_learn_split_about" = "You can receive 50% of the ad revenue in TON."; +"lng_channel_earn_learn_out_subtitle" = "Flexible withdrawals"; +"lng_channel_earn_learn_out_about" = "You can withdraw your TON any time."; +"lng_channel_earn_learn_coin_title" = "What is {emoji} TON?"; +"lng_channel_earn_learn_coin_about" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}"; +"lng_channel_earn_learn_close" = "Got it"; +"lng_channel_earn_chart_top_hours" = "Ad impressions"; +"lng_channel_earn_chart_revenue" = "Ad revenue"; +"lng_channel_earn_chart_overriden_detail_currency" = "Revenue in TON"; +"lng_channel_earn_chart_overriden_detail_usd" = "Revenue in USD"; + "lng_contact_add" = "Add"; "lng_contact_send_message" = "message"; +"lng_iv_open_in_browser" = "Open in Browser"; +"lng_iv_share" = "Share"; +"lng_iv_join_channel" = "Join"; +"lng_iv_window_title" = "Instant View"; + +"lng_limit_download_title" = "Download speed limited"; +"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}."; +"lng_limit_download_increase_times#one" = "**{count}** time"; +"lng_limit_download_increase_times#other" = "**{count}** times"; +"lng_limit_download_increase_speed" = "by **{percent}**"; +"lng_limit_download_subscribe_link" = "Telegram Premium"; +"lng_limit_upload_title" = "Upload speed limited"; +"lng_limit_upload_subscribe" = "Subscribe to {link} and increase upload speed {increase}."; +"lng_limit_upload_increase_times#one" = "**{count}** time"; +"lng_limit_upload_increase_times#other" = "**{count}** times"; +"lng_limit_upload_increase_speed" = "by **{percent}**"; +"lng_limit_upload_subscribe_link" = "Telegram Premium"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index 12666b6fd..b63b6d15f 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -21,5 +21,6 @@ ../../animations/writing.tgs ../../animations/hours.tgs ../../animations/phone.tgs + ../../animations/chat_link.tgs diff --git a/Telegram/Resources/qrc/telegram/iv.qrc b/Telegram/Resources/qrc/telegram/iv.qrc new file mode 100644 index 000000000..1c9e9eb6a --- /dev/null +++ b/Telegram/Resources/qrc/telegram/iv.qrc @@ -0,0 +1,9 @@ + + + ../../iv_html/page.css + ../../iv_html/page.js + ../../iv_html/highlight.9.12.0.css + ../../iv_html/highlight.9.12.0.js + ../../iv_html/morphdom-umd.min.2.7.2.js + + diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index b41618532..2627ef790 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.0.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 67536dd5d..7585dfef0 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 4,15,2,0 - PRODUCTVERSION 4,15,2,0 + FILEVERSION 4,16,0,0 + PRODUCTVERSION 4,16,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop" - VALUE "FileVersion", "4.15.2.0" + VALUE "FileVersion", "4.16.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.15.2.0" + VALUE "ProductVersion", "4.16.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 40604a779..ae31782f3 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 4,15,2,0 - PRODUCTVERSION 4,15,2,0 + FILEVERSION 4,16,0,0 + PRODUCTVERSION 4,16,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Radolyn Labs" VALUE "FileDescription", "AyuGram Desktop Updater" - VALUE "FileVersion", "4.15.2.0" + VALUE "FileVersion", "4.16.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "AyuGram Desktop" - VALUE "ProductVersion", "4.15.2.0" + VALUE "ProductVersion", "4.16.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp index d49f1bbf7..706b7a7cb 100644 --- a/Telegram/SourceFiles/api/api_bot.cpp +++ b/Telegram/SourceFiles/api/api_bot.cpp @@ -421,10 +421,10 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) { MTP_int(itemId), MTP_int(id), MTP_vector_from_range( - result - | ranges::views::transform([]( - not_null peer) { - return MTPInputPeer(peer->input); })) + result | ranges::views::transform([]( + not_null peer) { + return MTPInputPeer(peer->input); + })) )).done([=](const MTPUpdates &result) { peer->session().api().applyUpdates(result); }).send(); diff --git a/Telegram/SourceFiles/api/api_chat_links.cpp b/Telegram/SourceFiles/api/api_chat_links.cpp new file mode 100644 index 000000000..aa5be9f55 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_links.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 "api/api_chat_links.h" + +#include "api/api_text_entities.h" +#include "apiwrap.h" +#include "data/data_session.h" +#include "main/main_session.h" + +namespace Api { +namespace { + +[[nodiscard]] ChatLink FromMTP( + not_null session, + const MTPBusinessChatLink &link) { + const auto &data = link.data(); + return { + .link = qs(data.vlink()), + .title = qs(data.vtitle().value_or_empty()), + .message = { + qs(data.vmessage()), + EntitiesFromMTP( + session, + data.ventities().value_or_empty()) + }, + .clicks = data.vviews().v, + }; +} + +[[nodiscard]] MTPInputBusinessChatLink ToMTP( + not_null session, + const QString &title, + const TextWithEntities &message) { + auto entities = EntitiesToMTP( + session, + message.entities, + ConvertOption::SkipLocal); + using Flag = MTPDinputBusinessChatLink::Flag; + const auto flags = (title.isEmpty() ? Flag() : Flag::f_title) + | (entities.v.isEmpty() ? Flag() : Flag::f_entities); + return MTP_inputBusinessChatLink( + MTP_flags(flags), + MTP_string(message.text), + std::move(entities), + MTP_string(title)); +} + +} // namespace + +ChatLinks::ChatLinks(not_null api) : _api(api) { +} + +void ChatLinks::create( + const QString &title, + const TextWithEntities &message, + Fn done) { + const auto session = &_api->session(); + _api->request(MTPaccount_CreateBusinessChatLink( + ToMTP(session, title, message) + )).done([=](const MTPBusinessChatLink &result) { + const auto link = FromMTP(session, result); + _list.push_back(link); + _updates.fire({ .was = QString(), .now = link }); + if (done) done(link); + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(Link()); + }).send(); +} + +void ChatLinks::edit( + const QString &link, + const QString &title, + const TextWithEntities &message, + Fn done) { + const auto session = &_api->session(); + _api->request(MTPaccount_EditBusinessChatLink( + MTP_string(link), + ToMTP(session, title, message) + )).done([=](const MTPBusinessChatLink &result) { + const auto parsed = FromMTP(session, result); + if (parsed.link != link) { + LOG(("API Error: EditBusinessChatLink changed the link.")); + if (done) done(Link()); + return; + } + const auto i = ranges::find(_list, link, &Link::link); + if (i != end(_list)) { + *i = parsed; + _updates.fire({ .was = link, .now = parsed }); + if (done) done(parsed); + } else { + LOG(("API Error: EditBusinessChatLink link not found.")); + if (done) done(Link()); + } + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(Link()); + }).send(); +} + +void ChatLinks::destroy( + const QString &link, + Fn done) { + _api->request(MTPaccount_DeleteBusinessChatLink( + MTP_string(link) + )).done([=] { + const auto i = ranges::find(_list, link, &Link::link); + if (i != end(_list)) { + _list.erase(i); + _updates.fire({ .was = link }); + if (done) done(); + } else { + LOG(("API Error: DeleteBusinessChatLink link not found.")); + if (done) done(); + } + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(); + }).send(); +} + +void ChatLinks::preload() { + if (_loaded || _requestId) { + return; + } + _requestId = _api->request(MTPaccount_GetBusinessChatLinks( + )).done([=](const MTPaccount_BusinessChatLinks &result) { + const auto &data = result.data(); + const auto session = &_api->session(); + const auto owner = &session->data(); + owner->processUsers(data.vusers()); + owner->processChats(data.vchats()); + auto links = std::vector(); + links.reserve(data.vlinks().v.size()); + for (const auto &link : data.vlinks().v) { + links.push_back(FromMTP(session, link)); + } + _list = std::move(links); + _loaded = true; + _loadedUpdates.fire({}); + }).fail([=] { + _requestId = 0; + _loaded = true; + _loadedUpdates.fire({}); + }).send(); +} + +const std::vector &ChatLinks::list() const { + return _list; +} + +bool ChatLinks::loaded() const { + return _loaded; +} + +rpl::producer<> ChatLinks::loadedUpdates() const { + return _loadedUpdates.events(); +} + +rpl::producer ChatLinks::updates() const { + return _updates.events(); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_links.h b/Telegram/SourceFiles/api/api_chat_links.h new file mode 100644 index 000000000..34226eab9 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_links.h @@ -0,0 +1,64 @@ +/* +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 ChatLink { + QString link; + QString title; + TextWithEntities message; + int clicks = 0; +}; + +struct ChatLinkUpdate { + QString was; + std::optional now; +}; + +class ChatLinks final { +public: + explicit ChatLinks(not_null api); + + using Link = ChatLink; + using Update = ChatLinkUpdate; + + void create( + const QString &title, + const TextWithEntities &message, + Fn done = nullptr); + void edit( + const QString &link, + const QString &title, + const TextWithEntities &message, + Fn done = nullptr); + void destroy( + const QString &link, + Fn done = nullptr); + + void preload(); + [[nodiscard]] const std::vector &list() const; + [[nodiscard]] bool loaded() const; + [[nodiscard]] rpl::producer<> loadedUpdates() const; + [[nodiscard]] rpl::producer updates() const; + +private: + const not_null _api; + + std::vector _list; + rpl::event_stream<> _loadedUpdates; + mtpRequestId _requestId = 0; + bool _loaded = false; + + rpl::event_stream _updates; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 1714d9e2b..6761d2fff 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -487,9 +487,9 @@ void ChatParticipants::requestCountDelayed( } void ChatParticipants::add( + std::shared_ptr show, not_null peer, const std::vector> &users, - std::shared_ptr show, bool passGroupHistory, Fn done) { if (const auto chat = peer->asChat()) { @@ -498,19 +498,28 @@ void ChatParticipants::add( chat->inputChat, user->inputUser, MTP_int(passGroupHistory ? kForwardMessagesOnAdd : 0) - )).done([=](const MTPUpdates &result) { - chat->session().api().applyUpdates(result); + )).done([=](const MTPmessages_InvitedUsers &result) { + const auto &data = result.data(); + chat->session().api().applyUpdates(data.vupdates()); if (done) done(true); + ChatInviteForbidden( + show, + chat, + CollectForbiddenUsers(&chat->session(), result)); }).fail([=](const MTP::Error &error) { const auto type = error.type(); - ShowAddParticipantsError(type, peer, { 1, user }, show); + ShowAddParticipantsError(show, type, peer, user); if (done) done(false); }).afterDelay(kSmallDelayMs).send(); } } else if (const auto channel = peer->asChannel()) { const auto hasBot = ranges::any_of(users, &UserData::isBot); if (!peer->isMegagroup() && hasBot) { - ShowAddParticipantsError("USER_BOT", peer, users, show); + ShowAddParticipantsError( + show, + u"USER_BOT"_q, + peer, + { .users = users }); return; } auto list = QVector(); @@ -520,8 +529,9 @@ void ChatParticipants::add( _api.request(MTPchannels_InviteToChannel( channel->inputChannel, MTP_vector(list) - )).done([=](const MTPUpdates &result) { - channel->session().api().applyUpdates(result); + )).done([=](const MTPmessages_InvitedUsers &result) { + const auto &data = result.data(); + channel->session().api().applyUpdates(data.vupdates()); requestCountDelayed(channel); if (callback) callback(true); ChatInviteForbidden( @@ -529,7 +539,9 @@ void ChatParticipants::add( channel, CollectForbiddenUsers(&channel->session(), result)); }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, users, show); + ShowAddParticipantsError(show, error.type(), peer, { + .users = users, + }); if (callback) callback(false); }).afterDelay(kSmallDelayMs).send(); }; diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index 816cc6526..b8fc9fc67 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -97,9 +97,9 @@ public: not_null channel, const TLMembers &data); void add( + std::shared_ptr show, not_null peer, const std::vector> &users, - std::shared_ptr show = nullptr, bool passGroupHistory = true, Fn done = nullptr); diff --git a/Telegram/SourceFiles/api/api_earn.cpp b/Telegram/SourceFiles/api/api_earn.cpp new file mode 100644 index 000000000..e8f38e06b --- /dev/null +++ b/Telegram/SourceFiles/api/api_earn.cpp @@ -0,0 +1,88 @@ +/* +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_earn.h" + +#include "api/api_cloud_password.h" +#include "apiwrap.h" +#include "boxes/passcode_box.h" +#include "data/data_channel.h" +#include "data/data_session.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "ui/basic_click_handlers.h" +#include "ui/widgets/buttons.h" + +namespace Api { + +void RestrictSponsored( + not_null channel, + bool restricted, + Fn failed) { + channel->session().api().request(MTPchannels_RestrictSponsoredMessages( + channel->inputChannel, + MTP_bool(restricted)) + ).done([=](const MTPUpdates &updates) { + channel->session().api().applyUpdates(updates); + }).fail([=](const MTP::Error &error) { + failed(error.type()); + }).send(); +} + +void HandleWithdrawalButton( + not_null channel, + not_null button, + std::shared_ptr show) { + struct State { + rpl::lifetime lifetime; + bool loading = false; + }; + + const auto state = button->lifetime().make_state(); + const auto session = &channel->session(); + + session->api().cloudPassword().reload(); + button->setClickedCallback([=] { + if (state->loading) { + return; + } + state->loading = true; + state->lifetime = session->api().cloudPassword().state( + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) { + state->loading = false; + + auto fields = PasscodeBox::CloudFields::From(pass); + fields.customTitle = + tr::lng_channel_earn_balance_password_title(); + fields.customDescription = + tr::lng_channel_earn_balance_password_description(tr::now); + fields.customSubmitButton = tr::lng_passcode_submit(); + fields.customCheckCallback = crl::guard(button, [=]( + const Core::CloudPasswordResult &result) { + session->api().request( + MTPstats_GetBroadcastRevenueWithdrawalUrl( + channel->inputChannel, + result.result + )).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) { + const auto url = qs(r.data().vurl()); + + if (!url.isEmpty()) { + UrlClickHandler::Open(url); + } + }).fail([=](const MTP::Error &error) { + show->showToast(error.type()); + }).send(); + }); + show->show(Box(session, fields)); + }); + + }); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_earn.h b/Telegram/SourceFiles/api/api_earn.h new file mode 100644 index 000000000..93f2bf6eb --- /dev/null +++ b/Telegram/SourceFiles/api/api_earn.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 + +class ChannelData; + +namespace Ui { +class RippleButton; +class Show; +} // namespace Ui + +namespace Api { + +void RestrictSponsored( + not_null channel, + bool restricted, + Fn failed); + +void HandleWithdrawalButton( + not_null channel, + not_null button, + std::shared_ptr show); + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_global_privacy.cpp b/Telegram/SourceFiles/api/api_global_privacy.cpp index f958bdfe8..d1beddfb6 100644 --- a/Telegram/SourceFiles/api/api_global_privacy.cpp +++ b/Telegram/SourceFiles/api/api_global_privacy.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" namespace Api { @@ -40,9 +39,9 @@ void GlobalPrivacy::reload(Fn callback) { } }).send(); - _session->account().appConfig().value( + _session->appConfig().value( ) | rpl::start_with_next([=] { - _showArchiveAndMute = _session->account().appConfig().get( + _showArchiveAndMute = _session->appConfig().get( u"autoarchive_setting_available"_q, false); }, _session->lifetime()); @@ -75,12 +74,12 @@ rpl::producer GlobalPrivacy::showArchiveAndMute() const { } rpl::producer<> GlobalPrivacy::suggestArchiveAndMute() const { - return _session->account().appConfig().suggestionRequested( + return _session->appConfig().suggestionRequested( u"AUTOARCHIVE_POPULAR"_q); } void GlobalPrivacy::dismissArchiveAndMuteSuggestion() { - _session->account().appConfig().dismissSuggestion( + _session->appConfig().dismissSuggestion( u"AUTOARCHIVE_POPULAR"_q); } @@ -142,6 +141,8 @@ void GlobalPrivacy::update( using Flag = MTPDglobalPrivacySettings::Flag; _api.request(_requestId).cancel(); + const auto newRequirePremiumAllowed = _session->premium() + || _session->appConfig().newRequirePremiumFree(); const auto flags = Flag() | (archiveAndMute ? Flag::f_archive_and_mute_new_noncontact_peers @@ -153,7 +154,7 @@ void GlobalPrivacy::update( ? Flag::f_keep_archived_folders : Flag()) | (hideReadTime ? Flag::f_hide_read_marks : Flag()) - | ((newRequirePremium && _session->premium()) + | ((newRequirePremium && newRequirePremiumAllowed) ? Flag::f_new_noncontact_peers_require_premium : Flag()); _requestId = _api.request(MTPaccount_SetGlobalPrivacySettings( diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 72da2a489..5257feb2d 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/history.h" #include "history/history_item.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" #include "payments/payments_form.h" @@ -109,6 +108,18 @@ rpl::producer<> Premium::cloudSetUpdated() const { return _cloudSetUpdated.events(); } +auto Premium::helloStickers() const +-> const std::vector> & { + if (_helloStickers.empty()) { + const_cast(this)->reloadHelloStickers(); + } + return _helloStickers; +} + +rpl::producer<> Premium::helloStickersUpdated() const { + return _helloStickersUpdated.events(); +} + int64 Premium::monthlyAmount() const { return _monthlyAmount; } @@ -225,6 +236,33 @@ void Premium::reloadCloudSet() { }).send(); } +void Premium::reloadHelloStickers() { + if (_helloStickersRequestId) { + return; + } + _helloStickersRequestId = _api.request(MTPmessages_GetStickers( + MTP_string("\xf0\x9f\x91\x8b\xe2\xad\x90\xef\xb8\x8f"), + MTP_long(_helloStickersHash) + )).done([=](const MTPmessages_Stickers &result) { + _helloStickersRequestId = 0; + result.match([&](const MTPDmessages_stickersNotModified &) { + }, [&](const MTPDmessages_stickers &data) { + _helloStickersHash = data.vhash().v; + const auto owner = &_session->data(); + _helloStickers.clear(); + for (const auto &sticker : data.vstickers().v) { + const auto document = owner->processDocument(sticker); + if (document->sticker()) { + _helloStickers.push_back(document); + } + } + _helloStickersUpdated.fire({}); + }); + }).fail([=] { + _helloStickersRequestId = 0; + }).send(); +} + void Premium::checkGiftCode( const QString &slug, Fn done) { @@ -532,34 +570,34 @@ Data::SubscriptionOptions PremiumGiftCodeOptions::options(int amount) { int PremiumGiftCodeOptions::giveawayBoostsPerPremium() const { constexpr auto kFallbackCount = 4; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_boosts_per_premium"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayCountriesMax() const { constexpr auto kFallbackCount = 10; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_countries_max"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayAddPeersMax() const { constexpr auto kFallbackCount = 10; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_add_peers_max"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayPeriodMax() const { constexpr auto kFallbackCount = 3600 * 24 * 7; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_period_max"_q, kFallbackCount); } bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const { - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_gifts_purchase_available"_q, false); } @@ -609,4 +647,24 @@ RequirePremiumState ResolveRequiresPremiumToWrite( return RequirePremiumState::Unknown; } +rpl::producer RandomHelloStickerValue( + not_null session) { + const auto premium = &session->api().premium(); + const auto random = [=] { + const auto &v = premium->helloStickers(); + Assert(!v.empty()); + return v[base::RandomIndex(v.size())].get(); + }; + const auto &v = premium->helloStickers(); + if (!v.empty()) { + return rpl::single(random()); + } + return rpl::single( + nullptr + ) | rpl::then(premium->helloStickersUpdated( + ) | rpl::filter([=] { + return !premium->helloStickers().empty(); + }) | rpl::take(1) | rpl::map(random)); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index e1c3a7b41..9bacca92d 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -85,6 +85,10 @@ public: -> const std::vector> &; [[nodiscard]] rpl::producer<> cloudSetUpdated() const; + [[nodiscard]] auto helloStickers() const + -> const std::vector> &; + [[nodiscard]] rpl::producer<> helloStickersUpdated() const; + [[nodiscard]] int64 monthlyAmount() const; [[nodiscard]] QString monthlyCurrency() const; @@ -111,6 +115,7 @@ private: void reloadPromo(); void reloadStickers(); void reloadCloudSet(); + void reloadHelloStickers(); void requestPremiumRequiredSlice(); const not_null _session; @@ -133,6 +138,11 @@ private: std::vector> _cloudSet; rpl::event_stream<> _cloudSetUpdated; + mtpRequestId _helloStickersRequestId = 0; + uint64 _helloStickersHash = 0; + std::vector> _helloStickers; + rpl::event_stream<> _helloStickersUpdated; + int64 _monthlyAmount = 0; QString _monthlyCurrency; @@ -215,4 +225,7 @@ enum class RequirePremiumState { not_null peer, History *maybeHistory); +[[nodiscard]] rpl::producer RandomHelloStickerValue( + not_null session); + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_ringtones.cpp b/Telegram/SourceFiles/api/api_ringtones.cpp index 5b471851e..87522bf58 100644 --- a/Telegram/SourceFiles/api/api_ringtones.cpp +++ b/Telegram/SourceFiles/api/api_ringtones.cpp @@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/notify/data_notify_settings.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" #include "storage/file_upload.h" @@ -191,20 +190,20 @@ void Ringtones::remove(DocumentId id) { } int64 Ringtones::maxSize() const { - return _session->account().appConfig().get( - "ringtone_size_max", + return _session->appConfig().get( + u"ringtone_size_max"_q, 100 * 1024); } int Ringtones::maxSavedCount() const { - return _session->account().appConfig().get( - "ringtone_saved_count_max", + return _session->appConfig().get( + u"ringtone_saved_count_max"_q, 100); } crl::time Ringtones::maxDuration() const { - return crl::time(1000) * _session->account().appConfig().get( - "ringtone_duration_max", + return crl::time(1000) * _session->appConfig().get( + u"ringtone_duration_max"_q, 5); } diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 495b9467e..72bd06b3d 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -28,7 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_entity.h" // TextWithEntities. #include "ui/item_text_options.h" // Ui::ItemTextOptions. #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "storage/localimageloader.h" #include "storage/file_upload.h" @@ -239,8 +238,7 @@ bool SendDice(MessageToSend &message) { || !message.textWithTags.tags.isEmpty()) { return false; } - auto &account = message.action.history->session().account(); - auto &config = account.appConfig(); + auto &config = message.action.history->session().appConfig(); static const auto hardcoded = std::vector{ Stickers::DicePacks::kDiceString, Stickers::DicePacks::kDartString, diff --git a/Telegram/SourceFiles/api/api_sensitive_content.cpp b/Telegram/SourceFiles/api/api_sensitive_content.cpp index e2d3e49ed..97388d638 100644 --- a/Telegram/SourceFiles/api/api_sensitive_content.cpp +++ b/Telegram/SourceFiles/api/api_sensitive_content.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" namespace Api { @@ -22,7 +21,7 @@ constexpr auto kRefreshAppConfigTimeout = 3 * crl::time(1000); SensitiveContent::SensitiveContent(not_null api) : _session(&api->session()) , _api(&api->instance()) -, _appConfigReloadTimer([=] { _session->account().appConfig().refresh(); }) { +, _appConfigReloadTimer([=] { _session->appConfig().refresh(); }) { } void SensitiveContent::reload() { diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 789211f33..9bc31c483 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_statistics.h" #include "apiwrap.h" +#include "base/unixtime.h" #include "data/data_channel.h" #include "data/data_session.h" #include "data/data_stories.h" @@ -746,4 +747,125 @@ Data::BoostStatus Boosts::boostStatus() const { return _boostStatus; } +EarnStatistics::EarnStatistics(not_null channel) +: StatisticsRequestSender(channel) { +} + +rpl::producer EarnStatistics::request() { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + makeRequest(MTPstats_GetBroadcastRevenueStats( + MTP_flags(0), + channel()->inputChannel + )).done([=](const MTPstats_BroadcastRevenueStats &result) { + const auto &data = result.data(); + + _data = Data::EarnStatistics{ + .topHoursGraph = StatisticalGraphFromTL( + data.vtop_hours_graph()), + .revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()), + .currentBalance = data.vcurrent_balance().v, + .availableBalance = data.vavailable_balance().v, + .overallRevenue = data.voverall_revenue().v, + .usdRate = data.vusd_rate().v, + }; + + requestHistory({}, [=](Data::EarnHistorySlice &&slice) { + _data.firstHistorySlice = std::move(slice); + + api().request( + MTPchannels_GetFullChannel(channel()->inputChannel) + ).done([=](const MTPmessages_ChatFull &result) { + result.data().vfull_chat().match([&]( + const MTPDchannelFull &d) { + _data.switchedOff = d.is_restricted_sponsored(); + }, [](const auto &) { + }); + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + }); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + + return lifetime; + }; +} + +void EarnStatistics::requestHistory( + const Data::EarnHistorySlice::OffsetToken &token, + Fn done) { + if (_requestId) { + return; + } + constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice); + constexpr auto kTlLimit = tl::make_int(kLimit); + _requestId = api().request(MTPstats_GetBroadcastRevenueTransactions( + channel()->inputChannel, + MTP_int(token), + (!token) ? kTlFirstSlice : kTlLimit + )).done([=](const MTPstats_BroadcastRevenueTransactions &result) { + _requestId = 0; + + const auto &tlTransactions = result.data().vtransactions().v; + + auto list = std::vector(); + list.reserve(tlTransactions.size()); + for (const auto &tlTransaction : tlTransactions) { + list.push_back(tlTransaction.match([&]( + const MTPDbroadcastRevenueTransactionProceeds &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::In, + .amount = d.vamount().v, + .date = base::unixtime::parse(d.vfrom_date().v), + .dateTo = base::unixtime::parse(d.vto_date().v), + }; + }, [&](const MTPDbroadcastRevenueTransactionWithdrawal &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::Out, + .status = d.is_pending() + ? Data::EarnHistoryEntry::Status::Pending + : d.is_failed() + ? Data::EarnHistoryEntry::Status::Failed + : Data::EarnHistoryEntry::Status::Success, + .amount = (std::numeric_limits::max() + - d.vamount().v + + 1), + .date = base::unixtime::parse(d.vdate().v), + // .provider = qs(d.vprovider()), + .successDate = d.vtransaction_date() + ? base::unixtime::parse(d.vtransaction_date()->v) + : QDateTime(), + .successLink = d.vtransaction_url() + ? qs(*d.vtransaction_url()) + : QString(), + }; + }, [&](const MTPDbroadcastRevenueTransactionRefund &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::Return, + .amount = d.vamount().v, + .date = base::unixtime::parse(d.vdate().v), + // .provider = qs(d.vprovider()), + }; + })); + } + const auto nextToken = token + tlTransactions.size(); + done(Data::EarnHistorySlice{ + .list = std::move(list), + .total = result.data().vcount().v, + .allLoaded = (result.data().vcount().v == nextToken), + .token = Data::EarnHistorySlice::OffsetToken(nextToken), + }); + }).fail([=] { + _requestId = 0; + }).send(); +} + +Data::EarnStatistics EarnStatistics::data() const { + return _data; +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_statistics.h b/Telegram/SourceFiles/api/api_statistics.h index f5360adb2..f18cba71d 100644 --- a/Telegram/SourceFiles/api/api_statistics.h +++ b/Telegram/SourceFiles/api/api_statistics.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "data/data_boosts.h" +#include "data/data_channel_earn.h" #include "data/data_statistics.h" #include "mtproto/sender.h" @@ -107,6 +108,27 @@ private: }; +class EarnStatistics final : public StatisticsRequestSender { +public: + explicit EarnStatistics(not_null channel); + + [[nodiscard]] rpl::producer request(); + void requestHistory( + const Data::EarnHistorySlice::OffsetToken &token, + Fn done); + + [[nodiscard]] Data::EarnStatistics data() const; + + static constexpr auto kFirstSlice = int(5); + static constexpr auto kLimit = int(10); + +private: + Data::EarnStatistics _data; + + mtpRequestId _requestId = 0; + +}; + class Boosts final { public: explicit Boosts(not_null peer); diff --git a/Telegram/SourceFiles/api/api_transcribes.cpp b/Telegram/SourceFiles/api/api_transcribes.cpp index d7064abcd..dd983dc37 100644 --- a/Telegram/SourceFiles/api/api_transcribes.cpp +++ b/Telegram/SourceFiles/api/api_transcribes.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_item_helpers.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" @@ -36,10 +35,10 @@ bool Transcribes::freeFor(not_null item) const { bool Transcribes::trialsSupport() { if (!_trialsSupport) { - const auto count = _session->account().appConfig().get( + const auto count = _session->appConfig().get( u"transcribe_audio_trial_weekly_number"_q, 0); - const auto until = _session->account().appConfig().get( + const auto until = _session->appConfig().get( u"transcribe_audio_trial_cooldown_until"_q, 0); _trialsSupport = (count > 0) || (until > 0); @@ -49,7 +48,7 @@ bool Transcribes::trialsSupport() { TimeId Transcribes::trialsRefreshAt() { if (_trialsRefreshAt < 0) { - _trialsRefreshAt = _session->account().appConfig().get( + _trialsRefreshAt = _session->appConfig().get( u"transcribe_audio_trial_cooldown_until"_q, 0); } @@ -58,7 +57,7 @@ TimeId Transcribes::trialsRefreshAt() { int Transcribes::trialsCount() { if (_trialsCount < 0) { - _trialsCount = _session->account().appConfig().get( + _trialsCount = _session->appConfig().get( u"transcribe_audio_trial_weekly_number"_q, -1); return std::max(_trialsCount, 0); @@ -67,7 +66,7 @@ int Transcribes::trialsCount() { } crl::time Transcribes::trialsMaxLengthMs() const { - return 1000 * _session->account().appConfig().get( + return 1000 * _session->appConfig().get( u"transcribe_audio_trial_duration_max"_q, 300); } diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 8b54f8b82..b5deb99ae 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1128,6 +1128,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPPeer(), // saved_peer_id d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(), MTP_long(d.vvia_bot_id().value_or_empty()), + MTPlong(), // via_business_bot_id d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(), d.vdate(), d.vmessage(), @@ -1162,6 +1163,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPPeer(), // saved_peer_id d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(), MTP_long(d.vvia_bot_id().value_or_empty()), + MTPlong(), // via_business_bot_id d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(), d.vdate(), d.vmessage(), @@ -1949,7 +1951,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { const auto &d = update.c_updatePeerSettings(); const auto peerId = peerFromMTP(d.vpeer()); if (const auto peer = session().data().peerLoaded(peerId)) { - peer->setSettings(d.vsettings()); + peer->setBarSettings(d.vsettings()); } } break; diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp index edaae7c97..b27580aaa 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.cpp +++ b/Telegram/SourceFiles/api/api_user_privacy.cpp @@ -26,7 +26,9 @@ using TLInputRules = MTPVector; using TLRules = MTPVector; TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { - const auto collectInputUsers = [](const auto &peers) { + using Exceptions = UserPrivacy::Exceptions; + const auto collectInputUsers = [](const Exceptions &exceptions) { + const auto &peers = exceptions.peers; auto result = QVector(); result.reserve(peers.size()); for (const auto &peer : peers) { @@ -36,7 +38,8 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { } return result; }; - const auto collectInputChats = [](const auto &peers) { + const auto collectInputChats = [](const Exceptions &exceptions) { + const auto &peers = exceptions.peers; auto result = QVector(); result.reserve(peers.size()); for (const auto &peer : peers) { @@ -47,6 +50,7 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { return result; }; + using Option = UserPrivacy::Option; auto result = QVector(); result.reserve(kMaxRules); if (!rule.ignoreAlways) { @@ -62,6 +66,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { MTP_inputPrivacyValueAllowChatParticipants( MTP_vector(chats))); } + if (rule.always.premiums && (rule.option != Option::Everyone)) { + result.push_back(MTP_inputPrivacyValueAllowPremium()); + } } if (!rule.ignoreNever) { const auto users = collectInputUsers(rule.never); @@ -78,7 +85,6 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { } } result.push_back([&] { - using Option = UserPrivacy::Option; switch (rule.option) { case Option::Everyone: return MTP_inputPrivacyValueAllowAll(); case Option::Contacts: return MTP_inputPrivacyValueAllowContacts(); @@ -101,12 +107,14 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) { auto result = UserPrivacy::Rule(); auto optionSet = false; const auto setOption = [&](Option option) { - if (optionSet) return; + if (optionSet) { + return; + } optionSet = true; result.option = option; }; - auto &always = result.always; - auto &never = result.never; + auto &always = result.always.peers; + auto &never = result.never.peers; const auto feed = [&](const MTPPrivacyRule &rule) { rule.match([&](const MTPDprivacyValueAllowAll &) { setOption(Option::Everyone); @@ -114,6 +122,8 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) { setOption(Option::Contacts); }, [&](const MTPDprivacyValueAllowCloseFriends &) { setOption(Option::CloseFriends); + }, [&](const MTPDprivacyValueAllowPremium &) { + result.always.premiums = true; }, [&](const MTPDprivacyValueAllowUsers &data) { const auto &users = data.vusers().v; always.reserve(always.size() + users.size()); @@ -188,6 +198,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) { case Key::ProfilePhoto: return MTP_inputPrivacyKeyProfilePhoto(); case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages(); case Key::About: return MTP_inputPrivacyKeyAbout(); + case Key::Birthday: return MTP_inputPrivacyKeyBirthday(); } Unexpected("Key in Api::UserPrivacy::KetToTL."); } @@ -215,6 +226,8 @@ std::optional TLToKey(mtpTypeId type) { case mtpc_inputPrivacyKeyVoiceMessages: return Key::Voices; case mtpc_privacyKeyAbout: case mtpc_inputPrivacyKeyAbout: return Key::About; + case mtpc_privacyKeyBirthday: + case mtpc_inputPrivacyKeyBirthday: return Key::Birthday; } return std::nullopt; } diff --git a/Telegram/SourceFiles/api/api_user_privacy.h b/Telegram/SourceFiles/api/api_user_privacy.h index be8e452d9..471f41f48 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.h +++ b/Telegram/SourceFiles/api/api_user_privacy.h @@ -30,6 +30,7 @@ public: ProfilePhoto, Voices, About, + Birthday, }; enum class Option { Everyone, @@ -37,10 +38,14 @@ public: CloseFriends, Nobody, }; + struct Exceptions { + std::vector> peers; + bool premiums = false; + }; struct Rule { Option option = Option::Everyone; - std::vector> always; - std::vector> never; + Exceptions always; + Exceptions never; bool ignoreAlways = false; bool ignoreNever = false; }; diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp index 27134dea9..f610f833d 100644 --- a/Telegram/SourceFiles/api/api_who_reacted.cpp +++ b/Telegram/SourceFiles/api/api_who_reacted.cpp @@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_app_config.h" #include "main/main_session.h" -#include "main/main_account.h" #include "base/unixtime.h" #include "base/weak_ptr.h" #include "ui/controls/who_reacted_context_action.h" @@ -697,7 +696,7 @@ bool WhoReadExists(not_null item) { || user->readDatesPrivate()) { return false; } - const auto &appConfig = peer->session().account().appConfig(); + const auto &appConfig = peer->session().appConfig(); const auto expirePeriod = appConfig.get( "pm_read_date_expire_period", 7 * 86400); @@ -713,7 +712,7 @@ bool WhoReadExists(not_null item) { && (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) { return false; } - const auto &appConfig = peer->session().account().appConfig(); + const auto &appConfig = peer->session().appConfig(); const auto expirePeriod = appConfig.get( "chat_read_mark_expire_period", 7 * 86400); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d7791f285..0a19f05da 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_blocked_peers.h" +#include "api/api_chat_links.h" #include "api/api_chat_participants.h" #include "api/api_cloud_password.h" #include "api/api_hash.h" @@ -169,6 +170,7 @@ ApiWrap::ApiWrap(not_null session) , _globalPrivacy(std::make_unique(this)) , _userPrivacy(std::make_unique(this)) , _inviteLinks(std::make_unique(this)) +, _chatLinks(std::make_unique(this)) , _views(std::make_unique(this)) , _confirmPhone(std::make_unique(this)) , _peerPhoto(std::make_unique(this)) @@ -1246,7 +1248,7 @@ void ApiWrap::requestPeerSettings(not_null peer) { result.match([&](const MTPDmessages_peerSettings &data) { _session->data().processUsers(data.vusers()); _session->data().processChats(data.vchats()); - peer->setSettings(data.vsettings()); + peer->setBarSettings(data.vsettings()); _requestedPeerSettings.erase(peer); }); }).fail([=] { @@ -1775,7 +1777,7 @@ void ApiWrap::joinChannel(not_null channel) { } return QString(); }(); - if (!text.isEmpty()) { + if (show && !text.isEmpty()) { show->showToast(text, kJoinErrorDuration); } } @@ -2581,6 +2583,10 @@ void ApiWrap::refreshFileReference( request(MTPaccount_GetSavedRingtones(MTP_long(0))); }, [&](Data::FileOriginPremiumPreviews data) { request(MTPhelp_GetPremiumPromo()); + }, [&](Data::FileOriginWebPage data) { + request(MTPmessages_GetWebPage( + MTP_string(data.url), + MTP_int(0))); }, [&](Data::FileOriginStory data) { request(MTPstories_GetStoriesByID( _session->data().peer(data.peer)->input, @@ -3667,11 +3673,16 @@ void ApiWrap::cancelLocalItem(not_null item) { void ApiWrap::sendShortcutMessages( not_null peer, BusinessShortcutId id) { + auto ids = QVector(); + auto randomIds = QVector(); request(MTPmessages_SendQuickReplyMessages( peer->input, - MTP_int(id) + MTP_int(id), + MTP_vector(ids), + MTP_vector(randomIds) )).done([=](const MTPUpdates &result) { applyUpdates(result); + }).fail([=](const MTP::Error &error) { }).send(); } @@ -3903,13 +3914,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) { } void ApiWrap::sendBotStart( + std::shared_ptr show, not_null bot, PeerData *chat, const QString &startTokenForChat) { Expects(bot->isBot()); if (chat && chat->isChannel() && !chat->isMegagroup()) { - ShowAddParticipantsError("USER_BOT", chat, { 1, bot }); + ShowAddParticipantsError(show, "USER_BOT", chat, bot); return; } @@ -3943,7 +3955,7 @@ void ApiWrap::sendBotStart( }).fail([=](const MTP::Error &error) { if (chat) { const auto type = error.type(); - ShowAddParticipantsError(type, chat, { 1, bot }); + ShowAddParticipantsError(show, type, chat, bot); } }).send(); } @@ -4059,6 +4071,8 @@ void ApiWrap::uploadAlbumMedia( }; request(MTPmessages_UploadMedia( + MTP_flags(0), + MTPstring(), // business_connection_id item->history()->peer->input, media )).done([=](const MTPMessageMedia &result) { @@ -4495,6 +4509,10 @@ Api::InviteLinks &ApiWrap::inviteLinks() { return *_inviteLinks; } +Api::ChatLinks &ApiWrap::chatLinks() { + return *_chatLinks; +} + Api::ViewsManager &ApiWrap::views() { return *_views; } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 58165adec..b1c844702 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -53,6 +53,7 @@ class Key; namespace Ui { struct PreparedList; +class Show; } // namespace Ui namespace Api { @@ -69,6 +70,7 @@ class SensitiveContent; class GlobalPrivacy; class UserPrivacy; class InviteLinks; +class ChatLinks; class ViewsManager; class ConfirmPhone; class PeerPhoto; @@ -342,6 +344,7 @@ public: BusinessShortcutId id); void sendMessage(MessageToSend &&message); void sendBotStart( + std::shared_ptr show, not_null bot, PeerData *chat = nullptr, const QString &startTokenForChat = QString()); @@ -384,6 +387,7 @@ public: [[nodiscard]] Api::GlobalPrivacy &globalPrivacy(); [[nodiscard]] Api::UserPrivacy &userPrivacy(); [[nodiscard]] Api::InviteLinks &inviteLinks(); + [[nodiscard]] Api::ChatLinks &chatLinks(); [[nodiscard]] Api::ViewsManager &views(); [[nodiscard]] Api::ConfirmPhone &confirmPhone(); [[nodiscard]] Api::PeerPhoto &peerPhoto(); @@ -703,6 +707,7 @@ private: const std::unique_ptr _globalPrivacy; const std::unique_ptr _userPrivacy; const std::unique_ptr _inviteLinks; + const std::unique_ptr _chatLinks; const std::unique_ptr _views; const std::unique_ptr _confirmPhone; const std::unique_ptr _peerPhoto; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index d3715e884..ba35e8d98 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -66,11 +66,12 @@ void ChatCreateDone( not_null navigation, QImage image, TimeId ttlPeriod, - const MTPUpdates &updates, + const MTPmessages_InvitedUsers &result, Fn)> done) { - navigation->session().api().applyUpdates(updates); + const auto &data = result.data(); + navigation->session().api().applyUpdates(data.vupdates()); - const auto success = base::make_optional(&updates) + const auto success = base::make_optional(&data.vupdates()) | [](auto updates) -> std::optional*> { switch (updates->type()) { case mtpc_updates: @@ -109,7 +110,7 @@ void ChatCreateDone( ChatInviteForbidden( show, chat, - CollectForbiddenUsers(&chat->session(), updates)); + CollectForbiddenUsers(&chat->session(), result)); } }; if (!success) { @@ -168,19 +169,31 @@ TextWithEntities PeerFloodErrorText( } void ShowAddParticipantsError( + std::shared_ptr show, const QString &error, not_null chat, - const std::vector> &users, - std::shared_ptr show) { + not_null user) { + ShowAddParticipantsError( + std::move(show), + error, + chat, + { .users = { 1, user } }); +} + +void ShowAddParticipantsError( + std::shared_ptr show, + const QString &error, + not_null chat, + const ForbiddenInvites &forbidden) { if (error == u"USER_BOT"_q) { const auto channel = chat->asChannel(); - if ((users.size() == 1) - && users.front()->isBot() + if ((forbidden.users.size() == 1) + && forbidden.users.front()->isBot() && channel && !channel->isMegagroup() && channel->canAddAdmins()) { const auto makeAdmin = [=] { - const auto user = users.front(); + const auto user = forbidden.users.front(); const auto weak = std::make_shared>(); const auto close = [=](auto&&...) { if (*weak) { @@ -188,6 +201,7 @@ void ShowAddParticipantsError( } }; const auto saveCallback = SaveAdminCallback( + show, channel, user, close, @@ -198,9 +212,10 @@ void ShowAddParticipantsError( ChatAdminRightsInfo(), QString()); box->setSaveCallback(saveCallback); - *weak = Ui::show(std::move(box)); + *weak = box.data(); + show->showBox(std::move(box)); }; - Ui::show( + show->showBox( Ui::MakeConfirmBox({ .text = tr::lng_cant_invite_offer_admin(), .confirmed = makeAdmin, @@ -210,7 +225,7 @@ void ShowAddParticipantsError( return; } } - const auto hasBot = ranges::any_of(users, &UserData::isBot); + const auto hasBot = ranges::any_of(forbidden.users, &UserData::isBot); if (error == u"PEER_FLOOD"_q) { const auto type = (chat->isChat() || chat->isMegagroup()) ? PeerFloodType::InviteGroup @@ -218,8 +233,8 @@ void ShowAddParticipantsError( const auto text = PeerFloodErrorText(&chat->session(), type); Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); return; - } else if (error == u"USER_PRIVACY_RESTRICTED"_q && show) { - ChatInviteForbidden(show, chat, users); + } else if (error == u"USER_PRIVACY_RESTRICTED"_q) { + ChatInviteForbidden(show, chat, forbidden); return; } const auto text = [&] { @@ -230,8 +245,6 @@ void ShowAddParticipantsError( } else if (error == u"USER_KICKED"_q) { // Trying to return a user who was kicked by admin. return tr::lng_cant_invite_banned(tr::now); - } else if (error == u"USER_PRIVACY_RESTRICTED"_q) { - return tr::lng_cant_invite_privacy(tr::now); } else if (error == u"USER_NOT_MUTUAL_CONTACT"_q) { // Trying to return user who does not have me in contacts. return tr::lng_failed_add_not_mutual(tr::now); @@ -246,7 +259,7 @@ void ShowAddParticipantsError( } return tr::lng_failed_add_participant(tr::now); }(); - Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); + show->show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); } AddContactBox::AddContactBox( @@ -708,7 +721,7 @@ void GroupInfoBox::createGroup( MTP_vector(inputs), MTP_string(title), MTP_int(ttlPeriod()) - )).done([=](const MTPUpdates &result) { + )).done([=](const MTPmessages_InvitedUsers &result) { auto image = _photo->takeResultImage(); const auto period = ttlPeriod(); const auto navigation = _navigation; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index 6d19a2d7f..9bc7de652 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -34,6 +34,7 @@ template class Radioenum; class LinkButton; class UserpicButton; +class Show; } // namespace Ui enum class PeerFloodType { @@ -42,14 +43,21 @@ enum class PeerFloodType { InviteChannel, }; +struct ForbiddenInvites; + [[nodiscard]] TextWithEntities PeerFloodErrorText( not_null session, PeerFloodType type); void ShowAddParticipantsError( + std::shared_ptr show, const QString &error, not_null chat, - const std::vector> &users, - std::shared_ptr show = nullptr); + const ForbiddenInvites &forbidden); +void ShowAddParticipantsError( + std::shared_ptr show, + const QString &error, + not_null chat, + not_null user); class AddContactBox : public Ui::BoxContent { public: diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 877cf556e..59729b7fe 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -40,11 +40,11 @@ namespace { constexpr auto kBackgroundsInRow = 3; QImage TakeMiddleSample(QImage original, QSize size) { - size *= cIntRetinaFactor(); + size *= style::DevicePixelRatio(); const auto from = original.size(); if (from.isEmpty()) { auto result = original.scaled(size); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(style::DevicePixelRatio()); return result; } @@ -58,7 +58,7 @@ QImage TakeMiddleSample(QImage original, QSize size) { take.width(), take.height() ).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(style::DevicePixelRatio()); return result; } @@ -657,10 +657,10 @@ void BackgroundBox::Inner::validatePaperThumbnail( } else if (!paper.data.backgroundColors().empty()) { paper.thumbnail = Ui::PixmapFromImage( Ui::GenerateBackgroundImage( - st::backgroundSize * cIntRetinaFactor(), + st::backgroundSize * style::DevicePixelRatio(), paper.data.backgroundColors(), paper.data.gradientRotation())); - paper.thumbnail.setDevicePixelRatio(cRetinaFactor()); + paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio()); return; } else { return; @@ -680,7 +680,7 @@ void BackgroundBox::Inner::validatePaperThumbnail( paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample( original, st::backgroundSize)); - paper.thumbnail.setDevicePixelRatio(cRetinaFactor()); + paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio()); } bool BackgroundBox::Inner::forChannel() const { diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index d445ce1f5..3a9917719 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -467,14 +467,14 @@ void BackgroundPreviewBox::generateBackground() { return; } const auto size = QSize(st::boxWideWidth, st::boxWideWidth) - * cIntRetinaFactor(); + * style::DevicePixelRatio(); _generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.) ? Ui::GenerateBackgroundImage( size, _paper.backgroundColors(), _paper.gradientRotation()) : BlackImage(size)); - _generated.setDevicePixelRatio(cRetinaFactor()); + _generated.setDevicePixelRatio(style::DevicePixelRatio()); } not_null BackgroundPreviewBox::delegate() { @@ -889,7 +889,7 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) { void BackgroundPreviewBox::paintImage(Painter &p) { Expects(!_scaled.isNull()); - const auto factor = cIntRetinaFactor(); + const auto factor = style::DevicePixelRatio(); const auto size = st::boxWideWidth; const auto from = QRect( 0, diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 7285cc372..f70c7b88d 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -1020,3 +1020,27 @@ shortInfoBox: ShortInfoBox { labeled: infoLabeled; labeledOneLine: infoLabeledOneLine; } + +birthdayLabeled: FlatLabel(infoLabeled) { + margin: margins(0px, 0px, 0px, 0px); +} +birthdayLabel: FlatLabel(infoLabel) { + margin: margins(0px, 0px, 0px, 0px); +} +birthdayTodayIcon: icon {{ "menu/gift_premium", windowActiveTextFg }}; + +inviteForbiddenUserpicsPadding: margins(10px, 10px, 10px, 0px); +inviteForbiddenInfo: FlatLabel(defaultFlatLabel) { + minWidth: 240px; + align: align(top); +} +inviteForbiddenInfoPadding: margins(32px, 10px, 32px, 4px); +inviteForbiddenSubscribePadding: margins(16px, 12px, 16px, 16px); +inviteForbiddenOrLabelPadding: margins(32px, 0px, 32px, 0px); +inviteForbiddenTitle: FlatLabel(boxTitle) { + minWidth: 120px; + align: align(top); +} +inviteForbiddenTitlePadding: margins(32px, 4px, 32px, 0px); +inviteForbiddenLockBg: dialogsUnreadBgMuted; +inviteForbiddenLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadFg }}; diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 367742971..9fcb6b032 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/edit_privacy_box.h" #include "api/api_global_privacy.h" +#include "boxes/filters/edit_filter_chats_list.h" +#include "ui/effects/premium_graphics.h" #include "ui/layers/generic_box.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/shadow.h" @@ -23,18 +25,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "lang/lang_keys.h" #include "apiwrap.h" +#include "main/main_app_config.h" #include "main/main_session.h" #include "data/data_user.h" #include "data/data_chat.h" #include "data/data_channel.h" #include "data/data_peer_values.h" #include "window/window_session_controller.h" +#include "styles/style_boxes.h" #include "styles/style_settings.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" namespace { -namespace { + +constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value; + +using Exceptions = Api::UserPrivacy::Exceptions; void CreateRadiobuttonLock( not_null widget, @@ -89,37 +96,167 @@ void AddPremiumRequiredRow( }, row->lifetime()); } -} // namespace - class PrivacyExceptionsBoxController : public ChatsListBoxController { public: PrivacyExceptionsBoxController( not_null session, rpl::producer title, - const std::vector> &selected); + const Exceptions &selected, + bool allowChoosePremiums); Main::Session &session() const override; void rowClicked(not_null row) override; + bool isForeignRow(PeerListRowId itemId) override; + bool handleDeselectForeignRow(PeerListRowId itemId) override; + + [[nodiscard]] bool premiumsSelected() const; protected: void prepareViewHook() override; std::unique_ptr createRow(not_null history) override; private: + [[nodiscard]] object_ptr preparePremiumsRowList(); + const not_null _session; rpl::producer _title; - std::vector> _selected; + Exceptions _selected; + bool _allowChoosePremiums = false; + + PeerListContentDelegate *_typesDelegate = nullptr; + Fn _deselectOption; }; +struct RowSelectionChange { + not_null row; + bool checked = false; +}; + +class PremiumsRow final : public PeerListRow { +public: + PremiumsRow(); + + QString generateName() override; + QString generateShortName() override; + PaintRoundImageCallback generatePaintUserpicCallback( + bool forceRound) override; + bool useForumLikeUserpic() const override; + +}; + +class TypesController final : public PeerListController { +public: + TypesController(not_null session, bool premiums); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + + [[nodiscard]] bool premiumsSelected() const; + [[nodiscard]] rpl::producer premiumsChanges() const; + [[nodiscard]] auto rowSelectionChanges() const + -> rpl::producer; + +private: + [[nodiscard]] std::unique_ptr createRow() const; + + const not_null _session; + bool _premiums = false; + + rpl::event_stream<> _selectionChanged; + rpl::event_stream _rowSelectionChanges; + +}; + +PremiumsRow::PremiumsRow() : PeerListRow(kPremiumsRowId) { + setCustomStatus(tr::lng_edit_privacy_premium_status(tr::now)); +} + +QString PremiumsRow::generateName() { + return tr::lng_edit_privacy_premium(tr::now); +} + +QString PremiumsRow::generateShortName() { + return generateName(); +} + +PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback( + bool forceRound) { + return [=](QPainter &p, int x, int y, int outerWidth, int size) { + auto gradient = QLinearGradient( + QPointF(x, y), + QPointF(x + size, y + size)); + gradient.setStops(Ui::Premium::ButtonGradientStops()); + + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(gradient); + if (forceRound) { + p.drawEllipse(x, y, size, size); + } else { + const auto radius = size * Ui::ForumUserpicRadiusMultiplier(); + p.drawRoundedRect(x, y, size, size, radius, radius); + } + st::settingsPrivacyPremium.paintInCenter(p, { x, y, size, size }); + }; +} + +bool PremiumsRow::useForumLikeUserpic() const { + return true; +} + +TypesController::TypesController( + not_null session, + bool premiums) +: _session(session) +, _premiums(premiums) { +} + +Main::Session &TypesController::session() const { + return *_session; +} + +void TypesController::prepare() { + delegate()->peerListAppendRow(std::make_unique()); + delegate()->peerListRefreshRows(); +} + +bool TypesController::premiumsSelected() const { + const auto row = delegate()->peerListFindRow(kPremiumsRowId); + Assert(row != nullptr); + + return row->checked(); +} + +void TypesController::rowClicked(not_null row) { + const auto checked = !row->checked(); + delegate()->peerListSetRowChecked(row, checked); + _rowSelectionChanges.fire({ row, checked }); +} + +rpl::producer TypesController::premiumsChanges() const { + return _rowSelectionChanges.events( + ) | rpl::map([=] { + return premiumsSelected(); + }); +} + +auto TypesController::rowSelectionChanges() const +-> rpl::producer { + return _rowSelectionChanges.events(); +} + PrivacyExceptionsBoxController::PrivacyExceptionsBoxController( not_null session, rpl::producer title, - const std::vector> &selected) + const Exceptions &selected, + bool allowChoosePremiums) : ChatsListBoxController(session) , _session(session) , _title(std::move(title)) -, _selected(selected) { +, _selected(selected) +, _allowChoosePremiums(allowChoosePremiums) { } Main::Session &PrivacyExceptionsBoxController::session() const { @@ -128,7 +265,81 @@ Main::Session &PrivacyExceptionsBoxController::session() const { void PrivacyExceptionsBoxController::prepareViewHook() { delegate()->peerListSetTitle(std::move(_title)); - delegate()->peerListAddSelectedPeers(_selected); + if (_allowChoosePremiums || _selected.premiums) { + delegate()->peerListSetAboveWidget(preparePremiumsRowList()); + } + delegate()->peerListAddSelectedPeers(_selected.peers); +} + +bool PrivacyExceptionsBoxController::isForeignRow(PeerListRowId itemId) { + return (itemId == kPremiumsRowId); +} + +bool PrivacyExceptionsBoxController::handleDeselectForeignRow( + PeerListRowId itemId) { + if (isForeignRow(itemId)) { + _deselectOption(itemId); + return true; + } + return false; +} + +auto PrivacyExceptionsBoxController::preparePremiumsRowList() +-> object_ptr { + auto result = object_ptr((QWidget*)nullptr); + const auto container = result.data(); + container->add(CreatePeerListSectionSubtitle( + container, + tr::lng_edit_privacy_user_types())); + auto &lifetime = container->lifetime(); + _typesDelegate = lifetime.make_state(); + const auto controller = lifetime.make_state( + &session(), + _selected.premiums); + const auto content = result->add(object_ptr( + container, + controller)); + _typesDelegate->setContent(content); + controller->setDelegate(_typesDelegate); + + if (_selected.premiums) { + const auto row = _typesDelegate->peerListFindRow(kPremiumsRowId); + Assert(row != nullptr); + + content->changeCheckState(row, true, anim::type::instant); + this->delegate()->peerListSetForeignRowChecked( + row, + true, + anim::type::instant); + } + container->add(CreatePeerListSectionSubtitle( + container, + tr::lng_edit_privacy_users_and_groups())); + + controller->premiumsChanges( + ) | rpl::start_with_next([=](bool premiums) { + _selected.premiums = premiums; + }, lifetime); + + controller->rowSelectionChanges( + ) | rpl::start_with_next([=](RowSelectionChange update) { + this->delegate()->peerListSetForeignRowChecked( + update.row, + update.checked, + anim::type::normal); + }, lifetime); + + _deselectOption = [=](PeerListRowId itemId) { + if (const auto row = _typesDelegate->peerListFindRow(itemId)) { + _typesDelegate->peerListSetRowChecked(row, false); + } + }; + + return result; +} + +[[nodiscard]] bool PrivacyExceptionsBoxController::premiumsSelected() const { + return _selected.premiums; } void PrivacyExceptionsBoxController::rowClicked(not_null row) { @@ -144,7 +355,8 @@ void PrivacyExceptionsBoxController::rowClicked(not_null row) { } } -std::unique_ptr PrivacyExceptionsBoxController::createRow(not_null history) { +auto PrivacyExceptionsBoxController::createRow(not_null history) +-> std::unique_ptr { if (history->peer->isSelf() || history->peer->isRepliesChat()) { return nullptr; } else if (!history->peer->isUser() @@ -195,6 +407,11 @@ EditPrivacyBox::EditPrivacyBox( : _window(window) , _controller(std::move(controller)) , _value(value) { + if (_controller->allowPremiumsToggle(Exception::Always) + && _value.option == Option::Everyone) { + // If we switch from Everyone to Contacts or Nobody suggest Premiums. + _value.always.premiums = true; + } } void EditPrivacyBox::prepare() { @@ -209,11 +426,13 @@ void EditPrivacyBox::editExceptions( auto controller = std::make_unique( &_window->session(), _controller->exceptionBoxTitle(exception), - exceptions(exception)); + exceptions(exception), + _controller->allowPremiumsToggle(exception)); auto initBox = [=, controller = controller.get()]( not_null box) { box->addButton(tr::lng_settings_save(), crl::guard(this, [=] { - exceptions(exception) = box->collectSelectedRows(); + exceptions(exception).peers = box->collectSelectedRows(); + exceptions(exception).premiums = controller->premiumsSelected(); const auto type = [&] { switch (exception) { case Exception::Always: return Exception::Never; @@ -221,8 +440,8 @@ void EditPrivacyBox::editExceptions( } Unexpected("Invalid exception value."); }(); - auto &removeFrom = exceptions(type); - for (const auto peer : exceptions(exception)) { + auto &removeFrom = exceptions(type).peers; + for (const auto peer : exceptions(exception).peers) { removeFrom.erase( ranges::remove(removeFrom, peer), end(removeFrom)); @@ -236,7 +455,7 @@ void EditPrivacyBox::editExceptions( Box(std::move(controller), std::move(initBox))); } -std::vector> &EditPrivacyBox::exceptions(Exception exception) { +EditPrivacyBox::Exceptions &EditPrivacyBox::exceptions(Exception exception) { switch (exception) { case Exception::Always: return _value.always; case Exception::Never: return _value.never; @@ -339,16 +558,28 @@ void EditPrivacyBox::setupContent() { const auto addExceptionLink = [=](Exception exception) { const auto update = Ui::CreateChild>(content); auto label = update->events_starting_with({}) | rpl::map([=] { - return Settings::ExceptionUsersCount(exceptions(exception)); - }) | rpl::map([](int count) { - return count - ? tr::lng_edit_privacy_exceptions_count(tr::now, lt_count, count) + const auto &value = exceptions(exception); + const auto count = Settings::ExceptionUsersCount(value.peers); + const auto users = count + ? tr::lng_edit_privacy_exceptions_count( + tr::now, + lt_count, + count) : tr::lng_edit_privacy_exceptions_add(tr::now); + return !value.premiums + ? users + : !count + ? tr::lng_edit_privacy_premium(tr::now) + : tr::lng_edit_privacy_exceptions_premium_and( + tr::now, + lt_users, + users); }); _controller->handleExceptionsChange( exception, update->events_starting_with({}) | rpl::map([=] { - return Settings::ExceptionUsersCount(exceptions(exception)); + return Settings::ExceptionUsersCount( + exceptions(exception).peers); })); auto text = _controller->exceptionButtonTextKey(exception); const auto button = content->add( @@ -374,6 +605,7 @@ void EditPrivacyBox::setupContent() { }; auto above = _controller->setupAboveWidget( + _window, content, rpl::duplicate(optionValue), getDelegate()->outerContainer()); @@ -446,7 +678,7 @@ void EditPrivacyBox::setupContent() { addButton(tr::lng_settings_save(), [=] { const auto someAreDisallowed = (_value.option != Option::Everyone) - || !_value.never.empty(); + || !_value.never.peers.empty(); _controller->confirmSave(someAreDisallowed, crl::guard(this, [=] { _value.ignoreAlways = !showExceptionLink(Exception::Always); _value.ignoreNever = !showExceptionLink(Exception::Never); @@ -487,7 +719,10 @@ void EditMessagesPrivacyBox( constexpr auto kOptionAll = 0; constexpr auto kOptionPremium = 1; - const auto premium = controller->session().premium(); + const auto allowed = [=] { + return controller->session().premium() + || controller->session().appConfig().newRequirePremiumFree(); + }; const auto privacy = &controller->session().api().globalPrivacy(); const auto inner = box->verticalLayout(); inner->add(object_ptr(box)); @@ -549,7 +784,7 @@ void EditMessagesPrivacyBox( }), }); }; - if (!premium) { + if (!allowed()) { CreateRadiobuttonLock(restricted, st::messagePrivacyCheck); group->setChangedCallback([=](int value) { @@ -561,7 +796,7 @@ void EditMessagesPrivacyBox( } Ui::AddDividerText(inner, tr::lng_messages_privacy_about()); - if (!premium) { + if (!allowed()) { Ui::AddSkip(inner); Settings::AddButtonWithIcon( inner, @@ -580,7 +815,7 @@ void EditMessagesPrivacyBox( }); } else { box->addButton(tr::lng_settings_save(), [=] { - if (controller->session().premium()) { + if (allowed()) { privacy->updateNewRequirePremium( group->current() == kOptionPremium); box->closeBox(); diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index cfdc14ad7..3aae28403 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -48,7 +48,8 @@ public: [[nodiscard]] virtual rpl::producer warning() const { return nullptr; } - virtual void prepareWarningLabel(not_null warning) const { + virtual void prepareWarningLabel( + not_null warning) const { } [[nodiscard]] virtual rpl::producer exceptionButtonTextKey( Exception exception) const = 0; @@ -56,12 +57,17 @@ public: Exception exception) const = 0; [[nodiscard]] virtual auto exceptionsDescription() const -> rpl::producer = 0; + [[nodiscard]] virtual bool allowPremiumsToggle( + Exception exception) const { + return false; + } virtual void handleExceptionsChange( Exception exception, rpl::producer value) { } [[nodiscard]] virtual object_ptr setupAboveWidget( + not_null controller, not_null parent, rpl::producer