mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Merge tag 'v5.3.2' into dev
# Conflicts: # .github/workflows/mac_packaged.yml # Telegram/Resources/winrc/Telegram.rc # Telegram/Resources/winrc/Updater.rc # Telegram/SourceFiles/boxes/sticker_set_box.cpp # Telegram/SourceFiles/core/version.h # Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp # Telegram/lib_ui # snap/snapcraft.yaml
This commit is contained in:
commit
92dab0438f
234 changed files with 9807 additions and 2706 deletions
2
.github/workflows/win.yml
vendored
2
.github/workflows/win.yml
vendored
|
@ -169,6 +169,8 @@ jobs:
|
||||||
%TDESKTOP_BUILD_GENERATOR% ^
|
%TDESKTOP_BUILD_GENERATOR% ^
|
||||||
%TDESKTOP_BUILD_ARCH% ^
|
%TDESKTOP_BUILD_ARCH% ^
|
||||||
%TDESKTOP_BUILD_API% ^
|
%TDESKTOP_BUILD_API% ^
|
||||||
|
-D CMAKE_C_FLAGS="/WX" ^
|
||||||
|
-D CMAKE_CXX_FLAGS="/WX" ^
|
||||||
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF ^
|
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF ^
|
||||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
|
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
|
||||||
-D DESKTOP_APP_NO_PDB=ON ^
|
-D DESKTOP_APP_NO_PDB=ON ^
|
||||||
|
|
|
@ -341,6 +341,8 @@ PRIVATE
|
||||||
boxes/edit_caption_box.h
|
boxes/edit_caption_box.h
|
||||||
boxes/edit_privacy_box.cpp
|
boxes/edit_privacy_box.cpp
|
||||||
boxes/edit_privacy_box.h
|
boxes/edit_privacy_box.h
|
||||||
|
boxes/gift_credits_box.cpp
|
||||||
|
boxes/gift_credits_box.h
|
||||||
boxes/gift_premium_box.cpp
|
boxes/gift_premium_box.cpp
|
||||||
boxes/gift_premium_box.h
|
boxes/gift_premium_box.h
|
||||||
boxes/language_box.cpp
|
boxes/language_box.cpp
|
||||||
|
@ -544,6 +546,8 @@ PRIVATE
|
||||||
data/business/data_shortcut_messages.h
|
data/business/data_shortcut_messages.h
|
||||||
data/components/factchecks.cpp
|
data/components/factchecks.cpp
|
||||||
data/components/factchecks.h
|
data/components/factchecks.h
|
||||||
|
data/components/location_pickers.cpp
|
||||||
|
data/components/location_pickers.h
|
||||||
data/components/recent_peers.cpp
|
data/components/recent_peers.cpp
|
||||||
data/components/recent_peers.h
|
data/components/recent_peers.h
|
||||||
data/components/scheduled_messages.cpp
|
data/components/scheduled_messages.cpp
|
||||||
|
@ -1541,6 +1545,8 @@ PRIVATE
|
||||||
ui/chat/choose_send_as.h
|
ui/chat/choose_send_as.h
|
||||||
ui/chat/choose_theme_controller.cpp
|
ui/chat/choose_theme_controller.cpp
|
||||||
ui/chat/choose_theme_controller.h
|
ui/chat/choose_theme_controller.h
|
||||||
|
ui/controls/location_picker.cpp
|
||||||
|
ui/controls/location_picker.h
|
||||||
ui/controls/silent_toggle.cpp
|
ui/controls/silent_toggle.cpp
|
||||||
ui/controls/silent_toggle.h
|
ui/controls/silent_toggle.h
|
||||||
ui/controls/userpic_button.cpp
|
ui/controls/userpic_button.cpp
|
||||||
|
@ -1562,6 +1568,10 @@ PRIVATE
|
||||||
ui/image/image_location.h
|
ui/image/image_location.h
|
||||||
ui/image/image_location_factory.cpp
|
ui/image/image_location_factory.cpp
|
||||||
ui/image/image_location_factory.h
|
ui/image/image_location_factory.h
|
||||||
|
ui/text/format_song_document_name.cpp
|
||||||
|
ui/text/format_song_document_name.h
|
||||||
|
ui/widgets/label_with_custom_emoji.cpp
|
||||||
|
ui/widgets/label_with_custom_emoji.h
|
||||||
ui/countryinput.cpp
|
ui/countryinput.cpp
|
||||||
ui/countryinput.h
|
ui/countryinput.h
|
||||||
ui/dynamic_thumbnails.cpp
|
ui/dynamic_thumbnails.cpp
|
||||||
|
@ -1575,10 +1585,6 @@ PRIVATE
|
||||||
ui/resize_area.h
|
ui/resize_area.h
|
||||||
ui/search_field_controller.cpp
|
ui/search_field_controller.cpp
|
||||||
ui/search_field_controller.h
|
ui/search_field_controller.h
|
||||||
ui/text/format_song_document_name.cpp
|
|
||||||
ui/text/format_song_document_name.h
|
|
||||||
ui/widgets/label_with_custom_emoji.cpp
|
|
||||||
ui/widgets/label_with_custom_emoji.h
|
|
||||||
ui/unread_badge.cpp
|
ui/unread_badge.cpp
|
||||||
ui/unread_badge.h
|
ui/unread_badge.h
|
||||||
window/main_window.cpp
|
window/main_window.cpp
|
||||||
|
@ -1685,6 +1691,7 @@ PRIVATE
|
||||||
qrc/telegram/animations.qrc
|
qrc/telegram/animations.qrc
|
||||||
qrc/telegram/export.qrc
|
qrc/telegram/export.qrc
|
||||||
qrc/telegram/iv.qrc
|
qrc/telegram/iv.qrc
|
||||||
|
qrc/telegram/picker.qrc
|
||||||
qrc/telegram/telegram.qrc
|
qrc/telegram/telegram.qrc
|
||||||
qrc/telegram/sounds.qrc
|
qrc/telegram/sounds.qrc
|
||||||
winrc/Telegram.rc
|
winrc/Telegram.rc
|
||||||
|
@ -1915,9 +1922,14 @@ if (WIN32)
|
||||||
/DELAYLOAD:propsys.dll
|
/DELAYLOAD:propsys.dll
|
||||||
)
|
)
|
||||||
if (QT_VERSION GREATER 6)
|
if (QT_VERSION GREATER 6)
|
||||||
|
if (NOT build_winarm)
|
||||||
|
target_link_options(Telegram PRIVATE
|
||||||
|
/DELAYLOAD:API-MS-Win-EventLog-Legacy-l1-1-0.dll
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_options(Telegram
|
target_link_options(Telegram
|
||||||
PRIVATE
|
PRIVATE
|
||||||
/DELAYLOAD:API-MS-Win-EventLog-Legacy-l1-1-0.dll
|
|
||||||
/DELAYLOAD:API-MS-Win-Core-Console-l1-1-0.dll
|
/DELAYLOAD:API-MS-Win-Core-Console-l1-1-0.dll
|
||||||
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-0.dll
|
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-0.dll
|
||||||
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-1.dll
|
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-1.dll
|
||||||
|
@ -1934,7 +1946,7 @@ if (WIN32)
|
||||||
/DELAYLOAD:API-MS-Win-Core-WinRT-Error-l1-1-0.dll
|
/DELAYLOAD:API-MS-Win-Core-WinRT-Error-l1-1-0.dll
|
||||||
/DELAYLOAD:API-MS-Win-Core-WinRT-String-l1-1-0.dll
|
/DELAYLOAD:API-MS-Win-Core-WinRT-String-l1-1-0.dll
|
||||||
/DELAYLOAD:API-MS-Win-Security-CryptoAPI-l1-1-0.dll
|
/DELAYLOAD:API-MS-Win-Security-CryptoAPI-l1-1-0.dll
|
||||||
/DELAYLOAD:API-MS-Win-Shcore-Scaling-l1-1-1.dll
|
# /DELAYLOAD:API-MS-Win-Shcore-Scaling-l1-1-1.dll # We shadowed GetDpiForMonitor
|
||||||
/DELAYLOAD:authz.dll # Authz.lib
|
/DELAYLOAD:authz.dll # Authz.lib
|
||||||
/DELAYLOAD:comdlg32.dll
|
/DELAYLOAD:comdlg32.dll
|
||||||
/DELAYLOAD:dwrite.dll # DWrite.lib
|
/DELAYLOAD:dwrite.dll # DWrite.lib
|
||||||
|
|
BIN
Telegram/Resources/icons/chat/filled_location.png
Normal file
BIN
Telegram/Resources/icons/chat/filled_location.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 536 B |
BIN
Telegram/Resources/icons/chat/filled_location@2x.png
Normal file
BIN
Telegram/Resources/icons/chat/filled_location@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 987 B |
BIN
Telegram/Resources/icons/chat/filled_location@3x.png
Normal file
BIN
Telegram/Resources/icons/chat/filled_location@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 771 B |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -618,9 +618,6 @@ var IV = {
|
||||||
element.getAnimations().forEach(
|
element.getAnimations().forEach(
|
||||||
(animation) => animation.finish());
|
(animation) => animation.finish());
|
||||||
},
|
},
|
||||||
back: function () {
|
|
||||||
window.history.back();
|
|
||||||
},
|
|
||||||
menuShown: function (shown) {
|
menuShown: function (shown) {
|
||||||
var already = document.getElementById('menu_page_blocker');
|
var already = document.getElementById('menu_page_blocker');
|
||||||
if (already && shown) {
|
if (already && shown) {
|
||||||
|
|
|
@ -1115,6 +1115,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_faq" = "Telegram FAQ";
|
"lng_settings_faq" = "Telegram FAQ";
|
||||||
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
|
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
|
||||||
"lng_settings_features" = "Telegram Features";
|
"lng_settings_features" = "Telegram Features";
|
||||||
|
"lng_settings_credits" = "Your Stars";
|
||||||
"lng_settings_logout" = "Log Out";
|
"lng_settings_logout" = "Log Out";
|
||||||
"lng_sure_logout" = "Are you sure you want to log out?";
|
"lng_sure_logout" = "Are you sure you want to log out?";
|
||||||
|
|
||||||
|
@ -1447,6 +1448,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_info_topic_title" = "Topic Info";
|
"lng_info_topic_title" = "Topic Info";
|
||||||
"lng_profile_enable_notifications" = "Notifications";
|
"lng_profile_enable_notifications" = "Notifications";
|
||||||
"lng_profile_send_message" = "Send Message";
|
"lng_profile_send_message" = "Send Message";
|
||||||
|
"lng_profile_open_app" = "Open App";
|
||||||
|
"lng_profile_open_app_about" = "By launching this mini app, you agree to the {terms}.";
|
||||||
|
"lng_profile_open_app_terms" = "Terms of Service for Mini Apps";
|
||||||
"lng_info_add_as_contact" = "Add to contacts";
|
"lng_info_add_as_contact" = "Add to contacts";
|
||||||
"lng_profile_shared_media" = "Shared media";
|
"lng_profile_shared_media" = "Shared media";
|
||||||
"lng_profile_suggest_photo" = "Suggest Profile Photo";
|
"lng_profile_suggest_photo" = "Suggest Profile Photo";
|
||||||
|
@ -1844,6 +1848,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
||||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||||
|
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
||||||
"lng_action_suggested_photo_me" = "You suggested {user} to use this profile photo.";
|
"lng_action_suggested_photo_me" = "You suggested {user} to use this profile photo.";
|
||||||
"lng_action_suggested_photo" = "{user} suggests you to use this profile photo.";
|
"lng_action_suggested_photo" = "{user} suggests you to use this profile photo.";
|
||||||
"lng_action_suggested_photo_button" = "View Photo";
|
"lng_action_suggested_photo_button" = "View Photo";
|
||||||
|
@ -1888,6 +1893,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_boost_apply#one" = "{from} boosted the group";
|
"lng_action_boost_apply#one" = "{from} boosted the group";
|
||||||
"lng_action_boost_apply#other" = "{from} boosted the group {count} times";
|
"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_action_set_chat_intro" = "{from} added the message below for all empty chats. How?";
|
||||||
|
"lng_action_payment_refunded" = "{peer} refunded back {amount}";
|
||||||
|
|
||||||
"lng_similar_channels_title" = "Similar channels";
|
"lng_similar_channels_title" = "Similar channels";
|
||||||
"lng_similar_channels_view_all" = "View all";
|
"lng_similar_channels_view_all" = "View all";
|
||||||
|
@ -2340,6 +2346,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_summary_history_tab_out" = "Outgoing";
|
"lng_credits_summary_history_tab_out" = "Outgoing";
|
||||||
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
|
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
|
||||||
"lng_credits_summary_balance" = "Balance";
|
"lng_credits_summary_balance" = "Balance";
|
||||||
|
"lng_credits_gift_button" = "Gift Stars to Friends";
|
||||||
"lng_credits_box_out_title" = "Confirm Your Purchase";
|
"lng_credits_box_out_title" = "Confirm Your Purchase";
|
||||||
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
|
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
|
||||||
"lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?";
|
"lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?";
|
||||||
|
@ -2355,6 +2362,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star";
|
"lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star";
|
||||||
"lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars";
|
"lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars";
|
||||||
"lng_credits_box_out_about" = "Review the {link} for Stars.";
|
"lng_credits_box_out_about" = "Review the {link} for Stars.";
|
||||||
|
"lng_credits_box_out_about_link" = "https://telegram.org/tos/stars";
|
||||||
"lng_credits_media_done_title" = "Media Unlocked";
|
"lng_credits_media_done_title" = "Media Unlocked";
|
||||||
"lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}.";
|
"lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}.";
|
||||||
"lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}.";
|
"lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}.";
|
||||||
|
@ -2362,11 +2370,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_summary_in_toast_about#one" = "**{count}** Star added to your balance.";
|
"lng_credits_summary_in_toast_about#one" = "**{count}** Star added to your balance.";
|
||||||
"lng_credits_summary_in_toast_about#other" = "**{count}** Stars added to your balance.";
|
"lng_credits_summary_in_toast_about#other" = "**{count}** Stars added to your balance.";
|
||||||
"lng_credits_box_history_entry_peer" = "Recipient";
|
"lng_credits_box_history_entry_peer" = "Recipient";
|
||||||
|
"lng_credits_box_history_entry_peer_in" = "From";
|
||||||
"lng_credits_box_history_entry_via" = "Via";
|
"lng_credits_box_history_entry_via" = "Via";
|
||||||
"lng_credits_box_history_entry_play_market" = "Play Market";
|
"lng_credits_box_history_entry_play_market" = "Play Market";
|
||||||
"lng_credits_box_history_entry_app_store" = "App Store";
|
"lng_credits_box_history_entry_app_store" = "App Store";
|
||||||
"lng_credits_box_history_entry_fragment" = "Fragment";
|
"lng_credits_box_history_entry_fragment" = "Fragment";
|
||||||
|
"lng_credits_box_history_entry_anonymous" = "Unknown User";
|
||||||
|
"lng_credits_box_history_entry_gift_name" = "Received Gift";
|
||||||
|
"lng_credits_box_history_entry_gift_sent" = "Sent Gift";
|
||||||
|
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
|
||||||
|
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
|
||||||
|
"lng_credits_box_history_entry_gift_about_link" = "See Examples {emoji}";
|
||||||
|
"lng_credits_box_history_entry_gift_about_url" = "https://telegram.org/blog/telegram-stars";
|
||||||
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
||||||
|
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
|
||||||
|
"lng_credits_box_history_entry_via_premium_bot" = "Premium Bot";
|
||||||
"lng_credits_box_history_entry_id" = "Transaction ID";
|
"lng_credits_box_history_entry_id" = "Transaction ID";
|
||||||
"lng_credits_box_history_entry_id_copied" = "Transaction ID copied to clipboard.";
|
"lng_credits_box_history_entry_id_copied" = "Transaction ID copied to clipboard.";
|
||||||
"lng_credits_box_history_entry_success_date" = "Transaction date";
|
"lng_credits_box_history_entry_success_date" = "Transaction date";
|
||||||
|
@ -2379,9 +2397,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_small_balance_about" = "Buy **Stars** and use them on **{bot}** and other miniapps.";
|
"lng_credits_small_balance_about" = "Buy **Stars** and use them on **{bot}** and other miniapps.";
|
||||||
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
||||||
|
|
||||||
|
"lng_credits_gift_title" = "Gift Telegram Stars";
|
||||||
|
|
||||||
"lng_location_title" = "Location";
|
"lng_location_title" = "Location";
|
||||||
"lng_location_about" = "Display the location of your business on your account.";
|
"lng_location_about" = "Display the location of your business on your account.";
|
||||||
"lng_location_address" = "Enter Address";
|
"lng_location_address" = "Enter Address";
|
||||||
|
"lng_location_set_map" = "Set Location on Map";
|
||||||
"lng_location_fallback" = "You can set your location on the map from your mobile device.";
|
"lng_location_fallback" = "You can set your location on the map from your mobile device.";
|
||||||
|
|
||||||
"lng_hours_title" = "Business Hours";
|
"lng_hours_title" = "Business Hours";
|
||||||
|
@ -2810,12 +2831,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_prizes_badge" = "x{amount}";
|
"lng_prizes_badge" = "x{amount}";
|
||||||
|
|
||||||
"lng_prizes_results_title" = "Winners Selected!";
|
"lng_prizes_results_title" = "Winners Selected!";
|
||||||
|
"lng_prizes_results_title_one" = "Winner Selected!";
|
||||||
"lng_prizes_results_about#one" = "**{count}** winner of the {link} was randomly selected by Telegram.";
|
"lng_prizes_results_about#one" = "**{count}** winner of the {link} was randomly selected by Telegram.";
|
||||||
"lng_prizes_results_about#other" = "**{count}** winners of the {link} were randomly selected by Telegram.";
|
"lng_prizes_results_about#other" = "**{count}** winners of the {link} were randomly selected by Telegram.";
|
||||||
"lng_prizes_results_link" = "Giveaway";
|
"lng_prizes_results_link" = "Giveaway";
|
||||||
|
"lng_prizes_results_winner" = "Winner";
|
||||||
"lng_prizes_results_winners" = "Winners";
|
"lng_prizes_results_winners" = "Winners";
|
||||||
"lng_prizes_results_more#one" = "and {count} more!";
|
"lng_prizes_results_more#one" = "and {count} more!";
|
||||||
"lng_prizes_results_more#other" = "and {count} more!";
|
"lng_prizes_results_more#other" = "and {count} more!";
|
||||||
|
"lng_prizes_results_one" = "The winner received their gift link in a private message.";
|
||||||
"lng_prizes_results_all" = "All winners received gift links in private messages.";
|
"lng_prizes_results_all" = "All winners received gift links in private messages.";
|
||||||
"lng_prizes_results_some" = "Some winners couldn't be selected.";
|
"lng_prizes_results_some" = "Some winners couldn't be selected.";
|
||||||
|
|
||||||
|
@ -2845,6 +2869,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_gift_link_pending_toast" = "Only the recipient can see the link.";
|
"lng_gift_link_pending_toast" = "Only the recipient can see the link.";
|
||||||
"lng_gift_link_pending_footer" = "This link hasn't been activated yet.";
|
"lng_gift_link_pending_footer" = "This link hasn't been activated yet.";
|
||||||
|
|
||||||
|
"lng_gift_stars_title#one" = "{count} Star";
|
||||||
|
"lng_gift_stars_title#other" = "{count} Stars";
|
||||||
|
"lng_gift_stars_outgoing" = "With Stars, {user} will be able to unlock content and services on Telegram.";
|
||||||
|
"lng_gift_stars_incoming" = "Use Stars to unlock content and services on Telegram.";
|
||||||
|
|
||||||
"lng_accounts_limit_title" = "Limit Reached";
|
"lng_accounts_limit_title" = "Limit Reached";
|
||||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts.";
|
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts.";
|
||||||
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
|
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
|
||||||
|
@ -2920,6 +2949,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_masks_has_been_archived" = "Mask pack has been archived.";
|
"lng_masks_has_been_archived" = "Mask pack has been archived.";
|
||||||
"lng_masks_installed" = "Mask pack has been installed.";
|
"lng_masks_installed" = "Mask pack has been installed.";
|
||||||
"lng_emoji_nothing_found" = "No emoji found";
|
"lng_emoji_nothing_found" = "No emoji found";
|
||||||
|
"lng_stickers_context_reorder" = "Reorder";
|
||||||
|
"lng_stickers_context_edit_name" = "Edit name";
|
||||||
|
"lng_stickers_context_delete" = "Delete sticker";
|
||||||
|
"lng_stickers_context_delete_sure" = "Are you sure you want to delete the sticker from your sticker set?";
|
||||||
|
"lng_stickers_box_edit_name_title" = "Edit Sticker Set Name";
|
||||||
|
"lng_stickers_box_edit_name_about" = "Choose a name for your set.";
|
||||||
|
"lng_stickers_creator_badge" = "edit";
|
||||||
|
|
||||||
"lng_in_dlg_photo" = "Photo";
|
"lng_in_dlg_photo" = "Photo";
|
||||||
"lng_in_dlg_album" = "Album";
|
"lng_in_dlg_album" = "Album";
|
||||||
|
@ -3151,6 +3187,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_bot_close_warning_sure" = "Close anyway";
|
"lng_bot_close_warning_sure" = "Close anyway";
|
||||||
"lng_bot_add_to_side_menu" = "{bot} asks your permission to be added as an option to your main menu so you can access it any time.";
|
"lng_bot_add_to_side_menu" = "{bot} asks your permission to be added as an option to your main menu so you can access it any time.";
|
||||||
"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
|
"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
|
||||||
|
"lng_bot_no_scan_qr" = "QR Codes for bots are not supported on Desktop. Please use one of Telegram's mobile apps.";
|
||||||
|
"lng_bot_click_to_start" = "Click here to use this bot.";
|
||||||
|
"lng_bot_status_users#one" = "{count} user";
|
||||||
|
"lng_bot_status_users#other" = "{count} users";
|
||||||
|
|
||||||
"lng_typing" = "typing";
|
"lng_typing" = "typing";
|
||||||
"lng_user_typing" = "{user} is typing";
|
"lng_user_typing" = "{user} is typing";
|
||||||
|
@ -3186,6 +3226,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_unread_bar_some" = "Unread messages";
|
"lng_unread_bar_some" = "Unread messages";
|
||||||
|
|
||||||
"lng_maps_point" = "Location";
|
"lng_maps_point" = "Location";
|
||||||
|
"lng_maps_select_on_map" = "Select on the Map";
|
||||||
|
"lng_maps_point_send" = "Send This Location";
|
||||||
|
"lng_maps_point_set" = "Set This Location";
|
||||||
|
"lng_maps_or_choose" = "Or choose a venue";
|
||||||
|
"lng_maps_places_in_area" = "Places in this area";
|
||||||
|
"lng_maps_no_places" = "No places found";
|
||||||
|
"lng_maps_choose_to_search" = "Choose location to see places nearby.";
|
||||||
|
"lng_maps_venues_source" = "Powered by Foursquare";
|
||||||
"lng_live_location" = "Live Location";
|
"lng_live_location" = "Live Location";
|
||||||
"lng_live_location_now" = "updated just now";
|
"lng_live_location_now" = "updated just now";
|
||||||
"lng_live_location_minutes#one" = "updated {count} minute ago";
|
"lng_live_location_minutes#one" = "updated {count} minute ago";
|
||||||
|
@ -3740,6 +3788,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_payments_card_declined" = "Your card was declined.";
|
"lng_payments_card_declined" = "Your card was declined.";
|
||||||
"lng_payments_payment_failed" = "Payment failed. Your card has not been billed.";
|
"lng_payments_payment_failed" = "Payment failed. Your card has not been billed.";
|
||||||
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
|
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
|
||||||
|
"lng_payments_precheckout_timeout" = "The bot didn't respond in time. Your card has not been billed.";
|
||||||
|
"lng_payments_precheckout_stars_failed" = "The bot couldn't process your payment.";
|
||||||
|
"lng_payments_precheckout_stars_timeout" = "The bot didn't respond in time.";
|
||||||
"lng_payments_already_paid" = "You have already paid for this item.";
|
"lng_payments_already_paid" = "You have already paid for this item.";
|
||||||
|
|
||||||
"lng_payments_terms_title" = "Terms of Service";
|
"lng_payments_terms_title" = "Terms of Service";
|
||||||
|
@ -5267,6 +5318,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_iv_join_channel" = "Join";
|
"lng_iv_join_channel" = "Join";
|
||||||
"lng_iv_window_title" = "Instant View";
|
"lng_iv_window_title" = "Instant View";
|
||||||
"lng_iv_wrong_layout" = "Wrong layout?";
|
"lng_iv_wrong_layout" = "Wrong layout?";
|
||||||
|
"lng_iv_not_supported" = "This link appears to be invalid.";
|
||||||
|
|
||||||
"lng_limit_download_title" = "Download speed limited";
|
"lng_limit_download_title" = "Download speed limited";
|
||||||
"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}.";
|
"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}.";
|
||||||
|
@ -5295,12 +5347,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_recent_none" = "Recent search results\nwill appear here.";
|
"lng_recent_none" = "Recent search results\nwill appear here.";
|
||||||
"lng_recent_chats" = "Chats";
|
"lng_recent_chats" = "Chats";
|
||||||
"lng_recent_channels" = "Channels";
|
"lng_recent_channels" = "Channels";
|
||||||
|
"lng_recent_apps" = "Apps";
|
||||||
"lng_channels_none_title" = "No channels yet...";
|
"lng_channels_none_title" = "No channels yet...";
|
||||||
"lng_channels_none_about" = "You are not currently subscribed to any channels.";
|
"lng_channels_none_about" = "You are not currently subscribed to any channels.";
|
||||||
"lng_channels_your_title" = "Channels you joined";
|
"lng_channels_your_title" = "Channels you joined";
|
||||||
"lng_channels_your_more" = "Show more";
|
"lng_channels_your_more" = "Show more";
|
||||||
"lng_channels_your_less" = "Show less";
|
"lng_channels_your_less" = "Show less";
|
||||||
"lng_channels_recommended" = "Recommended channels";
|
"lng_channels_recommended" = "Recommended channels";
|
||||||
|
"lng_bot_apps_your" = "Apps you use";
|
||||||
|
"lng_bot_apps_popular" = "Popular apps";
|
||||||
|
|
||||||
"lng_font_box_title" = "Choose font family";
|
"lng_font_box_title" = "Choose font family";
|
||||||
"lng_font_default" = "Default";
|
"lng_font_default" = "Default";
|
||||||
|
|
120
Telegram/Resources/picker_html/picker.css
Normal file
120
Telegram/Resources/picker_html/picker.css
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
:root {
|
||||||
|
--font-sans: -apple-system, BlinkMacSystemFont, avenir next, avenir, Segoe UI Variable Text, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, tahoma, arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background-color: var(--td-window-bg);
|
||||||
|
color: var(--td-window-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.custom_scroll ::-webkit-scrollbar {
|
||||||
|
border-radius: 5px !important;
|
||||||
|
border: 3px solid transparent !important;
|
||||||
|
background-color: var(--td-scroll-bg) !important;
|
||||||
|
background-clip: content-box !important;
|
||||||
|
width: 10px !important;
|
||||||
|
}
|
||||||
|
html.custom_scroll ::-webkit-scrollbar:hover {
|
||||||
|
background-color: var(--td-scroll-bg-over) !important;
|
||||||
|
}
|
||||||
|
html.custom_scroll ::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 5px !important;
|
||||||
|
border: 3px solid transparent !important;
|
||||||
|
background-color: var(--td-scroll-bar-bg) !important;
|
||||||
|
background-clip: content-box !important;
|
||||||
|
}
|
||||||
|
html.custom_scroll ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--td-scroll-bar-bg-over) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#marker {
|
||||||
|
pointer-events: none;
|
||||||
|
display: none;
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#marker_drop {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
transition: margin 160ms ease-in-out;
|
||||||
|
}
|
||||||
|
#marker_drop.moving {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
#marker_shadow {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
#search_venues {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 2;
|
||||||
|
top: -30px;
|
||||||
|
transition: top 200ms ease-in-out;
|
||||||
|
}
|
||||||
|
#search_venues.shown {
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
#search_venues_inner {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
background: var(--td-window-bg);
|
||||||
|
color: var(--td-window-active-text-fg);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 5px 12px 6px;
|
||||||
|
box-shadow: 0 0 3px 0px var(--td-history-to-down-shadow);
|
||||||
|
}
|
||||||
|
#search_venues_inner:hover {
|
||||||
|
background: var(--td-window-bg-over);
|
||||||
|
}
|
||||||
|
#search_venues_content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
#search_venues_content:before {
|
||||||
|
content: var(--td-lng-maps-places-in-area);
|
||||||
|
}
|
||||||
|
#search_venues_inner .ripple .inner {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 1;
|
||||||
|
animation: ripple 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||||
|
background-color: var(--td-window-bg-ripple);
|
||||||
|
}
|
||||||
|
#search_venues_inner .ripple.hiding {
|
||||||
|
animation: fadeOut 200ms linear forwards;
|
||||||
|
}
|
||||||
|
@keyframes ripple {
|
||||||
|
to {
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes fadeOut {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
199
Telegram/Resources/picker_html/picker.js
Normal file
199
Telegram/Resources/picker_html/picker.js
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
var LocationPicker = {
|
||||||
|
startZoom: 14,
|
||||||
|
flySpeed: 2.4,
|
||||||
|
notify: function(message) {
|
||||||
|
if (window.external && window.external.invoke) {
|
||||||
|
window.external.invoke(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
LocationPicker.notify({
|
||||||
|
event: 'keydown',
|
||||||
|
modifier: e.ctrlKey ? 'ctrl' : 'cmd',
|
||||||
|
key: keyW ? 'w' : keyQ ? 'q' : 'm',
|
||||||
|
});
|
||||||
|
} else if (e.key === 'Escape' || e.keyCode === 27) {
|
||||||
|
e.preventDefault();
|
||||||
|
LocationPicker.notify({
|
||||||
|
event: 'keydown',
|
||||||
|
key: 'escape',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isNight: function() {
|
||||||
|
var html = document.getElementsByTagName('html')[0];
|
||||||
|
return html.style.getPropertyValue('--td-night') == '1';
|
||||||
|
},
|
||||||
|
lightPreset: function() {
|
||||||
|
return LocationPicker.isNight() ? 'night' : 'day';
|
||||||
|
},
|
||||||
|
updateStyles: function (styles) {
|
||||||
|
if (LocationPicker.styles !== styles) {
|
||||||
|
LocationPicker.styles = styles;
|
||||||
|
document.getElementsByTagName('html')[0].style = styles;
|
||||||
|
|
||||||
|
LocationPicker.map.setConfigProperty(
|
||||||
|
'basemap',
|
||||||
|
'lightPreset',
|
||||||
|
LocationPicker.lightPreset());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: function (params) {
|
||||||
|
mapboxgl.accessToken = params.token;
|
||||||
|
if (params.protocol) {
|
||||||
|
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = { container: 'map', config: {
|
||||||
|
basemap: { lightPreset: LocationPicker.lightPreset() }
|
||||||
|
} };
|
||||||
|
var center = params.center;
|
||||||
|
if (center) {
|
||||||
|
center = [center[1], center[0]];
|
||||||
|
options.center = center;
|
||||||
|
options.zoom = LocationPicker.startZoom;
|
||||||
|
} else if (params.bounds) {
|
||||||
|
options.bounds = params.bounds;
|
||||||
|
center = new mapboxgl.LngLatBounds(params.bounds).getCenter();
|
||||||
|
} else {
|
||||||
|
center = [0, 0];
|
||||||
|
}
|
||||||
|
LocationPicker.map = new mapboxgl.Map(options);
|
||||||
|
LocationPicker.createMarker(center);
|
||||||
|
LocationPicker.trackMovement();
|
||||||
|
LocationPicker.initSearchVenueRipple();
|
||||||
|
},
|
||||||
|
marker: function() {
|
||||||
|
return document.getElementById('marker_drop');
|
||||||
|
},
|
||||||
|
createMarker: function(center) {
|
||||||
|
document.getElementById('marker').style.display = 'flex';
|
||||||
|
},
|
||||||
|
clearMovingTimer: function() {
|
||||||
|
if (LocationPicker.clearMovingTimeoutId) {
|
||||||
|
clearTimeout(LocationPicker.clearMovingTimeoutId);
|
||||||
|
LocationPicker.clearMovingTimeoutId = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startMovingTimer: function(done) {
|
||||||
|
LocationPicker.clearMovingTimer();
|
||||||
|
LocationPicker.clearMovingTimeoutId = setTimeout(done, 500);
|
||||||
|
},
|
||||||
|
trackMovement: function() {
|
||||||
|
LocationPicker.map.on('movestart', function() {
|
||||||
|
LocationPicker.marker().classList.add('moving');
|
||||||
|
LocationPicker.clearMovingTimer();
|
||||||
|
LocationPicker.toggleSearchVenues(false);
|
||||||
|
LocationPicker.notify({ event: 'move_start' });
|
||||||
|
});
|
||||||
|
LocationPicker.map.on('moveend', function() {
|
||||||
|
LocationPicker.startMovingTimer(function() {
|
||||||
|
LocationPicker.marker().classList.remove('moving');
|
||||||
|
LocationPicker.notify({
|
||||||
|
event: 'move_end',
|
||||||
|
latitude: LocationPicker.map.getCenter().lat,
|
||||||
|
longitude: LocationPicker.map.getCenter().lng
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
narrowTo: function (point) {
|
||||||
|
LocationPicker.map.flyTo({
|
||||||
|
center: [point[1], point[0]],
|
||||||
|
zoom: LocationPicker.startZoom,
|
||||||
|
speed: LocationPicker.flySpeed,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
send: function () {
|
||||||
|
LocationPicker.notify({
|
||||||
|
event: 'send',
|
||||||
|
latitude: LocationPicker.map.getCenter().lat,
|
||||||
|
longitude: LocationPicker.map.getCenter().lng
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addRipple: function (button, x, y) {
|
||||||
|
const ripple = document.createElement('span');
|
||||||
|
ripple.classList.add('ripple');
|
||||||
|
|
||||||
|
const inner = document.createElement('span');
|
||||||
|
inner.classList.add('inner');
|
||||||
|
|
||||||
|
var rect = button.getBoundingClientRect();
|
||||||
|
x -= rect.x;
|
||||||
|
y -= rect.y;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initSearchVenueRipple: function() {
|
||||||
|
var button = document.getElementById('search_venues_inner');
|
||||||
|
button.addEventListener('mousedown', function (e) {
|
||||||
|
LocationPicker.addRipple(e.currentTarget, e.clientX, e.clientY);
|
||||||
|
LocationPicker.searchVenuesPressed = true;
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseup', function (e) {
|
||||||
|
const id = e.currentTarget.id;
|
||||||
|
setTimeout(function () {
|
||||||
|
LocationPicker.stopRipples(id);
|
||||||
|
}, 0);
|
||||||
|
if (LocationPicker.searchVenuesPressed) {
|
||||||
|
LocationPicker.searchVenuesPressed = false;
|
||||||
|
LocationPicker.toggleSearchVenues(false);
|
||||||
|
LocationPicker.notify({
|
||||||
|
event: 'search_venues',
|
||||||
|
latitude: LocationPicker.map.getCenter().lat,
|
||||||
|
longitude: LocationPicker.map.getCenter().lng
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseleave', function (e) {
|
||||||
|
LocationPicker.stopRipples(e.currentTarget);
|
||||||
|
LocationPicker.searchVenuesPressed = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleSearchVenues: function(shown) {
|
||||||
|
var button = document.getElementById('search_venues');
|
||||||
|
button.classList.toggle('shown', shown);
|
||||||
|
},
|
||||||
|
};
|
6
Telegram/Resources/qrc/telegram/picker.qrc
Normal file
6
Telegram/Resources/qrc/telegram/picker.qrc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/picker">
|
||||||
|
<file alias="picker.css">../../picker_html/picker.css</file>
|
||||||
|
<file alias="picker.js">../../picker_html/picker.js</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="5.2.2.0" />
|
Version="5.3.2.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
@ -37,6 +37,9 @@
|
||||||
<Extensions>
|
<Extensions>
|
||||||
<uap3:Extension Category="windows.protocol">
|
<uap3:Extension Category="windows.protocol">
|
||||||
<uap3:Protocol Name="tg" Parameters="-- "%1"" />
|
<uap3:Protocol Name="tg" Parameters="-- "%1"" />
|
||||||
|
<uap3:Extension Category="windows.protocol">
|
||||||
|
</uap3:Extension>
|
||||||
|
<uap3:Protocol Name="tonsite" Parameters="-- "%1"" />
|
||||||
</uap3:Extension>
|
</uap3:Extension>
|
||||||
<desktop:Extension
|
<desktop:Extension
|
||||||
Category="windows.startupTask"
|
Category="windows.startupTask"
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,2,2,0
|
FILEVERSION 5,3,2,0
|
||||||
PRODUCTVERSION 5,2,2,0
|
PRODUCTVERSION 5,3,2,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "5.2.2.0"
|
VALUE "FileVersion", "5.3.2.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.2.2.0"
|
VALUE "ProductVersion", "5.3.2.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,2,2,0
|
FILEVERSION 5,3,2,0
|
||||||
PRODUCTVERSION 5,2,2,0
|
PRODUCTVERSION 5,3,2,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "5.2.2.0"
|
VALUE "FileVersion", "5.3.2.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.2.2.0"
|
VALUE "ProductVersion", "5.3.2.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -155,6 +155,7 @@ int main(int argc, char *argv[])
|
||||||
QString remove;
|
QString remove;
|
||||||
int version = 0;
|
int version = 0;
|
||||||
[[maybe_unused]] bool targetwin64 = false;
|
[[maybe_unused]] bool targetwin64 = false;
|
||||||
|
[[maybe_unused]] bool targetwinarm = false;
|
||||||
[[maybe_unused]] bool targetarmac = false;
|
[[maybe_unused]] bool targetarmac = false;
|
||||||
QFileInfoList files;
|
QFileInfoList files;
|
||||||
for (int i = 0; i < argc; ++i) {
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
@ -165,6 +166,7 @@ int main(int argc, char *argv[])
|
||||||
if (remove.isEmpty()) remove = info.canonicalPath() + "/";
|
if (remove.isEmpty()) remove = info.canonicalPath() + "/";
|
||||||
} else if (string("-target") == argv[i] && i + 1 < argc) {
|
} else if (string("-target") == argv[i] && i + 1 < argc) {
|
||||||
targetwin64 = (string("win64") == argv[i + 1]);
|
targetwin64 = (string("win64") == argv[i + 1]);
|
||||||
|
targetwinarm = (string("winarm") == argv[i + 1]);
|
||||||
} else if (string("-arch") == argv[i] && i + 1 < argc) {
|
} else if (string("-arch") == argv[i] && i + 1 < argc) {
|
||||||
targetarmac = (string("arm64") == argv[i + 1]);
|
targetarmac = (string("arm64") == argv[i + 1]);
|
||||||
if (!targetarmac && string("x86_64") != argv[i + 1]) {
|
if (!targetarmac && string("x86_64") != argv[i + 1]) {
|
||||||
|
@ -493,7 +495,7 @@ int main(int argc, char *argv[])
|
||||||
cout << "Signature verified!\n";
|
cout << "Signature verified!\n";
|
||||||
RSA_free(pbKey);
|
RSA_free(pbKey);
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString outName((targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
|
QString outName((targetwinarm ? QString("tarm64upd%1") : targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
|
||||||
#elif defined Q_OS_MAC
|
#elif defined Q_OS_MAC
|
||||||
QString outName((targetarmac ? QString("tarmacupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
|
QString outName((targetarmac ? QString("tarmacupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -571,8 +571,8 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
|
||||||
}
|
}
|
||||||
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
|
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
|
||||||
WCHAR wstrPath[maxFileLen];
|
WCHAR wstrPath[maxFileLen];
|
||||||
DWORD wstrPathLen;
|
DWORD wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen);
|
||||||
if (wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
|
if (wstrPathLen) {
|
||||||
wsprintf(wstrPath + wstrPathLen, L"\\%s\\", _programName);
|
wsprintf(wstrPath + wstrPathLen, L"\\%s\\", _programName);
|
||||||
hDumpFile = _generateDumpFileAtPath(wstrPath);
|
hDumpFile = _generateDumpFileAtPath(wstrPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,11 +127,7 @@ void SendBotCallbackData(
|
||||||
UrlClickHandler::Open(link);
|
UrlClickHandler::Open(link);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto scoreLink = AppendShareGameScoreUrl(
|
BotGameUrlClickHandler(bot, link).onClick({
|
||||||
session,
|
|
||||||
link,
|
|
||||||
item->fullId());
|
|
||||||
BotGameUrlClickHandler(bot, scoreLink).onClick({
|
|
||||||
Qt::LeftButton,
|
Qt::LeftButton,
|
||||||
QVariant::fromValue(ClickHandlerContext{
|
QVariant::fromValue(ClickHandlerContext{
|
||||||
.itemId = item->fullId(),
|
.itemId = item->fullId(),
|
||||||
|
@ -492,20 +488,23 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||||
|
|
||||||
case ButtonType::WebView: {
|
case ButtonType::WebView: {
|
||||||
if (const auto bot = item->getMessageBot()) {
|
if (const auto bot = item->getMessageBot()) {
|
||||||
bot->session().attachWebView().request(
|
bot->session().attachWebView().open({
|
||||||
controller,
|
.bot = bot,
|
||||||
Api::SendAction(bot->owner().history(bot)),
|
.context = { .controller = controller },
|
||||||
bot,
|
.button = { .text = button->text, .url = button->data },
|
||||||
{ .text = button->text, .url = button->data });
|
.source = InlineBots::WebViewSourceButton{ .simple = false },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ButtonType::SimpleWebView: {
|
case ButtonType::SimpleWebView: {
|
||||||
if (const auto bot = item->getMessageBot()) {
|
if (const auto bot = item->getMessageBot()) {
|
||||||
bot->session().attachWebView().requestSimple(
|
bot->session().attachWebView().open({
|
||||||
controller,
|
.bot = bot,
|
||||||
bot,
|
.context = { .controller = controller },
|
||||||
{ .text = button->text, .url = button->data });
|
.button = {.text = button->text, .url = button->data },
|
||||||
|
.source = InlineBots::WebViewSourceButton{ .simple = true },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,10 @@ struct SendOptions {
|
||||||
bool invertCaption = false;
|
bool invertCaption = false;
|
||||||
bool hideViaBot = false;
|
bool hideViaBot = false;
|
||||||
crl::time ttlSeconds = 0;
|
crl::time ttlSeconds = 0;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const SendOptions &,
|
||||||
|
const SendOptions &) = default;
|
||||||
};
|
};
|
||||||
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
|
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
|
||||||
|
|
||||||
|
@ -52,6 +56,10 @@ struct SendAction {
|
||||||
MsgId replaceMediaOf = 0;
|
MsgId replaceMediaOf = 0;
|
||||||
|
|
||||||
[[nodiscard]] MTPInputReplyTo mtpReplyTo() const;
|
[[nodiscard]] MTPInputReplyTo mtpReplyTo() const;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const SendAction &,
|
||||||
|
const SendAction &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MessageToSend {
|
struct MessageToSend {
|
||||||
|
|
|
@ -102,6 +102,7 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
: QDateTime(),
|
: QDateTime(),
|
||||||
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
|
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
|
||||||
.in = (int64(tl.data().vstars().v) >= 0),
|
.in = (int64(tl.data().vstars().v) >= 0),
|
||||||
|
.gift = tl.data().is_gift(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,12 +134,12 @@ rpl::producer<rpl::no_value, QString> CreditsTopupOptions::request() {
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
auto lifetime = rpl::lifetime();
|
auto lifetime = rpl::lifetime();
|
||||||
|
|
||||||
using TLOption = MTPStarsTopupOption;
|
const auto giftBarePeerId = !_peer->isSelf() ? _peer->id.value : 0;
|
||||||
_api.request(MTPpayments_GetStarsTopupOptions(
|
|
||||||
)).done([=](const MTPVector<TLOption> &result) {
|
const auto optionsFromTL = [giftBarePeerId](const auto &options) {
|
||||||
_options = ranges::views::all(
|
return ranges::views::all(
|
||||||
result.v
|
options
|
||||||
) | ranges::views::transform([](const TLOption &option) {
|
) | ranges::views::transform([=](const auto &option) {
|
||||||
return Data::CreditTopupOption{
|
return Data::CreditTopupOption{
|
||||||
.credits = option.data().vstars().v,
|
.credits = option.data().vstars().v,
|
||||||
.product = qs(
|
.product = qs(
|
||||||
|
@ -146,12 +147,31 @@ rpl::producer<rpl::no_value, QString> CreditsTopupOptions::request() {
|
||||||
.currency = qs(option.data().vcurrency()),
|
.currency = qs(option.data().vcurrency()),
|
||||||
.amount = option.data().vamount().v,
|
.amount = option.data().vamount().v,
|
||||||
.extended = option.data().is_extended(),
|
.extended = option.data().is_extended(),
|
||||||
|
.giftBarePeerId = giftBarePeerId,
|
||||||
};
|
};
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
consumer.put_done();
|
};
|
||||||
}).fail([=](const MTP::Error &error) {
|
const auto fail = [=](const MTP::Error &error) {
|
||||||
consumer.put_error_copy(error.type());
|
consumer.put_error_copy(error.type());
|
||||||
}).send();
|
};
|
||||||
|
|
||||||
|
if (_peer->isSelf()) {
|
||||||
|
using TLOption = MTPStarsTopupOption;
|
||||||
|
_api.request(MTPpayments_GetStarsTopupOptions(
|
||||||
|
)).done([=](const MTPVector<TLOption> &result) {
|
||||||
|
_options = optionsFromTL(result.v);
|
||||||
|
consumer.put_done();
|
||||||
|
}).fail(fail).send();
|
||||||
|
} else if (const auto user = _peer->asUser()) {
|
||||||
|
using TLOption = MTPStarsGiftOption;
|
||||||
|
_api.request(MTPpayments_GetStarsGiftOptions(
|
||||||
|
MTP_flags(MTPpayments_GetStarsGiftOptions::Flag::f_user_id),
|
||||||
|
user->inputUser
|
||||||
|
)).done([=](const MTPVector<TLOption> &result) {
|
||||||
|
_options = optionsFromTL(result.v);
|
||||||
|
consumer.put_done();
|
||||||
|
}).fail(fail).send();
|
||||||
|
}
|
||||||
|
|
||||||
return lifetime;
|
return lifetime;
|
||||||
};
|
};
|
||||||
|
|
|
@ -128,7 +128,7 @@ mtpRequestId EditMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateRecentStickers) {
|
if (updateRecentStickers) {
|
||||||
api->requestRecentStickersForce(true);
|
api->requestSpecialStickersForce(false, false, true);
|
||||||
}
|
}
|
||||||
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||||
if constexpr (ErrorWithId<FailCallback>) {
|
if constexpr (ErrorWithId<FailCallback>) {
|
||||||
|
@ -153,9 +153,7 @@ mtpRequestId EditMessage(
|
||||||
const auto &text = item->originalText();
|
const auto &text = item->originalText();
|
||||||
const auto webpage = (!item->media() || !item->media()->webpage())
|
const auto webpage = (!item->media() || !item->media()->webpage())
|
||||||
? Data::WebPageDraft{ .removed = true }
|
? Data::WebPageDraft{ .removed = true }
|
||||||
: Data::WebPageDraft{
|
: Data::WebPageDraft::FromItem(item);
|
||||||
.id = item->media()->webpage()->id,
|
|
||||||
};
|
|
||||||
return EditMessage(
|
return EditMessage(
|
||||||
item,
|
item,
|
||||||
text,
|
text,
|
||||||
|
|
|
@ -36,7 +36,8 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
|
||||||
MTP_double(document->duration() / 1000.),
|
MTP_double(document->duration() / 1000.),
|
||||||
MTP_int(dimensions.width()),
|
MTP_int(dimensions.width()),
|
||||||
MTP_int(dimensions.height()),
|
MTP_int(dimensions.height()),
|
||||||
MTPint())); // preload_prefix_size
|
MTPint(), // preload_prefix_size
|
||||||
|
MTPdouble())); // video_start_ts
|
||||||
} else {
|
} else {
|
||||||
attributes.push_back(MTP_documentAttributeImageSize(
|
attributes.push_back(MTP_documentAttributeImageSize(
|
||||||
MTP_int(dimensions.width()),
|
MTP_int(dimensions.width()),
|
||||||
|
|
|
@ -62,6 +62,79 @@ void InnerFillMessagePostFlags(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
const auto session = &history->session();
|
||||||
|
const auto api = &session->api();
|
||||||
|
|
||||||
|
action.clearDraft = false;
|
||||||
|
action.generateLocal = false;
|
||||||
|
api->sendAction(action);
|
||||||
|
|
||||||
|
const auto randomId = base::RandomValue<uint64>();
|
||||||
|
|
||||||
|
auto flags = NewMessageFlags(peer);
|
||||||
|
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||||
|
if (action.replyTo) {
|
||||||
|
flags |= MessageFlag::HasReplyInfo;
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||||
|
}
|
||||||
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
|
InnerFillMessagePostFlags(action.options, peer, flags);
|
||||||
|
if (silentPost) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||||
|
}
|
||||||
|
const auto sendAs = action.options.sendAs;
|
||||||
|
if (sendAs) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||||
|
}
|
||||||
|
const auto messagePostAuthor = peer->isBroadcast()
|
||||||
|
? session->user()->name()
|
||||||
|
: QString();
|
||||||
|
|
||||||
|
if (action.options.scheduled) {
|
||||||
|
flags |= MessageFlag::IsOrWasScheduled;
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
flags |= MessageFlag::ShortcutMessage;
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
|
if (action.options.effectId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
|
}
|
||||||
|
if (action.options.invertCaption) {
|
||||||
|
flags |= MessageFlag::InvertMedia;
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &histories = history->owner().histories();
|
||||||
|
histories.sendPreparedMessage(
|
||||||
|
history,
|
||||||
|
action.replyTo,
|
||||||
|
randomId,
|
||||||
|
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||||
|
MTP_flags(sendFlags),
|
||||||
|
peer->input,
|
||||||
|
Data::Histories::ReplyToPlaceholder(),
|
||||||
|
std::move(inputMedia),
|
||||||
|
MTPstring(),
|
||||||
|
MTP_long(randomId),
|
||||||
|
MTPReplyMarkup(),
|
||||||
|
MTPvector<MTPMessageEntity>(),
|
||||||
|
MTP_int(action.options.scheduled),
|
||||||
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
|
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||||
|
MTP_long(action.options.effectId)
|
||||||
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
|
api->sendMessageFail(error, peer, randomId);
|
||||||
|
});
|
||||||
|
|
||||||
|
api->finishForwarding(action);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename MediaData>
|
template <typename MediaData>
|
||||||
void SendExistingMedia(
|
void SendExistingMedia(
|
||||||
MessageToSend &&message,
|
MessageToSend &&message,
|
||||||
|
@ -362,6 +435,33 @@ bool SendDice(MessageToSend &message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendLocation(SendAction action, float64 lat, float64 lon) {
|
||||||
|
SendSimpleMedia(
|
||||||
|
action,
|
||||||
|
MTP_inputMediaGeoPoint(
|
||||||
|
MTP_inputGeoPoint(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_double(lat),
|
||||||
|
MTP_double(lon),
|
||||||
|
MTPint()))); // accuracy_radius
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendVenue(SendAction action, Data::InputVenue venue) {
|
||||||
|
SendSimpleMedia(
|
||||||
|
action,
|
||||||
|
MTP_inputMediaVenue(
|
||||||
|
MTP_inputGeoPoint(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_double(venue.lat),
|
||||||
|
MTP_double(venue.lon),
|
||||||
|
MTPint()), // accuracy_radius
|
||||||
|
MTP_string(venue.title),
|
||||||
|
MTP_string(venue.address),
|
||||||
|
MTP_string(venue.provider),
|
||||||
|
MTP_string(venue.id),
|
||||||
|
MTP_string(venue.venueType)));
|
||||||
|
}
|
||||||
|
|
||||||
void FillMessagePostFlags(
|
void FillMessagePostFlags(
|
||||||
const SendAction &action,
|
const SendAction &action,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
|
|
@ -7,15 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace Main {
|
|
||||||
class Session;
|
|
||||||
} // namespace Main
|
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
class PhotoData;
|
class PhotoData;
|
||||||
class DocumentData;
|
class DocumentData;
|
||||||
struct FilePrepareResult;
|
struct FilePrepareResult;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct InputVenue;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
struct MessageToSend;
|
struct MessageToSend;
|
||||||
|
@ -33,6 +37,13 @@ void SendExistingPhoto(
|
||||||
|
|
||||||
bool SendDice(MessageToSend &message);
|
bool SendDice(MessageToSend &message);
|
||||||
|
|
||||||
|
// We can't create Data::LocationPoint() and use it
|
||||||
|
// for a local sending message, because we can't request
|
||||||
|
// map thumbnail in messages history without access hash.
|
||||||
|
void SendLocation(SendAction action, float64 lat, float64 lon);
|
||||||
|
|
||||||
|
void SendVenue(SendAction action, Data::InputVenue venue);
|
||||||
|
|
||||||
void FillMessagePostFlags(
|
void FillMessagePostFlags(
|
||||||
const SendAction &action,
|
const SendAction &action,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
|
|
@ -2629,7 +2629,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &resu
|
||||||
void ApiWrap::updateStickers() {
|
void ApiWrap::updateStickers() {
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
requestStickers(now);
|
requestStickers(now);
|
||||||
requestRecentStickers(now);
|
requestRecentStickers(now, false);
|
||||||
requestFavedStickers(now);
|
requestFavedStickers(now);
|
||||||
requestFeaturedStickers(now);
|
requestFeaturedStickers(now);
|
||||||
}
|
}
|
||||||
|
@ -2651,8 +2651,15 @@ void ApiWrap::updateCustomEmoji() {
|
||||||
requestFeaturedEmoji(now);
|
requestFeaturedEmoji(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::requestRecentStickersForce(bool attached) {
|
void ApiWrap::requestSpecialStickersForce(
|
||||||
requestRecentStickersWithHash(0, attached);
|
bool faved,
|
||||||
|
bool recent,
|
||||||
|
bool attached) {
|
||||||
|
if (faved) {
|
||||||
|
requestFavedStickers(std::nullopt);
|
||||||
|
} else if (recent || attached) {
|
||||||
|
requestRecentStickers(std::nullopt, attached);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::setGroupStickerSet(
|
void ApiWrap::setGroupStickerSet(
|
||||||
|
@ -2805,18 +2812,17 @@ void ApiWrap::requestCustomEmoji(TimeId now) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::requestRecentStickers(TimeId now, bool attached) {
|
void ApiWrap::requestRecentStickers(
|
||||||
const auto needed = attached
|
std::optional<TimeId> now,
|
||||||
? _session->data().stickers().recentAttachedUpdateNeeded(now)
|
bool attached) {
|
||||||
: _session->data().stickers().recentUpdateNeeded(now);
|
const auto needed = !now
|
||||||
|
? true
|
||||||
|
: attached
|
||||||
|
? _session->data().stickers().recentAttachedUpdateNeeded(*now)
|
||||||
|
: _session->data().stickers().recentUpdateNeeded(*now);
|
||||||
if (!needed) {
|
if (!needed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestRecentStickersWithHash(
|
|
||||||
Api::CountRecentStickersHash(_session, attached), attached);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApiWrap::requestRecentStickersWithHash(uint64 hash, bool attached) {
|
|
||||||
const auto requestId = [=]() -> mtpRequestId & {
|
const auto requestId = [=]() -> mtpRequestId & {
|
||||||
return attached
|
return attached
|
||||||
? _recentAttachedStickersUpdateRequest
|
? _recentAttachedStickersUpdateRequest
|
||||||
|
@ -2839,7 +2845,7 @@ void ApiWrap::requestRecentStickersWithHash(uint64 hash, bool attached) {
|
||||||
: MTPmessages_getRecentStickers::Flags(0);
|
: MTPmessages_getRecentStickers::Flags(0);
|
||||||
requestId() = request(MTPmessages_GetRecentStickers(
|
requestId() = request(MTPmessages_GetRecentStickers(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
MTP_long(hash)
|
MTP_long(now ? Api::CountRecentStickersHash(_session, attached) : 0)
|
||||||
)).done([=](const MTPmessages_RecentStickers &result) {
|
)).done([=](const MTPmessages_RecentStickers &result) {
|
||||||
finish();
|
finish();
|
||||||
|
|
||||||
|
@ -2866,13 +2872,15 @@ void ApiWrap::requestRecentStickersWithHash(uint64 hash, bool attached) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::requestFavedStickers(TimeId now) {
|
void ApiWrap::requestFavedStickers(std::optional<TimeId> now) {
|
||||||
if (!_session->data().stickers().favedUpdateNeeded(now)
|
if (now) {
|
||||||
|| _favedStickersUpdateRequest) {
|
if (!_session->data().stickers().favedUpdateNeeded(*now)
|
||||||
return;
|
|| _favedStickersUpdateRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_favedStickersUpdateRequest = request(MTPmessages_GetFavedStickers(
|
_favedStickersUpdateRequest = request(MTPmessages_GetFavedStickers(
|
||||||
MTP_long(Api::CountFavedStickersHash(_session))
|
MTP_long(now ? Api::CountFavedStickersHash(_session) : 0)
|
||||||
)).done([=](const MTPmessages_FavedStickers &result) {
|
)).done([=](const MTPmessages_FavedStickers &result) {
|
||||||
_session->data().stickers().setLastFavedUpdate(crl::now());
|
_session->data().stickers().setLastFavedUpdate(crl::now());
|
||||||
_favedStickersUpdateRequest = 0;
|
_favedStickersUpdateRequest = 0;
|
||||||
|
@ -4281,7 +4289,7 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (done) done(true);
|
if (done) done(true);
|
||||||
if (updateRecentStickers) {
|
if (updateRecentStickers) {
|
||||||
requestRecentStickersForce(true);
|
requestRecentStickers(std::nullopt, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
AyuWorker::markAsOnline(_session);
|
AyuWorker::markAsOnline(_session);
|
||||||
|
|
|
@ -244,7 +244,10 @@ public:
|
||||||
void updateSavedGifs();
|
void updateSavedGifs();
|
||||||
void updateMasks();
|
void updateMasks();
|
||||||
void updateCustomEmoji();
|
void updateCustomEmoji();
|
||||||
void requestRecentStickersForce(bool attached = false);
|
void requestSpecialStickersForce(
|
||||||
|
bool faved,
|
||||||
|
bool recent,
|
||||||
|
bool attached);
|
||||||
void setGroupStickerSet(
|
void setGroupStickerSet(
|
||||||
not_null<ChannelData*> megagroup,
|
not_null<ChannelData*> megagroup,
|
||||||
const StickerSetIdentifier &set);
|
const StickerSetIdentifier &set);
|
||||||
|
@ -477,9 +480,10 @@ private:
|
||||||
void requestStickers(TimeId now);
|
void requestStickers(TimeId now);
|
||||||
void requestMasks(TimeId now);
|
void requestMasks(TimeId now);
|
||||||
void requestCustomEmoji(TimeId now);
|
void requestCustomEmoji(TimeId now);
|
||||||
void requestRecentStickers(TimeId now, bool attached = false);
|
void requestRecentStickers(
|
||||||
void requestRecentStickersWithHash(uint64 hash, bool attached = false);
|
std::optional<TimeId> now,
|
||||||
void requestFavedStickers(TimeId now);
|
bool attached);
|
||||||
|
void requestFavedStickers(std::optional<TimeId> now);
|
||||||
void requestFeaturedStickers(TimeId now);
|
void requestFeaturedStickers(TimeId now);
|
||||||
void requestFeaturedEmoji(TimeId now);
|
void requestFeaturedEmoji(TimeId now);
|
||||||
void requestSavedGifs(TimeId now);
|
void requestSavedGifs(TimeId now);
|
||||||
|
|
|
@ -100,6 +100,8 @@ void AboutBox::showVersionHistory() {
|
||||||
url += u"win/%1.zip"_q;
|
url += u"win/%1.zip"_q;
|
||||||
} else if (Platform::IsWindows64Bit()) {
|
} else if (Platform::IsWindows64Bit()) {
|
||||||
url += u"win64/%1.zip"_q;
|
url += u"win64/%1.zip"_q;
|
||||||
|
} else if (Platform::IsWindowsARM64()) {
|
||||||
|
url += u"winarm/%1.zip"_q;
|
||||||
} else if (Platform::IsMac()) {
|
} else if (Platform::IsMac()) {
|
||||||
url += u"mac/%1.zip"_q;
|
url += u"mac/%1.zip"_q;
|
||||||
} else if (Platform::IsLinux()) {
|
} else if (Platform::IsLinux()) {
|
||||||
|
@ -155,6 +157,8 @@ QString currentVersionText() {
|
||||||
}
|
}
|
||||||
if (Platform::IsWindows64Bit()) {
|
if (Platform::IsWindows64Bit()) {
|
||||||
result += " x64";
|
result += " x64";
|
||||||
|
} else if (Platform::IsWindowsARM64()) {
|
||||||
|
result += " arm64";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ Data::ChatFilter ChangedFilter(
|
||||||
filter.id(),
|
filter.id(),
|
||||||
filter.title(),
|
filter.title(),
|
||||||
filter.iconEmoji(),
|
filter.iconEmoji(),
|
||||||
|
filter.colorIndex(),
|
||||||
filter.flags(),
|
filter.flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
filter.pinned(),
|
filter.pinned(),
|
||||||
|
@ -58,6 +59,7 @@ Data::ChatFilter ChangedFilter(
|
||||||
filter.id(),
|
filter.id(),
|
||||||
filter.title(),
|
filter.title(),
|
||||||
filter.iconEmoji(),
|
filter.iconEmoji(),
|
||||||
|
filter.colorIndex(),
|
||||||
filter.flags(),
|
filter.flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
filter.pinned(),
|
filter.pinned(),
|
||||||
|
|
|
@ -83,6 +83,7 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
|
||||||
rules.id(),
|
rules.id(),
|
||||||
rules.title(),
|
rules.title(),
|
||||||
rules.iconEmoji(),
|
rules.iconEmoji(),
|
||||||
|
rules.colorIndex(),
|
||||||
(rules.flags() & ~flag),
|
(rules.flags() & ~flag),
|
||||||
rules.always(),
|
rules.always(),
|
||||||
rules.pinned(),
|
rules.pinned(),
|
||||||
|
@ -104,6 +105,7 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
|
||||||
rules.id(),
|
rules.id(),
|
||||||
rules.title(),
|
rules.title(),
|
||||||
rules.iconEmoji(),
|
rules.iconEmoji(),
|
||||||
|
rules.colorIndex(),
|
||||||
rules.flags(),
|
rules.flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
|
@ -170,6 +172,7 @@ void EditExceptions(
|
||||||
rules.id(),
|
rules.id(),
|
||||||
rules.title(),
|
rules.title(),
|
||||||
rules.iconEmoji(),
|
rules.iconEmoji(),
|
||||||
|
rules.colorIndex(),
|
||||||
((rules.flags() & ~options)
|
((rules.flags() & ~options)
|
||||||
| rawController->chosenOptions()),
|
| rawController->chosenOptions()),
|
||||||
include ? std::move(changed) : std::move(removeFrom),
|
include ? std::move(changed) : std::move(removeFrom),
|
||||||
|
@ -240,6 +243,7 @@ void CreateIconSelector(
|
||||||
rules.id(),
|
rules.id(),
|
||||||
rules.title(),
|
rules.title(),
|
||||||
Ui::LookupFilterIcon(icon).emoji,
|
Ui::LookupFilterIcon(icon).emoji,
|
||||||
|
rules.colorIndex(),
|
||||||
rules.flags(),
|
rules.flags(),
|
||||||
rules.always(),
|
rules.always(),
|
||||||
rules.pinned(),
|
rules.pinned(),
|
||||||
|
|
180
Telegram/SourceFiles/boxes/gift_credits_box.cpp
Normal file
180
Telegram/SourceFiles/boxes/gift_credits_box.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
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 "boxes/gift_credits_box.h"
|
||||||
|
|
||||||
|
#include "api/api_credits.h"
|
||||||
|
#include "boxes/peer_list_controllers.h"
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/session/session_show.h"
|
||||||
|
#include "settings/settings_credits_graphics.h"
|
||||||
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/effects/premium_graphics.h"
|
||||||
|
#include "ui/effects/premium_stars_colored.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/widgets/label_with_custom_emoji.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_channel_earn.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_credits.h"
|
||||||
|
#include "styles/style_giveaway.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_premium.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
void GiftCreditsBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
Fn<void()> gifted) {
|
||||||
|
box->setStyle(st::creditsGiftBox);
|
||||||
|
box->setNoContentMargin(true);
|
||||||
|
box->addButton(tr::lng_create_group_back(), [=] { box->closeBox(); });
|
||||||
|
|
||||||
|
const auto content = box->setPinnedToTopContent(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box));
|
||||||
|
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||||
|
const auto userpicWrap = content->add(
|
||||||
|
object_ptr<Ui::CenterWrap<>>(
|
||||||
|
content,
|
||||||
|
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
|
||||||
|
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto widget = Ui::CreateChild<Ui::RpWidget>(content);
|
||||||
|
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||||
|
const auto stars = widget->lifetime().make_state<ColoredMiniStars>(
|
||||||
|
widget,
|
||||||
|
false,
|
||||||
|
Ui::Premium::MiniStars::Type::BiStars);
|
||||||
|
stars->setColorOverride(Ui::Premium::CreditsIconGradientStops());
|
||||||
|
widget->resize(
|
||||||
|
st::boxWidth - stUser.photoSize,
|
||||||
|
stUser.photoSize * 2);
|
||||||
|
content->sizeValue(
|
||||||
|
) | rpl::start_with_next([=](const QSize &size) {
|
||||||
|
widget->moveToLeft(stUser.photoSize / 2, 0);
|
||||||
|
const auto starsRect = Rect(widget->size());
|
||||||
|
stars->setPosition(starsRect.topLeft());
|
||||||
|
stars->setSize(starsRect.size());
|
||||||
|
widget->lower();
|
||||||
|
}, widget->lifetime());
|
||||||
|
widget->paintRequest(
|
||||||
|
) | rpl::start_with_next([=](const QRect &r) {
|
||||||
|
auto p = QPainter(widget);
|
||||||
|
p.fillRect(r, Qt::transparent);
|
||||||
|
stars->paint(p);
|
||||||
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
const auto arrow = Ui::Text::SingleCustomEmoji(
|
||||||
|
peer->owner().customEmojiManager().registerInternalEmoji(
|
||||||
|
st::topicButtonArrow,
|
||||||
|
st::channelEarnLearnArrowMargins,
|
||||||
|
false));
|
||||||
|
auto link = tr::lng_credits_box_history_entry_gift_about_link(
|
||||||
|
lt_emoji,
|
||||||
|
rpl::single(arrow),
|
||||||
|
Ui::Text::RichLangValue
|
||||||
|
) | rpl::map([](TextWithEntities text) {
|
||||||
|
return Ui::Text::Link(
|
||||||
|
std::move(text),
|
||||||
|
tr::lng_credits_box_history_entry_gift_about_url(tr::now));
|
||||||
|
});
|
||||||
|
content->add(
|
||||||
|
object_ptr<Ui::CenterWrap<>>(
|
||||||
|
content,
|
||||||
|
Ui::CreateLabelWithCustomEmoji(
|
||||||
|
content,
|
||||||
|
tr::lng_credits_box_history_entry_gift_out_about(
|
||||||
|
lt_user,
|
||||||
|
rpl::single(TextWithEntities{ peer->shortName() }),
|
||||||
|
lt_link,
|
||||||
|
std::move(link),
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
|
{ .session = &peer->session() },
|
||||||
|
st::creditsBoxAbout)),
|
||||||
|
st::boxRowPadding);
|
||||||
|
}
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
Ui::AddSkip(box->verticalLayout());
|
||||||
|
|
||||||
|
Settings::FillCreditOptions(
|
||||||
|
Main::MakeSessionShow(box->uiShow(), &peer->session()),
|
||||||
|
box->verticalLayout(),
|
||||||
|
peer,
|
||||||
|
0,
|
||||||
|
[=] { gifted(); box->uiShow()->hideLayer(); });
|
||||||
|
|
||||||
|
box->setPinnedToBottomContent(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowGiftCreditsBox(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Fn<void()> gifted) {
|
||||||
|
|
||||||
|
class Controller final : public ContactsBoxController {
|
||||||
|
public:
|
||||||
|
Controller(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Fn<void(not_null<PeerData*>)> choose)
|
||||||
|
: ContactsBoxController(session)
|
||||||
|
, _choose(std::move(choose)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<PeerListRow> createRow(
|
||||||
|
not_null<UserData*> user) override {
|
||||||
|
if (user->isSelf()
|
||||||
|
|| user->isBot()
|
||||||
|
|| user->isServiceUser()
|
||||||
|
|| user->isInaccessible()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return ContactsBoxController::createRow(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override {
|
||||||
|
_choose(row->peer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Fn<void(not_null<PeerData*>)> _choose;
|
||||||
|
|
||||||
|
};
|
||||||
|
auto initBox = [=](not_null<PeerListBox*> peersBox) {
|
||||||
|
peersBox->setTitle(tr::lng_credits_gift_title());
|
||||||
|
peersBox->addButton(tr::lng_cancel(), [=] { peersBox->closeBox(); });
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto show = controller->uiShow();
|
||||||
|
auto listController = std::make_unique<Controller>(
|
||||||
|
&controller->session(),
|
||||||
|
[=](not_null<PeerData*> peer) {
|
||||||
|
show->showBox(Box(GiftCreditsBox, peer, gifted));
|
||||||
|
});
|
||||||
|
show->showBox(
|
||||||
|
Box<PeerListBox>(std::move(listController), std::move(initBox)),
|
||||||
|
Ui::LayerOption::KeepOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
20
Telegram/SourceFiles/boxes/gift_credits_box.h
Normal file
20
Telegram/SourceFiles/boxes/gift_credits_box.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class SessionController;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
void ShowGiftCreditsBox(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Fn<void()> gifted);
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -1014,6 +1014,7 @@ void GiftPremiumValidator::showChoosePeerBox(const QString &ref) {
|
||||||
if (users.empty()) {
|
if (users.empty()) {
|
||||||
show->showToast(
|
show->showToast(
|
||||||
tr::lng_settings_gift_premium_choose(tr::now));
|
tr::lng_settings_gift_premium_choose(tr::now));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto giftBox = show->show(
|
const auto giftBox = show->show(
|
||||||
Box(GiftsBox, _controller, users, api, ref));
|
Box(GiftsBox, _controller, users, api, ref));
|
||||||
|
@ -1648,7 +1649,9 @@ void AddCreditsHistoryEntryTable(
|
||||||
st::giveawayGiftCodeTableMargin);
|
st::giveawayGiftCodeTableMargin);
|
||||||
const auto peerId = PeerId(entry.barePeerId);
|
const auto peerId = PeerId(entry.barePeerId);
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
auto text = tr::lng_credits_box_history_entry_peer();
|
auto text = entry.in
|
||||||
|
? tr::lng_credits_box_history_entry_peer_in()
|
||||||
|
: tr::lng_credits_box_history_entry_peer();
|
||||||
AddTableRow(table, std::move(text), controller, peerId);
|
AddTableRow(table, std::move(text), controller, peerId);
|
||||||
}
|
}
|
||||||
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
|
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
|
||||||
|
@ -1692,14 +1695,24 @@ void AddCreditsHistoryEntryTable(
|
||||||
} else if (entry.peerType == Type::Fragment) {
|
} else if (entry.peerType == Type::Fragment) {
|
||||||
AddTableRow(
|
AddTableRow(
|
||||||
table,
|
table,
|
||||||
tr::lng_credits_box_history_entry_via(),
|
(entry.gift
|
||||||
tr::lng_credits_box_history_entry_fragment(
|
? tr::lng_credits_box_history_entry_peer_in
|
||||||
Ui::Text::RichLangValue));
|
: tr::lng_credits_box_history_entry_via)(),
|
||||||
|
(entry.gift
|
||||||
|
? tr::lng_credits_box_history_entry_anonymous
|
||||||
|
: tr::lng_credits_box_history_entry_fragment)(
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
} else if (entry.peerType == Type::Ads) {
|
} else if (entry.peerType == Type::Ads) {
|
||||||
AddTableRow(
|
AddTableRow(
|
||||||
table,
|
table,
|
||||||
tr::lng_credits_box_history_entry_via(),
|
tr::lng_credits_box_history_entry_via(),
|
||||||
tr::lng_credits_box_history_entry_ads(Ui::Text::RichLangValue));
|
tr::lng_credits_box_history_entry_ads(Ui::Text::RichLangValue));
|
||||||
|
} else if (entry.peerType == Type::PremiumBot) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_credits_box_history_entry_via(),
|
||||||
|
tr::lng_credits_box_history_entry_via_premium_bot(
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
}
|
}
|
||||||
if (!entry.id.isEmpty()) {
|
if (!entry.id.isEmpty()) {
|
||||||
constexpr auto kOneLineCount = 18;
|
constexpr auto kOneLineCount = 18;
|
||||||
|
|
|
@ -459,6 +459,7 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
|
||||||
.customWallpaperLevel = group
|
.customWallpaperLevel = group
|
||||||
? levelLimits.groupCustomWallpaperLevelMin()
|
? levelLimits.groupCustomWallpaperLevelMin()
|
||||||
: levelLimits.channelCustomWallpaperLevelMin(),
|
: levelLimits.channelCustomWallpaperLevelMin(),
|
||||||
|
.sponsoredLevel = levelLimits.channelRestrictSponsoredLevelMin(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -418,7 +418,9 @@ void SimpleLimitBox(
|
||||||
BoxShowFinishes(box),
|
BoxShowFinishes(box),
|
||||||
0,
|
0,
|
||||||
descriptor.current,
|
descriptor.current,
|
||||||
descriptor.premiumLimit,
|
(descriptor.complexRatio
|
||||||
|
? descriptor.premiumLimit
|
||||||
|
: 2 * descriptor.current),
|
||||||
premiumPossible,
|
premiumPossible,
|
||||||
descriptor.phrase,
|
descriptor.phrase,
|
||||||
descriptor.icon);
|
descriptor.icon);
|
||||||
|
@ -769,7 +771,7 @@ void FilterLinksLimitBox(
|
||||||
premiumLimit,
|
premiumLimit,
|
||||||
&st::premiumIconChats,
|
&st::premiumIconChats,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
true });
|
/*true */}); // Don't use real ratio, "Free" doesn't fit.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -856,7 +858,7 @@ void ShareableFiltersLimitBox(
|
||||||
premiumLimit,
|
premiumLimit,
|
||||||
&st::premiumIconFolders,
|
&st::premiumIconFolders,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
true });
|
/*true*/ }); // Don't use real ratio, "Free" doesn't fit.
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterPinsLimitBox(
|
void FilterPinsLimitBox(
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "payments/payments_checkout_process.h"
|
#include "payments/payments_checkout_process.h"
|
||||||
#include "payments/payments_form.h"
|
#include "payments/payments_form.h"
|
||||||
#include "settings/settings_credits_graphics.h"
|
#include "settings/settings_credits_graphics.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
#include "ui/effects/premium_graphics.h"
|
#include "ui/effects/premium_graphics.h"
|
||||||
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
|
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
|
||||||
|
@ -257,6 +258,8 @@ void SendCreditsBox(
|
||||||
if (state->confirmButtonBusy.current()) {
|
if (state->confirmButtonBusy.current()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto show = box->uiShow();
|
||||||
|
const auto weak = MakeWeak(box.get());
|
||||||
state->confirmButtonBusy = true;
|
state->confirmButtonBusy = true;
|
||||||
session->api().request(
|
session->api().request(
|
||||||
MTPpayments_SendStarsForm(
|
MTPpayments_SendStarsForm(
|
||||||
|
@ -264,12 +267,31 @@ void SendCreditsBox(
|
||||||
MTP_long(form->formId),
|
MTP_long(form->formId),
|
||||||
form->inputInvoice)
|
form->inputInvoice)
|
||||||
).done([=](auto result) {
|
).done([=](auto result) {
|
||||||
state->confirmButtonBusy = false;
|
if (weak) {
|
||||||
box->closeBox();
|
state->confirmButtonBusy = false;
|
||||||
|
box->closeBox();
|
||||||
|
}
|
||||||
sent();
|
sent();
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
state->confirmButtonBusy = false;
|
if (weak) {
|
||||||
box->uiShow()->showToast(error.type());
|
state->confirmButtonBusy = false;
|
||||||
|
}
|
||||||
|
const auto id = error.type();
|
||||||
|
if (id == u"BOT_PRECHECKOUT_FAILED"_q) {
|
||||||
|
auto error = ::Ui::MakeInformBox(
|
||||||
|
tr::lng_payments_precheckout_stars_failed(tr::now));
|
||||||
|
error->boxClosing() | rpl::start_with_next([=] {
|
||||||
|
if (const auto paybox = weak.data()) {
|
||||||
|
paybox->closeBox();
|
||||||
|
}
|
||||||
|
}, error->lifetime());
|
||||||
|
show->showBox(std::move(error));
|
||||||
|
} else if (id == u"BOT_PRECHECKOUT_TIMEOUT"_q) {
|
||||||
|
show->showToast(
|
||||||
|
tr::lng_payments_precheckout_stars_timeout(tr::now));
|
||||||
|
} else {
|
||||||
|
show->showToast(id);
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
|
|
|
@ -1414,55 +1414,6 @@ std::vector<not_null<Data::Thread*>> ShareBox::Inner::selected() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AppendShareGameScoreUrl(
|
|
||||||
not_null<Main::Session*> session,
|
|
||||||
const QString &url,
|
|
||||||
const FullMsgId &fullId) {
|
|
||||||
auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
|
|
||||||
auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
|
|
||||||
const auto peer = fullId.peer
|
|
||||||
? session->data().peerLoaded(fullId.peer)
|
|
||||||
: static_cast<PeerData*>(nullptr);
|
|
||||||
const auto channelAccessHash = uint64((peer && peer->isChannel())
|
|
||||||
? peer->asChannel()->access
|
|
||||||
: 0);
|
|
||||||
shareHashDataInts[0] = session->userId().bare;
|
|
||||||
shareHashDataInts[1] = fullId.peer.value;
|
|
||||||
shareHashDataInts[2] = uint64(fullId.msg.bare);
|
|
||||||
shareHashDataInts[3] = channelAccessHash;
|
|
||||||
|
|
||||||
// Count SHA1() of data.
|
|
||||||
auto key128Size = 0x10;
|
|
||||||
auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized);
|
|
||||||
hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data());
|
|
||||||
|
|
||||||
//// Mix in channel access hash to the first 64 bits of SHA1 of data.
|
|
||||||
//*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= channelAccessHash;
|
|
||||||
|
|
||||||
// Encrypt data.
|
|
||||||
if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shareHash = shareHashEncrypted.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
|
||||||
auto shareUrl = u"tg://share_game_score?hash="_q + QString::fromLatin1(shareHash);
|
|
||||||
|
|
||||||
auto shareComponent = u"tgShareScoreUrl="_q + qthelp::url_encode(shareUrl);
|
|
||||||
|
|
||||||
auto hashPosition = url.indexOf('#');
|
|
||||||
if (hashPosition < 0) {
|
|
||||||
return url + '#' + shareComponent;
|
|
||||||
}
|
|
||||||
auto hash = url.mid(hashPosition + 1);
|
|
||||||
if (hash.indexOf('=') >= 0 || hash.indexOf('?') >= 0) {
|
|
||||||
return url + '&' + shareComponent;
|
|
||||||
}
|
|
||||||
if (!hash.isEmpty()) {
|
|
||||||
return url + '?' + shareComponent;
|
|
||||||
}
|
|
||||||
return url + shareComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
|
ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
|
||||||
const std::vector<not_null<Data::Thread*>> &result,
|
const std::vector<not_null<Data::Thread*>> &result,
|
||||||
const MessageIdsList &msgIds) {
|
const MessageIdsList &msgIds) {
|
||||||
|
@ -1624,9 +1575,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||||
}
|
}
|
||||||
|
|
||||||
void FastShareMessage(
|
void FastShareMessage(
|
||||||
not_null<Window::SessionController*> controller,
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
not_null<HistoryItem*> item) {
|
not_null<HistoryItem*> item) {
|
||||||
const auto show = controller->uiShow();
|
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
const auto owner = &history->owner();
|
const auto owner = &history->owner();
|
||||||
const auto session = &history->session();
|
const auto session = &history->session();
|
||||||
|
@ -1655,7 +1605,7 @@ void FastShareMessage(
|
||||||
}
|
}
|
||||||
if (item->hasDirectLink()) {
|
if (item->hasDirectLink()) {
|
||||||
using namespace HistoryView;
|
using namespace HistoryView;
|
||||||
CopyPostLink(controller, item->fullId(), Context::History);
|
CopyPostLink(show, item->fullId(), Context::History);
|
||||||
} else if (const auto bot = item->getMessageBot()) {
|
} else if (const auto bot = item->getMessageBot()) {
|
||||||
if (const auto media = item->media()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto game = media->game()) {
|
if (const auto game = media->game()) {
|
||||||
|
@ -1687,23 +1637,27 @@ void FastShareMessage(
|
||||||
auto copyLinkCallback = canCopyLink
|
auto copyLinkCallback = canCopyLink
|
||||||
? Fn<void()>(std::move(copyCallback))
|
? Fn<void()>(std::move(copyCallback))
|
||||||
: Fn<void()>();
|
: Fn<void()>();
|
||||||
controller->show(
|
show->show(Box<ShareBox>(ShareBox::Descriptor{
|
||||||
Box<ShareBox>(ShareBox::Descriptor{
|
.session = session,
|
||||||
.session = session,
|
.copyCallback = std::move(copyLinkCallback),
|
||||||
.copyCallback = std::move(copyLinkCallback),
|
.submitCallback = ShareBox::DefaultForwardCallback(
|
||||||
.submitCallback = ShareBox::DefaultForwardCallback(
|
show,
|
||||||
show,
|
history,
|
||||||
history,
|
msgIds),
|
||||||
msgIds),
|
.filterCallback = std::move(filterCallback),
|
||||||
.filterCallback = std::move(filterCallback),
|
.forwardOptions = {
|
||||||
.forwardOptions = {
|
.sendersCount = ItemsForwardSendersCount(items),
|
||||||
.sendersCount = ItemsForwardSendersCount(items),
|
.captionsCount = ItemsForwardCaptionsCount(items),
|
||||||
.captionsCount = ItemsForwardCaptionsCount(items),
|
.show = !hasOnlyForcedForwardedInfo,
|
||||||
.show = !hasOnlyForcedForwardedInfo,
|
},
|
||||||
},
|
.premiumRequiredError = SharePremiumRequiredError(),
|
||||||
.premiumRequiredError = SharePremiumRequiredError(),
|
}), Ui::LayerOption::CloseOther);
|
||||||
}),
|
}
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
|
void FastShareMessage(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
FastShareMessage(controller->uiShow(), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FastShareLink(
|
void FastShareLink(
|
||||||
|
@ -1805,111 +1759,3 @@ auto SharePremiumRequiredError()
|
||||||
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
|
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
|
||||||
return WritePremiumRequiredError;
|
return WritePremiumRequiredError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareGameScoreByHash(
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
const QString &hash) {
|
|
||||||
auto &session = controller->session();
|
|
||||||
auto key128Size = 0x10;
|
|
||||||
|
|
||||||
auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
|
||||||
if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() != key128Size + 0x20)) {
|
|
||||||
controller->show(
|
|
||||||
Ui::MakeInformBox(tr::lng_confirm_phone_link_invalid()),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt data.
|
|
||||||
auto hashData = QByteArray(hashEncrypted.size() - key128Size, Qt::Uninitialized);
|
|
||||||
if (!session.local().decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count SHA1() of data.
|
|
||||||
char dataSha1[20] = { 0 };
|
|
||||||
hashSha1(hashData.constData(), hashData.size(), dataSha1);
|
|
||||||
|
|
||||||
//// Mix out channel access hash from the first 64 bits of SHA1 of data.
|
|
||||||
//auto channelAccessHash = *reinterpret_cast<uint64*>(hashEncrypted.data()) ^ *reinterpret_cast<uint64*>(dataSha1);
|
|
||||||
|
|
||||||
//// Check next 64 bits of SHA1() of data.
|
|
||||||
//auto skipSha1Part = sizeof(channelAccessHash);
|
|
||||||
//if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) {
|
|
||||||
// Ui::show(Box<Ui::InformBox>(tr::lng_share_wrong_user(tr::now)));
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Check 128 bits of SHA1() of data.
|
|
||||||
if (memcmp(dataSha1, hashEncrypted.constData(), key128Size) != 0) {
|
|
||||||
controller->show(
|
|
||||||
Ui::MakeInformBox(tr::lng_share_wrong_user()),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto hashDataInts = reinterpret_cast<uint64*>(hashData.data());
|
|
||||||
if (hashDataInts[0] != session.userId().bare) {
|
|
||||||
controller->show(
|
|
||||||
Ui::MakeInformBox(tr::lng_share_wrong_user()),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto peerId = PeerId(hashDataInts[1]);
|
|
||||||
const auto channelAccessHash = hashDataInts[3];
|
|
||||||
if (!peerIsChannel(peerId) && channelAccessHash) {
|
|
||||||
// If there is no channel id, there should be no channel access_hash.
|
|
||||||
controller->show(
|
|
||||||
Ui::MakeInformBox(tr::lng_share_wrong_user()),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto msgId = MsgId(int64(hashDataInts[2]));
|
|
||||||
if (const auto item = session.data().message(peerId, msgId)) {
|
|
||||||
FastShareMessage(controller, item);
|
|
||||||
} else {
|
|
||||||
const auto weak = base::make_weak(controller);
|
|
||||||
const auto resolveMessageAndShareScore = crl::guard(weak, [=](
|
|
||||||
PeerData *peer) {
|
|
||||||
auto done = crl::guard(weak, [=] {
|
|
||||||
const auto item = weak->session().data().message(
|
|
||||||
peerId,
|
|
||||||
msgId);
|
|
||||||
if (item) {
|
|
||||||
FastShareMessage(weak.get(), item);
|
|
||||||
} else {
|
|
||||||
weak->show(
|
|
||||||
Ui::MakeInformBox(tr::lng_edit_deleted()),
|
|
||||||
Ui::LayerOption::CloseOther);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
auto &api = weak->session().api();
|
|
||||||
api.requestMessageData(peer, msgId, std::move(done));
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto peer = peerIsChannel(peerId)
|
|
||||||
? controller->session().data().peerLoaded(peerId)
|
|
||||||
: nullptr;
|
|
||||||
if (peer || !peerIsChannel(peerId)) {
|
|
||||||
resolveMessageAndShareScore(peer);
|
|
||||||
} else {
|
|
||||||
const auto owner = &controller->session().data();
|
|
||||||
controller->session().api().request(MTPchannels_GetChannels(
|
|
||||||
MTP_vector<MTPInputChannel>(
|
|
||||||
1,
|
|
||||||
MTP_inputChannel(
|
|
||||||
MTP_long(peerToChannel(peerId).bare),
|
|
||||||
MTP_long(channelAccessHash)))
|
|
||||||
)).done([=](const MTPmessages_Chats &result) {
|
|
||||||
result.match([&](const auto &data) {
|
|
||||||
owner->processChats(data.vchats());
|
|
||||||
});
|
|
||||||
if (const auto peer = owner->peerLoaded(peerId)) {
|
|
||||||
resolveMessageAndShareScore(peer);
|
|
||||||
}
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -59,13 +59,11 @@ class SlideWrap;
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
QString AppendShareGameScoreUrl(
|
class ShareBox;
|
||||||
not_null<Main::Session*> session,
|
|
||||||
const QString &url,
|
void FastShareMessage(
|
||||||
const FullMsgId &fullId);
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
void ShareGameScoreByHash(
|
not_null<HistoryItem*> item);
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
const QString &hash);
|
|
||||||
void FastShareMessage(
|
void FastShareMessage(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<HistoryItem*> item);
|
not_null<HistoryItem*> item);
|
||||||
|
|
|
@ -7,50 +7,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/sticker_set_box.h"
|
#include "boxes/sticker_set_box.h"
|
||||||
|
|
||||||
#include "data/data_document.h"
|
#include "api/api_common.h"
|
||||||
#include "data/data_session.h"
|
#include "api/api_toggling_media.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "apiwrap.h"
|
||||||
#include "data/data_document_media.h"
|
#include "base/unixtime.h"
|
||||||
#include "data/data_peer_values.h"
|
|
||||||
#include "data/stickers/data_stickers.h"
|
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
|
||||||
#include "menu/menu_send.h"
|
|
||||||
#include "lang/lang_keys.h"
|
|
||||||
#include "ui/boxes/confirm_box.h"
|
|
||||||
#include "boxes/premium_preview_box.h"
|
#include "boxes/premium_preview_box.h"
|
||||||
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
#include "chat_helpers/stickers_list_widget.h"
|
||||||
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "mtproto/sender.h"
|
#include "data/data_document.h"
|
||||||
#include "storage/storage_account.h"
|
#include "data/data_document_media.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "dialogs/ui/dialogs_layout.h"
|
#include "dialogs/ui/dialogs_layout.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/gradient_round_button.h"
|
#include "lottie/lottie_animation.h"
|
||||||
#include "ui/image/image.h"
|
#include "lottie/lottie_multi_player.h"
|
||||||
#include "ui/image/image_location_factory.h"
|
#include "main/main_session.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "mainwindow.h"
|
||||||
#include "ui/text/custom_emoji_instance.h"
|
#include "media/clip/media_clip_reader.h"
|
||||||
|
#include "menu/menu_send.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
#include "settings/settings_premium.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/cached_round_corners.h"
|
||||||
|
#include "ui/effects/animation_value_f.h"
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/emoji_config.h"
|
#include "ui/emoji_config.h"
|
||||||
|
#include "ui/image/image.h"
|
||||||
|
#include "ui/image/image_location_factory.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/power_saving.h"
|
#include "ui/power_saving.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "ui/text/custom_emoji_instance.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/fields/input_field.h"
|
||||||
|
#include "ui/widgets/gradient_round_button.h"
|
||||||
|
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||||
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "lottie/lottie_multi_player.h"
|
|
||||||
#include "lottie/lottie_animation.h"
|
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
|
||||||
#include "chat_helpers/stickers_list_widget.h"
|
|
||||||
#include "media/clip/media_clip_reader.h"
|
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "settings/settings_premium.h"
|
|
||||||
#include "base/unixtime.h"
|
|
||||||
#include "main/main_session.h"
|
|
||||||
#include "apiwrap.h"
|
|
||||||
#include "api/api_toggling_media.h"
|
|
||||||
#include "api/api_common.h"
|
|
||||||
#include "mainwidget.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
@ -75,10 +80,12 @@ constexpr auto kEmojiPerRow = 8;
|
||||||
constexpr auto kMinRepaintDelay = crl::time(33);
|
constexpr auto kMinRepaintDelay = crl::time(33);
|
||||||
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
||||||
constexpr auto kGrayLockOpacity = 0.3;
|
constexpr auto kGrayLockOpacity = 0.3;
|
||||||
|
constexpr auto kStickerMoveDuration = crl::time(200);
|
||||||
|
|
||||||
using Data::StickersSet;
|
using Data::StickersSet;
|
||||||
using Data::StickersPack;
|
using Data::StickersPack;
|
||||||
using SetFlag = Data::StickersSetFlag;
|
using SetFlag = Data::StickersSetFlag;
|
||||||
|
using TLStickerSet = MTPmessages_StickerSet;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QColor> ComputeImageColor(
|
[[nodiscard]] std::optional<QColor> ComputeImageColor(
|
||||||
const style::icon &lockIcon,
|
const style::icon &lockIcon,
|
||||||
|
@ -266,6 +273,20 @@ public:
|
||||||
[[nodiscard]] rpl::producer<uint64> setArchived() const;
|
[[nodiscard]] rpl::producer<uint64> setArchived() const;
|
||||||
[[nodiscard]] rpl::producer<> updateControls() const;
|
[[nodiscard]] rpl::producer<> updateControls() const;
|
||||||
|
|
||||||
|
void setReorderState(bool enabled) {
|
||||||
|
_dragging.enabled = enabled;
|
||||||
|
if (enabled) {
|
||||||
|
_shakeAnimation.init([=] { update(); });
|
||||||
|
_shakeAnimation.start();
|
||||||
|
} else {
|
||||||
|
_shakeAnimation.stop();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool reorderState() const {
|
||||||
|
return _dragging.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<Error> errors() const;
|
[[nodiscard]] rpl::producer<Error> errors() const;
|
||||||
|
|
||||||
void archiveStickers();
|
void archiveStickers();
|
||||||
|
@ -278,6 +299,12 @@ public:
|
||||||
: Data::StickersType::Stickers;
|
: Data::StickersType::Stickers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool amSetCreator() const {
|
||||||
|
return _amSetCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applySet(const TLStickerSet &set);
|
||||||
|
|
||||||
~Inner();
|
~Inner();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -313,6 +340,11 @@ private:
|
||||||
QPoint position,
|
QPoint position,
|
||||||
bool paused,
|
bool paused,
|
||||||
crl::time now) const;
|
crl::time now) const;
|
||||||
|
void shakeTransform(
|
||||||
|
QPainter &p,
|
||||||
|
int index,
|
||||||
|
QPoint position,
|
||||||
|
crl::time now) const;
|
||||||
void setupLottie(int index);
|
void setupLottie(int index);
|
||||||
void setupWebm(int index);
|
void setupWebm(int index);
|
||||||
void clipCallback(
|
void clipCallback(
|
||||||
|
@ -329,14 +361,19 @@ private:
|
||||||
void startOverAnimation(int index, float64 from, float64 to);
|
void startOverAnimation(int index, float64 from, float64 to);
|
||||||
int stickerFromGlobalPos(const QPoint &p) const;
|
int stickerFromGlobalPos(const QPoint &p) const;
|
||||||
|
|
||||||
void gotSet(const MTPmessages_StickerSet &set);
|
|
||||||
void installDone(const MTPmessages_StickerSetInstallResult &result);
|
void installDone(const MTPmessages_StickerSetInstallResult &result);
|
||||||
|
|
||||||
|
void requestReorder(not_null<DocumentData*> document, int index);
|
||||||
|
void fillDeleteStickerBox(not_null<Ui::GenericBox*> box, int index);
|
||||||
|
|
||||||
void chosen(
|
void chosen(
|
||||||
int index,
|
int index,
|
||||||
not_null<DocumentData*> sticker,
|
not_null<DocumentData*> sticker,
|
||||||
Api::SendOptions options);
|
Api::SendOptions options);
|
||||||
|
|
||||||
|
[[nodiscard]] QPoint posFromIndex(int index) const;
|
||||||
|
[[nodiscard]] bool isDraggedAnimating() const;
|
||||||
|
|
||||||
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
||||||
|
|
||||||
void showPreview();
|
void showPreview();
|
||||||
|
@ -373,6 +410,24 @@ private:
|
||||||
TimeId _setInstallDate = TimeId(0);
|
TimeId _setInstallDate = TimeId(0);
|
||||||
StickerType _setThumbnailType = StickerType::Webp;
|
StickerType _setThumbnailType = StickerType::Webp;
|
||||||
ImageWithLocation _setThumbnail;
|
ImageWithLocation _setThumbnail;
|
||||||
|
bool _amSetCreator = false;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
int index = -1;
|
||||||
|
int lastSelected = -1;
|
||||||
|
QPoint point;
|
||||||
|
} _dragging;
|
||||||
|
Ui::Animations::Basic _shakeAnimation;
|
||||||
|
std::deque<Fn<void()>> _reorderRequests;
|
||||||
|
std::optional<MTP::Sender> _apiReorder;
|
||||||
|
|
||||||
|
struct ShiftAnimation final {
|
||||||
|
Ui::Animations::Simple animation;
|
||||||
|
Ui::Animations::Simple yAnimation;
|
||||||
|
int shift = 0;
|
||||||
|
};
|
||||||
|
base::flat_map<int, ShiftAnimation> _shiftAnimations;
|
||||||
|
|
||||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||||
mutable StickerPremiumMark _premiumMark;
|
mutable StickerPremiumMark _premiumMark;
|
||||||
|
@ -545,9 +600,112 @@ void StickerSetBox::updateTitleAndButtons() {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChangeSetNameBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<Data::Session*> data,
|
||||||
|
const StickerSetIdentifier &input,
|
||||||
|
Fn<void(TLStickerSet)> done) {
|
||||||
|
struct State final {
|
||||||
|
rpl::variable<mtpRequestId> requestId = 0;
|
||||||
|
Ui::RpWidget* saveButton = nullptr;
|
||||||
|
};
|
||||||
|
box->setTitle(tr::lng_stickers_box_edit_name_title());
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
box,
|
||||||
|
tr::lng_stickers_box_edit_name_about(),
|
||||||
|
st::boxLabel));
|
||||||
|
const auto state = box->lifetime().make_state<State>();
|
||||||
|
|
||||||
|
const auto wasName = [&] {
|
||||||
|
const auto &sets = data->stickers().sets();
|
||||||
|
const auto it = sets.find(input.id);
|
||||||
|
return (it == sets.end()) ? QString() : it->second->title;
|
||||||
|
}();
|
||||||
|
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
box,
|
||||||
|
st::editStickerSetNameField.heightMin));
|
||||||
|
auto owned = object_ptr<Ui::InputField>(
|
||||||
|
wrap,
|
||||||
|
st::editStickerSetNameField,
|
||||||
|
tr::lng_stickers_context_edit_name(),
|
||||||
|
wasName);
|
||||||
|
const auto field = owned.data();
|
||||||
|
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||||
|
field->move(0, 0);
|
||||||
|
field->resize(width, field->height());
|
||||||
|
wrap->resize(width, field->height());
|
||||||
|
}, wrap->lifetime());
|
||||||
|
field->selectAll();
|
||||||
|
constexpr auto kMaxSetNameLength = 50;
|
||||||
|
field->setMaxLength(kMaxSetNameLength);
|
||||||
|
Ui::AddLengthLimitLabel(field, kMaxSetNameLength, kMaxSetNameLength + 1);
|
||||||
|
box->setFocusCallback([=] { field->setFocusFast(); });
|
||||||
|
const auto close = crl::guard(box, [=] { box->closeBox(); });
|
||||||
|
const auto save = [=, show = box->uiShow()] {
|
||||||
|
if (state->requestId.current()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto text = field->getLastText().trimmed();
|
||||||
|
if ((Ui::ComputeRealUnicodeCharactersCount(text) > kMaxSetNameLength)
|
||||||
|
|| text.isEmpty()) {
|
||||||
|
field->showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto buttonWidth = state->saveButton
|
||||||
|
? state->saveButton->width()
|
||||||
|
: 0;
|
||||||
|
state->requestId = data->session().api().request(
|
||||||
|
MTPstickers_RenameStickerSet(
|
||||||
|
Data::InputStickerSet(input),
|
||||||
|
MTP_string(text))
|
||||||
|
).done([=](const TLStickerSet &result) {
|
||||||
|
result.match([&](const MTPDmessages_stickerSet &d) {
|
||||||
|
data->stickers().feedSetFull(d);
|
||||||
|
data->stickers().notifyUpdated(Data::StickersType::Stickers);
|
||||||
|
}, [](const auto &) {
|
||||||
|
});
|
||||||
|
done(result);
|
||||||
|
close();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
show->showToast(error.type());
|
||||||
|
close();
|
||||||
|
}).send();
|
||||||
|
if (state->saveButton) {
|
||||||
|
state->saveButton->resizeToWidth(buttonWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state->saveButton = box->addButton(
|
||||||
|
rpl::conditional(
|
||||||
|
state->requestId.value() | rpl::map(rpl::mappers::_1 > 0),
|
||||||
|
rpl::single(QString()),
|
||||||
|
tr::lng_box_done()),
|
||||||
|
save);
|
||||||
|
if (const auto saveButton = state->saveButton) {
|
||||||
|
using namespace Info::Statistics;
|
||||||
|
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||||
|
saveButton,
|
||||||
|
saveButton->height() / 2,
|
||||||
|
&st::editStickerSetNameLoading);
|
||||||
|
AddChildToWidgetCenter(saveButton, loadingAnimation);
|
||||||
|
loadingAnimation->showOn(
|
||||||
|
state->requestId.value() | rpl::map(rpl::mappers::_1 > 0));
|
||||||
|
}
|
||||||
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
|
data->session().api().request(state->requestId.current()).cancel();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::updateButtons() {
|
void StickerSetBox::updateButtons() {
|
||||||
clearButtons();
|
clearButtons();
|
||||||
if (_inner->loaded()) {
|
if (_inner->reorderState()) {
|
||||||
|
addButton(tr::lng_box_done(), [=] {
|
||||||
|
_inner->setReorderState(false);
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
} else if (_inner->loaded()) {
|
||||||
const auto type = _inner->setType();
|
const auto type = _inner->setType();
|
||||||
const auto share = [=] {
|
const auto share = [=] {
|
||||||
copyStickersLink();
|
copyStickersLink();
|
||||||
|
@ -555,6 +713,34 @@ void StickerSetBox::updateButtons() {
|
||||||
? tr::lng_stickers_copied_emoji(tr::now)
|
? tr::lng_stickers_copied_emoji(tr::now)
|
||||||
: tr::lng_stickers_copied(tr::now));
|
: tr::lng_stickers_copied(tr::now));
|
||||||
};
|
};
|
||||||
|
const auto fillSetCreatorMenu = [&] {
|
||||||
|
using Filler = Fn<void(not_null<Ui::PopupMenu*>)>;
|
||||||
|
if (!_inner->amSetCreator()) {
|
||||||
|
return Filler(nullptr);
|
||||||
|
}
|
||||||
|
const auto data = &_session->data();
|
||||||
|
return Filler([=, show = _show, set = _set](
|
||||||
|
not_null<Ui::PopupMenu*> menu) {
|
||||||
|
const auto done = [inner = _inner](const TLStickerSet &set) {
|
||||||
|
if (const auto raw = inner.data()) {
|
||||||
|
raw->applySet(set);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
menu->addAction(
|
||||||
|
tr::lng_stickers_context_edit_name(tr::now),
|
||||||
|
[=] {
|
||||||
|
show->showBox(Box(ChangeSetNameBox, data, set, done));
|
||||||
|
},
|
||||||
|
&st::menuIconEdit);
|
||||||
|
menu->addAction(
|
||||||
|
tr::lng_stickers_context_reorder(tr::now),
|
||||||
|
[=] {
|
||||||
|
_inner->setReorderState(true);
|
||||||
|
updateButtons();
|
||||||
|
},
|
||||||
|
&st::menuIconManage);
|
||||||
|
});
|
||||||
|
}();
|
||||||
const auto addPackOwner = [=](const std::shared_ptr<base::unique_qptr<Ui::PopupMenu>> &menu)
|
const auto addPackOwner = [=](const std::shared_ptr<base::unique_qptr<Ui::PopupMenu>> &menu)
|
||||||
{
|
{
|
||||||
if (type == Data::StickersType::Stickers || type == Data::StickersType::Emoji) {
|
if (type == Data::StickersType::Stickers || type == Data::StickersType::Emoji) {
|
||||||
|
@ -629,6 +815,9 @@ void StickerSetBox::updateButtons() {
|
||||||
*menu = base::make_unique_q<Ui::PopupMenu>(
|
*menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
top,
|
top,
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
|
if (fillSetCreatorMenu) {
|
||||||
|
fillSetCreatorMenu(*menu);
|
||||||
|
}
|
||||||
(*menu)->addAction(
|
(*menu)->addAction(
|
||||||
((type == Data::StickersType::Emoji)
|
((type == Data::StickersType::Emoji)
|
||||||
? tr::lng_stickers_share_emoji
|
? tr::lng_stickers_share_emoji
|
||||||
|
@ -680,6 +869,9 @@ void StickerSetBox::updateButtons() {
|
||||||
remove,
|
remove,
|
||||||
&st::menuIconRemove);
|
&st::menuIconRemove);
|
||||||
} else {
|
} else {
|
||||||
|
if (fillSetCreatorMenu) {
|
||||||
|
fillSetCreatorMenu(*menu);
|
||||||
|
}
|
||||||
(*menu)->addAction(
|
(*menu)->addAction(
|
||||||
(type == Data::StickersType::Masks
|
(type == Data::StickersType::Masks
|
||||||
? tr::lng_masks_archive_pack(tr::now)
|
? tr::lng_masks_archive_pack(tr::now)
|
||||||
|
@ -732,8 +924,8 @@ StickerSetBox::Inner::Inner(
|
||||||
_api.request(MTPmessages_GetStickerSet(
|
_api.request(MTPmessages_GetStickerSet(
|
||||||
Data::InputStickerSet(_input),
|
Data::InputStickerSet(_input),
|
||||||
MTP_int(0) // hash
|
MTP_int(0) // hash
|
||||||
)).done([=](const MTPmessages_StickerSet &result) {
|
)).done([=](const TLStickerSet &result) {
|
||||||
gotSet(result);
|
applySet(result);
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
_errors.fire(Error::NotFound);
|
_errors.fire(Error::NotFound);
|
||||||
|
@ -749,7 +941,7 @@ StickerSetBox::Inner::Inner(
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
void StickerSetBox::Inner::applySet(const TLStickerSet &set) {
|
||||||
_pack.clear();
|
_pack.clear();
|
||||||
_emoji.clear();
|
_emoji.clear();
|
||||||
_elements.clear();
|
_elements.clear();
|
||||||
|
@ -793,7 +985,9 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
data.vset().match([&](const MTPDstickerSet &set) {
|
|
||||||
|
{
|
||||||
|
const auto &set = data.vset().data();
|
||||||
_setTitle = _session->data().stickers().getSetTitle(
|
_setTitle = _session->data().stickers().getSetTitle(
|
||||||
set);
|
set);
|
||||||
_setShortName = qs(set.vshort_name());
|
_setShortName = qs(set.vshort_name());
|
||||||
|
@ -804,6 +998,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||||
_setFlags = Data::ParseStickersSetFlags(set);
|
_setFlags = Data::ParseStickersSetFlags(set);
|
||||||
_setInstallDate = set.vinstalled_date().value_or(0);
|
_setInstallDate = set.vinstalled_date().value_or(0);
|
||||||
_setThumbnailDocumentId = set.vthumb_document_id().value_or_empty();
|
_setThumbnailDocumentId = set.vthumb_document_id().value_or_empty();
|
||||||
|
_amSetCreator = set.is_creator();
|
||||||
_setThumbnail = [&] {
|
_setThumbnail = [&] {
|
||||||
if (const auto thumbs = set.vthumbs()) {
|
if (const auto thumbs = set.vthumbs()) {
|
||||||
for (const auto &thumb : thumbs->v) {
|
for (const auto &thumb : thumbs->v) {
|
||||||
|
@ -836,7 +1031,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||||
set->emoji = _emoji;
|
set->emoji = _emoji;
|
||||||
set->setThumbnail(_setThumbnail, _setThumbnailType);
|
set->setThumbnail(_setThumbnail, _setThumbnailType);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}, [&](const MTPDmessages_stickerSetNotModified &data) {
|
}, [&](const MTPDmessages_stickerSetNotModified &data) {
|
||||||
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
||||||
});
|
});
|
||||||
|
@ -977,11 +1172,100 @@ void StickerSetBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
if (index < 0 || index >= _pack.size()) {
|
if (index < 0 || index >= _pack.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (_dragging.enabled) {
|
||||||
|
_previewTimer.cancel();
|
||||||
|
if (isDraggedAnimating()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_dragging.index = index;
|
||||||
|
_dragging.point = mapFromGlobal(QCursor::pos()) - posFromIndex(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_previewTimer.callOnce(QApplication::startDragTime());
|
_previewTimer.callOnce(QApplication::startDragTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
void StickerSetBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||||
updateSelected();
|
updateSelected();
|
||||||
|
const auto draggedAnimating = isDraggedAnimating();
|
||||||
|
if (_selected >= 0 && !draggedAnimating) {
|
||||||
|
_dragging.lastSelected = _selected;
|
||||||
|
}
|
||||||
|
if (_dragging.index >= 0
|
||||||
|
&& _dragging.index < _pack.size()
|
||||||
|
&& _dragging.lastSelected >= 0
|
||||||
|
&& !draggedAnimating) {
|
||||||
|
for (auto i = 0; i < _pack.size(); i++) {
|
||||||
|
if (i == _dragging.index) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto &entry = _shiftAnimations[i];
|
||||||
|
const auto wasShift = entry.shift;
|
||||||
|
if ((i >= _dragging.index) && (i <= _dragging.lastSelected)) {
|
||||||
|
if (entry.shift == 0) {
|
||||||
|
entry.shift = -1;
|
||||||
|
} else if (entry.shift == 1) {
|
||||||
|
entry.shift = 0;
|
||||||
|
}
|
||||||
|
} else if ((i < _dragging.index)
|
||||||
|
&& (i >= _dragging.lastSelected)) {
|
||||||
|
if (entry.shift == 0) {
|
||||||
|
entry.shift = 1;
|
||||||
|
} else if (entry.shift == -1) {
|
||||||
|
entry.shift = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((i < std::min(_dragging.index, _dragging.lastSelected))
|
||||||
|
|| (i > std::max(_dragging.index, _dragging.lastSelected))) {
|
||||||
|
entry.shift = 0;
|
||||||
|
}
|
||||||
|
if (wasShift != entry.shift) {
|
||||||
|
const auto fromPoint = posFromIndex(i + wasShift);
|
||||||
|
const auto toPoint = posFromIndex(i + entry.shift);
|
||||||
|
const auto toX = float64(toPoint.x());
|
||||||
|
const auto toY = float64(toPoint.y());
|
||||||
|
const auto ratio = [&] {
|
||||||
|
const auto fromX = entry.animation.value(toX);
|
||||||
|
const auto ratioX = std::min(toX, fromX)
|
||||||
|
/ std::max(toX, fromX);
|
||||||
|
const auto fromY = entry.yAnimation.value(toY);
|
||||||
|
const auto ratioY = std::min(toY, fromY)
|
||||||
|
/ std::max(toY, fromY);
|
||||||
|
return (ratioX == 1.)
|
||||||
|
? ratioY
|
||||||
|
: (ratioY == 1.)
|
||||||
|
? ratioX
|
||||||
|
: std::max(ratioX, ratioY);
|
||||||
|
}();
|
||||||
|
if (!entry.animation.animating()) {
|
||||||
|
entry.animation.stop();
|
||||||
|
entry.animation.start(
|
||||||
|
[=] { update(); },
|
||||||
|
fromPoint.x(),
|
||||||
|
toX,
|
||||||
|
kStickerMoveDuration);
|
||||||
|
} else {
|
||||||
|
entry.animation.change(
|
||||||
|
toX,
|
||||||
|
kStickerMoveDuration * (1. - ratio),
|
||||||
|
anim::linear);
|
||||||
|
}
|
||||||
|
if (!entry.yAnimation.animating()) {
|
||||||
|
entry.yAnimation.stop();
|
||||||
|
entry.yAnimation.start(
|
||||||
|
[=] { update(); },
|
||||||
|
fromPoint.y(),
|
||||||
|
toY,
|
||||||
|
kStickerMoveDuration);
|
||||||
|
} else {
|
||||||
|
entry.yAnimation.change(
|
||||||
|
toY,
|
||||||
|
kStickerMoveDuration * (1. - ratio),
|
||||||
|
anim::linear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
if (_previewShown >= 0) {
|
if (_previewShown >= 0) {
|
||||||
showPreviewAt(e->globalPos());
|
showPreviewAt(e->globalPos());
|
||||||
}
|
}
|
||||||
|
@ -1003,7 +1287,86 @@ void StickerSetBox::Inner::leaveEventHook(QEvent *e) {
|
||||||
setSelected(-1);
|
setSelected(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::requestReorder(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
int index) {
|
||||||
|
if (!_apiReorder) {
|
||||||
|
_apiReorder.emplace(&_session->mtp());
|
||||||
|
}
|
||||||
|
_reorderRequests.emplace_back([document, index, this] {
|
||||||
|
_apiReorder->request(
|
||||||
|
MTPstickers_ChangeStickerPosition(
|
||||||
|
document->mtpInput(),
|
||||||
|
MTP_int(index))
|
||||||
|
).done([this, document](const TLStickerSet &result) {
|
||||||
|
result.match([&](const MTPDmessages_stickerSet &d) {
|
||||||
|
document->owner().stickers().feedSetFull(d);
|
||||||
|
document->owner().stickers().notifyUpdated(
|
||||||
|
Data::StickersType::Stickers);
|
||||||
|
}, [](const auto &) {
|
||||||
|
});
|
||||||
|
if (!_reorderRequests.empty()) {
|
||||||
|
_reorderRequests.pop_front();
|
||||||
|
}
|
||||||
|
if (_reorderRequests.empty()) {
|
||||||
|
// applySet(result); // Causes stickers blink.
|
||||||
|
} else {
|
||||||
|
_reorderRequests.front()();
|
||||||
|
}
|
||||||
|
}).fail([show = _show](const MTP::Error &error) {
|
||||||
|
show->showToast(error.type());
|
||||||
|
}).send();
|
||||||
|
});
|
||||||
|
if (_reorderRequests.size() == 1) {
|
||||||
|
_reorderRequests.front()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
if (_dragging.index >= 0 && !isDraggedAnimating()) {
|
||||||
|
const auto fromPos = mapFromGlobal(e->globalPos()) - _dragging.point;
|
||||||
|
const auto toPos = posFromIndex(_dragging.lastSelected);
|
||||||
|
const auto document = _pack[_dragging.index];
|
||||||
|
const auto wasPosition = _dragging.index;
|
||||||
|
const auto nowPosition = _dragging.lastSelected;
|
||||||
|
const auto finish = [=, this] {
|
||||||
|
requestReorder(document, nowPosition);
|
||||||
|
base::reorder(_pack, wasPosition, nowPosition);
|
||||||
|
base::reorder(_elements, wasPosition, nowPosition);
|
||||||
|
_dragging = {};
|
||||||
|
_dragging.enabled = true;
|
||||||
|
_shiftAnimations.clear();
|
||||||
|
};
|
||||||
|
auto &entry = _shiftAnimations[_dragging.index];
|
||||||
|
entry.animation.stop();
|
||||||
|
entry.yAnimation.stop();
|
||||||
|
entry.animation.start(
|
||||||
|
[finish, toPos, this](float64 value) {
|
||||||
|
const auto index = _dragging.index;
|
||||||
|
if (value >= toPos.x()
|
||||||
|
&& index >= 0
|
||||||
|
&& !_shiftAnimations[index].yAnimation.animating()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
},
|
||||||
|
fromPos.x(),
|
||||||
|
toPos.x(),
|
||||||
|
kStickerMoveDuration);
|
||||||
|
entry.yAnimation.start(
|
||||||
|
[finish, toPos, this](float64 value) {
|
||||||
|
const auto index = _dragging.index;
|
||||||
|
if (value >= toPos.y()
|
||||||
|
&& index >= 0
|
||||||
|
&& !_shiftAnimations[index].animation.animating()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
},
|
||||||
|
fromPos.y(),
|
||||||
|
toPos.y(),
|
||||||
|
kStickerMoveDuration);
|
||||||
|
}
|
||||||
if (_previewShown >= 0) {
|
if (_previewShown >= 0) {
|
||||||
_previewShown = -1;
|
_previewShown = -1;
|
||||||
return;
|
return;
|
||||||
|
@ -1106,6 +1469,20 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
(isFaved
|
(isFaved
|
||||||
? &st::menuIconUnfave
|
? &st::menuIconUnfave
|
||||||
: &st::menuIconFave));
|
: &st::menuIconFave));
|
||||||
|
if (amSetCreator()) {
|
||||||
|
const auto addAction = Ui::Menu::CreateAddActionCallback(
|
||||||
|
_menu.get());
|
||||||
|
addAction({
|
||||||
|
.text = tr::lng_stickers_context_delete(tr::now),
|
||||||
|
.handler = [index, this, show = _show] {
|
||||||
|
show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
fillDeleteStickerBox(box, index);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
.icon = &st::menuIconDeleteAttention,
|
||||||
|
.isAttention = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_menu->empty()) {
|
if (_menu->empty()) {
|
||||||
_menu = nullptr;
|
_menu = nullptr;
|
||||||
|
@ -1114,6 +1491,129 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::fillDeleteStickerBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
int index) {
|
||||||
|
Expects(index >= 0 || index < _pack.size());
|
||||||
|
const auto document = _pack[index];
|
||||||
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
const auto show = _show;
|
||||||
|
|
||||||
|
const auto container = box->verticalLayout();
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
const auto line = container->add(object_ptr<Ui::RpWidget>(container));
|
||||||
|
line->resize(line->width(), _singleSize.height());
|
||||||
|
|
||||||
|
const auto sticker = Ui::CreateChild<Ui::RpWidget>(line);
|
||||||
|
auto &lifetime = sticker->lifetime();
|
||||||
|
struct State final {
|
||||||
|
rpl::variable<mtpRequestId> requestId = 0;
|
||||||
|
Ui::RpWidget* saveButton = nullptr;
|
||||||
|
};
|
||||||
|
const auto state = lifetime.make_state<State>();
|
||||||
|
sticker->resize(_singleSize);
|
||||||
|
{
|
||||||
|
const auto animation = lifetime.make_state<Ui::Animations::Basic>();
|
||||||
|
animation->init([=] { sticker->update(); });
|
||||||
|
animation->start();
|
||||||
|
}
|
||||||
|
sticker->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = Painter(sticker);
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
const auto paused = On(PowerSaving::kStickersPanel)
|
||||||
|
|| show->paused(ChatHelpers::PauseReason::Layer);
|
||||||
|
paintSticker(p, index, QPoint(), paused, crl::now());
|
||||||
|
if (_lottiePlayer && !paused) {
|
||||||
|
_lottiePlayer->markFrameShown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, sticker->lifetime());
|
||||||
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
line,
|
||||||
|
tr::lng_stickers_context_delete(),
|
||||||
|
box->getDelegate()->style().title);
|
||||||
|
line->widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
sticker->moveToLeft(st::boxRowPadding.left(), 0);
|
||||||
|
const auto skip = st::defaultBoxCheckbox.textPosition.x();
|
||||||
|
label->resizeToWidth(width
|
||||||
|
- rect::right(sticker)
|
||||||
|
- skip
|
||||||
|
- st::boxRowPadding.right());
|
||||||
|
label->moveToLeft(
|
||||||
|
rect::right(sticker) + skip,
|
||||||
|
((sticker->height() - label->height()) / 2));
|
||||||
|
}, label->lifetime());
|
||||||
|
|
||||||
|
sticker->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
tr::lng_stickers_context_delete_sure(),
|
||||||
|
st::boxLabel));
|
||||||
|
const auto save = [=] {
|
||||||
|
if (state->requestId.current()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weakBox = Ui::MakeWeak(box);
|
||||||
|
const auto buttonWidth = state->saveButton
|
||||||
|
? state->saveButton->width()
|
||||||
|
: 0;
|
||||||
|
state->requestId = document->owner().session().api().request(
|
||||||
|
MTPstickers_RemoveStickerFromSet(document->mtpInput()
|
||||||
|
)).done([=](const TLStickerSet &result) {
|
||||||
|
result.match([&](const MTPDmessages_stickerSet &d) {
|
||||||
|
document->owner().stickers().feedSetFull(d);
|
||||||
|
document->owner().stickers().notifyUpdated(
|
||||||
|
Data::StickersType::Stickers);
|
||||||
|
}, [](const auto &) {
|
||||||
|
});
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
applySet(result);
|
||||||
|
}
|
||||||
|
if (const auto strongBox = weakBox.data()) {
|
||||||
|
strongBox->closeBox();
|
||||||
|
}
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
if (const auto strongBox = weakBox.data()) {
|
||||||
|
strongBox->uiShow()->showToast(error.type());
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
if (state->saveButton) {
|
||||||
|
state->saveButton->resizeToWidth(buttonWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
state->saveButton = box->addButton(
|
||||||
|
rpl::conditional(
|
||||||
|
state->requestId.value() | rpl::map(rpl::mappers::_1 > 0),
|
||||||
|
rpl::single(QString()),
|
||||||
|
tr::lng_selected_delete()),
|
||||||
|
save,
|
||||||
|
st::attentionBoxButton);
|
||||||
|
if (const auto saveButton = state->saveButton) {
|
||||||
|
using namespace Info::Statistics;
|
||||||
|
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||||
|
saveButton,
|
||||||
|
saveButton->height() / 2,
|
||||||
|
&st::editStickerSetNameLoading);
|
||||||
|
AddChildToWidgetCenter(saveButton, loadingAnimation);
|
||||||
|
loadingAnimation->showOn(
|
||||||
|
state->requestId.value() | rpl::map(rpl::mappers::_1 > 0));
|
||||||
|
}
|
||||||
|
box->addButton(tr::lng_close(), [=] {
|
||||||
|
document->owner().session().api().request(
|
||||||
|
state->requestId.current()).cancel();
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::updateSelected() {
|
void StickerSetBox::Inner::updateSelected() {
|
||||||
auto selected = stickerFromGlobalPos(QCursor::pos());
|
auto selected = stickerFromGlobalPos(QCursor::pos());
|
||||||
setSelected(setType() == Data::StickersType::Masks ? -1 : selected);
|
setSelected(setType() == Data::StickersType::Masks ? -1 : selected);
|
||||||
|
@ -1124,7 +1624,11 @@ void StickerSetBox::Inner::setSelected(int selected) {
|
||||||
startOverAnimation(_selected, 1., 0.);
|
startOverAnimation(_selected, 1., 0.);
|
||||||
_selected = selected;
|
_selected = selected;
|
||||||
startOverAnimation(_selected, 0., 1.);
|
startOverAnimation(_selected, 0., 1.);
|
||||||
setCursor(_selected >= 0 ? style::cur_pointer : style::cur_default);
|
setCursor((_selected < 0)
|
||||||
|
? style::cur_default
|
||||||
|
: _dragging.enabled
|
||||||
|
? style::cur_sizeall
|
||||||
|
: style::cur_pointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1146,6 +1650,24 @@ void StickerSetBox::Inner::showPreview() {
|
||||||
showPreviewAt(QCursor::pos());
|
showPreviewAt(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPoint StickerSetBox::Inner::posFromIndex(int index) const {
|
||||||
|
return {
|
||||||
|
_padding.left() + (index % _perRow) * _singleSize.width(),
|
||||||
|
_padding.top() + (index / _perRow) * _singleSize.height(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StickerSetBox::Inner::isDraggedAnimating() const {
|
||||||
|
if (_dragging.index < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto it = _shiftAnimations.find(_dragging.index);
|
||||||
|
return (it == _shiftAnimations.end())
|
||||||
|
? false
|
||||||
|
: (it->second.animation.animating()
|
||||||
|
|| it->second.yAnimation.animating());
|
||||||
|
}
|
||||||
|
|
||||||
not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
|
not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
|
||||||
if (!_lottiePlayer) {
|
if (!_lottiePlayer) {
|
||||||
_lottiePlayer = std::make_unique<Lottie::MultiPlayer>(
|
_lottiePlayer = std::make_unique<Lottie::MultiPlayer>(
|
||||||
|
@ -1185,12 +1707,36 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
_pathGradient->startFrame(0, width(), width() / 2);
|
_pathGradient->startFrame(0, width(), width() / 2);
|
||||||
|
|
||||||
|
const auto indexUnderCursor = (_dragging.index >= 0
|
||||||
|
&& _dragging.index < _elements.size())
|
||||||
|
? stickerFromGlobalPos(QCursor::pos())
|
||||||
|
: -2;
|
||||||
|
const auto lastIndex = indexUnderCursor >= 0
|
||||||
|
? indexUnderCursor
|
||||||
|
: _dragging.lastSelected;
|
||||||
|
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
const auto paused = On(PowerSaving::kStickersPanel)
|
const auto paused = On(PowerSaving::kStickersPanel)
|
||||||
|| _show->paused(ChatHelpers::PauseReason::Layer);
|
|| _show->paused(ChatHelpers::PauseReason::Layer);
|
||||||
for (int32 i = from; i < to; ++i) {
|
for (int32 i = from; i < to; ++i) {
|
||||||
for (int32 j = 0; j < _perRow; ++j) {
|
for (int32 j = 0; j < _perRow; ++j) {
|
||||||
int32 index = i * _perRow + j;
|
int32 index = i * _perRow + j;
|
||||||
|
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
if (_dragging.index == index) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto it = _shiftAnimations.find(index);
|
||||||
|
if (it != _shiftAnimations.end()) {
|
||||||
|
const auto &entry = it->second;
|
||||||
|
const auto toPos = posFromIndex(index + entry.shift);
|
||||||
|
const auto pos = QPoint(
|
||||||
|
entry.animation.value(toPos.x()),
|
||||||
|
entry.yAnimation.value(toPos.y()));
|
||||||
|
paintSticker(p, index, pos, paused, now);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (index >= _elements.size()) {
|
if (index >= _elements.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1200,6 +1746,14 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
paintSticker(p, index, pos, paused, now);
|
paintSticker(p, index, pos, paused, now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_dragging.index >= 0 && _dragging.index < _elements.size()) {
|
||||||
|
const auto pos = isDraggedAnimating()
|
||||||
|
? QPoint(
|
||||||
|
_shiftAnimations[_dragging.index].animation.value(0),
|
||||||
|
_shiftAnimations[_dragging.index].yAnimation.value(0))
|
||||||
|
: (mapFromGlobal(QCursor::pos()) - _dragging.point);
|
||||||
|
paintSticker(p, _dragging.index, pos, paused, now);
|
||||||
|
}
|
||||||
|
|
||||||
if (_lottiePlayer && !paused) {
|
if (_lottiePlayer && !paused) {
|
||||||
_lottiePlayer->markFrameShown();
|
_lottiePlayer->markFrameShown();
|
||||||
|
@ -1355,18 +1909,99 @@ void StickerSetBox::Inner::customEmojiRepaint() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::shakeTransform(
|
||||||
|
QPainter &p,
|
||||||
|
int index,
|
||||||
|
QPoint position,
|
||||||
|
crl::time now) const {
|
||||||
|
constexpr auto kShakeADuration = crl::time(400);
|
||||||
|
constexpr auto kShakeXDuration = crl::time(kShakeADuration * 1.2);
|
||||||
|
constexpr auto kShakeYDuration = kShakeADuration;
|
||||||
|
const auto diff = ((index % 2) ? 0 : kShakeYDuration / 2)
|
||||||
|
+ (now - _shakeAnimation.started());
|
||||||
|
const auto pX = (diff % kShakeXDuration)
|
||||||
|
/ float64(kShakeXDuration);
|
||||||
|
const auto pY = (diff % kShakeYDuration)
|
||||||
|
/ float64(kShakeYDuration);
|
||||||
|
const auto pA = (diff % kShakeADuration)
|
||||||
|
/ float64(kShakeADuration);
|
||||||
|
|
||||||
|
constexpr auto kMaxA = 2.;
|
||||||
|
constexpr auto kMaxTranslation = .5;
|
||||||
|
constexpr auto kAStep = 1. / 5;
|
||||||
|
constexpr auto kXStep = 1. / 5;
|
||||||
|
constexpr auto kYStep = 1. / 4;
|
||||||
|
|
||||||
|
// 0, -kMaxA, 0, kMaxA, 0.
|
||||||
|
const auto angle = (pA < kAStep)
|
||||||
|
? anim::interpolateF(0., -kMaxA, pA / kAStep)
|
||||||
|
: (pA < kAStep * 2.)
|
||||||
|
? anim::interpolateF(-kMaxA, 0, (pA - kAStep) / kAStep)
|
||||||
|
: (pA < kAStep * 3.)
|
||||||
|
? anim::interpolateF(0, kMaxA, (pA - kAStep * 2.) / kAStep)
|
||||||
|
: (pA < kAStep * 4.)
|
||||||
|
? anim::interpolateF(kMaxA, 0, (pA - kAStep * 3.) / kAStep)
|
||||||
|
: anim::interpolateF(0, 0., (pA - kAStep * 4.) / kAStep);
|
||||||
|
|
||||||
|
// 0, kMaxTranslation, 0, -kMaxTranslation, 0.
|
||||||
|
const auto x = (pX < kXStep)
|
||||||
|
? anim::interpolateF(0., kMaxTranslation, pX / kXStep)
|
||||||
|
: (pX < kXStep * 2.)
|
||||||
|
? anim::interpolateF(kMaxTranslation, 0, (pX - kXStep) / kXStep)
|
||||||
|
: (pX < kXStep * 3.)
|
||||||
|
? anim::interpolateF(0, -kMaxTranslation, (pX - kXStep * 2.) / kXStep)
|
||||||
|
: (pX < kXStep * 4.)
|
||||||
|
? anim::interpolateF(-kMaxTranslation, 0, (pX - kXStep * 3.) / kXStep)
|
||||||
|
: anim::interpolateF(0, 0., (pX - kXStep * 4.) / kXStep);
|
||||||
|
|
||||||
|
// 0, kMaxTranslation, -kMaxTranslation, 0.
|
||||||
|
const auto y = (pY < kYStep)
|
||||||
|
? anim::interpolateF(0., kMaxTranslation, pY / kYStep)
|
||||||
|
: (pY < kYStep * 2.)
|
||||||
|
? anim::interpolateF(kMaxTranslation, 0, (pY - kYStep) / kYStep)
|
||||||
|
: (pY < kYStep * 3.)
|
||||||
|
? anim::interpolateF(0, -kMaxTranslation, (pY - kYStep * 2.) / kYStep)
|
||||||
|
: anim::interpolateF(-kMaxTranslation, 0, (pY - kYStep * 3) / kYStep);
|
||||||
|
|
||||||
|
const auto center = position + QPoint(
|
||||||
|
_singleSize.width() / 2,
|
||||||
|
_singleSize.height() / 2);
|
||||||
|
|
||||||
|
p.translate(center);
|
||||||
|
p.rotate(angle);
|
||||||
|
p.translate(-center);
|
||||||
|
p.translate(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::paintSticker(
|
void StickerSetBox::Inner::paintSticker(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
int index,
|
int index,
|
||||||
QPoint position,
|
QPoint position,
|
||||||
bool paused,
|
bool paused,
|
||||||
crl::time now) const {
|
crl::time now) const {
|
||||||
if (const auto over = _elements[index].overAnimation.value((index == _selected) ? 1. : 0.)) {
|
if (_dragging.index != index) {
|
||||||
p.setOpacity(over);
|
const auto over = _elements[index].overAnimation.value(
|
||||||
auto tl = position;
|
(index == _selected) ? 1. : 0.);
|
||||||
if (rtl()) tl.setX(width() - tl.x() - _singleSize.width());
|
if (over) {
|
||||||
Ui::FillRoundRect(p, QRect(tl, _singleSize), st::emojiPanHover, Ui::StickerHoverCorners);
|
p.setOpacity(over);
|
||||||
p.setOpacity(1);
|
Ui::FillRoundRect(
|
||||||
|
p,
|
||||||
|
QRect(
|
||||||
|
rtl()
|
||||||
|
? QPoint(
|
||||||
|
width() - position.x() - _singleSize.width(),
|
||||||
|
position.y())
|
||||||
|
: position,
|
||||||
|
_singleSize),
|
||||||
|
st::emojiPanHover,
|
||||||
|
Ui::StickerHoverCorners);
|
||||||
|
p.setOpacity(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hasShake = _shakeAnimation.animating();
|
||||||
|
if (hasShake) {
|
||||||
|
shakeTransform(p, index, position, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &element = _elements[index];
|
const auto &element = _elements[index];
|
||||||
|
@ -1452,6 +2087,9 @@ void StickerSetBox::Inner::paintSticker(
|
||||||
_singleSize,
|
_singleSize,
|
||||||
width());
|
width());
|
||||||
}
|
}
|
||||||
|
if (hasShake) {
|
||||||
|
p.resetTransform();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StickerSetBox::Inner::loaded() const {
|
bool StickerSetBox::Inner::loaded() const {
|
||||||
|
|
|
@ -39,7 +39,6 @@ namespace tgcalls {
|
||||||
class InstanceImpl;
|
class InstanceImpl;
|
||||||
class InstanceV2Impl;
|
class InstanceV2Impl;
|
||||||
class InstanceV2ReferenceImpl;
|
class InstanceV2ReferenceImpl;
|
||||||
class InstanceV2_4_0_0Impl;
|
|
||||||
class InstanceImplLegacy;
|
class InstanceImplLegacy;
|
||||||
void SetLegacyGlobalServerConfig(const std::string &serverConfig);
|
void SetLegacyGlobalServerConfig(const std::string &serverConfig);
|
||||||
} // namespace tgcalls
|
} // namespace tgcalls
|
||||||
|
@ -56,7 +55,6 @@ const auto kDefaultVersion = "2.4.4"_q;
|
||||||
const auto Register = tgcalls::Register<tgcalls::InstanceImpl>();
|
const auto Register = tgcalls::Register<tgcalls::InstanceImpl>();
|
||||||
const auto RegisterV2 = tgcalls::Register<tgcalls::InstanceV2Impl>();
|
const auto RegisterV2 = tgcalls::Register<tgcalls::InstanceV2Impl>();
|
||||||
const auto RegV2Ref = tgcalls::Register<tgcalls::InstanceV2ReferenceImpl>();
|
const auto RegV2Ref = tgcalls::Register<tgcalls::InstanceV2ReferenceImpl>();
|
||||||
const auto RegisterV240 = tgcalls::Register<tgcalls::InstanceV2_4_0_0Impl>();
|
|
||||||
const auto RegisterLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>();
|
const auto RegisterLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>();
|
||||||
|
|
||||||
[[nodiscard]] base::flat_set<int64> CollectEndpointIds(
|
[[nodiscard]] base::flat_set<int64> CollectEndpointIds(
|
||||||
|
|
|
@ -215,7 +215,7 @@ void Panel::initWindow() {
|
||||||
}
|
}
|
||||||
const auto shown = _layerBg->topShownLayer();
|
const auto shown = _layerBg->topShownLayer();
|
||||||
return (!shown || !shown->geometry().contains(widgetPoint))
|
return (!shown || !shown->geometry().contains(widgetPoint))
|
||||||
? (Flag::Move | Flag::FullScreen)
|
? (Flag::Move | Flag::Menu | Flag::FullScreen)
|
||||||
: Flag::None;
|
: Flag::None;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -276,8 +276,8 @@ void Panel::initControls() {
|
||||||
_layerBg->showBox(std::move(box));
|
_layerBg->showBox(std::move(box));
|
||||||
}
|
}
|
||||||
} else if (const auto source = env->uniqueDesktopCaptureSource()) {
|
} else if (const auto source = env->uniqueDesktopCaptureSource()) {
|
||||||
if (_call->isSharingScreen()) {
|
if (!chooseSourceActiveDeviceId().isEmpty()) {
|
||||||
_call->toggleScreenSharing(std::nullopt);
|
chooseSourceStop();
|
||||||
} else {
|
} else {
|
||||||
chooseSourceAccepted(*source, false);
|
chooseSourceAccepted(*source, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1195,24 +1195,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||||
const auto addVolumeItem = (!muted || isMe(participantPeer));
|
const auto addVolumeItem = (!muted || isMe(participantPeer));
|
||||||
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
|
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
|
||||||
const auto session = &_peer->session();
|
const auto session = &_peer->session();
|
||||||
const auto getCurrentWindow = [=]() -> Window::SessionController* {
|
const auto account = &session->account();
|
||||||
if (const auto window = Core::App().windowFor(participantPeer)) {
|
|
||||||
if (const auto controller = window->sessionController()) {
|
|
||||||
if (&controller->session() == session) {
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
const auto getWindow = [=] {
|
|
||||||
if (const auto current = getCurrentWindow()) {
|
|
||||||
return current;
|
|
||||||
} else if (&Core::App().domain().active() != &session->account()) {
|
|
||||||
Core::App().domain().activate(&session->account());
|
|
||||||
}
|
|
||||||
return getCurrentWindow();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = base::make_unique_q<Ui::PopupMenu>(
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||||
parent,
|
parent,
|
||||||
|
@ -1223,7 +1206,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||||
: st::groupCallPopupMenu));
|
: st::groupCallPopupMenu));
|
||||||
const auto weakMenu = Ui::MakeWeak(result.get());
|
const auto weakMenu = Ui::MakeWeak(result.get());
|
||||||
const auto withActiveWindow = [=](auto callback) {
|
const auto withActiveWindow = [=](auto callback) {
|
||||||
if (const auto window = getWindow()) {
|
if (const auto window = Core::App().activePrimaryWindow()) {
|
||||||
if (const auto menu = weakMenu.data()) {
|
if (const auto menu = weakMenu.data()) {
|
||||||
menu->discardParentReActivate();
|
menu->discardParentReActivate();
|
||||||
|
|
||||||
|
@ -1232,8 +1215,13 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||||
// PopupMenu::hide activates back the group call panel :(
|
// PopupMenu::hide activates back the group call panel :(
|
||||||
delete weakMenu;
|
delete weakMenu;
|
||||||
}
|
}
|
||||||
callback(window);
|
window->invokeForSessionController(
|
||||||
window->widget()->activate();
|
account,
|
||||||
|
participantPeer,
|
||||||
|
[&](not_null<Window::SessionController*> newController) {
|
||||||
|
callback(newController);
|
||||||
|
newController->widget()->activate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const auto showProfile = [=] {
|
const auto showProfile = [=] {
|
||||||
|
|
|
@ -410,7 +410,7 @@ void Panel::initWindow() {
|
||||||
}
|
}
|
||||||
const auto shown = _layerBg->topShownLayer();
|
const auto shown = _layerBg->topShownLayer();
|
||||||
return (!shown || !shown->geometry().contains(widgetPoint))
|
return (!shown || !shown->geometry().contains(widgetPoint))
|
||||||
? (Flag::Move | Flag::Maximize)
|
? (Flag::Move | Flag::Menu | Flag::Maximize)
|
||||||
: Flag::None;
|
: Flag::None;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,10 @@ stickersTrendingSubheaderFont: normalFont;
|
||||||
stickersTrendingSubheaderFg: windowSubTextFg;
|
stickersTrendingSubheaderFg: windowSubTextFg;
|
||||||
stickersTrendingSubheaderTop: 31px;
|
stickersTrendingSubheaderTop: 31px;
|
||||||
|
|
||||||
|
stickersHeaderBadgeFont: font(10px);
|
||||||
|
stickersHeaderBadgeFontTop: 12px;
|
||||||
|
stickersHeaderBadgeFontSkip: 12px;
|
||||||
|
|
||||||
emojiPanButtonRight: 7px;
|
emojiPanButtonRight: 7px;
|
||||||
emojiPanButtonTop: 8px;
|
emojiPanButtonTop: 8px;
|
||||||
emojiPanButton: RoundButton(defaultActiveButton) {
|
emojiPanButton: RoundButton(defaultActiveButton) {
|
||||||
|
@ -1409,6 +1413,22 @@ editTagLimit: FlatLabel(defaultFlatLabel) {
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editStickerSetNameField: InputField(defaultInputField) {
|
||||||
|
textMargins: margins(0px, 8px, 26px, 4px);
|
||||||
|
heightMin: 36px;
|
||||||
|
heightMax: 36px;
|
||||||
|
placeholderFg: placeholderFg;
|
||||||
|
placeholderFgActive: placeholderFgActive;
|
||||||
|
placeholderFgError: placeholderFgActive;
|
||||||
|
placeholderMargins: margins(2px, 0px, 2px, 0px);
|
||||||
|
placeholderScale: 0.;
|
||||||
|
placeholderFont: normalFont;
|
||||||
|
}
|
||||||
|
editStickerSetNameLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||||
|
color: lightButtonFg;
|
||||||
|
thickness: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
paidStarIcon: icon {{ "settings/premium/star", creditsBg1 }};
|
paidStarIcon: icon {{ "settings/premium/star", creditsBg1 }};
|
||||||
paidStarIconTop: 7px;
|
paidStarIconTop: 7px;
|
||||||
paidAmountAbout: FlatLabel(defaultFlatLabel) {
|
paidAmountAbout: FlatLabel(defaultFlatLabel) {
|
||||||
|
@ -1423,3 +1443,61 @@ paidTagLabel: FlatLabel(defaultFlatLabel) {
|
||||||
style: semiboldTextStyle;
|
style: semiboldTextStyle;
|
||||||
}
|
}
|
||||||
paidTagPadding: margins(16px, 6px, 16px, 6px);
|
paidTagPadding: margins(16px, 6px, 16px, 6px);
|
||||||
|
|
||||||
|
pickLocationWindow: size(364px, 680px);
|
||||||
|
pickLocationMapHeight: 220px;
|
||||||
|
pickLocationCollapsedHeight: 92px;
|
||||||
|
pickLocationRowHeight: 52px;
|
||||||
|
pickLocationButton: FlatButton {
|
||||||
|
height: pickLocationRowHeight;
|
||||||
|
bgColor: contactsBg;
|
||||||
|
overBgColor: contactsBgOver;
|
||||||
|
ripple: defaultRippleAnimation;
|
||||||
|
}
|
||||||
|
pickLocationButtonText: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 128px;
|
||||||
|
maxHeight: 20px;
|
||||||
|
style: semiboldTextStyle;
|
||||||
|
textFg: windowBoldFg;
|
||||||
|
}
|
||||||
|
pickLocationButtonStatus: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 128px;
|
||||||
|
maxHeight: 20px;
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
pickLocationButtonSkip: 6px;
|
||||||
|
pickLocationSendIcon: icon{{ "chat/filled_location", windowFgActive }};
|
||||||
|
pickLocationVenueItem: PeerListItem(defaultPeerListItem) {
|
||||||
|
height: pickLocationRowHeight;
|
||||||
|
photoSize: 42px;
|
||||||
|
photoPosition: point(18px, 5px);
|
||||||
|
namePosition: point(70px, 7px);
|
||||||
|
statusPosition: point(70px, 27px);
|
||||||
|
button: OutlineButton(defaultPeerListButton) {
|
||||||
|
textBg: contactsBg;
|
||||||
|
textBgOver: contactsBgOver;
|
||||||
|
font: normalFont;
|
||||||
|
padding: margins(11px, 5px, 11px, 5px);
|
||||||
|
ripple: defaultRippleAnimation;
|
||||||
|
}
|
||||||
|
statusFg: contactsStatusFg;
|
||||||
|
statusFgOver: contactsStatusFgOver;
|
||||||
|
statusFgActive: contactsStatusFgOnline;
|
||||||
|
}
|
||||||
|
pickLocationVenueList: PeerList(defaultPeerList) {
|
||||||
|
item: pickLocationVenueItem;
|
||||||
|
padding: margins(0px, 0px, 0px, 0px);
|
||||||
|
}
|
||||||
|
pickLocationIconSkip: 6px;
|
||||||
|
pickLocationLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||||
|
size: size(56px, 56px);
|
||||||
|
color: windowSubTextFg;
|
||||||
|
thickness: 4px;
|
||||||
|
}
|
||||||
|
pickLocationPromoHeight: 32px;
|
||||||
|
pickLocationChooseOnMap: RoundButton(defaultActiveButton) {
|
||||||
|
height: 44px;
|
||||||
|
textTop: 11px;
|
||||||
|
width: -96px;
|
||||||
|
font: font(15px semibold);
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "menu/menu_send.h" // SendMenu::FillSendMenu
|
#include "menu/menu_send.h" // SendMenu::FillSendMenu
|
||||||
|
#include "mtproto/mtproto_config.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "ui/controls/tabbed_search.h"
|
#include "ui/controls/tabbed_search.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -55,7 +56,6 @@ namespace ChatHelpers {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kSearchRequestDelay = 400;
|
constexpr auto kSearchRequestDelay = 400;
|
||||||
constexpr auto kSearchBotUsername = "gif"_cs;
|
|
||||||
constexpr auto kMinRepaintDelay = crl::time(33);
|
constexpr auto kMinRepaintDelay = crl::time(33);
|
||||||
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
||||||
|
|
||||||
|
@ -893,13 +893,11 @@ void GifsListWidget::searchForGifs(const QString &query) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_searchBot && !_searchBotRequestId) {
|
if (!_searchBot && !_searchBotRequestId) {
|
||||||
auto username = kSearchBotUsername.utf16();
|
const auto username = session().serverConfig().gifSearchUsername;
|
||||||
_searchBotRequestId = _api.request(MTPcontacts_ResolveUsername(
|
_searchBotRequestId = _api.request(MTPcontacts_ResolveUsername(
|
||||||
MTP_string(username)
|
MTP_string(username)
|
||||||
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||||
Expects(result.type() == mtpc_contacts_resolvedPeer);
|
auto &data = result.data();
|
||||||
|
|
||||||
auto &data = result.c_contacts_resolvedPeer();
|
|
||||||
session().data().processUsers(data.vusers());
|
session().data().processUsers(data.vusers());
|
||||||
session().data().processChats(data.vchats());
|
session().data().processChats(data.vchats());
|
||||||
const auto peer = session().data().peerLoaded(
|
const auto peer = session().data().peerLoaded(
|
||||||
|
|
|
@ -392,6 +392,9 @@ void InitMessageFieldHandlers(
|
||||||
Fn<bool()> customEmojiPaused,
|
Fn<bool()> customEmojiPaused,
|
||||||
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji,
|
Fn<bool(not_null<DocumentData*>)> allowPremiumEmoji,
|
||||||
const style::InputField *fieldStyle) {
|
const style::InputField *fieldStyle) {
|
||||||
|
const auto paused = [customEmojiPaused] {
|
||||||
|
return customEmojiPaused && customEmojiPaused();
|
||||||
|
};
|
||||||
field->setTagMimeProcessor(
|
field->setTagMimeProcessor(
|
||||||
FieldTagMimeProcessor(session, allowPremiumEmoji));
|
FieldTagMimeProcessor(session, allowPremiumEmoji));
|
||||||
field->setCustomTextContext([=](Fn<void()> repaint) {
|
field->setCustomTextContext([=](Fn<void()> repaint) {
|
||||||
|
@ -399,10 +402,10 @@ void InitMessageFieldHandlers(
|
||||||
.session = session,
|
.session = session,
|
||||||
.customEmojiRepaint = std::move(repaint),
|
.customEmojiRepaint = std::move(repaint),
|
||||||
});
|
});
|
||||||
}, [customEmojiPaused] {
|
}, [paused] {
|
||||||
return On(PowerSaving::kEmojiChat) || customEmojiPaused();
|
return On(PowerSaving::kEmojiChat) || paused();
|
||||||
}, [customEmojiPaused] {
|
}, [paused] {
|
||||||
return On(PowerSaving::kChatSpoiler) || customEmojiPaused();
|
return On(PowerSaving::kChatSpoiler) || paused();
|
||||||
});
|
});
|
||||||
field->setInstantReplaces(Ui::InstantReplaces::Default());
|
field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||||
field->setInstantReplacesEnabled(
|
field->setInstantReplacesEnabled(
|
||||||
|
@ -525,7 +528,7 @@ void InitMessageFieldGeometry(not_null<Ui::InputField*> field) {
|
||||||
st::historySendSize.height() - 2 * st::historySendPadding);
|
st::historySendSize.height() - 2 * st::historySendPadding);
|
||||||
field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
field->setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||||
|
|
||||||
field->document()->setDocumentMargin(4.);
|
field->setDocumentMargin(4.);
|
||||||
field->setAdditionalMargin(style::ConvertScale(4) - 4);
|
field->setAdditionalMargin(style::ConvertScale(4) - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
@ -21,6 +22,16 @@ GiftBoxPack::GiftBoxPack(not_null<Main::Session*> session)
|
||||||
|
|
||||||
GiftBoxPack::~GiftBoxPack() = default;
|
GiftBoxPack::~GiftBoxPack() = default;
|
||||||
|
|
||||||
|
int GiftBoxPack::monthsForStars(int stars) const {
|
||||||
|
if (stars <= 1000) {
|
||||||
|
return 3;
|
||||||
|
} else if (stars < 2500) {
|
||||||
|
return 6;
|
||||||
|
} else {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DocumentData *GiftBoxPack::lookup(int months) const {
|
DocumentData *GiftBoxPack::lookup(int months) const {
|
||||||
const auto it = ranges::lower_bound(_localMonths, months);
|
const auto it = ranges::lower_bound(_localMonths, months);
|
||||||
const auto fallback = _documents.empty() ? nullptr : _documents[0];
|
const auto fallback = _documents.empty() ? nullptr : _documents[0];
|
||||||
|
@ -38,6 +49,10 @@ DocumentData *GiftBoxPack::lookup(int months) const {
|
||||||
return (index >= _documents.size()) ? fallback : _documents[index];
|
return (index >= _documents.size()) ? fallback : _documents[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::FileOrigin GiftBoxPack::origin() const {
|
||||||
|
return Data::FileOriginStickerSet(_setId, _accessHash);
|
||||||
|
}
|
||||||
|
|
||||||
void GiftBoxPack::load() {
|
void GiftBoxPack::load() {
|
||||||
if (_requestId || !_documents.empty()) {
|
if (_requestId || !_documents.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -59,6 +74,7 @@ void GiftBoxPack::load() {
|
||||||
|
|
||||||
void GiftBoxPack::applySet(const MTPDmessages_stickerSet &data) {
|
void GiftBoxPack::applySet(const MTPDmessages_stickerSet &data) {
|
||||||
_setId = data.vset().data().vid().v;
|
_setId = data.vset().data().vid().v;
|
||||||
|
_accessHash = data.vset().data().vaccess_hash().v;
|
||||||
auto documents = base::flat_map<DocumentId, not_null<DocumentData*>>();
|
auto documents = base::flat_map<DocumentId, not_null<DocumentData*>>();
|
||||||
for (const auto &sticker : data.vdocuments().v) {
|
for (const auto &sticker : data.vdocuments().v) {
|
||||||
const auto document = _session->data().processDocument(sticker);
|
const auto document = _session->data().processDocument(sticker);
|
||||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
class DocumentData;
|
class DocumentData;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct FileOrigin;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -21,7 +25,9 @@ public:
|
||||||
~GiftBoxPack();
|
~GiftBoxPack();
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
|
[[nodiscard]] int monthsForStars(int stars) const;
|
||||||
[[nodiscard]] DocumentData *lookup(int months) const;
|
[[nodiscard]] DocumentData *lookup(int months) const;
|
||||||
|
[[nodiscard]] Data::FileOrigin origin() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SetId = uint64;
|
using SetId = uint64;
|
||||||
|
@ -32,6 +38,7 @@ private:
|
||||||
|
|
||||||
std::vector<DocumentData*> _documents;
|
std::vector<DocumentData*> _documents;
|
||||||
SetId _setId = 0;
|
SetId _setId = 0;
|
||||||
|
uint64 _accessHash = 0;
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "chat_helpers/stickers_list_widget.h"
|
#include "chat_helpers/stickers_list_widget.h"
|
||||||
|
|
||||||
|
#include "base/timer_rpl.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
|
@ -939,6 +940,9 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||||
if (sets.empty() && _section == Section::Search) {
|
if (sets.empty() && _section == Section::Search) {
|
||||||
paintEmptySearchResults(p);
|
paintEmptySearchResults(p);
|
||||||
}
|
}
|
||||||
|
const auto badgeText = tr::lng_stickers_creator_badge(tr::now);
|
||||||
|
const auto &badgeFont = st::stickersHeaderBadgeFont;
|
||||||
|
const auto badgeWidth = badgeFont->width(badgeText);
|
||||||
enumerateSections([&](const SectionInfo &info) {
|
enumerateSections([&](const SectionInfo &info) {
|
||||||
if (clip.top() >= info.rowsBottom) {
|
if (clip.top() >= info.rowsBottom) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1057,6 +1061,12 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||||
|
|
||||||
widthForTitle -= remove.width();
|
widthForTitle -= remove.width();
|
||||||
}
|
}
|
||||||
|
const auto amCreator = (set.flags & Data::StickersSetFlag::AmCreator);
|
||||||
|
if (amCreator) {
|
||||||
|
widthForTitle -= badgeWidth
|
||||||
|
+ st::stickersFeaturedUnreadSkip
|
||||||
|
+ st::stickersHeaderBadgeFontSkip;
|
||||||
|
}
|
||||||
if (titleWidth > widthForTitle) {
|
if (titleWidth > widthForTitle) {
|
||||||
titleText = st::stickersTrendingHeaderFont->elided(titleText, widthForTitle);
|
titleText = st::stickersTrendingHeaderFont->elided(titleText, widthForTitle);
|
||||||
titleWidth = st::stickersTrendingHeaderFont->width(titleText);
|
titleWidth = st::stickersTrendingHeaderFont->width(titleText);
|
||||||
|
@ -1064,6 +1074,39 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||||
p.setFont(st::emojiPanHeaderFont);
|
p.setFont(st::emojiPanHeaderFont);
|
||||||
p.setPen(st().headerFg);
|
p.setPen(st().headerFg);
|
||||||
p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st().headerTop, width(), titleText, titleWidth);
|
p.drawTextLeft(st().headerLeft - st().margin.left(), info.top + st().headerTop, width(), titleText, titleWidth);
|
||||||
|
if (amCreator) {
|
||||||
|
const auto badgeLeft = st().headerLeft
|
||||||
|
- st().margin.left()
|
||||||
|
+ titleWidth
|
||||||
|
+ st::stickersFeaturedUnreadSkip;
|
||||||
|
{
|
||||||
|
auto color = st().headerFg->c;
|
||||||
|
color.setAlphaF(st().headerFg->c.alphaF() * 0.15);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(color);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.drawRoundedRect(
|
||||||
|
style::rtlrect(
|
||||||
|
badgeLeft,
|
||||||
|
info.top + st::stickersHeaderBadgeFontTop,
|
||||||
|
badgeWidth + badgeFont->height,
|
||||||
|
badgeFont->height,
|
||||||
|
width()),
|
||||||
|
badgeFont->height / 2.,
|
||||||
|
badgeFont->height / 2.);
|
||||||
|
}
|
||||||
|
p.setPen(st().headerFg);
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
p.setFont(badgeFont);
|
||||||
|
p.drawText(
|
||||||
|
QRect(
|
||||||
|
badgeLeft + badgeFont->height / 2,
|
||||||
|
info.top + st::stickersHeaderBadgeFontTop,
|
||||||
|
badgeWidth,
|
||||||
|
badgeFont->height),
|
||||||
|
badgeText,
|
||||||
|
style::al_center);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (clip.top() + clip.height() <= info.rowsTop) {
|
if (clip.top() + clip.height() <= info.rowsTop) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1694,12 +1737,32 @@ QPoint StickersListWidget::buttonRippleTopLeft(int section) const {
|
||||||
+ st().removeSet.rippleAreaPosition;
|
+ st().removeSet.rippleAreaPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersListWidget::showStickerSetBox(not_null<DocumentData*> document) {
|
void StickersListWidget::showStickerSetBox(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
uint64 setId) {
|
||||||
if (document->sticker() && document->sticker()->set) {
|
if (document->sticker() && document->sticker()->set) {
|
||||||
checkHideWithBox(Box<StickerSetBox>(
|
checkHideWithBox(Box<StickerSetBox>(
|
||||||
_show,
|
_show,
|
||||||
document->sticker()->set,
|
document->sticker()->set,
|
||||||
document->sticker()->setType));
|
document->sticker()->setType));
|
||||||
|
} else if ((setId == Data::Stickers::FavedSetId)
|
||||||
|
|| (setId == Data::Stickers::RecentSetId)) {
|
||||||
|
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
constexpr auto kTimeout = 10000;
|
||||||
|
rpl::merge(
|
||||||
|
base::timer_once(kTimeout),
|
||||||
|
document->owner().stickers().updated(
|
||||||
|
Data::StickersType::Stickers)
|
||||||
|
) | rpl::start_with_next([=, weak = Ui::MakeWeak(this)] {
|
||||||
|
if (weak.data()) {
|
||||||
|
showStickerSetBox(document, setId);
|
||||||
|
}
|
||||||
|
lifetime->destroy();
|
||||||
|
}, *lifetime);
|
||||||
|
document->owner().session().api().requestSpecialStickersForce(
|
||||||
|
setId == Data::Stickers::FavedSetId,
|
||||||
|
setId == Data::Stickers::RecentSetId,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1756,8 +1819,8 @@ base::unique_qptr<Ui::PopupMenu> StickersListWidget::fillContextMenu(
|
||||||
isFaved ? &icons->menuUnfave : &icons->menuFave);
|
isFaved ? &icons->menuUnfave : &icons->menuFave);
|
||||||
|
|
||||||
if (_features.openStickerSets) {
|
if (_features.openStickerSets) {
|
||||||
menu->addAction(tr::lng_context_pack_info(tr::now), [=] {
|
menu->addAction(tr::lng_context_pack_info(tr::now), [=, id = set.id] {
|
||||||
showStickerSetBox(document);
|
showStickerSetBox(document, id);
|
||||||
}, &icons->menuStickerSet);
|
}, &icons->menuStickerSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1827,7 +1890,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
const auto document = set.stickers[sticker->index].document;
|
const auto document = set.stickers[sticker->index].document;
|
||||||
if (_features.openStickerSets
|
if (_features.openStickerSets
|
||||||
&& (e->modifiers() & Qt::ControlModifier)) {
|
&& (e->modifiers() & Qt::ControlModifier)) {
|
||||||
showStickerSetBox(document);
|
showStickerSetBox(document, set.id);
|
||||||
} else {
|
} else {
|
||||||
auto settings = &AyuSettings::getInstance();
|
auto settings = &AyuSettings::getInstance();
|
||||||
auto from = messageSentAnimationInfo(
|
auto from = messageSentAnimationInfo(
|
||||||
|
|
|
@ -350,7 +350,9 @@ private:
|
||||||
void refreshFooterIcons();
|
void refreshFooterIcons();
|
||||||
void refreshIcons(ValidateIconAnimations animations);
|
void refreshIcons(ValidateIconAnimations animations);
|
||||||
|
|
||||||
void showStickerSetBox(not_null<DocumentData*> document);
|
void showStickerSetBox(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
uint64 setId);
|
||||||
|
|
||||||
void cancelSetsSearch();
|
void cancelSetsSearch();
|
||||||
void showSearchResults();
|
void showSearchResults();
|
||||||
|
|
|
@ -192,8 +192,11 @@ Application::Application()
|
||||||
_platformIntegration->init();
|
_platformIntegration->init();
|
||||||
|
|
||||||
passcodeLockChanges(
|
passcodeLockChanges(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=](bool locked) {
|
||||||
_shouldLockAt = 0;
|
_shouldLockAt = 0;
|
||||||
|
if (locked) {
|
||||||
|
closeAdditionalWindows();
|
||||||
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
passcodeLockChanges(
|
passcodeLockChanges(
|
||||||
|
@ -215,6 +218,16 @@ Application::Application()
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::closeAdditionalWindows() {
|
||||||
|
Payments::CheckoutProcess::ClearAll();
|
||||||
|
for (const auto &[index, account] : _domain->accounts()) {
|
||||||
|
if (account->sessionExists()) {
|
||||||
|
account->session().attachWebView().closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_iv->closeAll();
|
||||||
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
if (_saveSettingsTimer && _saveSettingsTimer->isActive()) {
|
if (_saveSettingsTimer && _saveSettingsTimer->isActive()) {
|
||||||
Local::writeSettings();
|
Local::writeSettings();
|
||||||
|
@ -234,9 +247,7 @@ Application::~Application() {
|
||||||
//
|
//
|
||||||
// For example Domain::removeRedundantAccounts() is called from
|
// For example Domain::removeRedundantAccounts() is called from
|
||||||
// Domain::finish() and there is a violation on Ensures(started()).
|
// Domain::finish() and there is a violation on Ensures(started()).
|
||||||
Payments::CheckoutProcess::ClearAll();
|
closeAdditionalWindows();
|
||||||
InlineBots::AttachWebView::ClearAll();
|
|
||||||
_iv->closeAll();
|
|
||||||
|
|
||||||
_domain->finish();
|
_domain->finish();
|
||||||
|
|
||||||
|
@ -274,14 +285,9 @@ void Application::run() {
|
||||||
|
|
||||||
refreshGlobalProxy(); // Depends on app settings being read.
|
refreshGlobalProxy(); // Depends on app settings being read.
|
||||||
|
|
||||||
if (const auto old = Local::oldSettingsVersion()) {
|
if (const auto old = Local::oldSettingsVersion(); old < AppVersion) {
|
||||||
if (old < AppVersion) {
|
|
||||||
autoRegisterUrlScheme();
|
|
||||||
Platform::NewVersionLaunched(old);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Initial launch.
|
|
||||||
autoRegisterUrlScheme();
|
autoRegisterUrlScheme();
|
||||||
|
Platform::NewVersionLaunched(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cAutoStart() && !Platform::AutostartSupported()) {
|
if (cAutoStart() && !Platform::AutostartSupported()) {
|
||||||
|
@ -692,7 +698,8 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||||
if (const auto file = event->file(); !file.isEmpty()) {
|
if (const auto file = event->file(); !file.isEmpty()) {
|
||||||
_filesToOpen.append(file);
|
_filesToOpen.append(file);
|
||||||
_fileOpenTimer.callOnce(kFileOpenTimeoutMs);
|
_fileOpenTimer.callOnce(kFileOpenTimeoutMs);
|
||||||
} else if (event->url().scheme() == u"tg"_q) {
|
} else if (event->url().scheme() == u"tg"_q
|
||||||
|
|| event->url().scheme() == u"tonsite"_q) {
|
||||||
const auto url = QString::fromUtf8(
|
const auto url = QString::fromUtf8(
|
||||||
event->url().toEncoded().trimmed());
|
event->url().toEncoded().trimmed());
|
||||||
cSetStartUrl(url.mid(0, 8192));
|
cSetStartUrl(url.mid(0, 8192));
|
||||||
|
@ -1093,13 +1100,18 @@ void Application::checkSendPaths() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::checkStartUrl() {
|
void Application::checkStartUrl() {
|
||||||
if (!cStartUrl().isEmpty()
|
if (!cStartUrl().isEmpty()) {
|
||||||
&& _lastActivePrimaryWindow
|
|
||||||
&& !_lastActivePrimaryWindow->locked()) {
|
|
||||||
const auto url = cStartUrl();
|
const auto url = cStartUrl();
|
||||||
cSetStartUrl(QString());
|
if (!Core::App().passcodeLocked()) {
|
||||||
if (!openLocalUrl(url, {})) {
|
if (url.startsWith("tonsite://", Qt::CaseInsensitive)) {
|
||||||
cSetStartUrl(url);
|
cSetStartUrl(QString());
|
||||||
|
iv().showTonSite(url, {});
|
||||||
|
} else if (_lastActivePrimaryWindow) {
|
||||||
|
cSetStartUrl(QString());
|
||||||
|
if (!openLocalUrl(url, {})) {
|
||||||
|
cSetStartUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1350,7 +1362,7 @@ Window::Controller *Application::ensureSeparateWindowFor(
|
||||||
Window::Controller *Application::windowFor(Window::SeparateId id) const {
|
Window::Controller *Application::windowFor(Window::SeparateId id) const {
|
||||||
if (const auto separate = separateWindowFor(id)) {
|
if (const auto separate = separateWindowFor(id)) {
|
||||||
return separate;
|
return separate;
|
||||||
} else if (id && id.primary()) {
|
} else if (id && !id.primary()) {
|
||||||
return windowFor(not_null(id.account));
|
return windowFor(not_null(id.account));
|
||||||
}
|
}
|
||||||
return activePrimaryWindow();
|
return activePrimaryWindow();
|
||||||
|
@ -1807,11 +1819,13 @@ void Application::startShortcuts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::RegisterUrlScheme() {
|
void Application::RegisterUrlScheme() {
|
||||||
|
const auto arguments = Launcher::Instance().customWorkingDir()
|
||||||
|
? u"-workdir \"%1\""_q.arg(cWorkingDir())
|
||||||
|
: QString();
|
||||||
|
|
||||||
base::Platform::RegisterUrlScheme(base::Platform::UrlSchemeDescriptor{
|
base::Platform::RegisterUrlScheme(base::Platform::UrlSchemeDescriptor{
|
||||||
.executable = Platform::ExecutablePathForShortcuts(),
|
.executable = Platform::ExecutablePathForShortcuts(),
|
||||||
.arguments = Launcher::Instance().customWorkingDir()
|
.arguments = arguments,
|
||||||
? u"-workdir \"%1\""_q.arg(cWorkingDir())
|
|
||||||
: QString(),
|
|
||||||
.protocol = u"tg"_q,
|
.protocol = u"tg"_q,
|
||||||
.protocolName = u"Telegram Link"_q,
|
.protocolName = u"Telegram Link"_q,
|
||||||
.shortAppName = u"AyuGram"_q,
|
.shortAppName = u"AyuGram"_q,
|
||||||
|
@ -1819,6 +1833,17 @@ void Application::RegisterUrlScheme() {
|
||||||
.displayAppName = AppName.utf16(),
|
.displayAppName = AppName.utf16(),
|
||||||
.displayAppDescription = AppName.utf16(),
|
.displayAppDescription = AppName.utf16(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
base::Platform::RegisterUrlScheme(base::Platform::UrlSchemeDescriptor{
|
||||||
|
.executable = Platform::ExecutablePathForShortcuts(),
|
||||||
|
.arguments = arguments,
|
||||||
|
.protocol = u"tonsite"_q,
|
||||||
|
.protocolName = u"TonSite Link"_q,
|
||||||
|
.shortAppName = u"tdesktop"_q,
|
||||||
|
.longAppName = QCoreApplication::applicationName(),
|
||||||
|
.displayAppName = AppName.utf16(),
|
||||||
|
.displayAppDescription = AppName.utf16(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsAppLaunched() {
|
bool IsAppLaunched() {
|
||||||
|
|
|
@ -379,6 +379,7 @@ private:
|
||||||
|
|
||||||
void showOpenGLCrashNotification();
|
void showOpenGLCrashNotification();
|
||||||
void clearPasscodeLock();
|
void clearPasscodeLock();
|
||||||
|
void closeAdditionalWindows();
|
||||||
|
|
||||||
bool openCustomUrl(
|
bool openCustomUrl(
|
||||||
const QString &protocol,
|
const QString &protocol,
|
||||||
|
|
|
@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "inline_bots/bot_attach_web_view.h"
|
||||||
|
#include "data/data_game.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
@ -120,7 +122,9 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
|
||||||
return result;
|
return result;
|
||||||
}()));
|
}()));
|
||||||
} else {
|
} else {
|
||||||
const auto parsedUrl = QUrl::fromUserInput(url);
|
const auto parsedUrl = url.startsWith(u"tonsite://"_q)
|
||||||
|
? QUrl(url)
|
||||||
|
: QUrl::fromUserInput(url);
|
||||||
if (UrlRequiresConfirmation(parsedUrl) && !base::IsCtrlPressed()) {
|
if (UrlRequiresConfirmation(parsedUrl) && !base::IsCtrlPressed()) {
|
||||||
const auto my = context.value<ClickHandlerContext>();
|
const auto my = context.value<ClickHandlerContext>();
|
||||||
if (!my.show) {
|
if (!my.show) {
|
||||||
|
@ -171,23 +175,42 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
|
||||||
if (Core::InternalPassportLink(url)) {
|
if (Core::InternalPassportLink(url)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto openLink = [=] {
|
||||||
const auto open = [=] {
|
|
||||||
UrlClickHandler::Open(url, context.other);
|
UrlClickHandler::Open(url, context.other);
|
||||||
};
|
};
|
||||||
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive)) {
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
open();
|
const auto weakController = my.sessionWindow;
|
||||||
} else if (!_bot
|
const auto controller = weakController.get();
|
||||||
|| _bot->isVerified()
|
const auto item = controller
|
||||||
|
? controller->session().data().message(my.itemId)
|
||||||
|
: nullptr;
|
||||||
|
const auto media = item ? item->media() : nullptr;
|
||||||
|
const auto game = media ? media->game() : nullptr;
|
||||||
|
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive) || !_bot || !game) {
|
||||||
|
openLink();
|
||||||
|
}
|
||||||
|
const auto bot = _bot;
|
||||||
|
const auto title = game->title;
|
||||||
|
const auto itemId = my.itemId;
|
||||||
|
const auto openGame = [=] {
|
||||||
|
bot->session().attachWebView().open({
|
||||||
|
.bot = bot,
|
||||||
|
.button = {.url = url.toUtf8() },
|
||||||
|
.source = InlineBots::WebViewSourceGame{
|
||||||
|
.messageId = itemId,
|
||||||
|
.title = title,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (_bot->isVerified()
|
||||||
|| _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
|
|| _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
|
||||||
open();
|
openGame();
|
||||||
} else {
|
} else {
|
||||||
const auto my = context.other.value<ClickHandlerContext>();
|
|
||||||
if (const auto controller = my.sessionWindow.get()) {
|
if (const auto controller = my.sessionWindow.get()) {
|
||||||
const auto callback = [=, bot = _bot](Fn<void()> close) {
|
const auto callback = [=, bot = _bot](Fn<void()> close) {
|
||||||
close();
|
close();
|
||||||
bot->session().local().markBotTrustedOpenGame(bot->id);
|
bot->session().local().markBotTrustedOpenGame(bot->id);
|
||||||
open();
|
openGame();
|
||||||
};
|
};
|
||||||
controller->show(Ui::MakeConfirmBox({
|
controller->show(Ui::MakeConfirmBox({
|
||||||
.text = tr::lng_allow_bot_pass(
|
.text = tr::lng_allow_bot_pass(
|
||||||
|
|
|
@ -21,6 +21,10 @@ namespace Ui {
|
||||||
class Show;
|
class Show;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace InlineBots {
|
||||||
|
struct WebViewContext;
|
||||||
|
} // namespace InlineBots
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -38,10 +42,10 @@ class SessionController;
|
||||||
class PeerData;
|
class PeerData;
|
||||||
struct ClickHandlerContext {
|
struct ClickHandlerContext {
|
||||||
FullMsgId itemId;
|
FullMsgId itemId;
|
||||||
QString attachBotWebviewUrl;
|
|
||||||
// Is filled from sections.
|
// Is filled from sections.
|
||||||
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
||||||
base::weak_ptr<Window::SessionController> sessionWindow;
|
base::weak_ptr<Window::SessionController> sessionWindow;
|
||||||
|
std::shared_ptr<InlineBots::WebViewContext> botWebviewContext;
|
||||||
std::shared_ptr<Ui::Show> show;
|
std::shared_ptr<Ui::Show> show;
|
||||||
bool mayShowConfirmation = false;
|
bool mayShowConfirmation = false;
|
||||||
bool skipBotAutoLogin = false;
|
bool skipBotAutoLogin = false;
|
||||||
|
|
|
@ -224,7 +224,8 @@ QByteArray Settings::serialize() const {
|
||||||
+ Serialize::bytearraySize(ivPosition)
|
+ Serialize::bytearraySize(ivPosition)
|
||||||
+ Serialize::stringSize(noWarningExtensions)
|
+ Serialize::stringSize(noWarningExtensions)
|
||||||
+ Serialize::stringSize(_customFontFamily)
|
+ Serialize::stringSize(_customFontFamily)
|
||||||
+ sizeof(qint32) * 2;
|
+ sizeof(qint32) * 3
|
||||||
|
+ Serialize::bytearraySize(_tonsiteStorageToken);
|
||||||
|
|
||||||
auto result = QByteArray();
|
auto result = QByteArray();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
|
@ -376,7 +377,9 @@ QByteArray Settings::serialize() const {
|
||||||
qRound(_dialogsNoChatWidthRatio.current() * 1000000),
|
qRound(_dialogsNoChatWidthRatio.current() * 1000000),
|
||||||
0,
|
0,
|
||||||
1000000))
|
1000000))
|
||||||
<< qint32(_systemUnlockEnabled ? 1 : 0);
|
<< qint32(_systemUnlockEnabled ? 1 : 0)
|
||||||
|
<< qint32(!_weatherInCelsius ? 0 : *_weatherInCelsius ? 1 : 2)
|
||||||
|
<< _tonsiteStorageToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ensures(result.size() == size);
|
Ensures(result.size() == size);
|
||||||
|
@ -499,6 +502,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
QByteArray ivPosition;
|
QByteArray ivPosition;
|
||||||
QString customFontFamily = _customFontFamily;
|
QString customFontFamily = _customFontFamily;
|
||||||
qint32 systemUnlockEnabled = _systemUnlockEnabled ? 1 : 0;
|
qint32 systemUnlockEnabled = _systemUnlockEnabled ? 1 : 0;
|
||||||
|
qint32 weatherInCelsius = !_weatherInCelsius ? 0 : *_weatherInCelsius ? 1 : 2;
|
||||||
|
QByteArray tonsiteStorageToken = _tonsiteStorageToken;
|
||||||
|
|
||||||
stream >> themesAccentColors;
|
stream >> themesAccentColors;
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
|
@ -799,6 +804,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
stream >> systemUnlockEnabled;
|
stream >> systemUnlockEnabled;
|
||||||
}
|
}
|
||||||
|
if (!stream.atEnd()) {
|
||||||
|
stream >> weatherInCelsius;
|
||||||
|
}
|
||||||
|
if (!stream.atEnd()) {
|
||||||
|
stream >> tonsiteStorageToken;
|
||||||
|
}
|
||||||
if (stream.status() != QDataStream::Ok) {
|
if (stream.status() != QDataStream::Ok) {
|
||||||
LOG(("App Error: "
|
LOG(("App Error: "
|
||||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||||
|
@ -1008,6 +1019,10 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
}
|
}
|
||||||
_customFontFamily = customFontFamily;
|
_customFontFamily = customFontFamily;
|
||||||
_systemUnlockEnabled = (systemUnlockEnabled == 1);
|
_systemUnlockEnabled = (systemUnlockEnabled == 1);
|
||||||
|
_weatherInCelsius = !weatherInCelsius
|
||||||
|
? std::optional<bool>()
|
||||||
|
: (weatherInCelsius == 1);
|
||||||
|
_tonsiteStorageToken = tonsiteStorageToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Settings::getSoundPath(const QString &key) const {
|
QString Settings::getSoundPath(const QString &key) const {
|
||||||
|
|
|
@ -897,6 +897,20 @@ public:
|
||||||
_systemUnlockEnabled = enabled;
|
_systemUnlockEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<bool> weatherInCelsius() const {
|
||||||
|
return _weatherInCelsius;
|
||||||
|
}
|
||||||
|
void setWeatherInCelsius(bool value) {
|
||||||
|
_weatherInCelsius = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QByteArray tonsiteStorageToken() const {
|
||||||
|
return _tonsiteStorageToken;
|
||||||
|
}
|
||||||
|
void setTonsiteStorageToken(const QByteArray &value) {
|
||||||
|
_tonsiteStorageToken = value;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||||
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
||||||
|
|
||||||
|
@ -1028,6 +1042,8 @@ private:
|
||||||
WindowPosition _ivPosition;
|
WindowPosition _ivPosition;
|
||||||
QString _customFontFamily;
|
QString _customFontFamily;
|
||||||
bool _systemUnlockEnabled = false;
|
bool _systemUnlockEnabled = false;
|
||||||
|
std::optional<bool> _weatherInCelsius;
|
||||||
|
QByteArray _tonsiteStorageToken;
|
||||||
|
|
||||||
bool _tabbedReplacedWithInfo = false; // per-window
|
bool _tabbedReplacedWithInfo = false; // per-window
|
||||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||||
|
|
|
@ -296,13 +296,17 @@ bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context,
|
||||||
|
|
||||||
QString PlatformString() {
|
QString PlatformString() {
|
||||||
if (Platform::IsWindowsStoreBuild()) {
|
if (Platform::IsWindowsStoreBuild()) {
|
||||||
return Platform::IsWindows64Bit()
|
return Platform::IsWindowsARM64()
|
||||||
|
? u"WinStoreARM64"_q
|
||||||
|
: Platform::IsWindows64Bit()
|
||||||
? u"WinStore64Bit"_q
|
? u"WinStore64Bit"_q
|
||||||
: u"WinStore32Bit"_q;
|
: u"WinStore32Bit"_q;
|
||||||
} else if (Platform::IsWindows32Bit()) {
|
} else if (Platform::IsWindows32Bit()) {
|
||||||
return u"Windows32Bit"_q;
|
return u"Windows32Bit"_q;
|
||||||
} else if (Platform::IsWindows64Bit()) {
|
} else if (Platform::IsWindows64Bit()) {
|
||||||
return u"Windows64Bit"_q;
|
return u"Windows64Bit"_q;
|
||||||
|
} else if (Platform::IsWindowsARM64()) {
|
||||||
|
return u"WindowsARM64"_q;
|
||||||
} else if (Platform::IsMacStoreBuild()) {
|
} else if (Platform::IsMacStoreBuild()) {
|
||||||
return u"MacAppStore"_q;
|
return u"MacAppStore"_q;
|
||||||
} else if (Platform::IsMac()) {
|
} else if (Platform::IsMac()) {
|
||||||
|
|
243
Telegram/SourceFiles/core/current_geo_location.cpp
Normal file
243
Telegram/SourceFiles/core/current_geo_location.cpp
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
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 "core/current_geo_location.h"
|
||||||
|
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
|
#include "base/invoke_queued.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
#include "data/raw/raw_countries_bounds.h"
|
||||||
|
#include "platform/platform_current_geo_location.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
|
|
||||||
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
#include <QtCore/QJsonDocument>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kDestroyManagerTimeout = 20 * crl::time(1000);
|
||||||
|
|
||||||
|
[[nodiscard]] QString ChooseLanguage(const QString &language) {
|
||||||
|
// https://docs.mapbox.com/api/search/geocoding#language-coverage
|
||||||
|
auto result = language.toLower().replace('-', '_');
|
||||||
|
static const auto kGood = std::array{
|
||||||
|
// Global coverage.
|
||||||
|
u"de"_q, u"en"_q, u"es"_q, u"fr"_q, u"it"_q, u"nl"_q, u"pl"_q,
|
||||||
|
|
||||||
|
// Local coverage.
|
||||||
|
u"az"_q, u"bn"_q, u"ca"_q, u"cs"_q, u"da"_q, u"el"_q, u"fa"_q,
|
||||||
|
u"fi"_q, u"ga"_q, u"hu"_q, u"id"_q, u"is"_q, u"ja"_q, u"ka"_q,
|
||||||
|
u"km"_q, u"ko"_q, u"lt"_q, u"lv"_q, u"mn"_q, u"pt"_q, u"ro"_q,
|
||||||
|
u"sk"_q, u"sq"_q, u"sv"_q, u"th"_q, u"tl"_q, u"uk"_q, u"vi"_q,
|
||||||
|
u"zh"_q, u"zh_Hans"_q, u"zh_TW"_q,
|
||||||
|
|
||||||
|
// Limited coverage.
|
||||||
|
u"ar"_q, u"bs"_q, u"gu"_q, u"he"_q, u"hi"_q, u"kk"_q, u"lo"_q,
|
||||||
|
u"my"_q, u"nb"_q, u"ru"_q, u"sr"_q, u"te"_q, u"tk"_q, u"tr"_q,
|
||||||
|
u"zh_Hant"_q,
|
||||||
|
};
|
||||||
|
for (const auto &known : kGood) {
|
||||||
|
if (known.toLower() == result) {
|
||||||
|
return known;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto delimeter = result.indexOf('_'); delimeter > 0) {
|
||||||
|
result = result.mid(0, delimeter);
|
||||||
|
for (const auto &known : kGood) {
|
||||||
|
if (known == result) {
|
||||||
|
return known;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u"en"_q;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolveLocationAddressGeneric(
|
||||||
|
const GeoLocation &location,
|
||||||
|
const QString &language,
|
||||||
|
const QString &token,
|
||||||
|
Fn<void(GeoAddress)> callback) {
|
||||||
|
const auto partialUrl = u"https://api.mapbox.com/search/geocode/v6"
|
||||||
|
"/reverse?longitude=%1&latitude=%2&language=%3&access_token=%4"_q
|
||||||
|
.arg(location.point.y())
|
||||||
|
.arg(location.point.x())
|
||||||
|
.arg(ChooseLanguage(language));
|
||||||
|
static auto Cache = base::flat_map<QString, GeoAddress>();
|
||||||
|
const auto i = Cache.find(partialUrl);
|
||||||
|
if (i != end(Cache)) {
|
||||||
|
callback(i->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto finishWith = [=](GeoAddress result) {
|
||||||
|
Cache[partialUrl] = result;
|
||||||
|
callback(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State final : QObject {
|
||||||
|
explicit State(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, manager(this)
|
||||||
|
, destroyer([=] { if (sent.empty()) delete this; }) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkAccessManager manager;
|
||||||
|
std::vector<QPointer<QNetworkReply>> sent;
|
||||||
|
base::Timer destroyer;
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto state = QPointer<State>();
|
||||||
|
if (!state) {
|
||||||
|
state = Ui::CreateChild<State>(qApp);
|
||||||
|
}
|
||||||
|
const auto destroyReplyDelayed = [](QNetworkReply *reply) {
|
||||||
|
InvokeQueued(reply, [=] {
|
||||||
|
for (auto i = begin(state->sent); i != end(state->sent);) {
|
||||||
|
if (!*i || *i == reply) {
|
||||||
|
i = state->sent.erase(i);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete reply;
|
||||||
|
if (state->sent.empty()) {
|
||||||
|
state->destroyer.callOnce(kDestroyManagerTimeout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto request = QNetworkRequest(partialUrl.arg(token));
|
||||||
|
request.setRawHeader("Referer", "http://desktop-app-resource/");
|
||||||
|
|
||||||
|
const auto reply = state->manager.get(request);
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
destroyReplyDelayed(reply);
|
||||||
|
|
||||||
|
const auto json = QJsonDocument::fromJson(reply->readAll());
|
||||||
|
if (!json.isObject()) {
|
||||||
|
finishWith({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto features = json["features"].toArray();
|
||||||
|
if (features.isEmpty()) {
|
||||||
|
finishWith({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto feature = features.at(0).toObject();
|
||||||
|
const auto properties = feature["properties"].toObject();
|
||||||
|
const auto context = properties["context"].toObject();
|
||||||
|
auto names = QStringList();
|
||||||
|
auto add = [&](std::vector<QString> keys) {
|
||||||
|
for (const auto &key : keys) {
|
||||||
|
const auto value = context[key];
|
||||||
|
if (value.isObject()) {
|
||||||
|
const auto name = value.toObject()["name"].toString();
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
names.push_back(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
add({ /*u"address"_q, u"street"_q, */u"neighborhood"_q });
|
||||||
|
add({ u"place"_q, u"region"_q });
|
||||||
|
add({ u"country"_q });
|
||||||
|
finishWith({ .name = names.join(", ") });
|
||||||
|
});
|
||||||
|
QObject::connect(reply, &QNetworkReply::errorOccurred, [=] {
|
||||||
|
destroyReplyDelayed(reply);
|
||||||
|
|
||||||
|
finishWith({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
GeoLocation ResolveCurrentCountryLocation() {
|
||||||
|
const auto iso2 = Platform::SystemCountry().toUpper();
|
||||||
|
const auto &bounds = Raw::CountryBounds();
|
||||||
|
const auto i = bounds.find(iso2);
|
||||||
|
if (i == end(bounds)) {
|
||||||
|
return {
|
||||||
|
.accuracy = GeoLocationAccuracy::Failed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.point = {
|
||||||
|
(i->second.minLat + i->second.maxLat) / 2.,
|
||||||
|
(i->second.minLon + i->second.maxLon) / 2.,
|
||||||
|
},
|
||||||
|
.bounds = {
|
||||||
|
i->second.minLat,
|
||||||
|
i->second.minLon,
|
||||||
|
i->second.maxLat - i->second.minLat,
|
||||||
|
i->second.maxLon - i->second.minLon,
|
||||||
|
},
|
||||||
|
.accuracy = GeoLocationAccuracy::Country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolveCurrentGeoLocation(Fn<void(GeoLocation)> callback) {
|
||||||
|
using namespace Platform;
|
||||||
|
return ResolveCurrentExactLocation([done = std::move(callback)](
|
||||||
|
GeoLocation result) {
|
||||||
|
done(result.accuracy != GeoLocationAccuracy::Failed
|
||||||
|
? result
|
||||||
|
: ResolveCurrentCountryLocation());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolveLocationAddress(
|
||||||
|
const GeoLocation &location,
|
||||||
|
const QString &language,
|
||||||
|
const QString &token,
|
||||||
|
Fn<void(GeoAddress)> callback) {
|
||||||
|
auto done = [=, done = std::move(callback)](GeoAddress result) mutable {
|
||||||
|
if (!result && !token.isEmpty()) {
|
||||||
|
ResolveLocationAddressGeneric(
|
||||||
|
location,
|
||||||
|
language,
|
||||||
|
token,
|
||||||
|
std::move(done));
|
||||||
|
} else {
|
||||||
|
done(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Platform::ResolveLocationAddress(location, language, std::move(done));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AreTheSame(const GeoLocation &a, const GeoLocation &b) {
|
||||||
|
if (a.accuracy != GeoLocationAccuracy::Exact
|
||||||
|
|| b.accuracy != GeoLocationAccuracy::Exact) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto normalize = [](float64 value) {
|
||||||
|
value = std::fmod(value + 180., 360.);
|
||||||
|
return (value + (value < 0. ? 360. : 0.)) - 180.;
|
||||||
|
};
|
||||||
|
constexpr auto kEpsilon = 0.0001;
|
||||||
|
const auto lon1 = normalize(a.point.y());
|
||||||
|
const auto lon2 = normalize(b.point.y());
|
||||||
|
const auto diffLat = std::abs(a.point.x() - b.point.x());
|
||||||
|
if (std::abs(a.point.x()) >= (90. - kEpsilon)
|
||||||
|
|| std::abs(b.point.x()) >= (90. - kEpsilon)) {
|
||||||
|
return diffLat <= kEpsilon;
|
||||||
|
}
|
||||||
|
auto diffLon = std::abs(lon1 - lon2);
|
||||||
|
if (diffLon > 180.) {
|
||||||
|
diffLon = 360. - diffLon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return diffLat <= kEpsilon && diffLon <= kEpsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
60
Telegram/SourceFiles/core/current_geo_location.h
Normal file
60
Telegram/SourceFiles/core/current_geo_location.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
enum class GeoLocationAccuracy : uchar {
|
||||||
|
Exact,
|
||||||
|
Country,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GeoLocation {
|
||||||
|
QPointF point;
|
||||||
|
QRectF bounds;
|
||||||
|
GeoLocationAccuracy accuracy = GeoLocationAccuracy::Failed;
|
||||||
|
|
||||||
|
[[nodiscard]] bool exact() const {
|
||||||
|
return accuracy == GeoLocationAccuracy::Exact;
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool country() const {
|
||||||
|
return accuracy == GeoLocationAccuracy::Country;
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool failed() const {
|
||||||
|
return accuracy == GeoLocationAccuracy::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !failed();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] bool AreTheSame(const GeoLocation &a, const GeoLocation &b);
|
||||||
|
|
||||||
|
struct GeoAddress {
|
||||||
|
QString name;
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
return name.isEmpty();
|
||||||
|
}
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] GeoLocation ResolveCurrentCountryLocation();
|
||||||
|
void ResolveCurrentGeoLocation(Fn<void(GeoLocation)> callback);
|
||||||
|
|
||||||
|
void ResolveLocationAddress(
|
||||||
|
const GeoLocation &location,
|
||||||
|
const QString &language,
|
||||||
|
const QString &token,
|
||||||
|
Fn<void(GeoAddress)> callback);
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -331,21 +331,6 @@ bool ConfirmPhone(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShareGameScore(
|
|
||||||
Window::SessionController *controller,
|
|
||||||
const Match &match,
|
|
||||||
const QVariant &context) {
|
|
||||||
if (!controller) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto params = url_parse_params(
|
|
||||||
match->captured(1),
|
|
||||||
qthelp::UrlParamNameTransform::ToLower);
|
|
||||||
ShareGameScoreByHash(controller, params.value(u"hash"_q));
|
|
||||||
controller->window().activate();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ApplySocksProxy(
|
bool ApplySocksProxy(
|
||||||
Window::SessionController *controller,
|
Window::SessionController *controller,
|
||||||
const Match &match,
|
const Match &match,
|
||||||
|
@ -522,7 +507,9 @@ bool ResolveUsernameOrPhone(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
using ResolveType = Window::ResolveType;
|
using ResolveType = Window::ResolveType;
|
||||||
auto resolveType = ResolveType::Default;
|
auto resolveType = params.contains(u"profile"_q)
|
||||||
|
? ResolveType::Profile
|
||||||
|
: ResolveType::Default;
|
||||||
auto startToken = params.value(u"start"_q);
|
auto startToken = params.value(u"start"_q);
|
||||||
if (!startToken.isEmpty()) {
|
if (!startToken.isEmpty()) {
|
||||||
resolveType = ResolveType::BotStart;
|
resolveType = ResolveType::BotStart;
|
||||||
|
@ -592,8 +579,11 @@ bool ResolveUsernameOrPhone(
|
||||||
: (appname.isEmpty() && params.contains(u"startapp"_q))
|
: (appname.isEmpty() && params.contains(u"startapp"_q))
|
||||||
? params.value(u"startapp"_q)
|
? params.value(u"startapp"_q)
|
||||||
: std::optional<QString>()),
|
: std::optional<QString>()),
|
||||||
.attachBotMenuOpen = (appname.isEmpty()
|
.attachBotMainOpen = (appname.isEmpty()
|
||||||
&& params.contains(u"startapp"_q)),
|
&& params.contains(u"startapp"_q)),
|
||||||
|
.attachBotMainCompact = (appname.isEmpty()
|
||||||
|
&& params.contains(u"startapp"_q)
|
||||||
|
&& (params.value(u"mode"_q) == u"compact"_q)),
|
||||||
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
||||||
params.value(u"choose"_q)),
|
params.value(u"choose"_q)),
|
||||||
.voicechatHash = (params.contains(u"livestream"_q)
|
.voicechatHash = (params.contains(u"livestream"_q)
|
||||||
|
@ -604,7 +594,7 @@ bool ResolveUsernameOrPhone(
|
||||||
? std::make_optional(params.value(u"voicechat"_q))
|
? std::make_optional(params.value(u"voicechat"_q))
|
||||||
: std::nullopt),
|
: std::nullopt),
|
||||||
.clickFromMessageId = myContext.itemId,
|
.clickFromMessageId = myContext.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -645,7 +635,7 @@ bool ResolvePrivatePost(
|
||||||
}
|
}
|
||||||
: Window::RepliesByLinkInfo{ v::null },
|
: Window::RepliesByLinkInfo{ v::null },
|
||||||
.clickFromMessageId = my.itemId,
|
.clickFromMessageId = my.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = my.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = my.botWebviewContext,
|
||||||
});
|
});
|
||||||
controller->window().activate();
|
controller->window().activate();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1197,7 +1187,7 @@ bool ResolveChatLink(
|
||||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||||
.chatLinkSlug = match->captured(1),
|
.chatLinkSlug = match->captured(1),
|
||||||
.clickFromMessageId = myContext.itemId,
|
.clickFromMessageId = myContext.itemId,
|
||||||
.clickFromAttachBotWebviewUrl = myContext.attachBotWebviewUrl,
|
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1234,10 +1224,6 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||||
u"^confirmphone/?\\?(.+)(#|$)"_q,
|
u"^confirmphone/?\\?(.+)(#|$)"_q,
|
||||||
ConfirmPhone
|
ConfirmPhone
|
||||||
},
|
},
|
||||||
{
|
|
||||||
u"^share_game_score/?\\?(.+)(#|$)"_q,
|
|
||||||
ShareGameScore
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
u"^socks/?\\?(.+)(#|$)"_q,
|
u"^socks/?\\?(.+)(#|$)"_q,
|
||||||
ApplySocksProxy
|
ApplySocksProxy
|
||||||
|
@ -1291,7 +1277,7 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||||
ResolveBoost,
|
ResolveBoost,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
u"^message/?\\?slug=([a-zA-Z0-9\\.\\_]+)(&|$)"_q,
|
u"^message/?\\?slug=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"_q,
|
||||||
ResolveChatLink
|
ResolveChatLink
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1363,6 +1349,13 @@ QString TryConvertUrlToLocal(QString url) {
|
||||||
|
|
||||||
using namespace qthelp;
|
using namespace qthelp;
|
||||||
auto matchOptions = RegExOption::CaseInsensitive;
|
auto matchOptions = RegExOption::CaseInsensitive;
|
||||||
|
auto tonsiteMatch = (url.indexOf(u".ton") >= 0)
|
||||||
|
? regex_match(u"^(https?://)?[^/@:]+\\.ton($|/)"_q, url, matchOptions)
|
||||||
|
: RegularExpressionMatch(QRegularExpressionMatch());
|
||||||
|
if (tonsiteMatch) {
|
||||||
|
const auto protocol = tonsiteMatch->captured(1);
|
||||||
|
return u"tonsite://"_q + url.mid(protocol.size());
|
||||||
|
}
|
||||||
auto subdomainMatch = regex_match(u"^(https?://)?([a-zA-Z0-9\\_]+)\\.t\\.me(/\\d+)?/?(\\?.+)?"_q, url, matchOptions);
|
auto subdomainMatch = regex_match(u"^(https?://)?([a-zA-Z0-9\\_]+)\\.t\\.me(/\\d+)?/?(\\?.+)?"_q, url, matchOptions);
|
||||||
if (subdomainMatch) {
|
if (subdomainMatch) {
|
||||||
const auto name = subdomainMatch->captured(2);
|
const auto name = subdomainMatch->captured(2);
|
||||||
|
|
|
@ -242,6 +242,9 @@ bool UiIntegration::handleUrlClick(
|
||||||
} else if (local.startsWith(u"tg://"_q, Qt::CaseInsensitive)) {
|
} else if (local.startsWith(u"tg://"_q, Qt::CaseInsensitive)) {
|
||||||
Core::App().openLocalUrl(local, context);
|
Core::App().openLocalUrl(local, context);
|
||||||
return true;
|
return true;
|
||||||
|
} else if (local.startsWith(u"tonsite://"_q, Qt::CaseInsensitive)) {
|
||||||
|
Core::App().iv().showTonSite(local, context);
|
||||||
|
return true;
|
||||||
} else if (local.startsWith(u"internal:"_q, Qt::CaseInsensitive)) {
|
} else if (local.startsWith(u"internal:"_q, Qt::CaseInsensitive)) {
|
||||||
Core::App().openInternalUrl(local, context);
|
Core::App().openInternalUrl(local, context);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -245,6 +245,7 @@ QString FindUpdateFile() {
|
||||||
"^("
|
"^("
|
||||||
"tupdate|"
|
"tupdate|"
|
||||||
"tx64upd|"
|
"tx64upd|"
|
||||||
|
"tarm64upd|"
|
||||||
"tmacupd|"
|
"tmacupd|"
|
||||||
"tarmacupd|"
|
"tarmacupd|"
|
||||||
"tlinuxupd|"
|
"tlinuxupd|"
|
||||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
||||||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||||
constexpr auto AppFile = "AyuGram"_cs;
|
constexpr auto AppFile = "AyuGram"_cs;
|
||||||
constexpr auto AppVersion = 5002002;
|
constexpr auto AppVersion = 5003002;
|
||||||
constexpr auto AppVersionStr = "5.2.2";
|
constexpr auto AppVersionStr = "5.3.2";
|
||||||
constexpr auto AppBetaVersion = false;
|
constexpr auto AppBetaVersion = false;
|
||||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||||
|
|
|
@ -130,6 +130,42 @@ void BusinessInfo::saveChatIntro(ChatIntro data, Fn<void(QString)> fail) {
|
||||||
session->user()->setBusinessDetails(std::move(details));
|
session->user()->setBusinessDetails(std::move(details));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BusinessInfo::saveLocation(
|
||||||
|
BusinessLocation data,
|
||||||
|
Fn<void(QString)> fail) {
|
||||||
|
const auto session = &_owner->session();
|
||||||
|
auto details = session->user()->businessDetails();
|
||||||
|
const auto &was = details.location;
|
||||||
|
if (was == data) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const auto session = &_owner->session();
|
||||||
|
using Flag = MTPaccount_UpdateBusinessLocation::Flag;
|
||||||
|
session->api().request(MTPaccount_UpdateBusinessLocation(
|
||||||
|
MTP_flags((data.point ? Flag::f_geo_point : Flag())
|
||||||
|
| (data.address.isEmpty() ? Flag() : Flag::f_address)),
|
||||||
|
(data.point
|
||||||
|
? MTP_inputGeoPoint(
|
||||||
|
MTP_flags(0),
|
||||||
|
MTP_double(data.point->lat()),
|
||||||
|
MTP_double(data.point->lon()),
|
||||||
|
MTPint()) // accuracy_radius
|
||||||
|
: MTP_inputGeoPointEmpty()),
|
||||||
|
MTP_string(data.address)
|
||||||
|
)).fail([=](const MTP::Error &error) {
|
||||||
|
auto details = session->user()->businessDetails();
|
||||||
|
details.location = was;
|
||||||
|
session->user()->setBusinessDetails(std::move(details));
|
||||||
|
if (fail) {
|
||||||
|
fail(error.type());
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
details.location = std::move(data);
|
||||||
|
session->user()->setBusinessDetails(std::move(details));
|
||||||
|
}
|
||||||
|
|
||||||
void BusinessInfo::applyAwaySettings(AwaySettings data) {
|
void BusinessInfo::applyAwaySettings(AwaySettings data) {
|
||||||
if (_awaySettings == data) {
|
if (_awaySettings == data) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
void saveWorkingHours(WorkingHours data, Fn<void(QString)> fail);
|
void saveWorkingHours(WorkingHours data, Fn<void(QString)> fail);
|
||||||
void saveChatIntro(ChatIntro data, Fn<void(QString)> fail);
|
void saveChatIntro(ChatIntro data, Fn<void(QString)> fail);
|
||||||
|
void saveLocation(BusinessLocation data, Fn<void(QString)> fail);
|
||||||
|
|
||||||
void saveAwaySettings(AwaySettings data, Fn<void(QString)> fail);
|
void saveAwaySettings(AwaySettings data, Fn<void(QString)> fail);
|
||||||
void applyAwaySettings(AwaySettings data);
|
void applyAwaySettings(AwaySettings data);
|
||||||
|
|
44
Telegram/SourceFiles/data/components/location_pickers.cpp
Normal file
44
Telegram/SourceFiles/data/components/location_pickers.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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 "data/components/location_pickers.h"
|
||||||
|
|
||||||
|
#include "api/api_common.h"
|
||||||
|
#include "ui/controls/location_picker.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
struct LocationPickers::Entry {
|
||||||
|
Api::SendAction action;
|
||||||
|
base::weak_ptr<Ui::LocationPicker> picker;
|
||||||
|
};
|
||||||
|
|
||||||
|
LocationPickers::LocationPickers() = default;
|
||||||
|
|
||||||
|
LocationPickers::~LocationPickers() = default;
|
||||||
|
|
||||||
|
Ui::LocationPicker *LocationPickers::lookup(const Api::SendAction &action) {
|
||||||
|
for (auto i = begin(_pickers); i != end(_pickers);) {
|
||||||
|
if (const auto strong = i->picker.get()) {
|
||||||
|
if (i->action == action) {
|
||||||
|
return i->picker.get();
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
i = _pickers.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationPickers::emplace(
|
||||||
|
const Api::SendAction &action,
|
||||||
|
not_null<Ui::LocationPicker*> picker) {
|
||||||
|
_pickers.push_back({ action, picker });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
39
Telegram/SourceFiles/data/components/location_pickers.h
Normal file
39
Telegram/SourceFiles/data/components/location_pickers.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
struct SendAction;
|
||||||
|
} // namespace Api
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class LocationPicker;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class LocationPickers final {
|
||||||
|
public:
|
||||||
|
LocationPickers();
|
||||||
|
~LocationPickers();
|
||||||
|
|
||||||
|
Ui::LocationPicker *lookup(const Api::SendAction &action);
|
||||||
|
void emplace(
|
||||||
|
const Api::SendAction &action,
|
||||||
|
not_null<Ui::LocationPicker*> picker);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Entry;
|
||||||
|
|
||||||
|
std::vector<Entry> _pickers;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -41,12 +41,36 @@ constexpr auto kRequestTimeLimit = 10 * crl::time(1000);
|
||||||
) / 1'000'000.;
|
) / 1'000'000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MTPTopPeerCategory TypeToCategory(TopPeerType type) {
|
||||||
|
switch (type) {
|
||||||
|
case TopPeerType::Chat: return MTP_topPeerCategoryCorrespondents();
|
||||||
|
case TopPeerType::BotApp: return MTP_topPeerCategoryBotsApp();
|
||||||
|
}
|
||||||
|
Unexpected("Type in TypeToCategory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto TypeToGetFlags(TopPeerType type) {
|
||||||
|
using Flag = MTPcontacts_GetTopPeers::Flag;
|
||||||
|
switch (type) {
|
||||||
|
case TopPeerType::Chat: return Flag::f_correspondents;
|
||||||
|
case TopPeerType::BotApp: return Flag::f_bots_app;
|
||||||
|
}
|
||||||
|
Unexpected("Type in TypeToGetFlags.");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TopPeers::TopPeers(not_null<Main::Session*> session)
|
TopPeers::TopPeers(not_null<Main::Session*> session, TopPeerType type)
|
||||||
: _session(session) {
|
: _session(session)
|
||||||
|
, _type(type) {
|
||||||
|
if (_type == TopPeerType::Chat) {
|
||||||
|
loadAfterChats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopPeers::loadAfterChats() {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
crl::on_main(session, [=] {
|
crl::on_main(_session, [=] {
|
||||||
_session->data().chatsListLoadedEvents(
|
_session->data().chatsListLoadedEvents(
|
||||||
) | rpl::filter(_1 == nullptr) | rpl::start_with_next([=] {
|
) | rpl::filter(_1 == nullptr) | rpl::start_with_next([=] {
|
||||||
crl::on_main(_session, [=] {
|
crl::on_main(_session, [=] {
|
||||||
|
@ -84,7 +108,7 @@ void TopPeers::remove(not_null<PeerData*> peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestId = _session->api().request(MTPcontacts_ResetTopPeerRating(
|
_requestId = _session->api().request(MTPcontacts_ResetTopPeerRating(
|
||||||
MTP_topPeerCategoryCorrespondents(),
|
TypeToCategory(_type),
|
||||||
peer->input
|
peer->input
|
||||||
)).send();
|
)).send();
|
||||||
}
|
}
|
||||||
|
@ -160,11 +184,13 @@ void TopPeers::request() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestId = _session->api().request(MTPcontacts_GetTopPeers(
|
_requestId = _session->api().request(MTPcontacts_GetTopPeers(
|
||||||
MTP_flags(MTPcontacts_GetTopPeers::Flag::f_correspondents),
|
MTP_flags(TypeToGetFlags(_type)),
|
||||||
MTP_int(0),
|
MTP_int(0),
|
||||||
MTP_int(kLimit),
|
MTP_int(kLimit),
|
||||||
MTP_long(countHash())
|
MTP_long(countHash())
|
||||||
)).done([=](const MTPcontacts_TopPeers &result, const MTP::Response &response) {
|
)).done([=](
|
||||||
|
const MTPcontacts_TopPeers &result,
|
||||||
|
const MTP::Response &response) {
|
||||||
_lastReceivedDate = TimeId(response.outerMsgId >> 32);
|
_lastReceivedDate = TimeId(response.outerMsgId >> 32);
|
||||||
_lastReceived = crl::now();
|
_lastReceived = crl::now();
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
|
@ -176,19 +202,22 @@ void TopPeers::request() {
|
||||||
owner->processChats(data.vchats());
|
owner->processChats(data.vchats());
|
||||||
for (const auto &category : data.vcategories().v) {
|
for (const auto &category : data.vcategories().v) {
|
||||||
const auto &data = category.data();
|
const auto &data = category.data();
|
||||||
data.vcategory().match(
|
const auto cons = (_type == TopPeerType::Chat)
|
||||||
[&](const MTPDtopPeerCategoryCorrespondents &) {
|
? mtpc_topPeerCategoryCorrespondents
|
||||||
_list = ranges::views::all(
|
: mtpc_topPeerCategoryBotsApp;
|
||||||
data.vpeers().v
|
if (data.vcategory().type() != cons) {
|
||||||
) | ranges::views::transform([&](const MTPTopPeer &top) {
|
|
||||||
return TopPeer{
|
|
||||||
owner->peer(peerFromMTP(top.data().vpeer())),
|
|
||||||
top.data().vrating().v,
|
|
||||||
};
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
}, [](const auto &) {
|
|
||||||
LOG(("API Error: Unexpected top peer category."));
|
LOG(("API Error: Unexpected top peer category."));
|
||||||
});
|
continue;
|
||||||
|
}
|
||||||
|
_list = ranges::views::all(
|
||||||
|
data.vpeers().v
|
||||||
|
) | ranges::views::transform([&](
|
||||||
|
const MTPTopPeer &top) {
|
||||||
|
return TopPeer{
|
||||||
|
owner->peer(peerFromMTP(top.data().vpeer())),
|
||||||
|
top.data().vrating().v,
|
||||||
|
};
|
||||||
|
}) | ranges::to_vector;
|
||||||
}
|
}
|
||||||
updated();
|
updated();
|
||||||
}, [&](const MTPDcontacts_topPeersDisabled &) {
|
}, [&](const MTPDcontacts_topPeersDisabled &) {
|
||||||
|
|
|
@ -13,9 +13,14 @@ class Session;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
enum class TopPeerType {
|
||||||
|
Chat,
|
||||||
|
BotApp,
|
||||||
|
};
|
||||||
|
|
||||||
class TopPeers final {
|
class TopPeers final {
|
||||||
public:
|
public:
|
||||||
explicit TopPeers(not_null<Main::Session*> session);
|
TopPeers(not_null<Main::Session*> session, TopPeerType type);
|
||||||
~TopPeers();
|
~TopPeers();
|
||||||
|
|
||||||
[[nodiscard]] std::vector<not_null<PeerData*>> list() const;
|
[[nodiscard]] std::vector<not_null<PeerData*>> list() const;
|
||||||
|
@ -36,11 +41,13 @@ private:
|
||||||
float64 rating = 0.;
|
float64 rating = 0.;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void loadAfterChats();
|
||||||
void request();
|
void request();
|
||||||
[[nodiscard]] uint64 countHash() const;
|
[[nodiscard]] uint64 countHash() const;
|
||||||
void updated();
|
void updated();
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
const TopPeerType _type = {};
|
||||||
|
|
||||||
std::vector<TopPeer> _list;
|
std::vector<TopPeer> _list;
|
||||||
rpl::event_stream<> _updates;
|
rpl::event_stream<> _updates;
|
||||||
|
|
|
@ -1089,7 +1089,8 @@ void ApplyChannelUpdate(
|
||||||
| Flag::CanGetStatistics
|
| Flag::CanGetStatistics
|
||||||
| Flag::ViewAsMessages
|
| Flag::ViewAsMessages
|
||||||
| Flag::CanViewRevenue
|
| Flag::CanViewRevenue
|
||||||
| Flag::PaidMediaAllowed;
|
| Flag::PaidMediaAllowed
|
||||||
|
| Flag::CanViewCreditsRevenue;
|
||||||
channel->setFlags((channel->flags() & ~mask)
|
channel->setFlags((channel->flags() & ~mask)
|
||||||
| (update.is_can_set_username() ? Flag::CanSetUsername : Flag())
|
| (update.is_can_set_username() ? Flag::CanSetUsername : Flag())
|
||||||
| (update.is_can_view_participants()
|
| (update.is_can_view_participants()
|
||||||
|
@ -1107,7 +1108,10 @@ void ApplyChannelUpdate(
|
||||||
? Flag::ViewAsMessages
|
? Flag::ViewAsMessages
|
||||||
: Flag())
|
: Flag())
|
||||||
| (update.is_paid_media_allowed() ? Flag::PaidMediaAllowed : Flag())
|
| (update.is_paid_media_allowed() ? Flag::PaidMediaAllowed : Flag())
|
||||||
| (update.is_can_view_revenue() ? Flag::CanViewRevenue : Flag()));
|
| (update.is_can_view_revenue() ? Flag::CanViewRevenue : Flag())
|
||||||
|
| (update.is_can_view_stars_revenue()
|
||||||
|
? Flag::CanViewCreditsRevenue
|
||||||
|
: Flag()));
|
||||||
channel->setUserpicPhoto(update.vchat_photo());
|
channel->setUserpicPhoto(update.vchat_photo());
|
||||||
if (const auto migratedFrom = update.vmigrated_from_chat_id()) {
|
if (const auto migratedFrom = update.vmigrated_from_chat_id()) {
|
||||||
channel->addFlags(Flag::Megagroup);
|
channel->addFlags(Flag::Megagroup);
|
||||||
|
|
|
@ -67,6 +67,7 @@ enum class ChannelDataFlag : uint64 {
|
||||||
SimilarExpanded = (1ULL << 31),
|
SimilarExpanded = (1ULL << 31),
|
||||||
CanViewRevenue = (1ULL << 32),
|
CanViewRevenue = (1ULL << 32),
|
||||||
PaidMediaAllowed = (1ULL << 33),
|
PaidMediaAllowed = (1ULL << 33),
|
||||||
|
CanViewCreditsRevenue = (1ULL << 34),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
|
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
|
||||||
using ChannelDataFlags = base::flags<ChannelDataFlag>;
|
using ChannelDataFlags = base::flags<ChannelDataFlag>;
|
||||||
|
|
|
@ -47,6 +47,7 @@ ChatFilter::ChatFilter(
|
||||||
FilterId id,
|
FilterId id,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
const QString &iconEmoji,
|
const QString &iconEmoji,
|
||||||
|
std::optional<uint8> colorIndex,
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
std::vector<not_null<History*>> pinned,
|
std::vector<not_null<History*>> pinned,
|
||||||
|
@ -54,6 +55,7 @@ ChatFilter::ChatFilter(
|
||||||
: _id(id)
|
: _id(id)
|
||||||
, _title(title)
|
, _title(title)
|
||||||
, _iconEmoji(iconEmoji)
|
, _iconEmoji(iconEmoji)
|
||||||
|
, _colorIndex(colorIndex)
|
||||||
, _always(std::move(always))
|
, _always(std::move(always))
|
||||||
, _pinned(std::move(pinned))
|
, _pinned(std::move(pinned))
|
||||||
, _never(std::move(never))
|
, _never(std::move(never))
|
||||||
|
@ -99,6 +101,9 @@ ChatFilter ChatFilter::FromTL(
|
||||||
data.vid().v,
|
data.vid().v,
|
||||||
qs(data.vtitle()),
|
qs(data.vtitle()),
|
||||||
qs(data.vemoticon().value_or_empty()),
|
qs(data.vemoticon().value_or_empty()),
|
||||||
|
data.vcolor()
|
||||||
|
? std::make_optional(data.vcolor()->v)
|
||||||
|
: std::nullopt,
|
||||||
flags,
|
flags,
|
||||||
std::move(list),
|
std::move(list),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
|
@ -144,6 +149,9 @@ ChatFilter ChatFilter::FromTL(
|
||||||
data.vid().v,
|
data.vid().v,
|
||||||
qs(data.vtitle()),
|
qs(data.vtitle()),
|
||||||
qs(data.vemoticon().value_or_empty()),
|
qs(data.vemoticon().value_or_empty()),
|
||||||
|
data.vcolor()
|
||||||
|
? std::make_optional(data.vcolor()->v)
|
||||||
|
: std::nullopt,
|
||||||
(Flag::Chatlist
|
(Flag::Chatlist
|
||||||
| (data.is_has_my_invites() ? Flag::HasMyLinks : Flag())),
|
| (data.is_has_my_invites() ? Flag::HasMyLinks : Flag())),
|
||||||
std::move(list),
|
std::move(list),
|
||||||
|
@ -193,18 +201,20 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
|
||||||
}
|
}
|
||||||
if (_flags & Flag::Chatlist) {
|
if (_flags & Flag::Chatlist) {
|
||||||
using TLFlag = MTPDdialogFilterChatlist::Flag;
|
using TLFlag = MTPDdialogFilterChatlist::Flag;
|
||||||
const auto flags = TLFlag::f_emoticon;
|
const auto flags = TLFlag::f_emoticon
|
||||||
|
| (_colorIndex ? TLFlag::f_color : TLFlag(0));
|
||||||
return MTP_dialogFilterChatlist(
|
return MTP_dialogFilterChatlist(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
MTP_int(replaceId ? replaceId : _id),
|
MTP_int(replaceId ? replaceId : _id),
|
||||||
MTP_string(_title),
|
MTP_string(_title),
|
||||||
MTP_string(_iconEmoji),
|
MTP_string(_iconEmoji),
|
||||||
MTPint(), // color
|
MTP_int(_colorIndex.value_or(0)),
|
||||||
MTP_vector<MTPInputPeer>(pinned),
|
MTP_vector<MTPInputPeer>(pinned),
|
||||||
MTP_vector<MTPInputPeer>(include));
|
MTP_vector<MTPInputPeer>(include));
|
||||||
}
|
}
|
||||||
using TLFlag = MTPDdialogFilter::Flag;
|
using TLFlag = MTPDdialogFilter::Flag;
|
||||||
const auto flags = TLFlag::f_emoticon
|
const auto flags = TLFlag::f_emoticon
|
||||||
|
| (_colorIndex ? TLFlag::f_color : TLFlag(0))
|
||||||
| ((_flags & Flag::Contacts) ? TLFlag::f_contacts : TLFlag(0))
|
| ((_flags & Flag::Contacts) ? TLFlag::f_contacts : TLFlag(0))
|
||||||
| ((_flags & Flag::NonContacts) ? TLFlag::f_non_contacts : TLFlag(0))
|
| ((_flags & Flag::NonContacts) ? TLFlag::f_non_contacts : TLFlag(0))
|
||||||
| ((_flags & Flag::Groups) ? TLFlag::f_groups : TLFlag(0))
|
| ((_flags & Flag::Groups) ? TLFlag::f_groups : TLFlag(0))
|
||||||
|
@ -225,7 +235,7 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
|
||||||
MTP_int(replaceId ? replaceId : _id),
|
MTP_int(replaceId ? replaceId : _id),
|
||||||
MTP_string(_title),
|
MTP_string(_title),
|
||||||
MTP_string(_iconEmoji),
|
MTP_string(_iconEmoji),
|
||||||
MTPint(), // color
|
MTP_int(_colorIndex.value_or(0)),
|
||||||
MTP_vector<MTPInputPeer>(pinned),
|
MTP_vector<MTPInputPeer>(pinned),
|
||||||
MTP_vector<MTPInputPeer>(include),
|
MTP_vector<MTPInputPeer>(include),
|
||||||
MTP_vector<MTPInputPeer>(never));
|
MTP_vector<MTPInputPeer>(never));
|
||||||
|
@ -243,6 +253,10 @@ QString ChatFilter::iconEmoji() const {
|
||||||
return _iconEmoji;
|
return _iconEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<uint8> ChatFilter::colorIndex() const {
|
||||||
|
return _colorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
ChatFilter::Flags ChatFilter::flags() const {
|
ChatFilter::Flags ChatFilter::flags() const {
|
||||||
return _flags;
|
return _flags;
|
||||||
}
|
}
|
||||||
|
@ -572,7 +586,7 @@ void ChatFilters::applyInsert(ChatFilter filter, int position) {
|
||||||
|
|
||||||
_list.insert(
|
_list.insert(
|
||||||
begin(_list) + position,
|
begin(_list) + position,
|
||||||
ChatFilter(filter.id(), {}, {}, {}, {}, {}, {}));
|
ChatFilter(filter.id(), {}, {}, {}, {}, {}, {}, {}));
|
||||||
applyChange(*(begin(_list) + position), std::move(filter));
|
applyChange(*(begin(_list) + position), std::move(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,7 +613,7 @@ void ChatFilters::applyRemove(int position) {
|
||||||
Expects(position >= 0 && position < _list.size());
|
Expects(position >= 0 && position < _list.size());
|
||||||
|
|
||||||
const auto i = begin(_list) + position;
|
const auto i = begin(_list) + position;
|
||||||
applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {}, {}, {}));
|
applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {}, {}, {}, {}));
|
||||||
_list.erase(i);
|
_list.erase(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,6 +742,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
|
||||||
id,
|
id,
|
||||||
i->title(),
|
i->title(),
|
||||||
i->iconEmoji(),
|
i->iconEmoji(),
|
||||||
|
i->colorIndex(),
|
||||||
i->flags(),
|
i->flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
FilterId id,
|
FilterId id,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
const QString &iconEmoji,
|
const QString &iconEmoji,
|
||||||
|
std::optional<uint8> colorIndex,
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
std::vector<not_null<History*>> pinned,
|
std::vector<not_null<History*>> pinned,
|
||||||
|
@ -71,6 +72,7 @@ public:
|
||||||
[[nodiscard]] FilterId id() const;
|
[[nodiscard]] FilterId id() const;
|
||||||
[[nodiscard]] QString title() const;
|
[[nodiscard]] QString title() const;
|
||||||
[[nodiscard]] QString iconEmoji() const;
|
[[nodiscard]] QString iconEmoji() const;
|
||||||
|
[[nodiscard]] std::optional<uint8> colorIndex() const;
|
||||||
[[nodiscard]] Flags flags() const;
|
[[nodiscard]] Flags flags() const;
|
||||||
[[nodiscard]] bool chatlist() const;
|
[[nodiscard]] bool chatlist() const;
|
||||||
[[nodiscard]] bool hasMyLinks() const;
|
[[nodiscard]] bool hasMyLinks() const;
|
||||||
|
@ -84,6 +86,7 @@ private:
|
||||||
FilterId _id = 0;
|
FilterId _id = 0;
|
||||||
QString _title;
|
QString _title;
|
||||||
QString _iconEmoji;
|
QString _iconEmoji;
|
||||||
|
std::optional<uint8> _colorIndex;
|
||||||
base::flat_set<not_null<History*>> _always;
|
base::flat_set<not_null<History*>> _always;
|
||||||
std::vector<not_null<History*>> _pinned;
|
std::vector<not_null<History*>> _pinned;
|
||||||
base::flat_set<not_null<History*>> _never;
|
base::flat_set<not_null<History*>> _never;
|
||||||
|
@ -94,6 +97,7 @@ private:
|
||||||
inline bool operator==(const ChatFilter &a, const ChatFilter &b) {
|
inline bool operator==(const ChatFilter &a, const ChatFilter &b) {
|
||||||
return (a.title() == b.title())
|
return (a.title() == b.title())
|
||||||
&& (a.iconEmoji() == b.iconEmoji())
|
&& (a.iconEmoji() == b.iconEmoji())
|
||||||
|
&& (a.colorIndex() == b.colorIndex())
|
||||||
&& (a.flags() == b.flags())
|
&& (a.flags() == b.flags())
|
||||||
&& (a.always() == b.always())
|
&& (a.always() == b.always())
|
||||||
&& (a.never() == b.never());
|
&& (a.never() == b.never());
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct CreditTopupOption final {
|
||||||
QString currency;
|
QString currency;
|
||||||
uint64 amount = 0;
|
uint64 amount = 0;
|
||||||
bool extended = false;
|
bool extended = false;
|
||||||
|
uint64 giftBarePeerId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using CreditTopupOptions = std::vector<CreditTopupOption>;
|
using CreditTopupOptions = std::vector<CreditTopupOption>;
|
||||||
|
@ -57,7 +58,7 @@ struct CreditsHistoryEntry final {
|
||||||
QDateTime successDate;
|
QDateTime successDate;
|
||||||
QString successLink;
|
QString successLink;
|
||||||
bool in = false;
|
bool in = false;
|
||||||
|
bool gift = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreditsStatusSlice final {
|
struct CreditsStatusSlice final {
|
||||||
|
|
|
@ -26,6 +26,11 @@ LocationPoint::LocationPoint(const MTPDgeoPoint &point)
|
||||||
, _access(point.vaccess_hash().v) {
|
, _access(point.vaccess_hash().v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocationPoint::LocationPoint(float64 lat, float64 lon, IgnoreAccessHash)
|
||||||
|
: _lat(lat)
|
||||||
|
, _lon(lon) {
|
||||||
|
}
|
||||||
|
|
||||||
QString LocationPoint::latAsString() const {
|
QString LocationPoint::latAsString() const {
|
||||||
return AsString(_lat);
|
return AsString(_lat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,11 @@ public:
|
||||||
LocationPoint() = default;
|
LocationPoint() = default;
|
||||||
explicit LocationPoint(const MTPDgeoPoint &point);
|
explicit LocationPoint(const MTPDgeoPoint &point);
|
||||||
|
|
||||||
|
enum IgnoreAccessHash {
|
||||||
|
NoAccessHash,
|
||||||
|
};
|
||||||
|
LocationPoint(float64 lat, float64 lon, IgnoreAccessHash);
|
||||||
|
|
||||||
[[nodiscard]] QString latAsString() const;
|
[[nodiscard]] QString latAsString() const;
|
||||||
[[nodiscard]] QString lonAsString() const;
|
[[nodiscard]] QString lonAsString() const;
|
||||||
[[nodiscard]] MTPGeoPoint toMTP() const;
|
[[nodiscard]] MTPGeoPoint toMTP() const;
|
||||||
|
@ -45,6 +50,24 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct InputVenue {
|
||||||
|
float64 lat = 0.;
|
||||||
|
float64 lon = 0.;
|
||||||
|
QString title;
|
||||||
|
QString address;
|
||||||
|
QString provider;
|
||||||
|
QString id;
|
||||||
|
QString venueType;
|
||||||
|
|
||||||
|
[[nodiscard]] bool justLocation() const {
|
||||||
|
return id.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const InputVenue &,
|
||||||
|
const InputVenue &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] GeoPointLocation ComputeLocation(const LocationPoint &point);
|
[[nodiscard]] GeoPointLocation ComputeLocation(const LocationPoint &point);
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -121,8 +121,8 @@ struct AlbumCounts {
|
||||||
ImageRoundRadius radius,
|
ImageRoundRadius radius,
|
||||||
bool spoiler) {
|
bool spoiler) {
|
||||||
const auto original = image->original();
|
const auto original = image->original();
|
||||||
if (original.width() * 10 < original.height()
|
if (original.width() * 20 < original.height()
|
||||||
|| original.height() * 10 < original.width()) {
|
|| original.height() * 20 < original.width()) {
|
||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
@ -2303,8 +2303,9 @@ ClickHandlerPtr MediaDice::MakeHandler(
|
||||||
MediaGiftBox::MediaGiftBox(
|
MediaGiftBox::MediaGiftBox(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PeerData*> from,
|
not_null<PeerData*> from,
|
||||||
int months)
|
GiftType type,
|
||||||
: MediaGiftBox(parent, from, GiftCode{ .months = months }) {
|
int count)
|
||||||
|
: MediaGiftBox(parent, from, GiftCode{ .count = count, .type = type }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaGiftBox::MediaGiftBox(
|
MediaGiftBox::MediaGiftBox(
|
||||||
|
@ -2631,7 +2632,11 @@ const GiveawayResults *MediaGiveawayResults::giveawayResults() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities MediaGiveawayResults::notificationText() const {
|
TextWithEntities MediaGiveawayResults::notificationText() const {
|
||||||
return Ui::Text::Colorized({ tr::lng_prizes_results_title(tr::now) });
|
return Ui::Text::Colorized({
|
||||||
|
((_data.winnersCount == 1)
|
||||||
|
? tr::lng_prizes_results_title_one
|
||||||
|
: tr::lng_prizes_results_title)(tr::now)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MediaGiveawayResults::pinnedTextSubstring() const {
|
QString MediaGiveawayResults::pinnedTextSubstring() const {
|
||||||
|
|
|
@ -125,10 +125,16 @@ struct GiveawayResults {
|
||||||
bool all = false;
|
bool all = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GiftType : uchar {
|
||||||
|
Premium, // count - months
|
||||||
|
Stars, // count - stars
|
||||||
|
};
|
||||||
|
|
||||||
struct GiftCode {
|
struct GiftCode {
|
||||||
QString slug;
|
QString slug;
|
||||||
ChannelData *channel = nullptr;
|
ChannelData *channel = nullptr;
|
||||||
int months = 0;
|
int count = 0;
|
||||||
|
GiftType type = GiftType::Premium;
|
||||||
bool viaGiveaway = false;
|
bool viaGiveaway = false;
|
||||||
bool unclaimed = false;
|
bool unclaimed = false;
|
||||||
};
|
};
|
||||||
|
@ -591,7 +597,8 @@ public:
|
||||||
MediaGiftBox(
|
MediaGiftBox(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PeerData*> from,
|
not_null<PeerData*> from,
|
||||||
int months);
|
GiftType type,
|
||||||
|
int count);
|
||||||
MediaGiftBox(
|
MediaGiftBox(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<PeerData*> from,
|
not_null<PeerData*> from,
|
||||||
|
|
|
@ -57,6 +57,12 @@ std::optional<QString> OnlineTextSpecial(not_null<UserData*> user) {
|
||||||
} else if (user->isSupport()) {
|
} else if (user->isSupport()) {
|
||||||
return tr::lng_status_support(tr::now);
|
return tr::lng_status_support(tr::now);
|
||||||
} else if (user->isBot()) {
|
} else if (user->isBot()) {
|
||||||
|
if (const auto count = user->botInfo->activeUsers) {
|
||||||
|
return tr::lng_bot_status_users(
|
||||||
|
tr::now,
|
||||||
|
lt_count_decimal,
|
||||||
|
count);
|
||||||
|
}
|
||||||
return tr::lng_status_bot(tr::now);
|
return tr::lng_status_bot(tr::now);
|
||||||
} else if (user->isServiceUser()) {
|
} else if (user->isServiceUser()) {
|
||||||
return tr::lng_status_support(tr::now);
|
return tr::lng_status_support(tr::now);
|
||||||
|
@ -69,12 +75,14 @@ std::optional<QString> OnlineTextCommon(LastseenStatus status, TimeId now) {
|
||||||
return tr::lng_status_online(tr::now);
|
return tr::lng_status_online(tr::now);
|
||||||
} else if (status.isLongAgo()) {
|
} else if (status.isLongAgo()) {
|
||||||
return tr::lng_status_offline(tr::now);
|
return tr::lng_status_offline(tr::now);
|
||||||
} else if (status.isRecently() || status.isHidden()) {
|
} else if (status.isRecently()) {
|
||||||
return tr::lng_status_recently(tr::now);
|
return tr::lng_status_recently(tr::now);
|
||||||
} else if (status.isWithinWeek()) {
|
} else if (status.isWithinWeek()) {
|
||||||
return tr::lng_status_last_week(tr::now);
|
return tr::lng_status_last_week(tr::now);
|
||||||
} else if (status.isWithinMonth()) {
|
} else if (status.isWithinMonth()) {
|
||||||
return tr::lng_status_last_month(tr::now);
|
return tr::lng_status_last_month(tr::now);
|
||||||
|
} else if (status.isHidden()) {
|
||||||
|
return tr::lng_status_recently(tr::now);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -731,6 +731,8 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
|
||||||
result->botInfo->supportsAttachMenu = data.is_bot_attach_menu();
|
result->botInfo->supportsAttachMenu = data.is_bot_attach_menu();
|
||||||
result->botInfo->supportsBusiness = data.is_bot_business();
|
result->botInfo->supportsBusiness = data.is_bot_business();
|
||||||
result->botInfo->canEditInformation = data.is_bot_can_edit();
|
result->botInfo->canEditInformation = data.is_bot_can_edit();
|
||||||
|
result->botInfo->activeUsers = data.vbot_active_users().value_or_empty();
|
||||||
|
result->botInfo->hasMainApp = data.is_bot_has_main_app();
|
||||||
} else {
|
} else {
|
||||||
result->setBotInfoVersion(-1);
|
result->setBotInfoVersion(-1);
|
||||||
}
|
}
|
||||||
|
@ -3390,6 +3392,22 @@ void Session::documentApplyFields(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<DocumentData*> Session::venueIconDocument(const QString &icon) {
|
||||||
|
const auto i = _venueIcons.find(icon);
|
||||||
|
if (i != end(_venueIcons)) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
const auto result = documentFromWeb(MTP_webDocumentNoProxy(
|
||||||
|
MTP_string(u"https://ss3.4sqi.net/img/categories_v2/"_q
|
||||||
|
+ icon
|
||||||
|
+ u"_64.png"_q),
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_string("image/png"),
|
||||||
|
MTP_vector<MTPDocumentAttribute>()), {}, {});
|
||||||
|
_venueIcons.emplace(icon, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
not_null<WebPageData*> Session::webpage(WebPageId id) {
|
not_null<WebPageData*> Session::webpage(WebPageId id) {
|
||||||
auto i = _webpages.find(id);
|
auto i = _webpages.find(id);
|
||||||
if (i == _webpages.cend()) {
|
if (i == _webpages.cend()) {
|
||||||
|
@ -4582,7 +4600,8 @@ void Session::serviceNotification(
|
||||||
MTPVector<MTPUsername>(),
|
MTPVector<MTPUsername>(),
|
||||||
MTPint(), // stories_max_id
|
MTPint(), // stories_max_id
|
||||||
MTPPeerColor(), // color
|
MTPPeerColor(), // color
|
||||||
MTPPeerColor())); // profile_color
|
MTPPeerColor(), // profile_color
|
||||||
|
MTPint())); // bot_active_users
|
||||||
}
|
}
|
||||||
const auto history = this->history(PeerData::kServiceNotificationsId);
|
const auto history = this->history(PeerData::kServiceNotificationsId);
|
||||||
const auto insert = [=] {
|
const auto insert = [=] {
|
||||||
|
|
|
@ -559,6 +559,8 @@ public:
|
||||||
const MTPWebDocument &data,
|
const MTPWebDocument &data,
|
||||||
const ImageLocation &thumbnailLocation,
|
const ImageLocation &thumbnailLocation,
|
||||||
const ImageLocation &videoThumbnailLocation);
|
const ImageLocation &videoThumbnailLocation);
|
||||||
|
[[nodiscard]] not_null<DocumentData*> venueIconDocument(
|
||||||
|
const QString &icon);
|
||||||
|
|
||||||
[[nodiscard]] not_null<WebPageData*> webpage(WebPageId id);
|
[[nodiscard]] not_null<WebPageData*> webpage(WebPageId id);
|
||||||
not_null<WebPageData*> processWebpage(const MTPWebPage &data);
|
not_null<WebPageData*> processWebpage(const MTPWebPage &data);
|
||||||
|
@ -1002,6 +1004,7 @@ private:
|
||||||
FullStoryId,
|
FullStoryId,
|
||||||
base::flat_set<not_null<HistoryItem*>>> _storyItems;
|
base::flat_set<not_null<HistoryItem*>>> _storyItems;
|
||||||
base::flat_map<uint64, not_null<HistoryItem*>> _highlightings;
|
base::flat_map<uint64, not_null<HistoryItem*>> _highlightings;
|
||||||
|
base::flat_map<QString, not_null<DocumentData*>> _venueIcons;
|
||||||
|
|
||||||
base::flat_set<not_null<WebPageData*>> _webpagesUpdated;
|
base::flat_set<not_null<WebPageData*>> _webpagesUpdated;
|
||||||
base::flat_set<not_null<GameData*>> _gamesUpdated;
|
base::flat_set<not_null<GameData*>> _gamesUpdated;
|
||||||
|
|
|
@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/download_manager_mtproto.h"
|
#include "storage/download_manager_mtproto.h"
|
||||||
#include "storage/file_download.h" // kMaxFileInMemory
|
#include "storage/file_download.h" // kMaxFileInMemory
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/color_int_conversion.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -40,6 +41,7 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
return {
|
return {
|
||||||
.geometry = { corner / 100., size / 100. },
|
.geometry = { corner / 100., size / 100. },
|
||||||
.rotation = data.vrotation().v,
|
.rotation = data.vrotation().v,
|
||||||
|
.radius = data.vradius().value_or_empty(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +85,7 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
}, [&](const MTPDmediaAreaSuggestedReaction &data) {
|
}, [&](const MTPDmediaAreaSuggestedReaction &data) {
|
||||||
}, [&](const MTPDmediaAreaChannelPost &data) {
|
}, [&](const MTPDmediaAreaChannelPost &data) {
|
||||||
}, [&](const MTPDmediaAreaUrl &data) {
|
}, [&](const MTPDmediaAreaUrl &data) {
|
||||||
|
}, [&](const MTPDmediaAreaWeather &data) {
|
||||||
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
||||||
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
||||||
}, [&](const MTPDinputMediaAreaVenue &data) {
|
}, [&](const MTPDinputMediaAreaVenue &data) {
|
||||||
|
@ -105,6 +108,7 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
});
|
});
|
||||||
}, [&](const MTPDmediaAreaChannelPost &data) {
|
}, [&](const MTPDmediaAreaChannelPost &data) {
|
||||||
}, [&](const MTPDmediaAreaUrl &data) {
|
}, [&](const MTPDmediaAreaUrl &data) {
|
||||||
|
}, [&](const MTPDmediaAreaWeather &data) {
|
||||||
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
||||||
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
||||||
}, [&](const MTPDinputMediaAreaVenue &data) {
|
}, [&](const MTPDinputMediaAreaVenue &data) {
|
||||||
|
@ -127,6 +131,7 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
data.vmsg_id().v),
|
data.vmsg_id().v),
|
||||||
});
|
});
|
||||||
}, [&](const MTPDmediaAreaUrl &data) {
|
}, [&](const MTPDmediaAreaUrl &data) {
|
||||||
|
}, [&](const MTPDmediaAreaWeather &data) {
|
||||||
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
||||||
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
||||||
}, [&](const MTPDinputMediaAreaVenue &data) {
|
}, [&](const MTPDinputMediaAreaVenue &data) {
|
||||||
|
@ -147,6 +152,33 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
.area = ParseArea(data.vcoordinates()),
|
.area = ParseArea(data.vcoordinates()),
|
||||||
.url = qs(data.vurl()),
|
.url = qs(data.vurl()),
|
||||||
});
|
});
|
||||||
|
}, [&](const MTPDmediaAreaWeather &data) {
|
||||||
|
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
||||||
|
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
||||||
|
}, [&](const MTPDinputMediaAreaVenue &data) {
|
||||||
|
LOG(("API Error: Unexpected inputMediaAreaVenue from API."));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto ParseWeatherArea(const MTPMediaArea &area)
|
||||||
|
-> std::optional<WeatherArea> {
|
||||||
|
auto result = std::optional<WeatherArea>();
|
||||||
|
area.match([&](const MTPDmediaAreaVenue &data) {
|
||||||
|
}, [&](const MTPDmediaAreaGeoPoint &data) {
|
||||||
|
}, [&](const MTPDmediaAreaSuggestedReaction &data) {
|
||||||
|
}, [&](const MTPDmediaAreaChannelPost &data) {
|
||||||
|
}, [&](const MTPDmediaAreaUrl &data) {
|
||||||
|
}, [&](const MTPDmediaAreaWeather &data) {
|
||||||
|
result.emplace(WeatherArea{
|
||||||
|
.area = ParseArea(data.vcoordinates()),
|
||||||
|
.emoji = qs(data.vemoji()),
|
||||||
|
.color = Ui::Color32FromSerialized(data.vcolor().v),
|
||||||
|
.millicelsius = int(1000. * std::clamp(
|
||||||
|
data.vtemperature_c().v,
|
||||||
|
-274.,
|
||||||
|
1'000'000.)),
|
||||||
|
});
|
||||||
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
}, [&](const MTPDinputMediaAreaChannelPost &data) {
|
||||||
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
LOG(("API Error: Unexpected inputMediaAreaChannelPost from API."));
|
||||||
}, [&](const MTPDinputMediaAreaVenue &data) {
|
}, [&](const MTPDinputMediaAreaVenue &data) {
|
||||||
|
@ -689,6 +721,10 @@ const std::vector<UrlArea> &Story::urlAreas() const {
|
||||||
return _urlAreas;
|
return _urlAreas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<WeatherArea> &Story::weatherAreas() const {
|
||||||
|
return _weatherAreas;
|
||||||
|
}
|
||||||
|
|
||||||
void Story::applyChanges(
|
void Story::applyChanges(
|
||||||
StoryMedia media,
|
StoryMedia media,
|
||||||
const MTPDstoryItem &data,
|
const MTPDstoryItem &data,
|
||||||
|
@ -793,6 +829,7 @@ void Story::applyFields(
|
||||||
auto suggestedReactions = std::vector<SuggestedReaction>();
|
auto suggestedReactions = std::vector<SuggestedReaction>();
|
||||||
auto channelPosts = std::vector<ChannelPost>();
|
auto channelPosts = std::vector<ChannelPost>();
|
||||||
auto urlAreas = std::vector<UrlArea>();
|
auto urlAreas = std::vector<UrlArea>();
|
||||||
|
auto weatherAreas = std::vector<WeatherArea>();
|
||||||
if (const auto areas = data.vmedia_areas()) {
|
if (const auto areas = data.vmedia_areas()) {
|
||||||
for (const auto &area : areas->v) {
|
for (const auto &area : areas->v) {
|
||||||
if (const auto location = ParseLocation(area)) {
|
if (const auto location = ParseLocation(area)) {
|
||||||
|
@ -808,6 +845,8 @@ void Story::applyFields(
|
||||||
channelPosts.push_back(*post);
|
channelPosts.push_back(*post);
|
||||||
} else if (auto url = ParseUrlArea(area)) {
|
} else if (auto url = ParseUrlArea(area)) {
|
||||||
urlAreas.push_back(*url);
|
urlAreas.push_back(*url);
|
||||||
|
} else if (auto weather = ParseWeatherArea(area)) {
|
||||||
|
weatherAreas.push_back(*weather);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -821,6 +860,7 @@ void Story::applyFields(
|
||||||
= (_suggestedReactions != suggestedReactions);
|
= (_suggestedReactions != suggestedReactions);
|
||||||
const auto channelPostsChanged = (_channelPosts != channelPosts);
|
const auto channelPostsChanged = (_channelPosts != channelPosts);
|
||||||
const auto urlAreasChanged = (_urlAreas != urlAreas);
|
const auto urlAreasChanged = (_urlAreas != urlAreas);
|
||||||
|
const auto weatherAreasChanged = (_weatherAreas != weatherAreas);
|
||||||
const auto reactionChanged = (_sentReactionId != reaction);
|
const auto reactionChanged = (_sentReactionId != reaction);
|
||||||
|
|
||||||
_out = out;
|
_out = out;
|
||||||
|
@ -849,6 +889,9 @@ void Story::applyFields(
|
||||||
if (urlAreasChanged) {
|
if (urlAreasChanged) {
|
||||||
_urlAreas = std::move(urlAreas);
|
_urlAreas = std::move(urlAreas);
|
||||||
}
|
}
|
||||||
|
if (weatherAreasChanged) {
|
||||||
|
_weatherAreas = std::move(weatherAreas);
|
||||||
|
}
|
||||||
if (reactionChanged) {
|
if (reactionChanged) {
|
||||||
_sentReactionId = reaction;
|
_sentReactionId = reaction;
|
||||||
}
|
}
|
||||||
|
@ -859,7 +902,8 @@ void Story::applyFields(
|
||||||
|| mediaChanged
|
|| mediaChanged
|
||||||
|| locationsChanged
|
|| locationsChanged
|
||||||
|| channelPostsChanged
|
|| channelPostsChanged
|
||||||
|| urlAreasChanged;
|
|| urlAreasChanged
|
||||||
|
|| weatherAreasChanged;
|
||||||
const auto reactionsChanged = reactionChanged
|
const auto reactionsChanged = reactionChanged
|
||||||
|| suggestedReactionsChanged;
|
|| suggestedReactionsChanged;
|
||||||
if (!initial && (changed || reactionsChanged)) {
|
if (!initial && (changed || reactionsChanged)) {
|
||||||
|
|
|
@ -81,6 +81,7 @@ struct StoryViews {
|
||||||
struct StoryArea {
|
struct StoryArea {
|
||||||
QRectF geometry;
|
QRectF geometry;
|
||||||
float64 rotation = 0;
|
float64 rotation = 0;
|
||||||
|
float64 radius = 0;
|
||||||
|
|
||||||
friend inline bool operator==(
|
friend inline bool operator==(
|
||||||
const StoryArea &,
|
const StoryArea &,
|
||||||
|
@ -131,6 +132,17 @@ struct UrlArea {
|
||||||
const UrlArea &) = default;
|
const UrlArea &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WeatherArea {
|
||||||
|
StoryArea area;
|
||||||
|
QString emoji;
|
||||||
|
QColor color;
|
||||||
|
int millicelsius = 0;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const WeatherArea &,
|
||||||
|
const WeatherArea &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
class Story final {
|
class Story final {
|
||||||
public:
|
public:
|
||||||
Story(
|
Story(
|
||||||
|
@ -208,6 +220,8 @@ public:
|
||||||
-> const std::vector<ChannelPost> &;
|
-> const std::vector<ChannelPost> &;
|
||||||
[[nodiscard]] auto urlAreas() const
|
[[nodiscard]] auto urlAreas() const
|
||||||
-> const std::vector<UrlArea> &;
|
-> const std::vector<UrlArea> &;
|
||||||
|
[[nodiscard]] auto weatherAreas() const
|
||||||
|
-> const std::vector<WeatherArea> &;
|
||||||
|
|
||||||
void applyChanges(
|
void applyChanges(
|
||||||
StoryMedia media,
|
StoryMedia media,
|
||||||
|
@ -259,6 +273,7 @@ private:
|
||||||
std::vector<SuggestedReaction> _suggestedReactions;
|
std::vector<SuggestedReaction> _suggestedReactions;
|
||||||
std::vector<ChannelPost> _channelPosts;
|
std::vector<ChannelPost> _channelPosts;
|
||||||
std::vector<UrlArea> _urlAreas;
|
std::vector<UrlArea> _urlAreas;
|
||||||
|
std::vector<WeatherArea> _weatherAreas;
|
||||||
StoryViews _views;
|
StoryViews _views;
|
||||||
StoryViews _channelReactions;
|
StoryViews _channelReactions;
|
||||||
const TimeId _date = 0;
|
const TimeId _date = 0;
|
||||||
|
|
|
@ -40,12 +40,14 @@ struct BotInfo {
|
||||||
|
|
||||||
int version = 0;
|
int version = 0;
|
||||||
int descriptionVersion = 0;
|
int descriptionVersion = 0;
|
||||||
|
int activeUsers = 0;
|
||||||
bool inited : 1 = false;
|
bool inited : 1 = false;
|
||||||
bool readsAllHistory : 1 = false;
|
bool readsAllHistory : 1 = false;
|
||||||
bool cantJoinGroups : 1 = false;
|
bool cantJoinGroups : 1 = false;
|
||||||
bool supportsAttachMenu : 1 = false;
|
bool supportsAttachMenu : 1 = false;
|
||||||
bool canEditInformation : 1 = false;
|
bool canEditInformation : 1 = false;
|
||||||
bool supportsBusiness : 1 = false;
|
bool supportsBusiness : 1 = false;
|
||||||
|
bool hasMainApp : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class UserDataFlag : uint32 {
|
enum class UserDataFlag : uint32 {
|
||||||
|
|
193
Telegram/SourceFiles/data/raw/raw_countries_bounds.cpp
Normal file
193
Telegram/SourceFiles/data/raw/raw_countries_bounds.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
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 "data/raw/raw_countries_bounds.h"
|
||||||
|
|
||||||
|
// Source: https://github.com/sandstrom/country-bounding-boxes
|
||||||
|
|
||||||
|
namespace Raw {
|
||||||
|
|
||||||
|
const base::flat_map<QString, GeoBounds> &CountryBounds() {
|
||||||
|
static const auto result = base::flat_map<QString, GeoBounds>{
|
||||||
|
{ u"AF"_q, GeoBounds{ 60.53, 29.32, 75.16, 38.49 } },
|
||||||
|
{ u"AO"_q, GeoBounds{ 11.64, -17.93, 24.08, -4.44 } },
|
||||||
|
{ u"AL"_q, GeoBounds{ 19.3, 39.62, 21.02, 42.69 } },
|
||||||
|
{ u"AE"_q, GeoBounds{ 51.58, 22.5, 56.4, 26.06 } },
|
||||||
|
{ u"AR"_q, GeoBounds{ -73.42, -55.25, -53.63, -21.83 } },
|
||||||
|
{ u"AM"_q, GeoBounds{ 43.58, 38.74, 46.51, 41.25 } },
|
||||||
|
{ u"AQ"_q, GeoBounds{ -180.0, -90.0, 180.0, -63.27 } },
|
||||||
|
{ u"TF"_q, GeoBounds{ 68.72, -49.78, 70.56, -48.63 } },
|
||||||
|
{ u"AU"_q, GeoBounds{ 113.34, -43.63, 153.57, -10.67 } },
|
||||||
|
{ u"AT"_q, GeoBounds{ 9.48, 46.43, 16.98, 49.04 } },
|
||||||
|
{ u"AZ"_q, GeoBounds{ 44.79, 38.27, 50.39, 41.86 } },
|
||||||
|
{ u"BI"_q, GeoBounds{ 29.02, -4.5, 30.75, -2.35 } },
|
||||||
|
{ u"BE"_q, GeoBounds{ 2.51, 49.53, 6.16, 51.48 } },
|
||||||
|
{ u"BJ"_q, GeoBounds{ 0.77, 6.14, 3.8, 12.24 } },
|
||||||
|
{ u"BF"_q, GeoBounds{ -5.47, 9.61, 2.18, 15.12 } },
|
||||||
|
{ u"BD"_q, GeoBounds{ 88.08, 20.67, 92.67, 26.45 } },
|
||||||
|
{ u"BG"_q, GeoBounds{ 22.38, 41.23, 28.56, 44.23 } },
|
||||||
|
{ u"BS"_q, GeoBounds{ -78.98, 23.71, -77.0, 27.04 } },
|
||||||
|
{ u"BA"_q, GeoBounds{ 15.75, 42.65, 19.6, 45.23 } },
|
||||||
|
{ u"BY"_q, GeoBounds{ 23.2, 51.32, 32.69, 56.17 } },
|
||||||
|
{ u"BZ"_q, GeoBounds{ -89.23, 15.89, -88.11, 18.5 } },
|
||||||
|
{ u"BO"_q, GeoBounds{ -69.59, -22.87, -57.5, -9.76 } },
|
||||||
|
{ u"BR"_q, GeoBounds{ -73.99, -33.77, -34.73, 5.24 } },
|
||||||
|
{ u"BN"_q, GeoBounds{ 114.2, 4.01, 115.45, 5.45 } },
|
||||||
|
{ u"BT"_q, GeoBounds{ 88.81, 26.72, 92.1, 28.3 } },
|
||||||
|
{ u"BW"_q, GeoBounds{ 19.9, -26.83, 29.43, -17.66 } },
|
||||||
|
{ u"CF"_q, GeoBounds{ 14.46, 2.27, 27.37, 11.14 } },
|
||||||
|
{ u"CA"_q, GeoBounds{ -141.0, 41.68, -52.65, 73.23 } },
|
||||||
|
{ u"CH"_q, GeoBounds{ 6.02, 45.78, 10.44, 47.83 } },
|
||||||
|
{ u"CL"_q, GeoBounds{ -75.64, -55.61, -66.96, -17.58 } },
|
||||||
|
{ u"CN"_q, GeoBounds{ 73.68, 18.2, 135.03, 53.46 } },
|
||||||
|
{ u"CI"_q, GeoBounds{ -8.6, 4.34, -2.56, 10.52 } },
|
||||||
|
{ u"CM"_q, GeoBounds{ 8.49, 1.73, 16.01, 12.86 } },
|
||||||
|
{ u"CD"_q, GeoBounds{ 12.18, -13.26, 31.17, 5.26 } },
|
||||||
|
{ u"CG"_q, GeoBounds{ 11.09, -5.04, 18.45, 3.73 } },
|
||||||
|
{ u"CO"_q, GeoBounds{ -78.99, -4.3, -66.88, 12.44 } },
|
||||||
|
{ u"CR"_q, GeoBounds{ -85.94, 8.23, -82.55, 11.22 } },
|
||||||
|
{ u"CU"_q, GeoBounds{ -84.97, 19.86, -74.18, 23.19 } },
|
||||||
|
{ u"CY"_q, GeoBounds{ 32.26, 34.57, 34.0, 35.17 } },
|
||||||
|
{ u"CZ"_q, GeoBounds{ 12.24, 48.56, 18.85, 51.12 } },
|
||||||
|
{ u"DE"_q, GeoBounds{ 5.99, 47.3, 15.02, 54.98 } },
|
||||||
|
{ u"DJ"_q, GeoBounds{ 41.66, 10.93, 43.32, 12.7 } },
|
||||||
|
{ u"DK"_q, GeoBounds{ 8.09, 54.8, 12.69, 57.73 } },
|
||||||
|
{ u"DO"_q, GeoBounds{ -71.95, 17.6, -68.32, 19.88 } },
|
||||||
|
{ u"DZ"_q, GeoBounds{ -8.68, 19.06, 12.0, 37.12 } },
|
||||||
|
{ u"EC"_q, GeoBounds{ -80.97, -4.96, -75.23, 1.38 } },
|
||||||
|
{ u"EG"_q, GeoBounds{ 24.7, 22.0, 36.87, 31.59 } },
|
||||||
|
{ u"ER"_q, GeoBounds{ 36.32, 12.46, 43.08, 18.0 } },
|
||||||
|
{ u"ES"_q, GeoBounds{ -9.39, 35.95, 3.04, 43.75 } },
|
||||||
|
{ u"EE"_q, GeoBounds{ 23.34, 57.47, 28.13, 59.61 } },
|
||||||
|
{ u"ET"_q, GeoBounds{ 32.95, 3.42, 47.79, 14.96 } },
|
||||||
|
{ u"FI"_q, GeoBounds{ 20.65, 59.85, 31.52, 70.16 } },
|
||||||
|
{ u"FJ"_q, GeoBounds{ -180.0, -18.29, 180.0, -16.02 } },
|
||||||
|
{ u"FK"_q, GeoBounds{ -61.2, -52.3, -57.75, -51.1 } },
|
||||||
|
{ u"FR"_q, GeoBounds{ -5.0, 42.5, 9.56, 51.15 } },
|
||||||
|
{ u"GA"_q, GeoBounds{ 8.8, -3.98, 14.43, 2.33 } },
|
||||||
|
{ u"GB"_q, GeoBounds{ -7.57, 49.96, 1.68, 58.64 } },
|
||||||
|
{ u"GE"_q, GeoBounds{ 39.96, 41.06, 46.64, 43.55 } },
|
||||||
|
{ u"GH"_q, GeoBounds{ -3.24, 4.71, 1.06, 11.1 } },
|
||||||
|
{ u"GN"_q, GeoBounds{ -15.13, 7.31, -7.83, 12.59 } },
|
||||||
|
{ u"GM"_q, GeoBounds{ -16.84, 13.13, -13.84, 13.88 } },
|
||||||
|
{ u"GW"_q, GeoBounds{ -16.68, 11.04, -13.7, 12.63 } },
|
||||||
|
{ u"GQ"_q, GeoBounds{ 9.31, 1.01, 11.29, 2.28 } },
|
||||||
|
{ u"GR"_q, GeoBounds{ 20.15, 34.92, 26.6, 41.83 } },
|
||||||
|
{ u"GL"_q, GeoBounds{ -73.3, 60.04, -12.21, 83.65 } },
|
||||||
|
{ u"GT"_q, GeoBounds{ -92.23, 13.74, -88.23, 17.82 } },
|
||||||
|
{ u"GY"_q, GeoBounds{ -61.41, 1.27, -56.54, 8.37 } },
|
||||||
|
{ u"HN"_q, GeoBounds{ -89.35, 12.98, -83.15, 16.01 } },
|
||||||
|
{ u"HR"_q, GeoBounds{ 13.66, 42.48, 19.39, 46.5 } },
|
||||||
|
{ u"HT"_q, GeoBounds{ -74.46, 18.03, -71.62, 19.92 } },
|
||||||
|
{ u"HU"_q, GeoBounds{ 16.2, 45.76, 22.71, 48.62 } },
|
||||||
|
{ u"ID"_q, GeoBounds{ 95.29, -10.36, 141.03, 5.48 } },
|
||||||
|
{ u"IN"_q, GeoBounds{ 68.18, 7.97, 97.4, 35.49 } },
|
||||||
|
{ u"IE"_q, GeoBounds{ -9.98, 51.67, -6.03, 55.13 } },
|
||||||
|
{ u"IR"_q, GeoBounds{ 44.11, 25.08, 63.32, 39.71 } },
|
||||||
|
{ u"IQ"_q, GeoBounds{ 38.79, 29.1, 48.57, 37.39 } },
|
||||||
|
{ u"IS"_q, GeoBounds{ -24.33, 63.5, -13.61, 66.53 } },
|
||||||
|
{ u"IL"_q, GeoBounds{ 34.27, 29.5, 35.84, 33.28 } },
|
||||||
|
{ u"IT"_q, GeoBounds{ 6.75, 36.62, 18.48, 47.12 } },
|
||||||
|
{ u"JM"_q, GeoBounds{ -78.34, 17.7, -76.2, 18.52 } },
|
||||||
|
{ u"JO"_q, GeoBounds{ 34.92, 29.2, 39.2, 33.38 } },
|
||||||
|
{ u"JP"_q, GeoBounds{ 129.41, 31.03, 145.54, 45.55 } },
|
||||||
|
{ u"KZ"_q, GeoBounds{ 46.47, 40.66, 87.36, 55.39 } },
|
||||||
|
{ u"KE"_q, GeoBounds{ 33.89, -4.68, 41.86, 5.51 } },
|
||||||
|
{ u"KG"_q, GeoBounds{ 69.46, 39.28, 80.26, 43.3 } },
|
||||||
|
{ u"KH"_q, GeoBounds{ 102.35, 10.49, 107.61, 14.57 } },
|
||||||
|
{ u"KR"_q, GeoBounds{ 126.12, 34.39, 129.47, 38.61 } },
|
||||||
|
{ u"KW"_q, GeoBounds{ 46.57, 28.53, 48.42, 30.06 } },
|
||||||
|
{ u"LA"_q, GeoBounds{ 100.12, 13.88, 107.56, 22.46 } },
|
||||||
|
{ u"LB"_q, GeoBounds{ 35.13, 33.09, 36.61, 34.64 } },
|
||||||
|
{ u"LR"_q, GeoBounds{ -11.44, 4.36, -7.54, 8.54 } },
|
||||||
|
{ u"LY"_q, GeoBounds{ 9.32, 19.58, 25.16, 33.14 } },
|
||||||
|
{ u"LK"_q, GeoBounds{ 79.7, 5.97, 81.79, 9.82 } },
|
||||||
|
{ u"LS"_q, GeoBounds{ 27.0, -30.65, 29.33, -28.65 } },
|
||||||
|
{ u"LT"_q, GeoBounds{ 21.06, 53.91, 26.59, 56.37 } },
|
||||||
|
{ u"LU"_q, GeoBounds{ 5.67, 49.44, 6.24, 50.13 } },
|
||||||
|
{ u"LV"_q, GeoBounds{ 21.06, 55.62, 28.18, 57.97 } },
|
||||||
|
{ u"MA"_q, GeoBounds{ -17.02, 21.42, -1.12, 35.76 } },
|
||||||
|
{ u"MD"_q, GeoBounds{ 26.62, 45.49, 30.02, 48.47 } },
|
||||||
|
{ u"MG"_q, GeoBounds{ 43.25, -25.6, 50.48, -12.04 } },
|
||||||
|
{ u"MX"_q, GeoBounds{ -117.13, 14.54, -86.81, 32.72 } },
|
||||||
|
{ u"MK"_q, GeoBounds{ 20.46, 40.84, 22.95, 42.32 } },
|
||||||
|
{ u"ML"_q, GeoBounds{ -12.17, 10.1, 4.27, 24.97 } },
|
||||||
|
{ u"MM"_q, GeoBounds{ 92.3, 9.93, 101.18, 28.34 } },
|
||||||
|
{ u"ME"_q, GeoBounds{ 18.45, 41.88, 20.34, 43.52 } },
|
||||||
|
{ u"MN"_q, GeoBounds{ 87.75, 41.6, 119.77, 52.05 } },
|
||||||
|
{ u"MZ"_q, GeoBounds{ 30.18, -26.74, 40.78, -10.32 } },
|
||||||
|
{ u"MR"_q, GeoBounds{ -17.06, 14.62, -4.92, 27.4 } },
|
||||||
|
{ u"MW"_q, GeoBounds{ 32.69, -16.8, 35.77, -9.23 } },
|
||||||
|
{ u"MY"_q, GeoBounds{ 100.09, 0.77, 119.18, 6.93 } },
|
||||||
|
{ u"NA"_q, GeoBounds{ 11.73, -29.05, 25.08, -16.94 } },
|
||||||
|
{ u"NC"_q, GeoBounds{ 164.03, -22.4, 167.12, -20.11 } },
|
||||||
|
{ u"NE"_q, GeoBounds{ 0.3, 11.66, 15.9, 23.47 } },
|
||||||
|
{ u"NG"_q, GeoBounds{ 2.69, 4.24, 14.58, 13.87 } },
|
||||||
|
{ u"NI"_q, GeoBounds{ -87.67, 10.73, -83.15, 15.02 } },
|
||||||
|
{ u"NL"_q, GeoBounds{ 3.31, 50.8, 7.09, 53.51 } },
|
||||||
|
{ u"NO"_q, GeoBounds{ 4.99, 58.08, 31.29, 70.92 } },
|
||||||
|
{ u"NP"_q, GeoBounds{ 80.09, 26.4, 88.17, 30.42 } },
|
||||||
|
{ u"NZ"_q, GeoBounds{ 166.51, -46.64, 178.52, -34.45 } },
|
||||||
|
{ u"OM"_q, GeoBounds{ 52.0, 16.65, 59.81, 26.4 } },
|
||||||
|
{ u"PK"_q, GeoBounds{ 60.87, 23.69, 77.84, 37.13 } },
|
||||||
|
{ u"PA"_q, GeoBounds{ -82.97, 7.22, -77.24, 9.61 } },
|
||||||
|
{ u"PE"_q, GeoBounds{ -81.41, -18.35, -68.67, -0.06 } },
|
||||||
|
{ u"PH"_q, GeoBounds{ 117.17, 5.58, 126.54, 18.51 } },
|
||||||
|
{ u"PG"_q, GeoBounds{ 141.0, -10.65, 156.02, -2.5 } },
|
||||||
|
{ u"PL"_q, GeoBounds{ 14.07, 49.03, 24.03, 54.85 } },
|
||||||
|
{ u"PR"_q, GeoBounds{ -67.24, 17.95, -65.59, 18.52 } },
|
||||||
|
{ u"KP"_q, GeoBounds{ 124.27, 37.67, 130.78, 42.99 } },
|
||||||
|
{ u"PT"_q, GeoBounds{ -9.53, 36.84, -6.39, 42.28 } },
|
||||||
|
{ u"PY"_q, GeoBounds{ -62.69, -27.55, -54.29, -19.34 } },
|
||||||
|
{ u"QA"_q, GeoBounds{ 50.74, 24.56, 51.61, 26.11 } },
|
||||||
|
{ u"RO"_q, GeoBounds{ 20.22, 43.69, 29.63, 48.22 } },
|
||||||
|
{ u"RU"_q, GeoBounds{ -180.0, 41.15, 180.0, 81.25 } },
|
||||||
|
{ u"RW"_q, GeoBounds{ 29.02, -2.92, 30.82, -1.13 } },
|
||||||
|
{ u"SA"_q, GeoBounds{ 34.63, 16.35, 55.67, 32.16 } },
|
||||||
|
{ u"SD"_q, GeoBounds{ 21.94, 8.62, 38.41, 22.0 } },
|
||||||
|
{ u"SS"_q, GeoBounds{ 23.89, 3.51, 35.3, 12.25 } },
|
||||||
|
{ u"SN"_q, GeoBounds{ -17.63, 12.33, -11.47, 16.6 } },
|
||||||
|
{ u"SB"_q, GeoBounds{ 156.49, -10.83, 162.4, -6.6 } },
|
||||||
|
{ u"SL"_q, GeoBounds{ -13.25, 6.79, -10.23, 10.05 } },
|
||||||
|
{ u"SV"_q, GeoBounds{ -90.1, 13.15, -87.72, 14.42 } },
|
||||||
|
{ u"SO"_q, GeoBounds{ 40.98, -1.68, 51.13, 12.02 } },
|
||||||
|
{ u"RS"_q, GeoBounds{ 18.83, 42.25, 22.99, 46.17 } },
|
||||||
|
{ u"SR"_q, GeoBounds{ -58.04, 1.82, -53.96, 6.03 } },
|
||||||
|
{ u"SK"_q, GeoBounds{ 16.88, 47.76, 22.56, 49.57 } },
|
||||||
|
{ u"SI"_q, GeoBounds{ 13.7, 45.45, 16.56, 46.85 } },
|
||||||
|
{ u"SE"_q, GeoBounds{ 11.03, 55.36, 23.9, 69.11 } },
|
||||||
|
{ u"SZ"_q, GeoBounds{ 30.68, -27.29, 32.07, -25.66 } },
|
||||||
|
{ u"SY"_q, GeoBounds{ 35.7, 32.31, 42.35, 37.23 } },
|
||||||
|
{ u"TD"_q, GeoBounds{ 13.54, 7.42, 23.89, 23.41 } },
|
||||||
|
{ u"TG"_q, GeoBounds{ -0.05, 5.93, 1.87, 11.02 } },
|
||||||
|
{ u"TH"_q, GeoBounds{ 97.38, 5.69, 105.59, 20.42 } },
|
||||||
|
{ u"TJ"_q, GeoBounds{ 67.44, 36.74, 74.98, 40.96 } },
|
||||||
|
{ u"TM"_q, GeoBounds{ 52.5, 35.27, 66.55, 42.75 } },
|
||||||
|
{ u"TL"_q, GeoBounds{ 124.97, -9.39, 127.34, -8.27 } },
|
||||||
|
{ u"TT"_q, GeoBounds{ -61.95, 10.0, -60.9, 10.89 } },
|
||||||
|
{ u"TN"_q, GeoBounds{ 7.52, 30.31, 11.49, 37.35 } },
|
||||||
|
{ u"TR"_q, GeoBounds{ 26.04, 35.82, 44.79, 42.14 } },
|
||||||
|
{ u"TW"_q, GeoBounds{ 120.11, 21.97, 121.95, 25.3 } },
|
||||||
|
{ u"TZ"_q, GeoBounds{ 29.34, -11.72, 40.32, -0.95 } },
|
||||||
|
{ u"UG"_q, GeoBounds{ 29.58, -1.44, 35.04, 4.25 } },
|
||||||
|
{ u"UA"_q, GeoBounds{ 22.09, 44.36, 40.08, 52.34 } },
|
||||||
|
{ u"UY"_q, GeoBounds{ -58.43, -34.95, -53.21, -30.11 } },
|
||||||
|
{ u"US"_q, GeoBounds{ -125.0, 25.0, -66.96, 49.5 } },
|
||||||
|
{ u"UZ"_q, GeoBounds{ 55.93, 37.14, 73.06, 45.59 } },
|
||||||
|
{ u"VE"_q, GeoBounds{ -73.3, 0.72, -59.76, 12.16 } },
|
||||||
|
{ u"VN"_q, GeoBounds{ 102.17, 8.6, 109.34, 23.35 } },
|
||||||
|
{ u"VU"_q, GeoBounds{ 166.63, -16.6, 167.84, -14.63 } },
|
||||||
|
{ u"PS"_q, GeoBounds{ 34.93, 31.35, 35.55, 32.53 } },
|
||||||
|
{ u"YE"_q, GeoBounds{ 42.6, 12.59, 53.11, 19.0 } },
|
||||||
|
{ u"ZA"_q, GeoBounds{ 16.34, -34.82, 32.83, -22.09 } },
|
||||||
|
{ u"ZM"_q, GeoBounds{ 21.89, -17.96, 33.49, -8.24 } },
|
||||||
|
{ u"ZW"_q, GeoBounds{ 25.26, -22.27, 32.85, -15.51 } }
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raw
|
23
Telegram/SourceFiles/data/raw/raw_countries_bounds.h
Normal file
23
Telegram/SourceFiles/data/raw/raw_countries_bounds.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
namespace Raw {
|
||||||
|
|
||||||
|
struct GeoBounds {
|
||||||
|
double minLat = 0.;
|
||||||
|
double minLon = 0.;
|
||||||
|
double maxLat = 0.;
|
||||||
|
double maxLon = 0.;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] const base::flat_map<QString, GeoBounds> &CountryBounds();
|
||||||
|
|
||||||
|
} // namespace Raw
|
|
@ -228,7 +228,7 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
|
||||||
auto index = set->stickers.indexOf(document);
|
auto index = set->stickers.indexOf(document);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
if (set->dates.empty()) {
|
if (set->dates.empty()) {
|
||||||
session().api().requestRecentStickersForce();
|
session().api().requestSpecialStickersForce(false, true, false);
|
||||||
} else {
|
} else {
|
||||||
Assert(set->dates.size() == set->stickers.size());
|
Assert(set->dates.size() == set->stickers.size());
|
||||||
set->dates.erase(set->dates.begin() + index);
|
set->dates.erase(set->dates.begin() + index);
|
||||||
|
@ -260,7 +260,7 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
|
||||||
set->emoji[emoji].push_front(document);
|
set->emoji[emoji].push_front(document);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
session().api().requestRecentStickersForce();
|
session().api().requestSpecialStickersForce(false, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeRecentStickers = true;
|
writeRecentStickers = true;
|
||||||
|
|
|
@ -55,7 +55,8 @@ StickersSetFlags ParseStickersSetFlags(const MTPDstickerSet &data) {
|
||||||
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
||||||
//| (data.is_videos() ? Flag::Webm : Flag())
|
//| (data.is_videos() ? Flag::Webm : Flag())
|
||||||
| (data.is_text_color() ? Flag::TextColor : Flag())
|
| (data.is_text_color() ? Flag::TextColor : Flag())
|
||||||
| (data.is_channel_emoji_status() ? Flag::ChannelStatus : Flag());
|
| (data.is_channel_emoji_status() ? Flag::ChannelStatus : Flag())
|
||||||
|
| (data.is_creator() ? Flag::AmCreator : Flag());
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSet::StickersSet(
|
StickersSet::StickersSet(
|
||||||
|
|
|
@ -59,6 +59,7 @@ enum class StickersSetFlag : ushort {
|
||||||
Emoji = (1 << 9),
|
Emoji = (1 << 9),
|
||||||
TextColor = (1 << 10),
|
TextColor = (1 << 10),
|
||||||
ChannelStatus = (1 << 11),
|
ChannelStatus = (1 << 11),
|
||||||
|
AmCreator = (1 << 12),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
||||||
using StickersSetFlags = base::flags<StickersSetFlag>;
|
using StickersSetFlags = base::flags<StickersSetFlag>;
|
||||||
|
|
|
@ -3814,7 +3814,9 @@ ChosenRow InnerWidget::computeChosenRow() const {
|
||||||
|
|
||||||
bool InnerWidget::isUserpicPress() const {
|
bool InnerWidget::isUserpicPress() const {
|
||||||
return (_lastRowLocalMouseX >= 0)
|
return (_lastRowLocalMouseX >= 0)
|
||||||
&& (_lastRowLocalMouseX < _st->nameLeft);
|
&& (_lastRowLocalMouseX < _st->nameLeft)
|
||||||
|
&& (_collapsedSelected < 0
|
||||||
|
|| _collapsedSelected >= _collapsedRows.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InnerWidget::isUserpicPressOnWide() const {
|
bool InnerWidget::isUserpicPressOnWide() const {
|
||||||
|
|
|
@ -78,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "info/downloads/info_downloads_widget.h"
|
#include "info/downloads/info_downloads_widget.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
|
#include "inline_bots/bot_attach_web_view.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
@ -1282,6 +1283,27 @@ void Widget::updateSuggestions(anim::type animated) {
|
||||||
}
|
}
|
||||||
}, _suggestions->lifetime());
|
}, _suggestions->lifetime());
|
||||||
|
|
||||||
|
_suggestions->recentAppChosen(
|
||||||
|
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
|
||||||
|
if (const auto user = peer->asUser()) {
|
||||||
|
if (const auto info = user->botInfo.get()) {
|
||||||
|
if (info->hasMainApp) {
|
||||||
|
openBotMainApp(user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chosenRow({
|
||||||
|
.key = peer->owner().history(peer),
|
||||||
|
.newWindow = base::IsCtrlPressed(),
|
||||||
|
});
|
||||||
|
}, _suggestions->lifetime());
|
||||||
|
|
||||||
|
_suggestions->popularAppChosen(
|
||||||
|
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
|
||||||
|
controller()->showPeerInfo(peer);
|
||||||
|
}, _suggestions->lifetime());
|
||||||
|
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
|
|
||||||
_suggestions->show(animated, [=] {
|
_suggestions->show(animated, [=] {
|
||||||
|
@ -1293,6 +1315,17 @@ void Widget::updateSuggestions(anim::type animated) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::openBotMainApp(not_null<UserData*> bot) {
|
||||||
|
session().attachWebView().open({
|
||||||
|
.bot = bot,
|
||||||
|
.context = {
|
||||||
|
.controller = controller(),
|
||||||
|
.maySkipConfirmation = true,
|
||||||
|
},
|
||||||
|
.source = InlineBots::WebViewSourceBotProfile(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::changeOpenedSubsection(
|
void Widget::changeOpenedSubsection(
|
||||||
FnMut<void()> change,
|
FnMut<void()> change,
|
||||||
bool fromRight,
|
bool fromRight,
|
||||||
|
|
|
@ -214,6 +214,7 @@ private:
|
||||||
void refreshTopBars();
|
void refreshTopBars();
|
||||||
void showSearchInTopBar(anim::type animated);
|
void showSearchInTopBar(anim::type animated);
|
||||||
void checkUpdateStatus();
|
void checkUpdateStatus();
|
||||||
|
void openBotMainApp(not_null<UserData*> bot);
|
||||||
void changeOpenedSubsection(
|
void changeOpenedSubsection(
|
||||||
FnMut<void()> change,
|
FnMut<void()> change,
|
||||||
bool fromRight,
|
bool fromRight,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
class PeerListContent;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Thread;
|
class Thread;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
@ -67,21 +69,32 @@ public:
|
||||||
}
|
}
|
||||||
[[nodiscard]] auto recentPeerChosen() const
|
[[nodiscard]] auto recentPeerChosen() const
|
||||||
-> rpl::producer<not_null<PeerData*>> {
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
return _recentPeerChosen.events();
|
return _recent->chosen.events();
|
||||||
}
|
}
|
||||||
[[nodiscard]] auto myChannelChosen() const
|
[[nodiscard]] auto myChannelChosen() const
|
||||||
-> rpl::producer<not_null<PeerData*>> {
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
return _myChannelChosen.events();
|
return _myChannels->chosen.events();
|
||||||
}
|
}
|
||||||
[[nodiscard]] auto recommendationChosen() const
|
[[nodiscard]] auto recommendationChosen() const
|
||||||
-> rpl::producer<not_null<PeerData*>> {
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
return _recommendationChosen.events();
|
return _recommendations->chosen.events();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] auto recentAppChosen() const
|
||||||
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
|
return _recentApps->chosen.events();
|
||||||
|
}
|
||||||
|
[[nodiscard]] auto popularAppChosen() const
|
||||||
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
|
return _popularApps->chosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectListController;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Tab : uchar {
|
enum class Tab : uchar {
|
||||||
Chats,
|
Chats,
|
||||||
Channels,
|
Channels,
|
||||||
|
Apps,
|
||||||
};
|
};
|
||||||
enum class JumpResult : uchar {
|
enum class JumpResult : uchar {
|
||||||
NotApplied,
|
NotApplied,
|
||||||
|
@ -89,29 +102,54 @@ private:
|
||||||
AppliedAndOut,
|
AppliedAndOut,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ObjectList {
|
||||||
|
not_null<Ui::SlideWrap<PeerListContent>*> wrap;
|
||||||
|
rpl::variable<int> count;
|
||||||
|
Fn<bool()> choose;
|
||||||
|
Fn<JumpResult(Qt::Key, int)> selectJump;
|
||||||
|
Fn<uint64(QPoint)> updateFromParentDrag;
|
||||||
|
Fn<void()> dragLeft;
|
||||||
|
Fn<bool(not_null<QTouchEvent*>)> processTouch;
|
||||||
|
rpl::event_stream<not_null<PeerData*>> chosen;
|
||||||
|
};
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
void setupTabs();
|
void setupTabs();
|
||||||
void setupChats();
|
void setupChats();
|
||||||
void setupChannels();
|
void setupChannels();
|
||||||
|
void setupApps();
|
||||||
|
|
||||||
void selectJumpChats(Qt::Key direction, int pageSize);
|
void selectJumpChats(Qt::Key direction, int pageSize);
|
||||||
void selectJumpChannels(Qt::Key direction, int pageSize);
|
void selectJumpChannels(Qt::Key direction, int pageSize);
|
||||||
|
void selectJumpApps(Qt::Key direction, int pageSize);
|
||||||
|
|
||||||
[[nodiscard]] Data::Thread *updateFromChatsDrag(QPoint globalPosition);
|
[[nodiscard]] Data::Thread *updateFromChatsDrag(QPoint globalPosition);
|
||||||
[[nodiscard]] Data::Thread *updateFromChannelsDrag(
|
[[nodiscard]] Data::Thread *updateFromChannelsDrag(
|
||||||
QPoint globalPosition);
|
QPoint globalPosition);
|
||||||
|
[[nodiscard]] Data::Thread *updateFromAppsDrag(QPoint globalPosition);
|
||||||
[[nodiscard]] Data::Thread *fromListId(uint64 peerListRowId);
|
[[nodiscard]] Data::Thread *fromListId(uint64 peerListRowId);
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::SlideWrap<Ui::RpWidget>> setupRecentPeers(
|
[[nodiscard]] std::unique_ptr<ObjectList> setupRecentPeers(
|
||||||
RecentPeersList recentPeers);
|
RecentPeersList recentPeers);
|
||||||
[[nodiscard]] object_ptr<Ui::SlideWrap<Ui::RpWidget>> setupEmptyRecent();
|
[[nodiscard]] auto setupEmptyRecent()
|
||||||
[[nodiscard]] object_ptr<Ui::SlideWrap<Ui::RpWidget>> setupMyChannels();
|
|
||||||
[[nodiscard]] auto setupRecommendations()
|
|
||||||
-> object_ptr<Ui::SlideWrap<Ui::RpWidget>>;
|
-> object_ptr<Ui::SlideWrap<Ui::RpWidget>>;
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<ObjectList> setupMyChannels();
|
||||||
|
[[nodiscard]] std::unique_ptr<ObjectList> setupRecommendations();
|
||||||
[[nodiscard]] auto setupEmptyChannels()
|
[[nodiscard]] auto setupEmptyChannels()
|
||||||
-> object_ptr<Ui::SlideWrap<Ui::RpWidget>>;
|
-> object_ptr<Ui::SlideWrap<Ui::RpWidget>>;
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<ObjectList> setupRecentApps();
|
||||||
|
[[nodiscard]] std::unique_ptr<ObjectList> setupPopularApps();
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<ObjectList> setupObjectList(
|
||||||
|
not_null<Ui::ElasticScroll*> scroll,
|
||||||
|
not_null<Ui::VerticalLayout*> parent,
|
||||||
|
not_null<ObjectListController*> controller,
|
||||||
|
Fn<int()> addToScroll = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::SlideWrap<Ui::RpWidget>> setupEmpty(
|
[[nodiscard]] object_ptr<Ui::SlideWrap<Ui::RpWidget>> setupEmpty(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
SearchEmptyIcon icon,
|
SearchEmptyIcon icon,
|
||||||
|
@ -119,7 +157,7 @@ private:
|
||||||
|
|
||||||
void switchTab(Tab tab);
|
void switchTab(Tab tab);
|
||||||
void startShownAnimation(bool shown, Fn<void()> finish);
|
void startShownAnimation(bool shown, Fn<void()> finish);
|
||||||
void startSlideAnimation();
|
void startSlideAnimation(Tab was, Tab now);
|
||||||
void finishShow();
|
void finishShow();
|
||||||
|
|
||||||
void handlePressForChatPreview(PeerId id, Fn<void(bool)> callback);
|
void handlePressForChatPreview(PeerId id, Fn<void(bool)> callback);
|
||||||
|
@ -131,43 +169,30 @@ private:
|
||||||
|
|
||||||
const std::unique_ptr<Ui::ElasticScroll> _chatsScroll;
|
const std::unique_ptr<Ui::ElasticScroll> _chatsScroll;
|
||||||
const not_null<Ui::VerticalLayout*> _chatsContent;
|
const not_null<Ui::VerticalLayout*> _chatsContent;
|
||||||
|
|
||||||
const not_null<Ui::SlideWrap<TopPeersStrip>*> _topPeersWrap;
|
const not_null<Ui::SlideWrap<TopPeersStrip>*> _topPeersWrap;
|
||||||
const not_null<TopPeersStrip*> _topPeers;
|
const not_null<TopPeersStrip*> _topPeers;
|
||||||
|
rpl::event_stream<not_null<PeerData*>> _topPeerChosen;
|
||||||
|
|
||||||
|
const std::unique_ptr<ObjectList> _recent;
|
||||||
|
|
||||||
rpl::variable<int> _recentCount;
|
|
||||||
Fn<bool()> _recentPeersChoose;
|
|
||||||
Fn<JumpResult(Qt::Key, int)> _recentSelectJump;
|
|
||||||
Fn<uint64(QPoint)> _recentUpdateFromParentDrag;
|
|
||||||
Fn<void()> _recentDragLeft;
|
|
||||||
Fn<bool(not_null<QTouchEvent*>)> _recentProcessTouch;
|
|
||||||
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recentPeers;
|
|
||||||
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyRecent;
|
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyRecent;
|
||||||
|
|
||||||
const std::unique_ptr<Ui::ElasticScroll> _channelsScroll;
|
const std::unique_ptr<Ui::ElasticScroll> _channelsScroll;
|
||||||
const not_null<Ui::VerticalLayout*> _channelsContent;
|
const not_null<Ui::VerticalLayout*> _channelsContent;
|
||||||
|
|
||||||
rpl::variable<int> _myChannelsCount;
|
const std::unique_ptr<ObjectList> _myChannels;
|
||||||
Fn<bool()> _myChannelsChoose;
|
const std::unique_ptr<ObjectList> _recommendations;
|
||||||
Fn<JumpResult(Qt::Key, int)> _myChannelsSelectJump;
|
|
||||||
Fn<uint64(QPoint)> _myChannelsUpdateFromParentDrag;
|
|
||||||
Fn<void()> _myChannelsDragLeft;
|
|
||||||
Fn<bool(not_null<QTouchEvent*>)> _myChannelsProcessTouch;
|
|
||||||
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _myChannels;
|
|
||||||
|
|
||||||
rpl::variable<int> _recommendationsCount;
|
|
||||||
Fn<bool()> _recommendationsChoose;
|
|
||||||
Fn<JumpResult(Qt::Key, int)> _recommendationsSelectJump;
|
|
||||||
Fn<uint64(QPoint)> _recommendationsUpdateFromParentDrag;
|
|
||||||
Fn<void()> _recommendationsDragLeft;
|
|
||||||
Fn<bool(not_null<QTouchEvent*>)> _recommendationsProcessTouch;
|
|
||||||
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recommendations;
|
|
||||||
|
|
||||||
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyChannels;
|
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyChannels;
|
||||||
|
|
||||||
rpl::event_stream<not_null<PeerData*>> _topPeerChosen;
|
const std::unique_ptr<Ui::ElasticScroll> _appsScroll;
|
||||||
rpl::event_stream<not_null<PeerData*>> _recentPeerChosen;
|
const not_null<Ui::VerticalLayout*> _appsContent;
|
||||||
rpl::event_stream<not_null<PeerData*>> _myChannelChosen;
|
|
||||||
rpl::event_stream<not_null<PeerData*>> _recommendationChosen;
|
rpl::producer<> _recentAppsRefreshed;
|
||||||
|
Fn<bool(not_null<PeerData*>)> _recentAppsShows;
|
||||||
|
const std::unique_ptr<ObjectList> _recentApps;
|
||||||
|
const std::unique_ptr<ObjectList> _popularApps;
|
||||||
|
|
||||||
Ui::Animations::Simple _shownAnimation;
|
Ui::Animations::Simple _shownAnimation;
|
||||||
Fn<void()> _showFinished;
|
Fn<void()> _showFinished;
|
||||||
|
@ -179,6 +204,9 @@ private:
|
||||||
QPixmap _slideLeft;
|
QPixmap _slideLeft;
|
||||||
QPixmap _slideRight;
|
QPixmap _slideRight;
|
||||||
|
|
||||||
|
int _slideLeftTop = 0;
|
||||||
|
int _slideRightTop = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<TopPeersList> TopPeersContent(
|
[[nodiscard]] rpl::producer<TopPeersList> TopPeersContent(
|
||||||
|
|
|
@ -1515,6 +1515,20 @@ ServiceAction ParseServiceAction(
|
||||||
result.content = content;
|
result.content = content;
|
||||||
}, [&](const MTPDmessageActionRequestedPeerSentMe &data) {
|
}, [&](const MTPDmessageActionRequestedPeerSentMe &data) {
|
||||||
// Should not be in user inbox.
|
// Should not be in user inbox.
|
||||||
|
}, [&](const MTPDmessageActionPaymentRefunded &data) {
|
||||||
|
auto content = ActionPaymentRefunded();
|
||||||
|
content.currency = ParseString(data.vcurrency());
|
||||||
|
content.amount = data.vtotal_amount().v;
|
||||||
|
content.peerId = ParsePeerId(data.vpeer());
|
||||||
|
content.transactionId = data.vcharge().data().vid().v;
|
||||||
|
result.content = content;
|
||||||
|
}, [&](const MTPDmessageActionGiftStars &data) {
|
||||||
|
auto content = ActionGiftStars();
|
||||||
|
content.cost = Ui::FillAmountAndCurrency(
|
||||||
|
data.vamount().v,
|
||||||
|
qs(data.vcurrency())).toUtf8();
|
||||||
|
content.stars = data.vstars().v;
|
||||||
|
result.content = content;
|
||||||
}, [](const MTPDmessageActionEmpty &data) {});
|
}, [](const MTPDmessageActionEmpty &data) {});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -529,7 +529,7 @@ struct ActionWebViewDataSent {
|
||||||
|
|
||||||
struct ActionGiftPremium {
|
struct ActionGiftPremium {
|
||||||
Utf8String cost;
|
Utf8String cost;
|
||||||
int months;
|
int months = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ActionTopicCreate {
|
struct ActionTopicCreate {
|
||||||
|
@ -576,6 +576,18 @@ struct ActionBoostApply {
|
||||||
int boosts = 0;
|
int boosts = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ActionPaymentRefunded {
|
||||||
|
PeerId peerId = 0;
|
||||||
|
Utf8String currency;
|
||||||
|
uint64 amount = 0;
|
||||||
|
Utf8String transactionId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActionGiftStars {
|
||||||
|
Utf8String cost;
|
||||||
|
int stars = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct ServiceAction {
|
struct ServiceAction {
|
||||||
std::variant<
|
std::variant<
|
||||||
v::null_t,
|
v::null_t,
|
||||||
|
@ -617,7 +629,9 @@ struct ServiceAction {
|
||||||
ActionGiftCode,
|
ActionGiftCode,
|
||||||
ActionGiveawayLaunch,
|
ActionGiveawayLaunch,
|
||||||
ActionGiveawayResults,
|
ActionGiveawayResults,
|
||||||
ActionBoostApply> content;
|
ActionBoostApply,
|
||||||
|
ActionPaymentRefunded,
|
||||||
|
ActionGiftStars> content;
|
||||||
};
|
};
|
||||||
|
|
||||||
ServiceAction ParseServiceAction(
|
ServiceAction ParseServiceAction(
|
||||||
|
|
|
@ -1315,6 +1315,22 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||||
+ " boosted the group "
|
+ " boosted the group "
|
||||||
+ QByteArray::number(data.boosts)
|
+ QByteArray::number(data.boosts)
|
||||||
+ (data.boosts > 1 ? " times" : " time");
|
+ (data.boosts > 1 ? " times" : " time");
|
||||||
|
}, [&](const ActionPaymentRefunded &data) {
|
||||||
|
const auto amount = FormatMoneyAmount(data.amount, data.currency);
|
||||||
|
auto result = peers.wrapPeerName(data.peerId)
|
||||||
|
+ " refunded back "
|
||||||
|
+ amount;
|
||||||
|
return result;
|
||||||
|
}, [&](const ActionGiftStars &data) {
|
||||||
|
if (!data.stars || data.cost.isEmpty()) {
|
||||||
|
return serviceFrom + " sent you a gift.";
|
||||||
|
}
|
||||||
|
return serviceFrom
|
||||||
|
+ " sent you a gift for "
|
||||||
|
+ data.cost
|
||||||
|
+ ": "
|
||||||
|
+ QString::number(data.stars).toUtf8()
|
||||||
|
+ " Telegram Stars.";
|
||||||
}, [](v::null_t) { return QByteArray(); });
|
}, [](v::null_t) { return QByteArray(); });
|
||||||
|
|
||||||
if (!serviceText.isEmpty()) {
|
if (!serviceText.isEmpty()) {
|
||||||
|
|
|
@ -625,6 +625,22 @@ QByteArray SerializeMessage(
|
||||||
pushActor();
|
pushActor();
|
||||||
pushAction("boost_apply");
|
pushAction("boost_apply");
|
||||||
push("boosts", data.boosts);
|
push("boosts", data.boosts);
|
||||||
|
}, [&](const ActionPaymentRefunded &data) {
|
||||||
|
pushAction("refunded_payment");
|
||||||
|
push("amount", data.amount);
|
||||||
|
push("currency", data.currency);
|
||||||
|
pushBare("peer_name", wrapPeerName(data.peerId));
|
||||||
|
push("peer_id", data.peerId);
|
||||||
|
push("charge_id", data.transactionId);
|
||||||
|
}, [&](const ActionGiftStars &data) {
|
||||||
|
pushActor();
|
||||||
|
pushAction("send_stars_gift");
|
||||||
|
if (!data.cost.isEmpty()) {
|
||||||
|
push("cost", data.cost);
|
||||||
|
}
|
||||||
|
if (data.stars) {
|
||||||
|
push("stars", data.stars);
|
||||||
|
}
|
||||||
}, [](v::null_t) {});
|
}, [](v::null_t) {});
|
||||||
|
|
||||||
if (v::is_null(message.action.content)) {
|
if (v::is_null(message.action.content)) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item_helpers.h"
|
#include "history/history_item_helpers.h"
|
||||||
#include "history/history_translation.h"
|
#include "history/history_translation.h"
|
||||||
#include "history/history_unread_things.h"
|
#include "history/history_unread_things.h"
|
||||||
|
#include "core/ui_integration.h"
|
||||||
#include "dialogs/ui/dialogs_layout.h"
|
#include "dialogs/ui/dialogs_layout.h"
|
||||||
#include "data/business/data_shortcut_messages.h"
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/components/scheduled_messages.h"
|
#include "data/components/scheduled_messages.h"
|
||||||
|
@ -1136,14 +1137,23 @@ void History::applyServiceChanges(
|
||||||
}
|
}
|
||||||
if (paid) {
|
if (paid) {
|
||||||
// Toast on a current active window.
|
// Toast on a current active window.
|
||||||
|
const auto context = [=](not_null<QWidget*> toast) {
|
||||||
|
return Core::MarkedTextContext{
|
||||||
|
.session = &session(),
|
||||||
|
.customEmojiRepaint = [=] { toast->update(); },
|
||||||
|
};
|
||||||
|
};
|
||||||
Ui::Toast::Show({
|
Ui::Toast::Show({
|
||||||
.text = tr::lng_payments_success(
|
.text = tr::lng_payments_success(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_amount,
|
lt_amount,
|
||||||
Ui::Text::Bold(payment->amount),
|
Ui::Text::Wrapped(
|
||||||
|
payment->amount,
|
||||||
|
EntityType::Bold),
|
||||||
lt_title,
|
lt_title,
|
||||||
Ui::Text::Bold(paid->title),
|
Ui::Text::Bold(paid->title),
|
||||||
Ui::Text::WithEntities),
|
Ui::Text::WithEntities),
|
||||||
|
.textContext = context,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2232,8 +2232,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
if (!item->isService()
|
if (!item->isService()
|
||||||
&& peerIsChannel(itemId.peer)
|
&& peerIsChannel(itemId.peer)
|
||||||
&& !_peer->isMegagroup()) {
|
&& !_peer->isMegagroup()) {
|
||||||
|
constexpr auto kMinViewsCount = 10;
|
||||||
if (const auto channel = _peer->asChannel()) {
|
if (const auto channel = _peer->asChannel()) {
|
||||||
if (channel->flags() & ChannelDataFlag::CanGetStatistics) {
|
if ((channel->flags() & ChannelDataFlag::CanGetStatistics)
|
||||||
|
|| (channel->canPostMessages()
|
||||||
|
&& item->viewsCount() >= kMinViewsCount)) {
|
||||||
auto callback = crl::guard(controller, [=] {
|
auto callback = crl::guard(controller, [=] {
|
||||||
controller->showSection(
|
controller->showSection(
|
||||||
Info::Statistics::Make(channel, itemId, {}));
|
Info::Statistics::Make(channel, itemId, {}));
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue