Compare commits
138 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
232fceeee6 | ||
|
|
19c4130a1d | ||
|
|
dc5abf8ddd | ||
|
|
b334a1f4fd | ||
|
|
17f40d6a1f | ||
|
|
77078f704c | ||
|
|
df8176d671 | ||
|
|
cad8a85497 | ||
|
|
66093f0cb5 | ||
|
|
9633f93690 | ||
|
|
1b8352f878 | ||
|
|
27bcd35832 | ||
|
|
b401f0bfea | ||
|
|
36391617d7 | ||
|
|
5bf46c0e90 | ||
|
|
690e1013cf | ||
|
|
63c2db30f2 | ||
|
|
bf388d8a37 | ||
|
|
b4571b80d6 | ||
|
|
2cbd2725e5 | ||
|
|
7f969e5102 | ||
|
|
e878ccccb4 | ||
|
|
11bb4ec615 | ||
|
|
168cdaf350 | ||
|
|
0ae819397a | ||
|
|
627eba1c5a | ||
|
|
673eb1090f | ||
|
|
43f0da683f | ||
|
|
1bb6b07515 | ||
|
|
aacb720664 | ||
|
|
12dd72bddf | ||
|
|
891559b3d9 | ||
|
|
b43f8fcff7 | ||
|
|
ce40ecc7f9 | ||
|
|
d6c188d642 | ||
|
|
bd490421e8 | ||
|
|
b9b6d4dba1 | ||
|
|
349fbeeb23 | ||
|
|
68b1b595a5 | ||
|
|
cf4dfa55da | ||
|
|
8296d72923 | ||
|
|
2364b0ad4e | ||
|
|
076f0e0800 | ||
|
|
191ea6f0f4 | ||
|
|
7cf3babcbd | ||
|
|
98af2d3006 | ||
|
|
b3858d5d97 | ||
|
|
1d7ad701b4 | ||
|
|
9513aaa768 | ||
|
|
446f0f1653 | ||
|
|
7d9b999cb0 | ||
|
|
c647afec02 | ||
|
|
4c181b6d08 | ||
|
|
a4d3c694bc | ||
|
|
721b2ebe8a | ||
|
|
b135a09e00 | ||
|
|
6327d5ea38 | ||
|
|
2a99046bbd | ||
|
|
a3a48a38c8 | ||
|
|
4518067f9c | ||
|
|
54a12aa74f | ||
|
|
1dd83f3d34 | ||
|
|
c7c652a277 | ||
|
|
faf6c48f25 | ||
|
|
14f113266f | ||
|
|
5ebea97ded | ||
|
|
af350e2daa | ||
|
|
b0a24238e8 | ||
|
|
2ce8094932 | ||
|
|
ca0b34dcf0 | ||
|
|
833a259234 | ||
|
|
1ab8830ba8 | ||
|
|
40443b7547 | ||
|
|
ca1c826c5c | ||
|
|
bd1d7f4d96 | ||
|
|
5bee6310c0 | ||
|
|
3a38497c4c | ||
|
|
ae3659d15b | ||
|
|
25746d195c | ||
|
|
ae819eb1a6 | ||
|
|
48cf0a4382 | ||
|
|
5fe9c93cb6 | ||
|
|
cb99d611f3 | ||
|
|
0e93693856 | ||
|
|
9e93ecc154 | ||
|
|
2cf579426b | ||
|
|
6118ced862 | ||
|
|
8db8bc466e | ||
|
|
994d789c60 | ||
|
|
a043e22622 | ||
|
|
ccb3bbea15 | ||
|
|
1b73b34810 | ||
|
|
36de2e98d4 | ||
|
|
9fba9048af | ||
|
|
95b29f5f35 | ||
|
|
13eeddf479 | ||
|
|
3991be752c | ||
|
|
0f3ec7893d | ||
|
|
351a2eee08 | ||
|
|
6ff2c08764 | ||
|
|
d163135117 | ||
|
|
ecd217a79f | ||
|
|
4aa9c1fea3 | ||
|
|
2e60b28612 | ||
|
|
2bbe511a6f | ||
|
|
5cbdc2f739 | ||
|
|
6f88e46938 | ||
|
|
8209602cbd | ||
|
|
b3e547f4dd | ||
|
|
5289810b81 | ||
|
|
3cb333d323 | ||
|
|
2565b948d9 | ||
|
|
5101ea2a96 | ||
|
|
23150d4e2a | ||
|
|
66926ba25e | ||
|
|
b4a7d98fa0 | ||
|
|
620f6657ae | ||
|
|
577f4b6271 | ||
|
|
9c27271571 | ||
|
|
cad87f6818 | ||
|
|
a9a69c7d14 | ||
|
|
5990b0fabf | ||
|
|
6986430f37 | ||
|
|
04eff72ce8 | ||
|
|
985f557adf | ||
|
|
77b2572854 | ||
|
|
993b501996 | ||
|
|
bba45293da | ||
|
|
58fe2d7ecc | ||
|
|
cfddca8f58 | ||
|
|
6f50906952 | ||
|
|
1f39d16a35 | ||
|
|
54f697eba1 | ||
|
|
02e2fb1258 | ||
|
|
d01969ff1e | ||
|
|
e4d8a06cf9 | ||
|
|
8ec64f4167 | ||
|
|
06ea927095 |
1
.github/workflows/snap.yml
vendored
@@ -54,6 +54,7 @@ jobs:
|
||||
|
||||
- name: First set up.
|
||||
run: |
|
||||
sudo iptables -P FORWARD ACCEPT
|
||||
sudo snap install --classic snapcraft
|
||||
sudo usermod -aG lxd $USER
|
||||
sudo snap run lxd init --auto
|
||||
|
||||
18
.github/workflows/win.yml
vendored
@@ -82,9 +82,6 @@ jobs:
|
||||
submodules: recursive
|
||||
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
|
||||
|
||||
- name: Python installs.
|
||||
run: pip3 install --upgrade pywin32 six
|
||||
|
||||
- name: Set up environment paths.
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -119,13 +116,20 @@ jobs:
|
||||
DEFINE=""
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||
echo Define from matrix: $DEFINE
|
||||
echo "Define from matrix: $DEFINE"
|
||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.arch }}_${{ matrix.defines }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
|
||||
|
||||
API="-D TDESKTOP_API_TEST=ON"
|
||||
if [ ${{ github.ref == 'refs/heads/nightly' }} ]; then
|
||||
echo "Use the open credentials."
|
||||
API="-D TDESKTOP_API_ID=611335 -D TDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c"
|
||||
fi
|
||||
echo "TDESKTOP_BUILD_API=$API" >> $GITHUB_ENV
|
||||
|
||||
- name: Free up some disk space.
|
||||
run: |
|
||||
del /S Libraries\*.pdb
|
||||
@@ -139,7 +143,7 @@ jobs:
|
||||
|
||||
call configure.bat ^
|
||||
${{ matrix.arch }} ^
|
||||
-D TDESKTOP_API_TEST=ON ^
|
||||
%TDESKTOP_BUILD_API% ^
|
||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
|
||||
-D DESKTOP_APP_NO_PDB=ON ^
|
||||
%TDESKTOP_BUILD_DEFINE% ^
|
||||
@@ -149,13 +153,13 @@ jobs:
|
||||
msbuild -m Telegram.sln /p:Configuration=Debug,Platform=${{ matrix.arch }},DebugSymbols=false,DebugType=none
|
||||
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || ${{ github.ref == 'refs/heads/nightly' }}
|
||||
run: |
|
||||
mkdir artifact
|
||||
move %TBUILD%\%REPO_NAME%\out\Debug\Telegram.exe artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
name: Upload artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || ${{ github.ref == 'refs/heads/nightly' }}
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: artifact\
|
||||
|
||||
@@ -36,6 +36,10 @@ get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
|
||||
get_filename_component(submodules_loc "Telegram" REALPATH)
|
||||
get_filename_component(cmake_helpers_loc "cmake" REALPATH)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED AND WIN32)
|
||||
set(Python_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/python/Scripts/python)
|
||||
endif()
|
||||
|
||||
include(cmake/variables.cmake)
|
||||
include(cmake/nice_target_sources.cmake)
|
||||
include(cmake/target_compile_options_if_exists.cmake)
|
||||
@@ -52,7 +56,7 @@ include(cmake/options.cmake)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
if (WIN32)
|
||||
set(qt_version 5.15.4)
|
||||
set(qt_version 5.15.7)
|
||||
elseif (APPLE)
|
||||
set(qt_version 6.3.2)
|
||||
else()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
add_executable(Telegram WIN32 MACOSX_BUNDLE)
|
||||
init_non_host_target(Telegram)
|
||||
init_non_host_target(Telegram ltcg)
|
||||
|
||||
add_subdirectory(lib_rpl)
|
||||
add_subdirectory(lib_crl)
|
||||
@@ -184,6 +184,8 @@ PRIVATE
|
||||
boxes/peers/edit_forum_topic_box.h
|
||||
boxes/peers/edit_linked_chat_box.cpp
|
||||
boxes/peers/edit_linked_chat_box.h
|
||||
boxes/peers/edit_members_visible.cpp
|
||||
boxes/peers/edit_members_visible.h
|
||||
boxes/peers/edit_participant_box.cpp
|
||||
boxes/peers/edit_participant_box.h
|
||||
boxes/peers/edit_participants_box.cpp
|
||||
@@ -667,14 +669,18 @@ PRIVATE
|
||||
history/view/media/history_view_media_common.h
|
||||
history/view/media/history_view_media_grouped.cpp
|
||||
history/view/media/history_view_media_grouped.h
|
||||
history/view/media/history_view_media_spoiler.cpp
|
||||
history/view/media/history_view_media_spoiler.h
|
||||
history/view/media/history_view_media_unwrapped.cpp
|
||||
history/view/media/history_view_media_unwrapped.h
|
||||
history/view/media/history_view_photo.cpp
|
||||
history/view/media/history_view_photo.h
|
||||
history/view/media/history_view_poll.cpp
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/history_view_service_media_gift.cpp
|
||||
history/view/media/history_view_service_media_gift.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_slot_machine.cpp
|
||||
history/view/media/history_view_slot_machine.h
|
||||
history/view/media/history_view_sticker.cpp
|
||||
@@ -684,6 +690,8 @@ PRIVATE
|
||||
history/view/media/history_view_sticker_player_abstract.h
|
||||
history/view/media/history_view_theme_document.cpp
|
||||
history/view/media/history_view_theme_document.h
|
||||
history/view/media/history_view_userpic_suggestion.cpp
|
||||
history/view/media/history_view_userpic_suggestion.h
|
||||
history/view/media/history_view_web_page.cpp
|
||||
history/view/media/history_view_web_page.h
|
||||
history/view/reactions/history_view_reactions.cpp
|
||||
@@ -764,6 +772,8 @@ PRIVATE
|
||||
history/history_item_components.h
|
||||
history/history_item_edition.cpp
|
||||
history/history_item_edition.h
|
||||
history/history_item_helpers.cpp
|
||||
history/history_item_helpers.h
|
||||
history/history_item_reply_markup.cpp
|
||||
history/history_item_reply_markup.h
|
||||
history/history_item_text.cpp
|
||||
@@ -772,10 +782,6 @@ PRIVATE
|
||||
history/history_inner_widget.h
|
||||
history/history_location_manager.cpp
|
||||
history/history_location_manager.h
|
||||
history/history_message.cpp
|
||||
history/history_message.h
|
||||
history/history_service.cpp
|
||||
history/history_service.h
|
||||
history/history_unread_things.cpp
|
||||
history/history_unread_things.h
|
||||
history/history_view_highlight_manager.cpp
|
||||
@@ -1254,6 +1260,10 @@ PRIVATE
|
||||
ui/chat/choose_send_as.h
|
||||
ui/chat/choose_theme_controller.cpp
|
||||
ui/chat/choose_theme_controller.h
|
||||
ui/controls/silent_toggle.cpp
|
||||
ui/controls/silent_toggle.h
|
||||
ui/controls/userpic_button.cpp
|
||||
ui/controls/userpic_button.h
|
||||
ui/effects/emoji_fly_animation.cpp
|
||||
ui/effects/emoji_fly_animation.h
|
||||
ui/effects/message_sending_animation_common.h
|
||||
@@ -1282,8 +1292,6 @@ PRIVATE
|
||||
ui/resize_area.h
|
||||
ui/search_field_controller.cpp
|
||||
ui/search_field_controller.h
|
||||
ui/special_buttons.cpp
|
||||
ui/special_buttons.h
|
||||
ui/text/format_song_document_name.cpp
|
||||
ui/text/format_song_document_name.h
|
||||
ui/unread_badge.cpp
|
||||
@@ -1601,17 +1609,6 @@ if (WIN32)
|
||||
/DELAYLOAD:wtsapi32.dll
|
||||
/DELAYLOAD:propsys.dll
|
||||
)
|
||||
|
||||
if (NOT build_win64 AND DESKTOP_APP_SPECIAL_TARGET)
|
||||
target_compile_options(Telegram
|
||||
PRIVATE
|
||||
$<IF:$<CONFIG:Debug>,,/GL>
|
||||
)
|
||||
target_link_options(Telegram
|
||||
PRIVATE
|
||||
$<IF:$<CONFIG:Debug>,,/LTCG>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_prepare_qrc(Telegram)
|
||||
|
||||
BIN
Telegram/Resources/icons/info/edit/hidden_members.png
Normal file
|
After Width: | Height: | Size: 461 B |
BIN
Telegram/Resources/icons/info/edit/hidden_members@2x.png
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
Telegram/Resources/icons/info/edit/hidden_members@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/menu/photo_set.png
Normal file
|
After Width: | Height: | Size: 722 B |
BIN
Telegram/Resources/icons/menu/photo_set@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/menu/photo_set@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/menu/photo_suggest.png
Normal file
|
After Width: | Height: | Size: 768 B |
BIN
Telegram/Resources/icons/menu/photo_suggest@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/menu/photo_suggest@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/menu/spoiler_off.png
Normal file
|
After Width: | Height: | Size: 797 B |
BIN
Telegram/Resources/icons/menu/spoiler_off@2x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/menu/spoiler_off@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/menu/spoiler_on.png
Normal file
|
After Width: | Height: | Size: 713 B |
BIN
Telegram/Resources/icons/menu/spoiler_on@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/menu/spoiler_on@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@@ -166,6 +166,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_scam_badge" = "SCAM";
|
||||
"lng_fake_badge" = "FAKE";
|
||||
|
||||
"lng_remember" = "Remember this choice";
|
||||
|
||||
"lng_channels_limit_title" = "Too Many Communities";
|
||||
"lng_channels_limit1#one" = "You are a member of **{count}** groups and channels.";
|
||||
"lng_channels_limit1#other" = "You are a member of **{count}** groups and channels.";
|
||||
@@ -306,18 +308,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_caption_voice" = "Sorry, you can't edit your message while you're having an unsent voice message.";
|
||||
|
||||
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
|
||||
"lng_start_msgs" = "START MESSAGING";
|
||||
"lng_start_msgs" = "Start messaging";
|
||||
|
||||
"lng_intro_next" = "NEXT";
|
||||
"lng_intro_finish" = "SIGN UP";
|
||||
"lng_intro_submit" = "SUBMIT";
|
||||
"lng_intro_next" = "Next";
|
||||
"lng_intro_finish" = "Sign up";
|
||||
"lng_intro_submit" = "Submit";
|
||||
|
||||
"lng_photo_caption" = "Caption";
|
||||
"lng_photos_comment" = "Comment";
|
||||
|
||||
"lng_intro_qr_title" = "Scan From Mobile Telegram";
|
||||
"lng_intro_qr_step1" = "Open Telegram on your phone";
|
||||
"lng_intro_qr_step2" = "Go to Settings > Devices > Scan QR Code";
|
||||
"lng_intro_qr_step2" = "Go to Settings > Devices > Link Desktop";
|
||||
"lng_intro_qr_step3" = "Scan this image to Log In";
|
||||
"lng_intro_qr_skip" = "Or log in using your phone number";
|
||||
|
||||
@@ -391,8 +393,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_settings_save" = "Save";
|
||||
"lng_settings_upload" = "Set Profile Photo";
|
||||
"lng_settings_crop_profile" = "Select an area for your profile photo";
|
||||
"lng_settings_uploading_photo" = "Uploading photo...";
|
||||
"lng_settings_edit" = "Edit";
|
||||
"lng_settings_drop_area_subtitle" = "to set it as your photo";
|
||||
|
||||
@@ -493,6 +493,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_change_lang" = "Change language";
|
||||
"lng_languages" = "Languages";
|
||||
"lng_languages_none" = "No languages found.";
|
||||
"lng_languages_count#one" = "{count} language";
|
||||
"lng_languages_count#other" = "{count} languages";
|
||||
"lng_sure_save_language" = "Telegram will restart in order to change language";
|
||||
"lng_settings_update_automatically" = "Update automatically";
|
||||
"lng_settings_install_beta" = "Install beta versions";
|
||||
@@ -691,6 +693,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_ttl_select_chats_title" = "Select Chats";
|
||||
"lng_settings_ttl_select_chats_subtitle" = "to apply the self-destruct timer";
|
||||
"lng_settings_ttl_select_chats_subtitle_chosen" = "will have the self-destruct timer";
|
||||
"lng_settings_ttl_select_chats_sorry" = "Sorry, you can't set self-destruct timer for this chat.";
|
||||
"lng_settings_ttl_select_chats_status" = "auto-delete after {after_duration}";
|
||||
"lng_settings_ttl_select_chats_status_disabled" = "auto-deletion disabled";
|
||||
"lng_settings_ttl_select_chats_toast#one" = "Self-destruct timer for {duration} has been enabled in {count} selected chat.";
|
||||
@@ -1036,6 +1039,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_forwards_always_empty" = "Always allow";
|
||||
"lng_edit_privacy_forwards_never_empty" = "Never allow";
|
||||
"lng_edit_privacy_forwards_exceptions" = "These settings will override the values above.";
|
||||
"lng_edit_privacy_forwards_exceptions_everyone" = "You can add users or entire groups which will not see your profile photo.";
|
||||
"lng_edit_privacy_forwards_exceptions_nobody" = "Add users or entire groups which will still see your profile photo.";
|
||||
"lng_edit_privacy_forwards_always_title" = "Always allow";
|
||||
"lng_edit_privacy_forwards_never_title" = "Never allow";
|
||||
"lng_edit_privacy_forwards_sample_message" = "Reinhardt, we need to find you some new tunes 🎶";
|
||||
@@ -1050,6 +1055,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_profile_photo_exceptions" = "These settings will override the values above.";
|
||||
"lng_edit_privacy_profile_photo_always_title" = "Always allow";
|
||||
"lng_edit_privacy_profile_photo_never_title" = "Never allow";
|
||||
"lng_edit_privacy_profile_photo_public_set" = "Set Public Photo";
|
||||
"lng_edit_privacy_profile_photo_public_update" = "Update Public Photo";
|
||||
"lng_edit_privacy_profile_photo_public_remove" = "Remove Public Photo";
|
||||
"lng_edit_privacy_profile_photo_public_about" = "You can upload a public photo for those who are restricted from viewing your real profile photo.";
|
||||
"lng_edit_privacy_profile_photo_public_toast" = "This photo is now set for those who are restricted from viewing your main photo.";
|
||||
|
||||
"lng_edit_privacy_voices_title" = "Voice messages settings";
|
||||
"lng_edit_privacy_voices_header" = "Who can send me voice messages";
|
||||
@@ -1136,6 +1146,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_set_group_photo" = "Set Photo";
|
||||
"lng_profile_add_participant" = "Add Members";
|
||||
"lng_profile_add_via_link" = "Invite via Link";
|
||||
"lng_profile_hide_participants" = "Hide Members";
|
||||
"lng_profile_hide_participants_about" = "Switch this on to hide the list of members in this group. Admins will remain visible.";
|
||||
"lng_profile_view_channel" = "View Channel";
|
||||
"lng_profile_view_discussion" = "View discussion";
|
||||
"lng_profile_join_channel" = "Join Channel";
|
||||
@@ -1178,6 +1190,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_profile_info_section" = "Info";
|
||||
"lng_info_tab_media" = "Media";
|
||||
"lng_info_public_photo" = "public photo";
|
||||
"lng_info_mobile_label" = "Mobile";
|
||||
"lng_info_mobile_hidden" = "Hidden";
|
||||
"lng_info_username_label" = "Username";
|
||||
@@ -1195,6 +1208,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_info_add_as_contact" = "Add to contacts";
|
||||
"lng_profile_shared_media" = "Shared media";
|
||||
"lng_profile_suggest_photo" = "Suggest Profile Photo";
|
||||
"lng_profile_set_photo_for" = "Set Profile Photo";
|
||||
"lng_profile_photo_reset" = "Reset to Original";
|
||||
"lng_profile_set_for_done" = "You will now always see this photo for {user}'s account.";
|
||||
"lng_profile_suggest_sure" = "You can suggest {user} to set this photo for their page.";
|
||||
"lng_profile_suggest_button" = "Suggest";
|
||||
"lng_profile_set_personal_sure" = "Only you will see this photo and it will replace any photo {user} sets for themselves.";
|
||||
"lng_profile_accept_photo_sure" = "{user} suggests you to use this profile photo for your Telegram account.";
|
||||
"lng_profile_set_photo_button" = "Set Photo";
|
||||
"lng_profile_accept_video_sure" = "{user} suggests you to use this profile video for your Telegram account.";
|
||||
"lng_profile_set_video_button" = "Set Video";
|
||||
"lng_profile_changed_photo_title" = "Photo updated";
|
||||
"lng_profile_changed_photo_about" = "You can change it in {link}.";
|
||||
"lng_profile_changed_photo_link" = "Settings";
|
||||
"lng_media_type_photos" = "Photos";
|
||||
"lng_media_type_gifs" = "GIFs";
|
||||
"lng_media_type_videos" = "Videos";
|
||||
@@ -1428,9 +1455,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_create_channel_link_pending" = "Checking name...";
|
||||
"lng_create_channel_link_copied" = "Link copied to clipboard";
|
||||
|
||||
"lng_create_group_crop" = "Select an area for group photo";
|
||||
"lng_create_channel_crop" = "Select an area for channel photo";
|
||||
|
||||
"lng_failed_add_participant" = "Could not add user. Please try again later.";
|
||||
"lng_failed_add_not_mutual" = "Sorry, if a person leaves a group, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
|
||||
"lng_failed_add_not_mutual_channel" = "Sorry, if a person leaves a channel, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
|
||||
@@ -1553,12 +1577,23 @@ 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_gift_received" = "{user} sent you a gift for {cost}";
|
||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||
"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_button" = "View Photo";
|
||||
"lng_action_suggested_video_me" = "You suggested {user} to use this profile video.";
|
||||
"lng_action_suggested_video" = "{user} suggests you to use this profile video.";
|
||||
"lng_action_suggested_video_button" = "View Video";
|
||||
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topic closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
"lng_action_topic_hidden_inside" = "Topic hidden";
|
||||
"lng_action_topic_unhidden_inside" = "Topic unhidden";
|
||||
"lng_action_topic_created" = "«{topic}» was created";
|
||||
"lng_action_topic_closed" = "«{topic}» was closed";
|
||||
"lng_action_topic_reopened" = "«{topic}» was reopened";
|
||||
"lng_action_topic_hidden" = "«{topic}» was hidden";
|
||||
"lng_action_topic_unhidden" = "«{topic}» was unhidden";
|
||||
"lng_action_topic_placeholder" = "topic";
|
||||
"lng_action_topic_renamed" = "{from} renamed the {link} to «{title}»";
|
||||
"lng_action_topic_icon_changed" = "{from} changed the {link} icon to {emoji}";
|
||||
@@ -2312,6 +2347,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_animated_reactions_many#one" = "Reactions contain emoji from **{count} pack**.";
|
||||
"lng_context_animated_reactions_many#other" = "Reactions contain emoji from **{count} packs**.";
|
||||
|
||||
"lng_context_spoiler_effect" = "Hide with Spoiler";
|
||||
"lng_context_disable_spoiler" = "Remove Spoiler";
|
||||
|
||||
"lng_downloads_section" = "Downloads";
|
||||
"lng_downloads_view_in_chat" = "View in chat";
|
||||
"lng_downloads_view_in_section" = "View in downloads";
|
||||
@@ -2325,17 +2363,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
"lng_send_images_selected#one" = "{count} image selected";
|
||||
"lng_send_images_selected#other" = "{count} images selected";
|
||||
"lng_send_photos#one" = "Send {count} photo";
|
||||
"lng_send_photos#other" = "Send {count} photos";
|
||||
"lng_send_separate_photos" = "Send as separate photos";
|
||||
"lng_send_separate_photos_videos" = "Send as separate media";
|
||||
"lng_send_files_selected#one" = "{count} file selected";
|
||||
"lng_send_files_selected#other" = "{count} files selected";
|
||||
"lng_send_files#one" = "Send {count} file";
|
||||
"lng_send_files#other" = "Send {count} files";
|
||||
"lng_send_grouped" = "Group items";
|
||||
"lng_send_compressed_one" = "Compress the image";
|
||||
"lng_send_compressed" = "Compress images";
|
||||
"lng_send_media_invalid_files" = "Sorry, no valid files found.";
|
||||
"lng_send_image" = "Send an image";
|
||||
"lng_send_file" = "Send a file";
|
||||
"lng_send_video" = "Send a video file";
|
||||
|
||||
"lng_forward_choose" = "Choose recipient...";
|
||||
"lng_forward_cant" = "Sorry, no way to forward here :(";
|
||||
@@ -2454,6 +2490,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_mediaview_group_photo" = "Group Photo";
|
||||
"lng_mediaview_channel_photo" = "Channel Photo";
|
||||
"lng_mediaview_profile_photo" = "Profile Photo";
|
||||
"lng_mediaview_profile_public_photo" = "Public Photo";
|
||||
"lng_mediaview_file_n_of_amount" = "{file} {n} of {amount}";
|
||||
"lng_mediaview_n_of_amount" = "Photo {n} of {amount}";
|
||||
"lng_mediaview_doc_image" = "File";
|
||||
@@ -3102,6 +3139,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_participant_joined_by_link_channel" = "{from} joined the channel via {link}";
|
||||
"lng_admin_log_participant_approved_by_link" = "{from} was approved to join the group via {link} by {user}";
|
||||
"lng_admin_log_participant_approved_by_link_channel" = "{from} was approved to join the channel via {link} by {user}";
|
||||
"lng_admin_log_participant_approved_by_request" = "{from} joined to the group via public request, approved by {user}";
|
||||
"lng_admin_log_participant_approved_by_request_channel" = "{from} joined to the channel via public request, approved by {user}";
|
||||
"lng_admin_log_revoke_invite_link" = "{from} revoked invite link {link}";
|
||||
"lng_admin_log_delete_invite_link" = "{from} deleted invite link {link}";
|
||||
"lng_admin_log_participant_left" = "{from} left the group";
|
||||
@@ -3161,6 +3200,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_topics_changed" = "{from} changed topic {topic} to {new_topic}";
|
||||
"lng_admin_log_topics_closed" = "{from} closed topic {topic}";
|
||||
"lng_admin_log_topics_reopened" = "{from} reopened topic {topic}";
|
||||
"lng_admin_log_topics_hidden" = "{from} hid topic {topic}";
|
||||
"lng_admin_log_topics_unhidden" = "{from} unhid topic {topic}";
|
||||
"lng_admin_log_topics_deleted" = "{from} deleted topic {topic}";
|
||||
"lng_admin_log_topics_pinned" = "{from} pinned topic {topic}";
|
||||
"lng_admin_log_topics_unpinned" = "{from} unpinned topic {topic}";
|
||||
|
||||
@@ -58,15 +58,15 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
|
||||
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
|
||||
|
||||
inputMediaEmpty#9664f57f = InputMedia;
|
||||
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaUploadedPhoto#1e287d04 flags:# spoiler:flags.2?true file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaPhoto#b3ba0635 flags:# spoiler:flags.1?true id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
|
||||
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
|
||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#33473058 flags:# id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia;
|
||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#33473058 flags:# spoiler:flags.2?true id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia;
|
||||
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
||||
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaPhotoExternal#e5bbfe1a flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||
inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
|
||||
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
|
||||
@@ -113,7 +113,7 @@ userEmpty#d3bc4b7a id:long = User;
|
||||
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
|
||||
userStatusEmpty#9d05049 = UserStatus;
|
||||
userStatusOnline#edb93949 expires:int = UserStatus;
|
||||
@@ -129,7 +129,7 @@ channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
|
||||
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
|
||||
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
|
||||
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
|
||||
|
||||
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
||||
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
||||
@@ -146,11 +146,11 @@ message#38116ee0 flags:# out:flags.1?true mentioned:flags.4?true media_unread:fl
|
||||
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
||||
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
|
||||
messageMediaUnsupported#9f84f49e = MessageMedia;
|
||||
messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true spoiler:flags.4?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
||||
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
|
||||
messageMediaGame#fdb19008 game:Game = MessageMedia;
|
||||
@@ -194,6 +194,8 @@ messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
||||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction;
|
||||
messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
|
||||
messageActionAttachMenuBotAllowed#e7e75f97 = MessageAction;
|
||||
|
||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
@@ -244,7 +246,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
|
||||
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
|
||||
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
|
||||
|
||||
userFull#c4b1fc3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
|
||||
userFull#f8d32aed flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
|
||||
|
||||
contact#145ade0b user_id:long mutual:Bool = Contact;
|
||||
|
||||
@@ -302,7 +304,6 @@ updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction
|
||||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
|
||||
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
|
||||
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||
updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
|
||||
@@ -403,6 +404,7 @@ updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?tru
|
||||
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
|
||||
updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update;
|
||||
updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<int> = Update;
|
||||
updateUser#20529438 user_id:long = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -547,7 +549,7 @@ documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_strea
|
||||
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
|
||||
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
|
||||
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
|
||||
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true alt:string stickerset:InputStickerSet = DocumentAttribute;
|
||||
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true text_color:flags.1?true alt:string stickerset:InputStickerSet = DocumentAttribute;
|
||||
|
||||
messages.stickersNotModified#f1749a22 = messages.Stickers;
|
||||
messages.stickers#30a6ec7e hash:long stickers:Vector<Document> = messages.Stickers;
|
||||
@@ -625,7 +627,7 @@ keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
|
||||
|
||||
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
|
||||
replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup;
|
||||
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
|
||||
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true persistent:flags.4?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
|
||||
replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
|
||||
|
||||
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
|
||||
@@ -780,6 +782,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
|
||||
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
|
||||
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
|
||||
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = StickerSetCovered;
|
||||
stickerSetNoCovered#77b15d1c set:StickerSet = StickerSetCovered;
|
||||
|
||||
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
|
||||
|
||||
@@ -1379,7 +1382,7 @@ attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;
|
||||
|
||||
attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;
|
||||
|
||||
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
|
||||
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true request_write_access:flags.2?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
|
||||
|
||||
attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
|
||||
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;
|
||||
@@ -1781,7 +1784,7 @@ messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int =
|
||||
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
|
||||
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
|
||||
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
|
||||
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
|
||||
messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool;
|
||||
messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
|
||||
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
|
||||
@@ -1804,10 +1807,11 @@ updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||
|
||||
photos.updateProfilePhoto#72d4742c id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
photos.updateProfilePhoto#1c3d5956 flags:# fallback:flags.0?true id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#89f30f69 flags:# fallback:flags.3?true file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||
photos.uploadContactProfilePhoto#b91a83bf flags:# suggest:flags.3?true save:flags.4?true user_id:InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
|
||||
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
|
||||
@@ -1896,6 +1900,7 @@ channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messa
|
||||
channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:InputChannel order:Vector<int> = Updates;
|
||||
channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool;
|
||||
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@@ -1974,4 +1979,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
||||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 150
|
||||
// LAYER 151
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.4.0.0" />
|
||||
Version="4.4.2.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,4,0,0
|
||||
PRODUCTVERSION 4,4,0,0
|
||||
FILEVERSION 4,4,2,0
|
||||
PRODUCTVERSION 4,4,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "4.4.0.0"
|
||||
VALUE "FileVersion", "4.4.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.4.0.0"
|
||||
VALUE "ProductVersion", "4.4.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,4,0,0
|
||||
PRODUCTVERSION 4,4,0,0
|
||||
FILEVERSION 4,4,2,0
|
||||
PRODUCTVERSION 4,4,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "4.4.0.0"
|
||||
VALUE "FileVersion", "4.4.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.4.0.0"
|
||||
VALUE "ProductVersion", "4.4.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -79,12 +79,16 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
const auto nowDate = now.date();
|
||||
const auto lastDate = lastTime.date();
|
||||
if (lastDate == nowDate) {
|
||||
result.active = QLocale().toString(lastTime, cTimeFormat());
|
||||
result.active = QLocale().toString(
|
||||
lastTime.time(),
|
||||
QLocale::ShortFormat);
|
||||
} else if (lastDate.year() == nowDate.year()
|
||||
&& lastDate.weekNumber() == nowDate.weekNumber()) {
|
||||
result.active = langDayOfWeek(lastDate);
|
||||
} else {
|
||||
result.active = QLocale().toString(lastDate, cDateFormat());
|
||||
result.active = QLocale().toString(
|
||||
lastDate,
|
||||
QLocale::ShortFormat);
|
||||
}
|
||||
}
|
||||
result.location = country;
|
||||
|
||||
@@ -110,7 +110,9 @@ void SendBotCallbackData(
|
||||
const auto showAlert = data.is_alert();
|
||||
|
||||
if (!message.isEmpty()) {
|
||||
if (showAlert) {
|
||||
if (!show->valid()) {
|
||||
return;
|
||||
} else if (showAlert) {
|
||||
show->showBox(Ui::MakeInformBox(message));
|
||||
} else {
|
||||
if (withPassword) {
|
||||
|
||||
@@ -81,6 +81,7 @@ void SubmitChatInvite(
|
||||
} else if (type == u"CHANNELS_TOO_MUCH"_q) {
|
||||
strongController->show(
|
||||
Box(ChannelsLimitBox, &strongController->session()));
|
||||
return;
|
||||
}
|
||||
|
||||
strongController->hideLayer();
|
||||
|
||||
@@ -365,6 +365,7 @@ void ChatParticipants::requestForAdd(
|
||||
|
||||
void ChatParticipants::requestLast(not_null<ChannelData*> channel) {
|
||||
if (!channel->isMegagroup()
|
||||
|| !channel->canViewMembers()
|
||||
|| _participantsRequests.contains(channel)) {
|
||||
return;
|
||||
}
|
||||
@@ -532,6 +533,7 @@ ChatParticipants::Parsed ChatParticipants::ParseRecent(
|
||||
const TLMembers &data) {
|
||||
const auto result = Parse(channel, data);
|
||||
const auto applyLast = channel->isMegagroup()
|
||||
&& channel->canViewMembers()
|
||||
&& (channel->mgInfo->lastParticipants.size() <= result.list.size());
|
||||
if (applyLast) {
|
||||
ApplyLastList(channel, result.availableCount, result.list);
|
||||
|
||||
@@ -206,7 +206,7 @@ void EditMessageWithUploadedPhoto(
|
||||
EditMessageWithUploadedMedia(
|
||||
item,
|
||||
options,
|
||||
PrepareUploadedPhoto(std::move(info)));
|
||||
PrepareUploadedPhoto(item, std::move(info)));
|
||||
}
|
||||
|
||||
mtpRequestId EditCaption(
|
||||
|
||||
@@ -75,10 +75,14 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
|
||||
|
||||
} // namespace
|
||||
|
||||
MTPInputMedia PrepareUploadedPhoto(RemoteFileInfo info) {
|
||||
const auto flags = info.attachedStickers.empty()
|
||||
? MTPDinputMediaUploadedPhoto::Flags(0)
|
||||
: MTPDinputMediaUploadedPhoto::Flag::f_stickers;
|
||||
MTPInputMedia PrepareUploadedPhoto(
|
||||
not_null<HistoryItem*> item,
|
||||
RemoteFileInfo info) {
|
||||
using Flag = MTPDinputMediaUploadedPhoto::Flag;
|
||||
const auto spoiler = item->media()
|
||||
&& item->media()->hasSpoiler();
|
||||
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
|
||||
| (info.attachedStickers.empty() ? Flag() : Flag::f_stickers);
|
||||
return MTP_inputMediaUploadedPhoto(
|
||||
MTP_flags(flags),
|
||||
info.file,
|
||||
@@ -93,12 +97,13 @@ MTPInputMedia PrepareUploadedDocument(
|
||||
if (!item || !item->media() || !item->media()->document()) {
|
||||
return MTP_inputMediaEmpty();
|
||||
}
|
||||
const auto emptyFlag = MTPDinputMediaUploadedDocument::Flags(0);
|
||||
using DocFlags = MTPDinputMediaUploadedDocument::Flag;
|
||||
const auto flags = emptyFlag
|
||||
| (info.thumb ? DocFlags::f_thumb : emptyFlag)
|
||||
| (item->groupId() ? DocFlags::f_nosound_video : emptyFlag)
|
||||
| (info.attachedStickers.empty() ? DocFlags::f_stickers : emptyFlag);
|
||||
using Flag = MTPDinputMediaUploadedDocument::Flag;
|
||||
const auto spoiler = item->media()
|
||||
&& item->media()->hasSpoiler();
|
||||
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
|
||||
| (info.thumb ? Flag::f_thumb : Flag())
|
||||
| (item->groupId() ? Flag::f_nosound_video : Flag())
|
||||
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag());
|
||||
const auto document = item->media()->document();
|
||||
return MTP_inputMediaUploadedDocument(
|
||||
MTP_flags(flags),
|
||||
|
||||
@@ -13,7 +13,9 @@ namespace Api {
|
||||
|
||||
struct RemoteFileInfo;
|
||||
|
||||
MTPInputMedia PrepareUploadedPhoto(RemoteFileInfo info);
|
||||
MTPInputMedia PrepareUploadedPhoto(
|
||||
not_null<HistoryItem*> item,
|
||||
RemoteFileInfo info);
|
||||
|
||||
MTPInputMedia PrepareUploadedDocument(
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -13,10 +13,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_user_photos.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/file_upload.h"
|
||||
@@ -112,7 +114,54 @@ PeerPhoto::PeerPhoto(not_null<ApiWrap*> api)
|
||||
});
|
||||
}
|
||||
|
||||
void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
|
||||
void PeerPhoto::upload(
|
||||
not_null<PeerData*> peer,
|
||||
QImage &&image,
|
||||
Fn<void()> done) {
|
||||
upload(peer, std::move(image), UploadType::Default, std::move(done));
|
||||
}
|
||||
|
||||
void PeerPhoto::uploadFallback(not_null<PeerData*> peer, QImage &&image) {
|
||||
upload(peer, std::move(image), UploadType::Fallback, nullptr);
|
||||
}
|
||||
|
||||
void PeerPhoto::updateSelf(
|
||||
not_null<PhotoData*> photo,
|
||||
Data::FileOrigin origin,
|
||||
Fn<void()> done) {
|
||||
const auto send = [=](auto resend) -> void {
|
||||
const auto usedFileReference = photo->fileReference();
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
photo->mtpInput()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
_session->data().processPhoto(data.vphoto());
|
||||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
||||
photo->session().api().refreshFileReference(origin, [=](
|
||||
const auto &) {
|
||||
if (photo->fileReference() != usedFileReference) {
|
||||
resend(resend);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
send(send);
|
||||
}
|
||||
|
||||
void PeerPhoto::upload(
|
||||
not_null<PeerData*> peer,
|
||||
QImage &&image,
|
||||
UploadType type,
|
||||
Fn<void()> done) {
|
||||
peer = peer->migrateToOrMe();
|
||||
const auto ready = PreparePeerPhoto(
|
||||
_api.instance().mainDcId(),
|
||||
@@ -125,19 +174,26 @@ void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
|
||||
const auto already = ranges::find(
|
||||
_uploads,
|
||||
peer,
|
||||
[](const auto &pair) { return pair.second; });
|
||||
[](const auto &pair) { return pair.second.peer; });
|
||||
if (already != end(_uploads)) {
|
||||
_session->uploader().cancel(already->first);
|
||||
_uploads.erase(already);
|
||||
}
|
||||
_uploads.emplace(fakeId, peer);
|
||||
_uploads.emplace(
|
||||
fakeId,
|
||||
UploadValue{ peer, type, std::move(done) });
|
||||
_session->uploader().uploadMedia(fakeId, ready);
|
||||
}
|
||||
|
||||
void PeerPhoto::suggest(not_null<PeerData*> peer, QImage &&image) {
|
||||
upload(peer, std::move(image), UploadType::Suggestion, nullptr);
|
||||
}
|
||||
|
||||
void PeerPhoto::clear(not_null<PhotoData*> photo) {
|
||||
const auto self = _session->user();
|
||||
if (self->userpicPhotoId() == photo->id) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
MTP_inputPhotoEmpty()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
self->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
@@ -158,12 +214,44 @@ void PeerPhoto::clear(not_null<PhotoData*> photo) {
|
||||
)).done(applier).send();
|
||||
}
|
||||
} else {
|
||||
_api.request(MTPphotos_DeletePhotos(
|
||||
MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
|
||||
)).send();
|
||||
const auto fallbackPhotoId = SyncUserFallbackPhotoViewer(self);
|
||||
if (fallbackPhotoId && (*fallbackPhotoId) == photo->id) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(MTPphotos_UpdateProfilePhoto::Flag::f_fallback),
|
||||
MTP_inputPhotoEmpty()
|
||||
)).send();
|
||||
_session->storage().add(Storage::UserPhotosSetBack(
|
||||
peerToUser(self->id),
|
||||
PhotoId()));
|
||||
} else {
|
||||
_api.request(MTPphotos_DeletePhotos(
|
||||
MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
|
||||
)).send();
|
||||
_session->storage().remove(Storage::UserPhotosRemoveOne(
|
||||
peerToUser(self->id),
|
||||
photo->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerPhoto::clearPersonal(not_null<UserData*> user) {
|
||||
_api.request(MTPphotos_UploadContactProfilePhoto(
|
||||
MTP_flags(MTPphotos_UploadContactProfilePhoto::Flag::f_save),
|
||||
user->inputUser,
|
||||
MTPInputFile(),
|
||||
MTPInputFile(), // video
|
||||
MTPdouble() // video_start_ts
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
_session->data().processPhoto(data.vphoto());
|
||||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
}).send();
|
||||
|
||||
if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
|
||||
_session->storage().remove(Storage::UserPhotosRemoveOne(
|
||||
peerToUser(self->id),
|
||||
photo->id));
|
||||
peerToUser(user->id),
|
||||
user->userpicPhotoId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +261,7 @@ void PeerPhoto::set(not_null<PeerData*> peer, not_null<PhotoData*> photo) {
|
||||
}
|
||||
if (peer == _session->user()) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
MTP_flags(0),
|
||||
photo->mtpInput()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
@@ -199,25 +288,40 @@ void PeerPhoto::set(not_null<PeerData*> peer, not_null<PhotoData*> photo) {
|
||||
}
|
||||
|
||||
void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
|
||||
const auto maybePeer = _uploads.take(msgId);
|
||||
if (!maybePeer) {
|
||||
const auto maybeUploadValue = _uploads.take(msgId);
|
||||
if (!maybeUploadValue) {
|
||||
return;
|
||||
}
|
||||
const auto peer = *maybePeer;
|
||||
const auto peer = maybeUploadValue->peer;
|
||||
const auto type = maybeUploadValue->type;
|
||||
const auto done = maybeUploadValue->done;
|
||||
const auto applier = [=](const MTPUpdates &result) {
|
||||
_session->updates().applyUpdates(result);
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
if (peer->isSelf()) {
|
||||
_api.request(MTPphotos_UploadProfilePhoto(
|
||||
MTP_flags(MTPphotos_UploadProfilePhoto::Flag::f_file),
|
||||
MTP_flags(MTPphotos_UploadProfilePhoto::Flag::f_file
|
||||
| ((type == UploadType::Fallback)
|
||||
? MTPphotos_UploadProfilePhoto::Flag::f_fallback
|
||||
: MTPphotos_UploadProfilePhoto::Flags(0))),
|
||||
file,
|
||||
MTPInputFile(), // video
|
||||
MTPdouble() // video_start_ts
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
_session->data().processPhoto(data.vphoto());
|
||||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
const auto photoId = _session->data().processPhoto(
|
||||
result.data().vphoto())->id;
|
||||
_session->data().processUsers(result.data().vusers());
|
||||
if (type == UploadType::Fallback) {
|
||||
_session->storage().add(Storage::UserPhotosSetBack(
|
||||
peerToUser(peer->id),
|
||||
photoId));
|
||||
}
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}).send();
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
const auto history = _session->data().history(chat);
|
||||
@@ -239,6 +343,29 @@ void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
|
||||
MTPInputFile(), // video
|
||||
MTPdouble()) // video_start_ts
|
||||
)).done(applier).afterRequest(history->sendRequestId).send();
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
using Flag = MTPphotos_UploadContactProfilePhoto::Flag;
|
||||
_api.request(MTPphotos_UploadContactProfilePhoto(
|
||||
MTP_flags(Flag::f_file
|
||||
| ((type == UploadType::Suggestion)
|
||||
? Flag::f_suggest
|
||||
: Flag::f_save)),
|
||||
user->inputUser,
|
||||
file,
|
||||
MTPInputFile(), // video
|
||||
MTPdouble() // video_start_ts
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
_session->data().processPhoto(data.vphoto());
|
||||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
if (type != UploadType::Suggestion) {
|
||||
user->updateFullForced();
|
||||
}
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,26 +384,34 @@ void PeerPhoto::requestUserPhotos(
|
||||
)).done([this, user](const MTPphotos_Photos &result) {
|
||||
_userPhotosRequests.remove(user);
|
||||
|
||||
const auto fullCount = result.match([](const MTPDphotos_photos &d) {
|
||||
auto fullCount = result.match([](const MTPDphotos_photos &d) {
|
||||
return int(d.vphotos().v.size());
|
||||
}, [](const MTPDphotos_photosSlice &d) {
|
||||
return d.vcount().v;
|
||||
});
|
||||
|
||||
auto &owner = _session->data();
|
||||
auto photoIds = result.match([&](const auto &data) {
|
||||
auto &owner = _session->data();
|
||||
owner.processUsers(data.vusers());
|
||||
|
||||
auto photoIds = std::vector<PhotoId>();
|
||||
photoIds.reserve(data.vphotos().v.size());
|
||||
|
||||
for (const auto &photo : data.vphotos().v) {
|
||||
if (const auto photoData = owner.processPhoto(photo)) {
|
||||
photoIds.push_back(photoData->id);
|
||||
for (const auto &single : data.vphotos().v) {
|
||||
const auto photo = owner.processPhoto(single);
|
||||
if (!photo->isNull()) {
|
||||
photoIds.push_back(photo->id);
|
||||
}
|
||||
}
|
||||
return photoIds;
|
||||
});
|
||||
if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
|
||||
const auto photo = owner.photo(user->userpicPhotoId());
|
||||
if (!photo->isNull()) {
|
||||
++fullCount;
|
||||
photoIds.insert(begin(photoIds), photo->id);
|
||||
}
|
||||
}
|
||||
|
||||
_session->storage().add(Storage::UserPhotosAddSlice(
|
||||
peerToUser(user->id),
|
||||
@@ -289,4 +424,21 @@ void PeerPhoto::requestUserPhotos(
|
||||
_userPhotosRequests.emplace(user, requestId);
|
||||
}
|
||||
|
||||
// Non-personal photo in case a personal photo is set.
|
||||
void PeerPhoto::registerNonPersonalPhoto(
|
||||
not_null<UserData*> user,
|
||||
not_null<PhotoData*> photo) {
|
||||
_nonPersonalPhotos.emplace_or_assign(user, photo);
|
||||
}
|
||||
|
||||
void PeerPhoto::unregisterNonPersonalPhoto(not_null<UserData*> user) {
|
||||
_nonPersonalPhotos.erase(user);
|
||||
}
|
||||
|
||||
PhotoData *PeerPhoto::nonPersonalPhoto(
|
||||
not_null<UserData*> user) const {
|
||||
const auto i = _nonPersonalPhotos.find(user);
|
||||
return (i != end(_nonPersonalPhotos)) ? i->second.get() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -13,6 +13,10 @@ class ApiWrap;
|
||||
class PeerData;
|
||||
class UserData;
|
||||
|
||||
namespace Data {
|
||||
struct FileOrigin;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -24,22 +28,61 @@ public:
|
||||
using UserPhotoId = PhotoId;
|
||||
explicit PeerPhoto(not_null<ApiWrap*> api);
|
||||
|
||||
void upload(not_null<PeerData*> peer, QImage &&image);
|
||||
void upload(
|
||||
not_null<PeerData*> peer,
|
||||
QImage &&image,
|
||||
Fn<void()> done = nullptr);
|
||||
void uploadFallback(not_null<PeerData*> peer, QImage &&image);
|
||||
void updateSelf(
|
||||
not_null<PhotoData*> photo,
|
||||
Data::FileOrigin origin,
|
||||
Fn<void()> done = nullptr);
|
||||
void suggest(not_null<PeerData*> peer, QImage &&image);
|
||||
void clear(not_null<PhotoData*> photo);
|
||||
void clearPersonal(not_null<UserData*> user);
|
||||
void set(not_null<PeerData*> peer, not_null<PhotoData*> photo);
|
||||
|
||||
void requestUserPhotos(not_null<UserData*> user, UserPhotoId afterId);
|
||||
|
||||
// Non-personal photo in case a personal photo is set.
|
||||
void registerNonPersonalPhoto(
|
||||
not_null<UserData*> user,
|
||||
not_null<PhotoData*> photo);
|
||||
void unregisterNonPersonalPhoto(not_null<UserData*> user);
|
||||
[[nodiscard]] PhotoData *nonPersonalPhoto(
|
||||
not_null<UserData*> user) const;
|
||||
|
||||
private:
|
||||
enum class UploadType {
|
||||
Default,
|
||||
Suggestion,
|
||||
Fallback,
|
||||
};
|
||||
|
||||
void ready(const FullMsgId &msgId, const MTPInputFile &file);
|
||||
void upload(
|
||||
not_null<PeerData*> peer,
|
||||
QImage &&image,
|
||||
UploadType type,
|
||||
Fn<void()> done);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
|
||||
base::flat_map<FullMsgId, not_null<PeerData*>> _uploads;
|
||||
struct UploadValue {
|
||||
not_null<PeerData*> peer;
|
||||
UploadType type = UploadType::Default;
|
||||
Fn<void()> done;
|
||||
};
|
||||
|
||||
base::flat_map<FullMsgId, UploadValue> _uploads;
|
||||
|
||||
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
|
||||
|
||||
base::flat_map<
|
||||
not_null<UserData*>,
|
||||
not_null<PhotoData*>> _nonPersonalPhotos;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -16,7 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_poll.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h" // ShouldSendSilent
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h" // ShouldSendSilent
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -44,9 +44,10 @@ void SendReport(
|
||||
Ui::ReportReason reason,
|
||||
const QString &comment,
|
||||
std::variant<v::null_t, MessageIdsList, not_null<PhotoData*>> data) {
|
||||
auto done = [=] {
|
||||
auto weak = Ui::MakeWeak(toastParent.get());
|
||||
auto done = crl::guard(toastParent, [=] {
|
||||
Ui::Toast::Show(toastParent, tr::lng_report_thanks(tr::now));
|
||||
};
|
||||
});
|
||||
v::match(data, [&](v::null_t) {
|
||||
peer->session().api().request(MTPaccount_ReportPeer(
|
||||
peer->input,
|
||||
|
||||
@@ -20,7 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_changes.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h" // NewMessageFlags.
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h" // NewMessageFlags.
|
||||
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
|
||||
#include "chat_helpers/stickers_dice_pack.h" // DicePacks::kDiceString.
|
||||
#include "ui/text/text_entity.h" // TextWithEntities.
|
||||
@@ -392,6 +393,7 @@ void SendConfirmedFile(
|
||||
action.replyTo = file->to.replyTo;
|
||||
action.topicRootId = file->to.topicRootId;
|
||||
action.generateLocal = true;
|
||||
action.replaceMediaOf = file->to.replaceMediaOf;
|
||||
session->api().sendAction(action);
|
||||
|
||||
auto caption = TextWithEntities{
|
||||
@@ -439,13 +441,17 @@ void SendConfirmedFile(
|
||||
|
||||
const auto media = MTPMessageMedia([&] {
|
||||
if (file->type == SendMediaType::Photo) {
|
||||
using Flag = MTPDmessageMediaPhoto::Flag;
|
||||
return MTP_messageMediaPhoto(
|
||||
MTP_flags(MTPDmessageMediaPhoto::Flag::f_photo),
|
||||
MTP_flags(Flag::f_photo
|
||||
| (file->spoiler ? Flag::f_spoiler : Flag())),
|
||||
file->photo,
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::File) {
|
||||
using Flag = MTPDmessageMediaDocument::Flag;
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(MTPDmessageMediaDocument::Flag::f_document),
|
||||
MTP_flags(Flag::f_document
|
||||
| (file->spoiler ? Flag::f_spoiler : Flag())),
|
||||
file->document,
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::Audio) {
|
||||
|
||||
@@ -1513,7 +1513,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
// Request last active supergroup participants if the 'from' user was not loaded yet.
|
||||
// This will optimize similar getDifference() calls for almost all next messages.
|
||||
if (isDataLoaded == DataIsLoadedResult::FromNotLoaded && channel && channel->isMegagroup()) {
|
||||
if (channel->mgInfo->lastParticipants.size() < _session->serverConfig().chatSizeMax
|
||||
if (channel->canViewMembers()
|
||||
&& channel->mgInfo->lastParticipants.size() < _session->serverConfig().chatSizeMax
|
||||
&& (channel->mgInfo->lastParticipants.empty()
|
||||
|| channel->mgInfo->lastParticipants.size() < channel->membersCount())) {
|
||||
session().api().chatParticipants().requestLast(channel);
|
||||
@@ -1895,26 +1896,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateUserPhoto: {
|
||||
auto &d = update.c_updateUserPhoto();
|
||||
if (auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
user->setPhoto(d.vphoto());
|
||||
user->loadUserpic();
|
||||
// After that update we don't have enough information to
|
||||
// create a 'photo' with all necessary fields. So if
|
||||
// we receive second such update we end up with a 'photo_id'
|
||||
// in user_photos list without a loaded 'photo'.
|
||||
// It fails to show in media overview if you try to open it.
|
||||
//
|
||||
//if (mtpIsTrue(d.vprevious()) || !user->userpicPhotoId()) {
|
||||
session().storage().remove(Storage::UserPhotosRemoveAfter(
|
||||
peerToUser(user->id),
|
||||
user->userpicPhotoId()));
|
||||
//} else {
|
||||
// session().storage().add(Storage::UserPhotosAddNew(
|
||||
// peerToUser(user->id),
|
||||
// user->userpicPhotoId()));
|
||||
//}
|
||||
case mtpc_updateUser: {
|
||||
auto &d = update.c_updateUser();
|
||||
if (const auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
if (user->wasFullUpdated()) {
|
||||
user->updateFullForced();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -571,7 +571,10 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
const auto peer = history->peer;
|
||||
const auto chat = peer->asChat();
|
||||
const auto megagroup = peer->asMegagroup();
|
||||
if ((!chat && !megagroup) || peer->isForum()) {
|
||||
if ((!chat && !megagroup)
|
||||
|| peer->isForum()
|
||||
|| (megagroup
|
||||
&& (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) {
|
||||
return false;
|
||||
}
|
||||
const auto &appConfig = peer->session().account().appConfig();
|
||||
|
||||
@@ -67,8 +67,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "main/main_account.h"
|
||||
@@ -690,7 +691,11 @@ QString ApiWrap::exportDirectMessageLink(
|
||||
auto linkThreadId = MsgId();
|
||||
auto linkThreadIsTopic = false;
|
||||
if (inRepliesContext) {
|
||||
if (const auto rootId = item->replyToTop()) {
|
||||
linkThreadIsTopic = item->history()->isForum();
|
||||
const auto rootId = linkThreadIsTopic
|
||||
? item->topicRootId()
|
||||
: item->replyToTop();
|
||||
if (rootId) {
|
||||
const auto root = item->history()->owner().message(
|
||||
channel->id,
|
||||
rootId);
|
||||
@@ -710,7 +715,6 @@ QString ApiWrap::exportDirectMessageLink(
|
||||
} else {
|
||||
// Reply in a thread, maybe comment in a private channel.
|
||||
linkThreadId = rootId;
|
||||
linkThreadIsTopic = (item->topicRootId() == rootId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2415,6 +2419,12 @@ void ApiWrap::refreshFileReference(
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}, [&](Data::FileOriginFullUser data) {
|
||||
if (const auto user = _session->data().user(data.userId)) {
|
||||
request(MTPusers_GetFullUser(user->inputUser));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}, [&](Data::FileOriginPeerPhoto data) {
|
||||
fail();
|
||||
}, [&](Data::FileOriginStickerSet data) {
|
||||
@@ -3055,7 +3065,7 @@ void ApiWrap::sharedMediaDone(
|
||||
}
|
||||
|
||||
void ApiWrap::sendAction(const SendAction &action) {
|
||||
if (!action.options.scheduled) {
|
||||
if (!action.options.scheduled && !action.replaceMediaOf) {
|
||||
const auto topic = action.topicRootId
|
||||
? action.history->peer->forumTopicFor(action.topicRootId)
|
||||
: nullptr;
|
||||
@@ -3147,7 +3157,11 @@ void ApiWrap::forwardMessages(
|
||||
if (sendAs) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_send_as;
|
||||
}
|
||||
if (action.topicRootId) {
|
||||
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
||||
const auto topMsgId = (action.topicRootId == kGeneralId)
|
||||
? MsgId(0)
|
||||
: action.topicRootId;
|
||||
if (topMsgId) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_top_msg_id;
|
||||
}
|
||||
|
||||
@@ -3169,7 +3183,7 @@ void ApiWrap::forwardMessages(
|
||||
MTP_vector<MTPint>(ids),
|
||||
MTP_vector<MTPlong>(randomIds),
|
||||
peer->input,
|
||||
MTP_int(action.topicRootId),
|
||||
MTP_int(topMsgId),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
@@ -3222,7 +3236,7 @@ void ApiWrap::forwardMessages(
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
item,
|
||||
action.topicRootId);
|
||||
topMsgId);
|
||||
_session->data().registerMessageRandomId(randomId, newId);
|
||||
if (!localIds) {
|
||||
localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
|
||||
@@ -3364,7 +3378,8 @@ void ApiWrap::editMedia(
|
||||
std::move(file.information),
|
||||
type,
|
||||
to,
|
||||
caption));
|
||||
caption,
|
||||
file.spoiler));
|
||||
}
|
||||
|
||||
void ApiWrap::sendFiles(
|
||||
@@ -3405,6 +3420,7 @@ void ApiWrap::sendFiles(
|
||||
uploadWithType,
|
||||
to,
|
||||
caption,
|
||||
file.spoiler,
|
||||
album));
|
||||
caption = TextWithTags();
|
||||
}
|
||||
@@ -3424,14 +3440,17 @@ void ApiWrap::sendFile(
|
||||
const SendAction &action) {
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
auto caption = TextWithTags();
|
||||
const auto spoiler = false;
|
||||
const auto information = nullptr;
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
QString(),
|
||||
fileContent,
|
||||
nullptr,
|
||||
information,
|
||||
type,
|
||||
to,
|
||||
caption));
|
||||
caption,
|
||||
spoiler));
|
||||
}
|
||||
|
||||
void ApiWrap::sendUploadedPhoto(
|
||||
@@ -3439,7 +3458,7 @@ void ApiWrap::sendUploadedPhoto(
|
||||
Api::RemoteFileInfo info,
|
||||
Api::SendOptions options) {
|
||||
if (const auto item = _session->data().message(localId)) {
|
||||
const auto media = Api::PrepareUploadedPhoto(std::move(info));
|
||||
const auto media = Api::PrepareUploadedPhoto(item, std::move(info));
|
||||
if (const auto groupId = item->groupId()) {
|
||||
uploadAlbumMedia(item, groupId, media);
|
||||
} else {
|
||||
@@ -3790,7 +3809,9 @@ void ApiWrap::uploadAlbumMedia(
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
auto spoiler = false;
|
||||
if (const auto media = item->media()) {
|
||||
spoiler = media->hasSpoiler();
|
||||
if (const auto photo = media->photo()) {
|
||||
photo->setWaitingForAlbum();
|
||||
} else if (const auto document = media->document()) {
|
||||
@@ -3807,10 +3828,10 @@ void ApiWrap::uploadAlbumMedia(
|
||||
return;
|
||||
}
|
||||
const auto &fields = photo->c_photo();
|
||||
const auto flags = MTPDinputMediaPhoto::Flags(0)
|
||||
| (data.vttl_seconds()
|
||||
? MTPDinputMediaPhoto::Flag::f_ttl_seconds
|
||||
: MTPDinputMediaPhoto::Flag(0));
|
||||
using Flag = MTPDinputMediaPhoto::Flag;
|
||||
const auto flags = Flag()
|
||||
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
|
||||
| (spoiler ? Flag::f_spoiler : Flag());
|
||||
const auto media = MTP_inputMediaPhoto(
|
||||
MTP_flags(flags),
|
||||
MTP_inputPhoto(
|
||||
@@ -3829,10 +3850,10 @@ void ApiWrap::uploadAlbumMedia(
|
||||
return;
|
||||
}
|
||||
const auto &fields = document->c_document();
|
||||
const auto flags = MTPDinputMediaDocument::Flags(0)
|
||||
| (data.vttl_seconds()
|
||||
? MTPDinputMediaDocument::Flag::f_ttl_seconds
|
||||
: MTPDinputMediaDocument::Flag(0));
|
||||
using Flag = MTPDinputMediaDocument::Flag;
|
||||
const auto flags = Flag()
|
||||
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
|
||||
| (spoiler ? Flag::f_spoiler : Flag());
|
||||
const auto media = MTP_inputMediaDocument(
|
||||
MTP_flags(flags),
|
||||
MTP_inputDocument(
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/application.h"
|
||||
@@ -116,7 +117,7 @@ void AboutBox::showVersionHistory() {
|
||||
"version of Telegram Desktop was copied to the clipboard."),
|
||||
Ui::LayerOption::CloseOther);
|
||||
} else {
|
||||
UrlClickHandler::Open(Core::App().changelogLink());
|
||||
File::OpenUrl(Core::App().changelogLink());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/about_sponsored_box.h"
|
||||
|
||||
#include "core/file_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -14,8 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
@@ -50,7 +49,7 @@ void AboutSponsoredBox(not_null<Ui::GenericBox*> box) {
|
||||
(rowSize.height() - buttonSize.height()) / 2);
|
||||
}, row->lifetime());
|
||||
button->addClickHandler([=] {
|
||||
QDesktopServices::openUrl({ kUrl.utf8() });
|
||||
File::OpenUrl(kUrl.utf8());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
|
||||
#include "window/window_session_controller.h"
|
||||
#include "menu/menu_ttl.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/fields/special_fields.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/text/format_values.h"
|
||||
@@ -465,10 +465,7 @@ void GroupInfoBox::prepare() {
|
||||
_photo.create(
|
||||
this,
|
||||
&_navigation->parentController()->window(),
|
||||
((_type == Type::Channel)
|
||||
? tr::lng_create_channel_crop
|
||||
: tr::lng_create_group_crop)(tr::now),
|
||||
Ui::UserpicButton::Role::ChangePhoto,
|
||||
Ui::UserpicButton::Role::ChoosePhoto,
|
||||
st::defaultUserpicButton);
|
||||
_title.create(
|
||||
this,
|
||||
|
||||
@@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
@@ -62,6 +62,18 @@ defaultUserpicButton: UserpicButton {
|
||||
uploadIcon: defaultUploadUserpicIcon;
|
||||
uploadIconPosition: point(-1px, 1px);
|
||||
}
|
||||
uploadUserpicSize: 32px;
|
||||
uploadUserpicButton: UserpicButton(defaultUserpicButton) {
|
||||
size: size(uploadUserpicSize, uploadUserpicSize);
|
||||
photoSize: uploadUserpicSize;
|
||||
changeIcon: icon {{ "settings/photo", activeButtonFg }};
|
||||
changeIconPosition: point(4px, 4px);
|
||||
}
|
||||
uploadUserpicButtonBorder: 2px;
|
||||
restoreUserpicIcon: UserpicButton(defaultUserpicButton) {
|
||||
size: size(22px, 22px);
|
||||
photoSize: 22px;
|
||||
}
|
||||
|
||||
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
|
||||
align: align(center);
|
||||
@@ -565,9 +577,6 @@ colorResultInput: InputField(colorValueInput) {
|
||||
|
||||
changePhoneButton: RoundButton(defaultActiveButton) {
|
||||
width: 256px;
|
||||
height: 42px;
|
||||
textTop: 11px;
|
||||
font: boxButtonFont;
|
||||
}
|
||||
changePhoneButtonPadding: margins(0px, 32px, 0px, 44px);
|
||||
changePhoneTitle: FlatLabel(boxTitle) {
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_messages_search.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_histories.h"
|
||||
@@ -27,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -229,11 +231,32 @@ void DeleteMessagesBox::prepare() {
|
||||
auto count = int(_ids.size());
|
||||
if (hasScheduledMessages()) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
const auto &settings = Core::App().settings();
|
||||
const auto revokeByDefault =
|
||||
!settings.rememberedDeleteMessageOnlyForYou();
|
||||
_revoke.create(
|
||||
this,
|
||||
revoke->checkbox,
|
||||
true,
|
||||
revokeByDefault,
|
||||
st::defaultBoxCheckbox);
|
||||
_revokeRemember.create(
|
||||
this,
|
||||
object_ptr<Ui::Checkbox>(
|
||||
this,
|
||||
tr::lng_remember(),
|
||||
false,
|
||||
st::defaultBoxCheckbox));
|
||||
_revokeRemember->hide(anim::type::instant);
|
||||
_revoke->checkedValue(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
_revokeRemember->toggle(
|
||||
checked != revokeByDefault,
|
||||
anim::type::normal);
|
||||
}, _revokeRemember->lifetime());
|
||||
_revokeRemember->heightValue(
|
||||
) | rpl::start_with_next([=](int h) {
|
||||
setDimensions(st::boxWidth, _fullHeight + h);
|
||||
}, lifetime());
|
||||
appendDetails(std::move(revoke->description));
|
||||
} else if (peer->isChannel()) {
|
||||
if (peer->isMegagroup()) {
|
||||
@@ -309,6 +332,7 @@ void DeleteMessagesBox::prepare() {
|
||||
+ st::boxLittleSkip;
|
||||
}
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
_fullHeight = fullHeight;
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
@@ -450,6 +474,11 @@ void DeleteMessagesBox::resizeEvent(QResizeEvent *e) {
|
||||
_revoke->resizeToNaturalWidth(availableWidth);
|
||||
_revoke->moveToLeft(padding.left(), top);
|
||||
top += _revoke->heightNoMargins() + st::boxLittleSkip;
|
||||
if (_revokeRemember) {
|
||||
_revokeRemember->resizeToNaturalWidth(availableWidth);
|
||||
_revokeRemember->moveToLeft(padding.left(),top);
|
||||
top += _revokeRemember->heightNoMargins();
|
||||
}
|
||||
}
|
||||
if (_autoDeleteSettings) {
|
||||
top += st::boxMediumSkip - st::boxLittleSkip;
|
||||
@@ -469,6 +498,14 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void DeleteMessagesBox::deleteAndClear() {
|
||||
if (_revoke
|
||||
&& _revokeRemember
|
||||
&& _revokeRemember->toggled()
|
||||
&& _revokeRemember->entity()->checked()) {
|
||||
Core::App().settings().setRememberedDeleteMessageOnlyForYou(
|
||||
!_revoke->checked());
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
|
||||
const auto session = _session;
|
||||
const auto invokeCallbackAndClose = [&] {
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Ui {
|
||||
class Checkbox;
|
||||
class FlatLabel;
|
||||
class LinkButton;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
class DeleteMessagesBox final : public Ui::BoxContent {
|
||||
@@ -73,11 +75,14 @@ private:
|
||||
|
||||
object_ptr<Ui::FlatLabel> _text = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _revoke = { nullptr };
|
||||
object_ptr<Ui::SlideWrap<Ui::Checkbox>> _revokeRemember = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _banUser = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
|
||||
object_ptr<Ui::LinkButton> _autoDeleteSettings = { nullptr };
|
||||
|
||||
int _fullHeight = 0;
|
||||
|
||||
Fn<void()> _deleteConfirmedCallback;
|
||||
|
||||
};
|
||||
|
||||
@@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "editor/editor_layer_widget.h"
|
||||
#include "editor/photo_editor.h"
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "history/history_drag_area.h"
|
||||
#include "history/history_item.h"
|
||||
@@ -175,12 +177,15 @@ void EditCaptionBox::rebuildPreview() {
|
||||
Window::GifPauseReason::Layer);
|
||||
};
|
||||
|
||||
applyChanges();
|
||||
|
||||
_previewHasSpoiler = nullptr;
|
||||
if (_preparedList.files.empty()) {
|
||||
const auto media = _historyItem->media();
|
||||
const auto photo = media->photo();
|
||||
const auto document = media->document();
|
||||
_isPhoto = (photo != nullptr);
|
||||
if (photo || document->isVideoFile() || document->isAnimation()) {
|
||||
_isPhoto = (photo != nullptr);
|
||||
const auto media = Ui::CreateChild<Ui::ItemSingleMediaPreview>(
|
||||
this,
|
||||
gifPaused,
|
||||
@@ -189,7 +194,6 @@ void EditCaptionBox::rebuildPreview() {
|
||||
_photoMedia = media->sharedPhotoMedia();
|
||||
_content.reset(media);
|
||||
} else {
|
||||
_isPhoto = false;
|
||||
_content.reset(Ui::CreateChild<Ui::ItemSingleFilePreview>(
|
||||
this,
|
||||
_historyItem,
|
||||
@@ -203,11 +207,12 @@ void EditCaptionBox::rebuildPreview() {
|
||||
gifPaused,
|
||||
file,
|
||||
Ui::AttachControls::Type::EditOnly);
|
||||
if (media) {
|
||||
_isPhoto = media->isPhoto();
|
||||
_isPhoto = (media && media->isPhoto());
|
||||
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
||||
if (media && (!withCheckbox || !_asFile)) {
|
||||
_previewHasSpoiler = [media] { return media->hasSpoiler(); };
|
||||
_content.reset(media);
|
||||
} else {
|
||||
_isPhoto = false;
|
||||
_content.reset(Ui::CreateChild<Ui::SingleFilePreview>(
|
||||
this,
|
||||
file,
|
||||
@@ -299,7 +304,7 @@ void EditCaptionBox::setupControls() {
|
||||
{}
|
||||
) | rpl::map([=] {
|
||||
return _controller->session().settings().photoEditorHintShown()
|
||||
? _isPhoto
|
||||
? (_isPhoto && !_asFile)
|
||||
: false;
|
||||
});
|
||||
|
||||
@@ -316,7 +321,7 @@ void EditCaptionBox::setupControls() {
|
||||
this,
|
||||
object_ptr<Ui::Checkbox>(
|
||||
this,
|
||||
tr::lng_send_compressed(tr::now),
|
||||
tr::lng_send_compressed_one(tr::now),
|
||||
true,
|
||||
st::defaultBoxCheckbox),
|
||||
st::editMediaCheckboxMargins)
|
||||
@@ -329,7 +334,9 @@ void EditCaptionBox::setupControls() {
|
||||
anim::type::instant
|
||||
)->entity()->checkedChanges(
|
||||
) | rpl::start_with_next([&](bool checked) {
|
||||
applyChanges();
|
||||
_asFile = !checked;
|
||||
rebuildPreview();
|
||||
}, _controls->lifetime());
|
||||
|
||||
_controls->resizeToWidth(st::sendMediaPreviewSize);
|
||||
@@ -430,6 +437,8 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
return;
|
||||
}
|
||||
auto copy = large->original();
|
||||
const auto wasSpoiler = hasSpoiler();
|
||||
|
||||
_preparedList = Storage::PrepareMediaFromImage(
|
||||
std::move(copy),
|
||||
QByteArray(),
|
||||
@@ -437,6 +446,7 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
auto &file = _preparedList.files.front();
|
||||
file.spoiler = wasSpoiler;
|
||||
const auto image = std::get_if<ImageInfo>(
|
||||
&file.information->media);
|
||||
|
||||
@@ -445,13 +455,18 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
rebuildPreview();
|
||||
};
|
||||
const auto fileImage = std::make_shared<Image>(*large);
|
||||
auto editor = base::make_unique_q<Editor::PhotoEditor>(
|
||||
this,
|
||||
&controller->window(),
|
||||
fileImage,
|
||||
Editor::PhotoModifications());
|
||||
const auto raw = editor.get();
|
||||
auto layer = std::make_unique<Editor::LayerWidget>(
|
||||
this,
|
||||
std::move(editor));
|
||||
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
|
||||
controller->showLayer(
|
||||
std::make_unique<Editor::LayerWidget>(
|
||||
this,
|
||||
&controller->window(),
|
||||
fileImage,
|
||||
Editor::PhotoModifications(),
|
||||
std::move(callback)),
|
||||
std::move(layer),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
}, lifetime());
|
||||
@@ -581,11 +596,20 @@ bool EditCaptionBox::setPreparedList(Ui::PreparedList &&list) {
|
||||
tr::lng_edit_media_album_error(tr::now));
|
||||
return false;
|
||||
}
|
||||
const auto wasSpoiler = hasSpoiler();
|
||||
_preparedList = std::move(list);
|
||||
_preparedList.files.front().spoiler = wasSpoiler;
|
||||
rebuildPreview();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditCaptionBox::hasSpoiler() const {
|
||||
return _preparedList.files.empty()
|
||||
? (_historyItem->media()
|
||||
&& _historyItem->media()->hasSpoiler())
|
||||
: _preparedList.files.front().spoiler;
|
||||
}
|
||||
|
||||
void EditCaptionBox::captionResized() {
|
||||
updateBoxSize();
|
||||
resizeEvent(0);
|
||||
@@ -690,6 +714,12 @@ bool EditCaptionBox::validateLength(const QString &text) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditCaptionBox::applyChanges() {
|
||||
if (!_preparedList.files.empty() && _previewHasSpoiler) {
|
||||
_preparedList.files.front().spoiler = _previewHasSpoiler();
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::save() {
|
||||
if (_saveRequestId) {
|
||||
return;
|
||||
@@ -727,10 +757,14 @@ void EditCaptionBox::save() {
|
||||
action.replaceMediaOf = item->fullId().msg;
|
||||
|
||||
Storage::ApplyModifications(_preparedList);
|
||||
if (!_preparedList.files.empty()) {
|
||||
_preparedList.files.front().spoiler = false;
|
||||
applyChanges();
|
||||
}
|
||||
|
||||
_controller->session().api().editMedia(
|
||||
std::move(_preparedList),
|
||||
(!_asFile && _isPhoto && CanBeCompressed(_albumType))
|
||||
(_isPhoto && !_asFile && CanBeCompressed(_albumType))
|
||||
? SendMediaType::Photo
|
||||
: SendMediaType::File,
|
||||
_field->getTextWithAppliedMarkdown(),
|
||||
|
||||
@@ -64,11 +64,13 @@ private:
|
||||
void setupDragArea();
|
||||
|
||||
bool validateLength(const QString &text) const;
|
||||
void applyChanges();
|
||||
void save();
|
||||
|
||||
bool fileFromClipboard(not_null<const QMimeData*> data);
|
||||
|
||||
int errorTopSkip() const;
|
||||
[[nodiscard]] int errorTopSkip() const;
|
||||
[[nodiscard]] bool hasSpoiler() const;
|
||||
|
||||
bool setPreparedList(Ui::PreparedList &&list);
|
||||
|
||||
@@ -83,6 +85,7 @@ private:
|
||||
const base::unique_qptr<Ui::EmojiButton> _emojiToggle;
|
||||
|
||||
base::unique_qptr<Ui::AbstractSinglePreview> _content;
|
||||
Fn<bool()> _previewHasSpoiler;
|
||||
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
|
||||
base::unique_qptr<QObject> _emojiFilter;
|
||||
|
||||
|
||||
@@ -276,6 +276,11 @@ void EditPrivacyBox::setupContent() {
|
||||
? tr::lng_edit_privacy_exceptions_count(tr::now, lt_count, count)
|
||||
: tr::lng_edit_privacy_exceptions_add(tr::now);
|
||||
});
|
||||
_controller->handleExceptionsChange(
|
||||
exception,
|
||||
update->events_starting_with({}) | rpl::map([=] {
|
||||
return Settings::ExceptionUsersCount(exceptions(exception));
|
||||
}));
|
||||
auto text = _controller->exceptionButtonTextKey(exception);
|
||||
const auto always = (exception == Exception::Always);
|
||||
const auto button = content->add(
|
||||
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
Exception exception) const = 0;
|
||||
[[nodiscard]] virtual auto exceptionsDescription()
|
||||
const -> rpl::producer<QString> = 0;
|
||||
virtual void handleExceptionsChange(
|
||||
Exception exception,
|
||||
rpl::producer<int> value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget(
|
||||
not_null<QWidget*> parent,
|
||||
@@ -72,7 +76,7 @@ public:
|
||||
}
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupBelowWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<QWidget*> parent) const {
|
||||
not_null<QWidget*> parent) {
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "ui/basic_click_handlers.h" // UrlClickHandler::Open.
|
||||
#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/special_buttons.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -86,7 +86,6 @@ void GiftBox(
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||
top,
|
||||
user,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::defaultUserpicButton);
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
top->widthValue(
|
||||
|
||||
@@ -1117,7 +1117,8 @@ void LanguageBox::prepare() {
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, translateEnabled->lifetime());
|
||||
|
||||
const auto label = lifetime().make_state<rpl::event_stream<QLocale>>();
|
||||
using Locales = std::vector<QLocale>;
|
||||
const auto label = lifetime().make_state<rpl::event_stream<Locales>>();
|
||||
const auto translateSkipWrap = topContainer->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
topContainer,
|
||||
@@ -1129,30 +1130,27 @@ void LanguageBox::prepare() {
|
||||
const auto translateSkip = Settings::AddButtonWithLabel(
|
||||
translateSkipWrap->entity(),
|
||||
tr::lng_translate_settings_choose(),
|
||||
label->events() | rpl::map(Ui::LanguageName),
|
||||
label->events(
|
||||
) | rpl::map([](const Locales &locales) {
|
||||
return (locales.size() > 1)
|
||||
? tr::lng_languages_count(tr::now, lt_count, locales.size())
|
||||
: Ui::LanguageName(locales.front());
|
||||
}),
|
||||
st::settingsButtonNoIcon);
|
||||
|
||||
{
|
||||
const auto settingsLang =
|
||||
Core::App().settings().skipTranslationForLanguage();
|
||||
const auto locale = (settingsLang == QLocale::English)
|
||||
? QLocale(Lang::LanguageIdOrDefault(Lang::Id()))
|
||||
: (settingsLang == QLocale::C)
|
||||
? QLocale(QLocale::English)
|
||||
: QLocale(settingsLang);
|
||||
label->fire_copy(locale);
|
||||
}
|
||||
label->fire(Ui::Translate::LocalesFromSettings());
|
||||
translateSkip->setClickedCallback([=] {
|
||||
Ui::BoxShow(this).showBox(
|
||||
Box(Ui::ChooseLanguageBox, [=](QLocale locale) {
|
||||
label->fire_copy(locale);
|
||||
const auto result = (locale.language() == QLocale::English)
|
||||
? QLocale::c()
|
||||
: locale;
|
||||
Core::App().settings().setSkipTranslationForLanguage(
|
||||
result.language());
|
||||
Box(Ui::ChooseLanguageBox, [=](std::vector<QLocale> locales) {
|
||||
label->fire_copy(locales);
|
||||
const auto result = ranges::views::all(
|
||||
locales
|
||||
) | ranges::views::transform([](const QLocale &l) {
|
||||
return int(l.language());
|
||||
}) | ranges::to_vector;
|
||||
Core::App().settings().setSkipTranslationForLanguages(result);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}),
|
||||
}, Ui::Translate::LocalesFromSettings()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
});
|
||||
Settings::AddSkip(topContainer);
|
||||
|
||||
@@ -363,10 +363,6 @@ void PeerListController::peerListSearchRefreshRows() {
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
rpl::producer<int> PeerListController::onlineCountValue() const {
|
||||
return rpl::single(0);
|
||||
}
|
||||
|
||||
void PeerListController::setDescriptionText(const QString &text) {
|
||||
if (text.isEmpty()) {
|
||||
setDescription(nullptr);
|
||||
@@ -735,7 +731,6 @@ int PeerListRow::paintNameIconGetWidth(
|
||||
.premiumFg = &(selected
|
||||
? st::dialogsVerifiedIconBgOver
|
||||
: st::dialogsVerifiedIconBg),
|
||||
.preview = st::windowBgOver->c,
|
||||
.customEmojiRepaint = repaint,
|
||||
.now = now,
|
||||
.paused = false,
|
||||
|
||||
@@ -529,8 +529,6 @@ public:
|
||||
Unexpected("PeerListController::customRowRippleMaskGenerator.");
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual rpl::producer<int> onlineCountValue() const;
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
return chat->participants;
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
if (channel->isMegagroup()) {
|
||||
if (channel->isMegagroup() && channel->canViewMembers()) {
|
||||
const auto &participants = channel->mgInfo->lastParticipants;
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
}
|
||||
@@ -140,6 +140,7 @@ bool AddParticipantsBoxController::isAlreadyIn(
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
return _alreadyIn.contains(user)
|
||||
|| (channel->isMegagroup()
|
||||
&& channel->canViewMembers()
|
||||
&& base::contains(channel->mgInfo->lastParticipants, user));
|
||||
}
|
||||
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -41,7 +42,8 @@ void SendRequest(
|
||||
bool sharePhone,
|
||||
const QString &first,
|
||||
const QString &last,
|
||||
const QString &phone) {
|
||||
const QString &phone,
|
||||
Fn<void()> done) {
|
||||
const auto wasContact = user->isContact();
|
||||
using Flag = MTPcontacts_AddContact::Flag;
|
||||
user->session().api().request(MTPcontacts_AddContact(
|
||||
@@ -73,6 +75,7 @@ void SendRequest(
|
||||
}
|
||||
box->closeBox();
|
||||
}
|
||||
done();
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -103,6 +106,7 @@ private:
|
||||
QString _phone;
|
||||
Fn<void()> _focus;
|
||||
Fn<void()> _save;
|
||||
Fn<std::optional<QImage>()> _updatedPersonalPhoto;
|
||||
|
||||
};
|
||||
|
||||
@@ -136,15 +140,17 @@ void Controller::setupContent() {
|
||||
}
|
||||
|
||||
void Controller::setupCover() {
|
||||
_box->addRow(
|
||||
const auto cover = _box->addRow(
|
||||
object_ptr<Info::Profile::Cover>(
|
||||
_box,
|
||||
_user,
|
||||
_window,
|
||||
_user,
|
||||
Info::Profile::Cover::Role::EditContact,
|
||||
(_phone.isEmpty()
|
||||
? tr::lng_contact_mobile_hidden()
|
||||
: rpl::single(Ui::FormatPhone(_phone)))),
|
||||
style::margins())->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
style::margins());
|
||||
_updatedPersonalPhoto = [=] { return cover->updatedPersonalPhoto(); };
|
||||
}
|
||||
|
||||
void Controller::setupNameFields() {
|
||||
@@ -198,13 +204,29 @@ void Controller::initNameFields(
|
||||
(inverted ? last : first)->showError();
|
||||
return;
|
||||
}
|
||||
const auto user = _user;
|
||||
const auto personal = _updatedPersonalPhoto
|
||||
? _updatedPersonalPhoto()
|
||||
: std::nullopt;
|
||||
const auto done = [=] {
|
||||
if (personal) {
|
||||
if (personal->isNull()) {
|
||||
user->session().api().peerPhoto().clearPersonal(user);
|
||||
} else {
|
||||
user->session().api().peerPhoto().upload(
|
||||
user,
|
||||
base::duplicate(*personal));
|
||||
}
|
||||
}
|
||||
};
|
||||
SendRequest(
|
||||
Ui::MakeWeak(_box),
|
||||
_user,
|
||||
user,
|
||||
_sharePhone && _sharePhone->checked(),
|
||||
firstValue,
|
||||
lastValue,
|
||||
_phone);
|
||||
_phone,
|
||||
done);
|
||||
};
|
||||
const auto submit = [=] {
|
||||
const auto firstValue = first->getLastText().trimmed();
|
||||
|
||||
@@ -177,7 +177,7 @@ bool DefaultIconEmoji::readyInDefaultState() {
|
||||
return !paintIconFrame(result);
|
||||
}) | rpl::start_with_next([=](QRect clip) {
|
||||
auto args = Ui::Text::CustomEmoji::Context{
|
||||
.preview = st::windowBgOver->c,
|
||||
.textColor = st::windowFg->c,
|
||||
.now = crl::now(),
|
||||
.paused = controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer),
|
||||
|
||||
78
Telegram/SourceFiles/boxes/peers/edit_members_visible.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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/peers/edit_members_visible.h"
|
||||
|
||||
#include "boxes/peers/edit_peer_info_box.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] int EnableHideMembersMin(not_null<ChannelData*> channel) {
|
||||
return channel->session().account().appConfig().get<int>(
|
||||
u"hidden_members_group_size_min"_q,
|
||||
100);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateMembersVisibleButton(
|
||||
not_null<ChannelData*> megagroup) {
|
||||
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||
const auto container = result.data();
|
||||
|
||||
const auto min = EnableHideMembersMin(megagroup);
|
||||
if (!megagroup->canBanMembers() || megagroup->membersCount() < min) {
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
struct State {
|
||||
rpl::event_stream<bool> toggled;
|
||||
};
|
||||
Settings::AddSkip(container);
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
const auto button = container->add(
|
||||
EditPeerInfoBox::CreateButton(
|
||||
container,
|
||||
tr::lng_profile_hide_participants(),
|
||||
rpl::single(QString()),
|
||||
[] {},
|
||||
st::manageGroupTopicsButton,
|
||||
{ &st::infoRoundedIconHideMembers, Settings::kIconDarkBlue }
|
||||
))->toggleOn(rpl::single(
|
||||
(megagroup->flags() & ChannelDataFlag::ParticipantsHidden) != 0
|
||||
) | rpl::then(state->toggled.events()));
|
||||
Settings::AddSkip(container);
|
||||
Settings::AddDividerText(
|
||||
container,
|
||||
tr::lng_profile_hide_participants_about());
|
||||
|
||||
button->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
megagroup->session().api().request(
|
||||
MTPchannels_ToggleParticipantsHidden(
|
||||
megagroup->inputChannel,
|
||||
MTP_bool(toggled)
|
||||
)
|
||||
).done([=](const MTPUpdates &result) {
|
||||
megagroup->session().api().applyUpdates(result);
|
||||
}).send();
|
||||
}, button->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
19
Telegram/SourceFiles/boxes/peers/edit_members_visible.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
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/object_ptr.h"
|
||||
|
||||
class ChannelData;
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
} // namespace Ui
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateMembersVisibleButton(
|
||||
not_null<ChannelData*> megagroup);
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
@@ -20,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/painter.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
@@ -85,11 +85,7 @@ EditParticipantBox::Inner::Inner(
|
||||
: RpWidget(parent)
|
||||
, _peer(peer)
|
||||
, _user(user)
|
||||
, _userPhoto(
|
||||
this,
|
||||
_user,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::rightsPhotoButton)
|
||||
, _userPhoto(this, _user, st::rightsPhotoButton)
|
||||
, _hasAdminRights(hasAdminRights)
|
||||
, _rows(this) {
|
||||
_rows->heightValue(
|
||||
@@ -97,7 +93,7 @@ EditParticipantBox::Inner::Inner(
|
||||
resizeToWidth(width());
|
||||
}, lifetime());
|
||||
|
||||
_userPhoto->setPointerCursor(false);
|
||||
_userPhoto->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_userName.setText(
|
||||
st::rightsNameStyle,
|
||||
_user->name(),
|
||||
@@ -622,21 +618,21 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||
channel->inputChannel,
|
||||
user->inputUser,
|
||||
result.result
|
||||
)).done([=, toastParent = _show.toastParent()](const MTPUpdates &result) {
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
if (!box && !weak) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ui::Toast::Show(
|
||||
toastParent,
|
||||
(box ? Ui::BoxShow(box) : weak->_show).toastParent(),
|
||||
(channel->isBroadcast()
|
||||
? tr::lng_rights_transfer_done_channel
|
||||
: tr::lng_rights_transfer_done_group)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
user->shortName()));
|
||||
if (box) {
|
||||
Ui::BoxShow(box).hideLayer();
|
||||
} else if (weak) {
|
||||
weak->_show.hideLayer();
|
||||
}
|
||||
(box ? Ui::BoxShow(box) : weak->_show).hideLayer();
|
||||
}).fail(crl::guard(this, [=](const MTP::Error &error) {
|
||||
if (weak) {
|
||||
_transferRequestId = 0;
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
#include "boxes/peers/prepare_short_info_box.h" // PrepareShortInfoBox
|
||||
#include "boxes/peers/edit_members_visible.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/max_invite_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
@@ -500,7 +501,7 @@ void ParticipantsAdditionalData::fillFromChat(not_null<ChatData*> chat) {
|
||||
void ParticipantsAdditionalData::fillFromChannel(
|
||||
not_null<ChannelData*> channel) {
|
||||
const auto information = channel->mgInfo.get();
|
||||
if (!information) {
|
||||
if (!information || !channel->canViewMembers()) {
|
||||
return;
|
||||
}
|
||||
if (information->creator) {
|
||||
@@ -900,7 +901,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
|
||||
});
|
||||
} else if (auto row = createRow(user)) {
|
||||
delegate()->peerListPrependRow(std::move(row));
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
if (_onlineSorter) {
|
||||
_onlineSorter->sort();
|
||||
}
|
||||
@@ -913,7 +914,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
|
||||
if (const auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
@@ -1169,9 +1170,11 @@ void ParticipantsBoxController::restoreState(
|
||||
}
|
||||
|
||||
rpl::producer<int> ParticipantsBoxController::onlineCountValue() const {
|
||||
return _onlineSorter
|
||||
? _onlineSorter->onlineCountValue()
|
||||
: rpl::single(0);
|
||||
return _onlineCountValue.value();
|
||||
}
|
||||
|
||||
rpl::producer<int> ParticipantsBoxController::fullCountValue() const {
|
||||
return _fullCountValue.value();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::prepare() {
|
||||
@@ -1188,11 +1191,14 @@ void ParticipantsBoxController::prepare() {
|
||||
Unexpected("Role in ParticipantsBoxController::prepare()");
|
||||
}();
|
||||
if (const auto megagroup = _peer->asMegagroup()) {
|
||||
if ((_role == Role::Admins)
|
||||
if (_role == Role::Members) {
|
||||
delegate()->peerListSetAboveWidget(CreateMembersVisibleButton(
|
||||
megagroup));
|
||||
} else if ((_role == Role::Admins)
|
||||
&& (megagroup->amCreator() || megagroup->hasAdminRights())) {
|
||||
const auto validator = AntiSpamMenu::AntiSpamValidator(
|
||||
_navigation->parentController(),
|
||||
_peer->asChannel());
|
||||
megagroup);
|
||||
delegate()->peerListSetAboveWidget(validator.createButton());
|
||||
}
|
||||
}
|
||||
@@ -1201,17 +1207,48 @@ void ParticipantsBoxController::prepare() {
|
||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||
|
||||
if (_role == Role::Profile) {
|
||||
auto visible = _peer->isMegagroup()
|
||||
? Info::Profile::CanViewParticipantsValue(_peer->asMegagroup())
|
||||
: rpl::single(true);
|
||||
std::move(visible) | rpl::start_with_next([=](bool visible) {
|
||||
if (!visible) {
|
||||
_onlineCountValue = 0;
|
||||
_onlineSorter = nullptr;
|
||||
} else if (!_onlineSorter) {
|
||||
_onlineSorter = std::make_unique<ParticipantsOnlineSorter>(
|
||||
_peer,
|
||||
delegate());
|
||||
_onlineCountValue = _onlineSorter->onlineCountValue();
|
||||
}
|
||||
unload();
|
||||
rebuild();
|
||||
}, lifetime());
|
||||
} else {
|
||||
rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::unload() {
|
||||
while (delegate()->peerListFullRowsCount() > 0) {
|
||||
delegate()->peerListRemoveRow(
|
||||
delegate()->peerListRowAt(
|
||||
delegate()->peerListFullRowsCount() - 1));
|
||||
}
|
||||
if (const auto requestId = base::take(_loadRequestId)) {
|
||||
_api.request(requestId).cancel();
|
||||
}
|
||||
_allLoaded = false;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::rebuild() {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
prepareChatRows(chat);
|
||||
} else {
|
||||
loadMoreRows();
|
||||
}
|
||||
if (_role == Role::Profile && !_onlineSorter) {
|
||||
_onlineSorter = std::make_unique<ParticipantsOnlineSorter>(
|
||||
_peer,
|
||||
delegate());
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
|
||||
QPointer<Ui::BoxContent> ParticipantsBoxController::showBox(
|
||||
@@ -1287,7 +1324,7 @@ void ParticipantsBoxController::rebuildChatParticipants(
|
||||
}
|
||||
_onlineSorter->sort();
|
||||
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
chatListReady();
|
||||
}
|
||||
|
||||
@@ -1340,7 +1377,7 @@ void ParticipantsBoxController::rebuildChatAdmins(
|
||||
}
|
||||
}
|
||||
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
chatListReady();
|
||||
}
|
||||
|
||||
@@ -1362,7 +1399,7 @@ void ParticipantsBoxController::rebuildRowTypes() {
|
||||
delegate()->peerListRowAt(i).get());
|
||||
row->setType(computeType(row->user()));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::loadMoreRows() {
|
||||
@@ -1401,11 +1438,13 @@ void ParticipantsBoxController::loadMoreRows() {
|
||||
MTP_int(perPage),
|
||||
MTP_long(participantsHash)
|
||||
)).done([=](const MTPchannels_ChannelParticipants &result) {
|
||||
auto added = false;
|
||||
const auto firstLoad = !_offset;
|
||||
_loadRequestId = 0;
|
||||
|
||||
auto wasRecentRequest = firstLoad
|
||||
&& (_role == Role::Members || _role == Role::Profile);
|
||||
&& (_role == Role::Members || _role == Role::Profile)
|
||||
&& channel->canViewMembers();
|
||||
|
||||
result.match([&](const MTPDchannels_channelParticipants &data) {
|
||||
const auto &[availableCount, list] = wasRecentRequest
|
||||
@@ -1414,7 +1453,9 @@ void ParticipantsBoxController::loadMoreRows() {
|
||||
for (const auto &data : list) {
|
||||
if (const auto participant = _additional.applyParticipant(
|
||||
data)) {
|
||||
appendRow(participant);
|
||||
if (appendRow(participant)) {
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto size = list.size()) {
|
||||
@@ -1427,7 +1468,9 @@ void ParticipantsBoxController::loadMoreRows() {
|
||||
LOG(("API Error: "
|
||||
"channels.channelParticipantsNotModified received!"));
|
||||
});
|
||||
|
||||
if (!firstLoad && !added) {
|
||||
_allLoaded = true;
|
||||
}
|
||||
if (_allLoaded
|
||||
|| (firstLoad && delegate()->peerListFullRowsCount() > 0)) {
|
||||
refreshDescription();
|
||||
@@ -1435,7 +1478,7 @@ void ParticipantsBoxController::loadMoreRows() {
|
||||
if (_onlineSorter) {
|
||||
_onlineSorter->sort();
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}).fail([this] {
|
||||
_loadRequestId = 0;
|
||||
}).send();
|
||||
@@ -1457,7 +1500,7 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() {
|
||||
return false;
|
||||
}
|
||||
const auto megagroup = _peer->asMegagroup();
|
||||
if (!megagroup) {
|
||||
if (!megagroup || !megagroup->canViewMembers()) {
|
||||
return false;
|
||||
}
|
||||
const auto info = megagroup->mgInfo.get();
|
||||
@@ -1659,7 +1702,7 @@ void ParticipantsBoxController::editAdminDone(
|
||||
}
|
||||
}
|
||||
recomputeTypeFor(user);
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::showRestricted(not_null<UserData*> user) {
|
||||
@@ -1723,7 +1766,7 @@ void ParticipantsBoxController::editRestrictedDone(
|
||||
}
|
||||
}
|
||||
recomputeTypeFor(participant);
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::kickParticipant(not_null<PeerData*> participant) {
|
||||
@@ -1748,7 +1791,7 @@ void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) {
|
||||
_editBox = nullptr;
|
||||
if (const auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
_peer->session().api().chatParticipants().add(_peer, { 1, user });
|
||||
}
|
||||
@@ -1764,7 +1807,7 @@ void ParticipantsBoxController::kickParticipantSure(
|
||||
|
||||
if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
}
|
||||
auto &session = _peer->session();
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
@@ -1838,7 +1881,7 @@ void ParticipantsBoxController::removeKicked(
|
||||
&& !delegate()->peerListFullRowsCount()) {
|
||||
setDescriptionText(tr::lng_blocked_list_not_found(tr::now));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
refreshRows();
|
||||
removeKicked(participant);
|
||||
}
|
||||
|
||||
@@ -2055,6 +2098,11 @@ void ParticipantsBoxController::fullListRefresh() {
|
||||
delegate()->peerListRowAt(count - 1));
|
||||
}
|
||||
loadMoreRows();
|
||||
refreshRows();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::refreshRows() {
|
||||
_fullCountValue = delegate()->peerListFullRowsCount();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,8 @@ public:
|
||||
std::unique_ptr<PeerListState> saveState() const override;
|
||||
void restoreState(std::unique_ptr<PeerListState> state) override;
|
||||
|
||||
rpl::producer<int> onlineCountValue() const override;
|
||||
[[nodiscard]] rpl::producer<int> onlineCountValue() const;
|
||||
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
||||
|
||||
protected:
|
||||
// Allow child controllers not providing navigation.
|
||||
@@ -229,6 +230,8 @@ private:
|
||||
void rebuildChatAdmins(not_null<ChatData*> chat);
|
||||
void chatListReady();
|
||||
void rebuildRowTypes();
|
||||
void rebuild();
|
||||
void unload();
|
||||
|
||||
void addNewItem();
|
||||
void addNewParticipants();
|
||||
@@ -266,6 +269,7 @@ private:
|
||||
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
||||
void subscribeToCreatorChange(not_null<ChannelData*> channel);
|
||||
void fullListRefresh();
|
||||
void refreshRows();
|
||||
|
||||
// It may be nullptr in subclasses of this controller.
|
||||
Window::SessionNavigation *_navigation = nullptr;
|
||||
@@ -278,6 +282,8 @@ private:
|
||||
bool _allLoaded = false;
|
||||
ParticipantsAdditionalData _additional;
|
||||
std::unique_ptr<ParticipantsOnlineSorter> _onlineSorter;
|
||||
rpl::variable<int> _onlineCountValue;
|
||||
rpl::variable<int> _fullCountValue;
|
||||
Ui::BoxPointer _editBox;
|
||||
Ui::BoxPointer _addBox;
|
||||
QPointer<Ui::BoxContent> _editParticipantBox;
|
||||
|
||||
@@ -43,8 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -470,9 +470,10 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
|
||||
_wrap,
|
||||
object_ptr<Ui::UserpicButton>(
|
||||
_wrap,
|
||||
&_navigation->parentController()->window(),
|
||||
_navigation->parentController(),
|
||||
_peer,
|
||||
Ui::UserpicButton::Role::ChangePhoto,
|
||||
Ui::UserpicButton::Source::PeerPhoto,
|
||||
st::defaultUserpicButton),
|
||||
st::editPeerPhotoMargins);
|
||||
_controls.photo = photoWrap->entity();
|
||||
|
||||
@@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
|
||||
#include "history/history_message.h" // GetErrorTextForSending.
|
||||
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
||||
#include "history/history.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
@@ -1374,7 +1374,7 @@ QString PrepareRequestedRowStatus(TimeId date) {
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
const auto parsed = base::unixtime::parse(date);
|
||||
const auto parsedDate = parsed.date();
|
||||
const auto time = QLocale().toString(parsed.time(), cTimeFormat());
|
||||
const auto time = QLocale().toString(parsed.time(), QLocale::ShortFormat);
|
||||
const auto generic = [&] {
|
||||
return tr::lng_group_requests_status_date_time(
|
||||
tr::now,
|
||||
|
||||
@@ -29,7 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
||||
@@ -354,7 +354,9 @@ bool ProcessCurrent(
|
||||
|
||||
[[nodiscard]] PreparedShortInfoUserpic UserpicValue(
|
||||
not_null<PeerData*> peer,
|
||||
const style::ShortInfoCover &st) {
|
||||
const style::ShortInfoCover &st,
|
||||
rpl::producer<UserPhotosSlice> slices,
|
||||
Fn<bool(not_null<UserpicState*>)> customProcess) {
|
||||
const auto moveRequests = std::make_shared<rpl::event_stream<int>>();
|
||||
auto move = [=](int shift) {
|
||||
moveRequests->fire_copy(shift);
|
||||
@@ -367,7 +369,7 @@ bool ProcessCurrent(
|
||||
state->size = size;
|
||||
state->roundMask = Images::CornersMask(radius);
|
||||
const auto push = [=](bool force = false) {
|
||||
if (ProcessCurrent(peer, state) || force) {
|
||||
if (customProcess(state) || force) {
|
||||
consumer.put_next_copy(state->current);
|
||||
}
|
||||
};
|
||||
@@ -381,17 +383,12 @@ bool ProcessCurrent(
|
||||
push();
|
||||
}, lifetime);
|
||||
|
||||
if (const auto user = peer->asUser()) {
|
||||
UserPhotosReversedViewer(
|
||||
&peer->session(),
|
||||
UserPhotosSlice::Key(peerToUser(user->id), PhotoId()),
|
||||
kOverviewLimit,
|
||||
kOverviewLimit
|
||||
) | rpl::start_with_next([=](UserPhotosSlice &&slice) {
|
||||
state->userSlice = std::move(slice);
|
||||
push();
|
||||
}, lifetime);
|
||||
}
|
||||
rpl::duplicate(
|
||||
slices
|
||||
) | rpl::start_with_next([=](UserPhotosSlice &&slice) {
|
||||
state->userSlice = std::move(slice);
|
||||
push();
|
||||
}, lifetime);
|
||||
|
||||
moveRequests->events(
|
||||
) | rpl::filter([=] {
|
||||
@@ -429,7 +426,7 @@ object_ptr<Ui::BoxContent> PrepareShortInfoBox(
|
||||
: peer->isBroadcast()
|
||||
? PeerShortInfoType::Channel
|
||||
: PeerShortInfoType::Group;
|
||||
auto userpic = UserpicValue(peer, st::shortInfoCover);
|
||||
auto userpic = PrepareShortInfoUserpic(peer, st::shortInfoCover);
|
||||
auto result = Box<PeerShortInfoBox>(
|
||||
type,
|
||||
FieldsValue(peer),
|
||||
@@ -467,5 +464,39 @@ rpl::producer<QString> PrepareShortInfoStatus(not_null<PeerData*> peer) {
|
||||
PreparedShortInfoUserpic PrepareShortInfoUserpic(
|
||||
not_null<PeerData*> peer,
|
||||
const style::ShortInfoCover &st) {
|
||||
return UserpicValue(peer, st);
|
||||
auto slices = peer->isUser()
|
||||
? UserPhotosReversedViewer(
|
||||
&peer->session(),
|
||||
UserPhotosSlice::Key(peerToUser(peer->asUser()->id), PhotoId()),
|
||||
kOverviewLimit,
|
||||
kOverviewLimit)
|
||||
: rpl::never<UserPhotosSlice>();
|
||||
auto process = [=](not_null<UserpicState*> state) {
|
||||
return ProcessCurrent(peer, state);
|
||||
};
|
||||
return UserpicValue(peer, st, std::move(slices), std::move(process));
|
||||
}
|
||||
|
||||
PreparedShortInfoUserpic PrepareShortInfoFallbackUserpic(
|
||||
not_null<PeerData*> peer,
|
||||
const style::ShortInfoCover &st) {
|
||||
Expects(peer->isUser());
|
||||
|
||||
const auto photoId = SyncUserFallbackPhotoViewer(peer->asUser());
|
||||
auto slices = photoId
|
||||
? rpl::single<UserPhotosSlice>(UserPhotosSlice(
|
||||
Storage::UserPhotosKey(peerToUser(peer->id), *photoId),
|
||||
std::deque<PhotoId>({ *photoId }),
|
||||
1,
|
||||
1,
|
||||
1))
|
||||
: (rpl::never<UserPhotosSlice>() | rpl::type_erased());
|
||||
auto process = [=](not_null<UserpicState*> state) {
|
||||
if (photoId) {
|
||||
ProcessFullPhoto(peer, state, peer->owner().photo(*photoId));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return UserpicValue(peer, st, std::move(slices), std::move(process));
|
||||
}
|
||||
|
||||
@@ -45,3 +45,7 @@ struct PreparedShortInfoUserpic {
|
||||
[[nodiscard]] PreparedShortInfoUserpic PrepareShortInfoUserpic(
|
||||
not_null<PeerData*> peer,
|
||||
const style::ShortInfoCover &st);
|
||||
|
||||
[[nodiscard]] PreparedShortInfoUserpic PrepareShortInfoFallbackUserpic(
|
||||
not_null<PeerData*> peer,
|
||||
const style::ShortInfoCover &st);
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace {
|
||||
constexpr auto kPremiumShift = 21. / 240;
|
||||
constexpr auto kReactionsPerRow = 5;
|
||||
constexpr auto kDisabledOpacity = 0.5;
|
||||
constexpr auto kPreviewsCount = int(PremiumPreview::kCount);
|
||||
constexpr auto kToggleStickerTimeout = 2 * crl::time(1000);
|
||||
constexpr auto kStarOpacityOff = 0.1;
|
||||
constexpr auto kStarOpacityOn = 1.;
|
||||
@@ -744,19 +743,21 @@ struct VideoPreviewDocument {
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSwitch(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<rpl::variable<PremiumPreview>*> selected) {
|
||||
not_null<rpl::variable<PremiumPreview>*> selected,
|
||||
std::vector<PremiumPreview> order) {
|
||||
const auto padding = st::premiumDotPadding;
|
||||
const auto width = padding.left() + st::premiumDot + padding.right();
|
||||
const auto height = padding.top() + st::premiumDot + padding.bottom();
|
||||
const auto stops = Ui::Premium::ButtonGradientStops();
|
||||
auto result = object_ptr<Ui::FixedHeightWidget>(parent.get(), height);
|
||||
const auto raw = result.data();
|
||||
for (auto i = 0; i != kPreviewsCount; ++i) {
|
||||
const auto section = PremiumPreview(i);
|
||||
const auto count = order.size();
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
const auto section = order[i];
|
||||
const auto button = Ui::CreateChild<Ui::AbstractButton>(raw);
|
||||
parent->widthValue(
|
||||
) | rpl::start_with_next([=](int outer) {
|
||||
const auto full = width * kPreviewsCount;
|
||||
const auto full = width * count;
|
||||
const auto left = (outer - full) / 2 + (i * width);
|
||||
button->setGeometry(left, 0, width, height);
|
||||
}, button->lifetime());
|
||||
@@ -770,7 +771,7 @@ struct VideoPreviewDocument {
|
||||
p.setBrush((selected->current() == section)
|
||||
? anim::gradient_color_at(
|
||||
stops,
|
||||
float64(i) / (kPreviewsCount - 1))
|
||||
float64(i) / (count - 1))
|
||||
: st::windowBgRipple->c);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(
|
||||
@@ -815,15 +816,23 @@ void PreviewBox(
|
||||
Fn<void()> preload;
|
||||
std::vector<Hiding> hiding;
|
||||
rpl::variable<PremiumPreview> selected;
|
||||
std::vector<PremiumPreview> order;
|
||||
};
|
||||
const auto state = outer->lifetime().make_state<State>();
|
||||
state->selected = descriptor.section;
|
||||
state->order = Settings::PremiumPreviewOrder(&controller->session());
|
||||
|
||||
const auto index = [=](PremiumPreview section) {
|
||||
const auto it = ranges::find(state->order, section);
|
||||
return (it == end(state->order))
|
||||
? 0
|
||||
: std::distance(begin(state->order), it);
|
||||
};
|
||||
|
||||
const auto move = [=](int delta) {
|
||||
using Type = PremiumPreview;
|
||||
const auto count = int(Type::kCount);
|
||||
const auto count = int(state->order.size());
|
||||
const auto now = state->selected.current();
|
||||
state->selected = Type((int(now) + count + delta) % count);
|
||||
state->selected = state->order[(index(now) + count + delta) % count];
|
||||
};
|
||||
|
||||
const auto buttonsParent = box->verticalLayout().get();
|
||||
@@ -912,7 +921,7 @@ void PreviewBox(
|
||||
}
|
||||
};
|
||||
animationCallback();
|
||||
const auto toLeft = int(now) > int(was);
|
||||
const auto toLeft = index(now) > index(was);
|
||||
auto start = state->content->x() + (toLeft ? single : -single);
|
||||
for (const auto &hiding : state->hiding) {
|
||||
const auto left = hiding.widget->x();
|
||||
@@ -955,14 +964,10 @@ void PreviewBox(
|
||||
}, outer->lifetime());
|
||||
|
||||
auto title = state->selected.value(
|
||||
) | rpl::map([=](PremiumPreview section) {
|
||||
return SectionTitle(section);
|
||||
}) | rpl::flatten_latest();
|
||||
) | rpl::map(SectionTitle) | rpl::flatten_latest();
|
||||
|
||||
auto text = state->selected.value(
|
||||
) | rpl::map([=](PremiumPreview section) {
|
||||
return SectionAbout(section);
|
||||
}) | rpl::flatten_latest();
|
||||
) | rpl::map(SectionAbout) | rpl::flatten_latest();
|
||||
|
||||
const auto padding = st::premiumPreviewAboutPadding;
|
||||
const auto available = size.width() - padding.left() - padding.right();
|
||||
@@ -985,7 +990,7 @@ void PreviewBox(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(box, std::move(textLabel)),
|
||||
padding);
|
||||
box->addRow(
|
||||
CreateSwitch(box->verticalLayout(), &state->selected),
|
||||
CreateSwitch(box->verticalLayout(), &state->selected, state->order),
|
||||
st::premiumDotsMargin);
|
||||
const auto showFinished = [=] {
|
||||
state->showFinished = true;
|
||||
|
||||
@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/reactions/history_view_reactions_strip.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -447,7 +447,7 @@ void AddReactionCustomIcon(
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto size = Data::FrameSizeFromTag(tag) / ratio;
|
||||
state->custom->paint(p, {
|
||||
.preview = st::windowBgRipple->c,
|
||||
.textColor = st::windowFg->c,
|
||||
.now = crl::now(),
|
||||
.position = QPoint(
|
||||
(widget->width() - size) / 2,
|
||||
|
||||
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
@@ -62,6 +63,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
@@ -215,19 +218,50 @@ rpl::producer<int> SendFilesBox::Block::itemModifyRequest() const {
|
||||
|
||||
void SendFilesBox::Block::setSendWay(Ui::SendFilesWay way) {
|
||||
if (!_isAlbum) {
|
||||
if (_isSingleMedia) {
|
||||
const auto media = static_cast<Ui::SingleMediaPreview*>(
|
||||
_preview.get());
|
||||
media->setSendWay(way);
|
||||
}
|
||||
return;
|
||||
}
|
||||
applyAlbumOrder();
|
||||
applyChanges();
|
||||
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
|
||||
album->setSendWay(way);
|
||||
}
|
||||
|
||||
void SendFilesBox::Block::applyAlbumOrder() {
|
||||
void SendFilesBox::Block::toggleSpoilers(bool enabled) {
|
||||
if (_isAlbum) {
|
||||
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
|
||||
album->toggleSpoilers(enabled);
|
||||
} else if (_isSingleMedia) {
|
||||
const auto media = static_cast<Ui::SingleMediaPreview*>(
|
||||
_preview.get());
|
||||
media->setSpoiler(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::Block::applyChanges() {
|
||||
if (!_isAlbum) {
|
||||
if (_isSingleMedia) {
|
||||
const auto media = static_cast<Ui::SingleMediaPreview*>(
|
||||
_preview.get());
|
||||
if (media->canHaveSpoiler()) {
|
||||
(*_items)[_from].spoiler = media->hasSpoiler();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto album = static_cast<Ui::AlbumPreview*>(_preview.get());
|
||||
const auto order = album->takeOrder();
|
||||
const auto guard = gsl::finally([&] {
|
||||
const auto spoilered = album->collectSpoileredIndices();
|
||||
for (auto i = 0, count = int(order.size()); i != count; ++i) {
|
||||
if (album->canHaveSpoiler(i)) {
|
||||
(*_items)[_from + i].spoiler = spoilered.contains(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
const auto isIdentity = [&] {
|
||||
for (auto i = 0, count = int(order.size()); i != count; ++i) {
|
||||
if (order[i] != i) {
|
||||
@@ -277,7 +311,7 @@ SendFilesBox::SendFilesBox(
|
||||
void SendFilesBox::initPreview() {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
refreshControls();
|
||||
refreshControls(true);
|
||||
|
||||
updateBoxSize();
|
||||
|
||||
@@ -325,19 +359,6 @@ void SendFilesBox::enqueueNextPrepare() {
|
||||
}
|
||||
|
||||
void SendFilesBox::prepare() {
|
||||
_send = addButton(
|
||||
(_sendType == Api::SendType::Normal
|
||||
? tr::lng_send_button()
|
||||
: tr::lng_create_group_next()),
|
||||
[=] { send({}); });
|
||||
if (_sendType == Api::SendType::Normal) {
|
||||
SendMenu::SetupMenuAndShortcuts(
|
||||
_send,
|
||||
[=] { return _sendMenuType; },
|
||||
[=] { sendSilent(); },
|
||||
[=] { sendScheduled(); });
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
initSendWay();
|
||||
setupCaption();
|
||||
setupSendWayControls();
|
||||
@@ -351,11 +372,6 @@ void SendFilesBox::prepare() {
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_addFile = addLeftButton(
|
||||
tr::lng_stickers_featured_add(),
|
||||
base::fn_delayed(st::historyAttach.ripple.hideDuration, this, [=] {
|
||||
openDialogToAddFileToAlbum();
|
||||
}));
|
||||
setupDragArea();
|
||||
}
|
||||
|
||||
@@ -385,19 +401,25 @@ void SendFilesBox::setupDragArea() {
|
||||
areas.photo->setDroppedCallback(droppedCallback(true));
|
||||
}
|
||||
|
||||
void SendFilesBox::refreshAllAfterChanges(int fromItem) {
|
||||
void SendFilesBox::refreshAllAfterChanges(int fromItem, Fn<void()> perform) {
|
||||
auto fromBlock = 0;
|
||||
for (auto count = int(_blocks.size()); fromBlock != count; ++fromBlock) {
|
||||
if (_blocks[fromBlock].tillIndex() >= fromItem) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto index = fromBlock; index < _blocks.size(); ++index) {
|
||||
_blocks[index].applyChanges();
|
||||
}
|
||||
if (perform) {
|
||||
perform();
|
||||
}
|
||||
generatePreviewFrom(fromBlock);
|
||||
{
|
||||
auto sendWay = _sendWay.current();
|
||||
sendWay.setHasCompressedStickers(_list.hasSticker());
|
||||
_sendWay = sendWay;
|
||||
}
|
||||
generatePreviewFrom(fromBlock);
|
||||
_inner->resizeToWidth(st::boxWideWidth);
|
||||
refreshControls();
|
||||
captionResized();
|
||||
@@ -431,6 +453,103 @@ void SendFilesBox::openDialogToAddFileToAlbum() {
|
||||
crl::guard(this, callback));
|
||||
}
|
||||
|
||||
void SendFilesBox::refreshButtons() {
|
||||
clearButtons();
|
||||
|
||||
_send = addButton(
|
||||
(_sendType == Api::SendType::Normal
|
||||
? tr::lng_send_button()
|
||||
: tr::lng_create_group_next()),
|
||||
[=] { send({}); });
|
||||
if (_sendType == Api::SendType::Normal) {
|
||||
SendMenu::SetupMenuAndShortcuts(
|
||||
_send,
|
||||
[=] { return _sendMenuType; },
|
||||
[=] { sendSilent(); },
|
||||
[=] { sendScheduled(); });
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
_addFile = addLeftButton(
|
||||
tr::lng_stickers_featured_add(),
|
||||
base::fn_delayed(st::historyAttach.ripple.hideDuration, this, [=] {
|
||||
openDialogToAddFileToAlbum();
|
||||
}));
|
||||
|
||||
addMenuButton();
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSendMenu() const {
|
||||
return (_sendMenuType != SendMenu::Type::Disabled);
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSpoilerMenu() const {
|
||||
const auto allAreVideo = !ranges::any_of(_list.files, [](const auto &f) {
|
||||
using Type = Ui::PreparedFile::Type;
|
||||
return (f.type != Type::Video);
|
||||
});
|
||||
const auto allAreMedia = !ranges::any_of(_list.files, [](const auto &f) {
|
||||
using Type = Ui::PreparedFile::Type;
|
||||
return (f.type != Type::Photo) && (f.type != Type::Video);
|
||||
});
|
||||
return allAreVideo
|
||||
|| (allAreMedia && _sendWay.current().sendImagesAsPhotos());
|
||||
}
|
||||
|
||||
void SendFilesBox::applyBlockChanges() {
|
||||
for (auto &block : _blocks) {
|
||||
block.applyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
bool SendFilesBox::allWithSpoilers() {
|
||||
applyBlockChanges();
|
||||
return ranges::all_of(_list.files, &Ui::PreparedFile::spoiler);
|
||||
}
|
||||
|
||||
void SendFilesBox::toggleSpoilers(bool enabled) {
|
||||
for (auto &file : _list.files) {
|
||||
file.spoiler = enabled;
|
||||
}
|
||||
for (auto &block : _blocks) {
|
||||
block.toggleSpoilers(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::addMenuButton() {
|
||||
if (!hasSendMenu() && !hasSpoilerMenu()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto top = addTopButton(st::infoTopBarMenu);
|
||||
top->setClickedCallback([=] {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
top,
|
||||
st::popupMenuExpandedSeparator);
|
||||
if (hasSpoilerMenu()) {
|
||||
const auto spoilered = allWithSpoilers();
|
||||
_menu->addAction(
|
||||
(spoilered
|
||||
? tr::lng_context_disable_spoiler(tr::now)
|
||||
: tr::lng_context_spoiler_effect(tr::now)),
|
||||
[=] { toggleSpoilers(!spoilered); },
|
||||
spoilered ? &st::menuIconSpoilerOff : &st::menuIconSpoiler);
|
||||
if (hasSendMenu()) {
|
||||
_menu->addSeparator();
|
||||
}
|
||||
}
|
||||
if (hasSendMenu()) {
|
||||
SendMenu::FillSendMenu(
|
||||
_menu.get(),
|
||||
_sendMenuType,
|
||||
[=] { sendSilent(); },
|
||||
[=] { sendScheduled(); });
|
||||
}
|
||||
_menu->popup(QCursor::pos());
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SendFilesBox::initSendWay() {
|
||||
_sendWay = [&] {
|
||||
auto result = Core::App().settings().sendFilesWay();
|
||||
@@ -454,6 +573,9 @@ void SendFilesBox::initSendWay() {
|
||||
for (auto &block : _blocks) {
|
||||
block.setSendWay(value);
|
||||
}
|
||||
if (!hasSendMenu()) {
|
||||
refreshButtons();
|
||||
}
|
||||
setInnerFocus();
|
||||
}, lifetime());
|
||||
}
|
||||
@@ -489,11 +611,7 @@ void SendFilesBox::generatePreviewFrom(int fromBlock) {
|
||||
|
||||
using Type = Ui::PreparedFile::Type;
|
||||
|
||||
const auto eraseFrom = _blocks.begin() + fromBlock;
|
||||
for (auto i = eraseFrom; i != _blocks.end(); ++i) {
|
||||
i->applyAlbumOrder();
|
||||
}
|
||||
_blocks.erase(eraseFrom, _blocks.end());
|
||||
_blocks.erase(_blocks.begin() + fromBlock, _blocks.end());
|
||||
|
||||
const auto fromItem = _blocks.empty() ? 0 : _blocks.back().tillIndex();
|
||||
Assert(fromItem <= _list.files.size());
|
||||
@@ -559,8 +677,9 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
closeBox();
|
||||
return;
|
||||
}
|
||||
_list.files.erase(_list.files.begin() + index);
|
||||
refreshAllAfterChanges(from);
|
||||
refreshAllAfterChanges(index, [&] {
|
||||
_list.files.erase(_list.files.begin() + index);
|
||||
});
|
||||
});
|
||||
}, widget->lifetime());
|
||||
|
||||
@@ -571,8 +690,9 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
if (list.files.empty()) {
|
||||
return;
|
||||
}
|
||||
_list.files[index] = std::move(list.files.front());
|
||||
refreshAllAfterChanges(from);
|
||||
refreshAllAfterChanges(from, [&] {
|
||||
_list.files[index] = std::move(list.files.front());
|
||||
});
|
||||
};
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
if (_sendLimit != SendLimit::One) {
|
||||
@@ -626,22 +746,27 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
}, widget->lifetime());
|
||||
}
|
||||
|
||||
void SendFilesBox::refreshControls() {
|
||||
void SendFilesBox::refreshControls(bool initial) {
|
||||
if (initial || !hasSendMenu()) {
|
||||
refreshButtons();
|
||||
}
|
||||
refreshTitleText();
|
||||
updateSendWayControlsVisibility();
|
||||
updateSendWayControls();
|
||||
updateCaptionPlaceholder();
|
||||
}
|
||||
|
||||
void SendFilesBox::setupSendWayControls() {
|
||||
const auto groupFilesFirst = _sendWay.current().groupFiles();
|
||||
const auto asPhotosFirst = _sendWay.current().sendImagesAsPhotos();
|
||||
_groupFiles.create(
|
||||
this,
|
||||
tr::lng_send_grouped(tr::now),
|
||||
_sendWay.current().groupFiles(),
|
||||
groupFilesFirst,
|
||||
st::defaultBoxCheckbox);
|
||||
_sendImagesAsPhotos.create(
|
||||
this,
|
||||
tr::lng_send_compressed(tr::now),
|
||||
_sendWay.current().sendImagesAsPhotos(),
|
||||
asPhotosFirst,
|
||||
st::defaultBoxCheckbox);
|
||||
|
||||
_sendWay.changes(
|
||||
@@ -664,17 +789,35 @@ void SendFilesBox::setupSendWayControls() {
|
||||
_sendWay = sendWay;
|
||||
}, lifetime());
|
||||
|
||||
_wayRemember.create(
|
||||
this,
|
||||
tr::lng_remember(tr::now),
|
||||
false,
|
||||
st::defaultBoxCheckbox);
|
||||
_wayRemember->hide();
|
||||
rpl::combine(
|
||||
_groupFiles->checkedValue(),
|
||||
_sendImagesAsPhotos->checkedValue()
|
||||
) | rpl::start_with_next([=](bool groupFiles, bool asPhoto) {
|
||||
_wayRemember->setVisible(
|
||||
(groupFiles != groupFilesFirst) || (asPhoto != asPhotosFirst));
|
||||
captionResized();
|
||||
}, lifetime());
|
||||
|
||||
_hintLabel.create(
|
||||
this,
|
||||
tr::lng_edit_photo_editor_hint(tr::now),
|
||||
st::editMediaHintLabel);
|
||||
}
|
||||
|
||||
void SendFilesBox::updateSendWayControlsVisibility() {
|
||||
void SendFilesBox::updateSendWayControls() {
|
||||
const auto onlyOne = (_sendLimit == SendLimit::One);
|
||||
_groupFiles->setVisible(_list.hasGroupOption(onlyOne));
|
||||
_sendImagesAsPhotos->setVisible(
|
||||
_list.hasSendImagesAsPhotosOption(onlyOne));
|
||||
_sendImagesAsPhotos->setText((_list.files.size() > 1)
|
||||
? tr::lng_send_compressed(tr::now)
|
||||
: tr::lng_send_compressed_one(tr::now));
|
||||
|
||||
_hintLabel->setVisible(
|
||||
_controller->session().settings().photoEditorHintShown()
|
||||
@@ -892,20 +1035,27 @@ void SendFilesBox::addFile(Ui::PreparedFile &&file) {
|
||||
}
|
||||
|
||||
void SendFilesBox::refreshTitleText() {
|
||||
using Type = Ui::PreparedFile::Type;
|
||||
const auto count = int(_list.files.size());
|
||||
if (count > 1) {
|
||||
const auto imagesCount = ranges::count(
|
||||
_list.files,
|
||||
Ui::PreparedFile::Type::Photo,
|
||||
Type::Photo,
|
||||
&Ui::PreparedFile::type);
|
||||
_titleText = (imagesCount == count)
|
||||
? tr::lng_send_images_selected(tr::now, lt_count, count)
|
||||
: tr::lng_send_files_selected(tr::now, lt_count, count);
|
||||
_titleHeight = st::boxTitleHeight;
|
||||
} else {
|
||||
_titleText = QString();
|
||||
_titleHeight = count ? st::boxPhotoPadding.top() : 0;
|
||||
const auto type = _list.files.empty()
|
||||
? Type::None
|
||||
: _list.files.front().type;
|
||||
_titleText = (type == Type::Photo)
|
||||
? tr::lng_send_image(tr::now)
|
||||
: (type == Type::Video)
|
||||
? tr::lng_send_video(tr::now)
|
||||
: tr::lng_send_file(tr::now);
|
||||
}
|
||||
_titleHeight = st::boxTitleHeight;
|
||||
}
|
||||
|
||||
void SendFilesBox::updateBoxSize() {
|
||||
@@ -913,9 +1063,10 @@ void SendFilesBox::updateBoxSize() {
|
||||
if (_caption) {
|
||||
footerHeight += st::boxPhotoCaptionSkip + _caption->height();
|
||||
}
|
||||
const auto pairs = std::array<std::pair<RpWidget*, int>, 3>{ {
|
||||
const auto pairs = std::array<std::pair<RpWidget*, int>, 4>{ {
|
||||
{ _groupFiles.data(), st::boxPhotoCompressedSkip },
|
||||
{ _sendImagesAsPhotos.data(), st::boxPhotoCompressedSkip },
|
||||
{ _wayRemember.data(), st::boxPhotoCompressedSkip },
|
||||
{ _hintLabel.data(), st::editMediaLabelMargins.top() },
|
||||
} };
|
||||
for (const auto &pair : pairs) {
|
||||
@@ -980,10 +1131,11 @@ void SendFilesBox::updateControlsGeometry() {
|
||||
_emojiToggle->update();
|
||||
}
|
||||
}
|
||||
const auto pairs = std::array<std::pair<RpWidget*, int>, 3>{ {
|
||||
const auto pairs = std::array<std::pair<RpWidget*, int>, 4>{ {
|
||||
{ _hintLabel.data(), st::editMediaLabelMargins.top() },
|
||||
{ _groupFiles.data(), st::boxPhotoCompressedSkip },
|
||||
{ _sendImagesAsPhotos.data(), st::boxPhotoCompressedSkip },
|
||||
{ _wayRemember.data(), st::boxPhotoCompressedSkip },
|
||||
} };
|
||||
for (const auto &pair : ranges::views::reverse(pairs)) {
|
||||
const auto pointer = pair.first;
|
||||
@@ -1052,12 +1204,15 @@ void SendFilesBox::send(
|
||||
return;
|
||||
}
|
||||
|
||||
saveSendWaySettings();
|
||||
|
||||
for (auto &block : _blocks) {
|
||||
block.applyAlbumOrder();
|
||||
if (_wayRemember && _wayRemember->checked()) {
|
||||
saveSendWaySettings();
|
||||
}
|
||||
|
||||
for (auto &item : _list.files) {
|
||||
item.spoiler = false;
|
||||
}
|
||||
applyBlockChanges();
|
||||
|
||||
Storage::ApplyModifications(_list);
|
||||
|
||||
_confirmed = true;
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
|
||||
@@ -36,6 +37,7 @@ class EmojiButton;
|
||||
class AlbumPreview;
|
||||
class VerticalLayout;
|
||||
class FlatLabel;
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
@@ -106,7 +108,8 @@ private:
|
||||
[[nodiscard]] rpl::producer<int> itemModifyRequest() const;
|
||||
|
||||
void setSendWay(Ui::SendFilesWay way);
|
||||
void applyAlbumOrder();
|
||||
void toggleSpoilers(bool enabled);
|
||||
void applyChanges();
|
||||
|
||||
private:
|
||||
base::unique_qptr<Ui::RpWidget> _preview;
|
||||
@@ -117,16 +120,24 @@ private:
|
||||
bool _isSingleMedia = false;
|
||||
|
||||
};
|
||||
|
||||
void initSendWay();
|
||||
void initPreview();
|
||||
[[nodiscard]] bool hasSendMenu() const;
|
||||
[[nodiscard]] bool hasSpoilerMenu() const;
|
||||
[[nodiscard]] bool allWithSpoilers();
|
||||
void addMenuButton();
|
||||
void applyBlockChanges();
|
||||
void toggleSpoilers(bool enabled);
|
||||
|
||||
bool validateLength(const QString &text) const;
|
||||
void refreshControls();
|
||||
void refreshButtons();
|
||||
void refreshControls(bool initial = false);
|
||||
void setupSendWayControls();
|
||||
void setupCaption();
|
||||
|
||||
void setupEmojiPanel();
|
||||
void updateSendWayControlsVisibility();
|
||||
void updateSendWayControls();
|
||||
void updateEmojiPanelGeometry();
|
||||
void emojiFilterForGeometry(not_null<QEvent*> event);
|
||||
|
||||
@@ -152,7 +163,7 @@ private:
|
||||
void pushBlock(int from, int till);
|
||||
|
||||
void openDialogToAddFileToAlbum();
|
||||
void refreshAllAfterChanges(int fromItem);
|
||||
void refreshAllAfterChanges(int fromItem, Fn<void()> perform = nullptr);
|
||||
|
||||
void enqueueNextPrepare();
|
||||
void addPreparedAsyncFile(Ui::PreparedFile &&file);
|
||||
@@ -187,6 +198,7 @@ private:
|
||||
|
||||
object_ptr<Ui::Checkbox> _groupFiles = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _sendImagesAsPhotos = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _wayRemember = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _hintLabel = { nullptr };
|
||||
rpl::variable<Ui::SendFilesWay> _sendWay = Ui::SendFilesWay();
|
||||
|
||||
@@ -199,6 +211,8 @@ private:
|
||||
Fn<void()> _whenReadySend;
|
||||
bool _preparing = false;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
QPointer<Ui::RoundButton> _send;
|
||||
QPointer<Ui::RoundButton> _addFile;
|
||||
|
||||
|
||||
@@ -218,29 +218,43 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||
return Type::Other;
|
||||
}
|
||||
|
||||
[[nodiscard]] style::color ColorForType(Type type) {
|
||||
switch (type) {
|
||||
case Type::Windows:
|
||||
case Type::Mac:
|
||||
case Type::Other:
|
||||
return st::historyPeer4UserpicBg; // blue
|
||||
case Type::Ubuntu:
|
||||
return st::historyPeer8UserpicBg; // orange
|
||||
case Type::Linux:
|
||||
return st::historyPeer5UserpicBg; // purple
|
||||
case Type::iPhone:
|
||||
case Type::iPad:
|
||||
return st::historyPeer7UserpicBg; // sea
|
||||
case Type::Android:
|
||||
return st::historyPeer2UserpicBg; // green
|
||||
case Type::Web:
|
||||
case Type::Chrome:
|
||||
case Type::Edge:
|
||||
case Type::Firefox:
|
||||
case Type::Safari:
|
||||
return st::historyPeer6UserpicBg; // pink
|
||||
}
|
||||
Unexpected("Type in ColorForType.");
|
||||
[[nodiscard]] QBrush GradientForType(Type type, int size) {
|
||||
const auto colors = [&]() -> std::pair<style::color, style::color> {
|
||||
switch (type) {
|
||||
case Type::Windows:
|
||||
case Type::Mac:
|
||||
case Type::Other:
|
||||
// Blue.
|
||||
return { st::historyPeer4UserpicBg, st::historyPeer4UserpicBg2 };
|
||||
case Type::Ubuntu:
|
||||
// Orange.
|
||||
return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 };
|
||||
case Type::Linux:
|
||||
// Purple.
|
||||
return { st::historyPeer5UserpicBg, st::historyPeer5UserpicBg2 };
|
||||
case Type::iPhone:
|
||||
case Type::iPad:
|
||||
// Sea.
|
||||
return { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 };
|
||||
case Type::Android:
|
||||
// Green.
|
||||
return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 };
|
||||
case Type::Web:
|
||||
case Type::Chrome:
|
||||
case Type::Edge:
|
||||
case Type::Firefox:
|
||||
case Type::Safari:
|
||||
// Pink.
|
||||
return { st::historyPeer6UserpicBg, st::historyPeer6UserpicBg2 };
|
||||
}
|
||||
Unexpected("Type in GradientForType.");
|
||||
}();
|
||||
auto gradient = QLinearGradient(0, 0, 0, size);
|
||||
gradient.setStops({
|
||||
{ 0.0, colors.first->c },
|
||||
{ 1.0, colors.second->c },
|
||||
});
|
||||
return QBrush(std::move(gradient));
|
||||
}
|
||||
|
||||
[[nodiscard]] const style::icon &IconForType(Type type) {
|
||||
@@ -308,7 +322,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||
|
||||
auto p = QPainter(&result);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setBrush(ColorForType(type));
|
||||
p.setBrush(GradientForType(type, size));
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(rect);
|
||||
IconForType(type).paintInCenter(p, rect);
|
||||
@@ -344,7 +358,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||
|
||||
auto p = QPainter(&state->background);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setBrush(ColorForType(type));
|
||||
p.setBrush(GradientForType(type, size));
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(rect);
|
||||
if (const auto icon = IconBigForType(type)) {
|
||||
|
||||
@@ -30,7 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "menu/menu_check_item.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/history_view_element.h" // HistoryView::Context.
|
||||
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
@@ -1402,11 +1403,16 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
api.sendMessage(std::move(message));
|
||||
}
|
||||
const auto topicRootId = thread->topicRootId();
|
||||
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
||||
const auto topMsgId = (topicRootId == kGeneralId)
|
||||
? MsgId(0)
|
||||
: topicRootId;
|
||||
const auto peer = thread->peer();
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
histories.sendRequest(history, requestType, [=](
|
||||
Fn<void()> finish) {
|
||||
auto &api = history->session().api();
|
||||
const auto sendFlags = commonSendFlags
|
||||
| (topicRootId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (ShouldSendSilent(peer, options)
|
||||
? Flag::f_silent
|
||||
: Flag(0));
|
||||
@@ -1417,7 +1423,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
MTP_vector<MTPint>(mtpMsgIds),
|
||||
MTP_vector<MTPlong>(generateRandom()),
|
||||
peer->input,
|
||||
MTP_int(topicRootId),
|
||||
MTP_int(topMsgId),
|
||||
MTP_int(options.scheduled),
|
||||
MTP_inputPeerEmpty() // send_as
|
||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||
|
||||
@@ -331,7 +331,6 @@ private:
|
||||
std::vector<Element> _elements;
|
||||
std::unique_ptr<Lottie::MultiPlayer> _lottiePlayer;
|
||||
|
||||
mutable Ui::Text::CustomEmojiColored _colored;
|
||||
base::flat_map<
|
||||
not_null<DocumentData*>,
|
||||
std::unique_ptr<Ui::Text::CustomEmoji>> _customEmoji;
|
||||
@@ -1335,10 +1334,8 @@ void StickerSetBox::Inner::paintSticker(
|
||||
(_singleSize.height() - size.height()) / 2);
|
||||
auto lottieFrame = QImage();
|
||||
if (element.emoji) {
|
||||
_colored.color = st::profileVerifiedCheckBg->c;
|
||||
element.emoji->paint(p, {
|
||||
.preview = st::windowBgOver->c,
|
||||
.colored = &_colored,
|
||||
.textColor = st::windowFg->c,
|
||||
.now = now,
|
||||
.position = ppos,
|
||||
.paused = paused,
|
||||
@@ -1438,6 +1435,7 @@ void StickerSetBox::Inner::install() {
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::archiveStickers() {
|
||||
const auto toastParent = Window::Show(_controller).toastParent();
|
||||
_api.request(MTPmessages_InstallStickerSet(
|
||||
Data::InputStickerSet(_input),
|
||||
MTP_boolTrue()
|
||||
@@ -1445,9 +1443,9 @@ void StickerSetBox::Inner::archiveStickers() {
|
||||
if (result.type() == mtpc_messages_stickerSetInstallResultSuccess) {
|
||||
_setArchived.fire_copy(_setId);
|
||||
}
|
||||
}).fail([toastParent = Window::Show(_controller).toastParent()] {
|
||||
}).fail(crl::guard(toastParent, [=] {
|
||||
Ui::Toast::Show(toastParent, Lang::Hard::ServerError());
|
||||
}).send();
|
||||
})).send();
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::updateItems() {
|
||||
|
||||
@@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -35,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/unread_badge_paint.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
void setCounter(int counter);
|
||||
|
||||
QString _text;
|
||||
Dialogs::Ui::UnreadBadgeStyle _st;
|
||||
Ui::UnreadBadgeStyle _st;
|
||||
|
||||
};
|
||||
|
||||
@@ -335,7 +335,7 @@ void StickersBox::CounterWidget::setCounter(int counter) {
|
||||
auto dummy = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
|
||||
auto p = QPainter(&dummy);
|
||||
|
||||
const auto badge = Dialogs::Ui::PaintUnreadBadge(p, _text, 0, 0, _st);
|
||||
const auto badge = Ui::PaintUnreadBadge(p, _text, 0, 0, _st);
|
||||
|
||||
resize(badge.width(), st::stickersFeaturedBadgeSize);
|
||||
}
|
||||
@@ -346,7 +346,7 @@ void StickersBox::CounterWidget::paintEvent(QPaintEvent *e) {
|
||||
if (!_text.isEmpty()) {
|
||||
const auto unreadRight = rtl() ? 0 : width();
|
||||
const auto unreadTop = 0;
|
||||
Dialogs::Ui::PaintUnreadBadge(p, _text, unreadRight, unreadTop, _st);
|
||||
Ui::PaintUnreadBadge(p, _text, unreadRight, unreadTop, _st);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,21 +9,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "settings/settings_common.h"
|
||||
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
||||
#include "spellcheck/platform/platform_language.h"
|
||||
#endif
|
||||
#include "ui/effects/loading_element.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h" // inviteLinkListItem.
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h" // settingsSubsectionTitlePadding.
|
||||
|
||||
@@ -35,48 +41,169 @@ namespace {
|
||||
[[nodiscard]] std::vector<QLocale::Language> Languages() {
|
||||
return std::vector<QLocale::Language>{
|
||||
QLocale::English,
|
||||
QLocale::Afrikaans,
|
||||
QLocale::Albanian,
|
||||
QLocale::Amharic,
|
||||
QLocale::Arabic,
|
||||
QLocale::Armenian,
|
||||
QLocale::Azerbaijani,
|
||||
QLocale::Basque,
|
||||
QLocale::Belarusian,
|
||||
QLocale::Bosnian,
|
||||
QLocale::Bulgarian,
|
||||
QLocale::Burmese,
|
||||
QLocale::Catalan,
|
||||
QLocale::Chinese,
|
||||
QLocale::Croatian,
|
||||
QLocale::Czech,
|
||||
QLocale::Danish,
|
||||
QLocale::Dutch,
|
||||
QLocale::Esperanto,
|
||||
QLocale::Estonian,
|
||||
QLocale::Finnish,
|
||||
QLocale::French,
|
||||
QLocale::Gaelic,
|
||||
QLocale::Galician,
|
||||
QLocale::Georgian,
|
||||
QLocale::German,
|
||||
QLocale::Greek,
|
||||
QLocale::Gusii,
|
||||
QLocale::Hausa,
|
||||
QLocale::Hebrew,
|
||||
QLocale::Hindi,
|
||||
QLocale::Hungarian,
|
||||
QLocale::Icelandic,
|
||||
QLocale::Igbo,
|
||||
QLocale::Indonesian,
|
||||
QLocale::Irish,
|
||||
QLocale::Italian,
|
||||
QLocale::Japanese,
|
||||
QLocale::Kazakh,
|
||||
QLocale::Kinyarwanda,
|
||||
QLocale::Korean,
|
||||
QLocale::Kurdish,
|
||||
QLocale::Lao,
|
||||
QLocale::Latvian,
|
||||
QLocale::Lithuanian,
|
||||
QLocale::Luxembourgish,
|
||||
QLocale::Macedonian,
|
||||
QLocale::Malagasy,
|
||||
QLocale::Malay,
|
||||
QLocale::Maltese,
|
||||
QLocale::Maori,
|
||||
QLocale::Mongolian,
|
||||
QLocale::Nepali,
|
||||
QLocale::Pashto,
|
||||
QLocale::Persian,
|
||||
QLocale::Polish,
|
||||
QLocale::Portuguese,
|
||||
QLocale::Romanian,
|
||||
QLocale::Russian,
|
||||
QLocale::Serbian,
|
||||
QLocale::Shona,
|
||||
QLocale::Sindhi,
|
||||
QLocale::Sinhala,
|
||||
QLocale::Slovak,
|
||||
QLocale::Slovenian,
|
||||
QLocale::Somali,
|
||||
QLocale::Spanish,
|
||||
QLocale::Sundanese,
|
||||
QLocale::Swahili,
|
||||
QLocale::Swedish,
|
||||
QLocale::Tajik,
|
||||
QLocale::Tamil,
|
||||
QLocale::Tatar,
|
||||
QLocale::Teso,
|
||||
QLocale::Thai,
|
||||
QLocale::Turkish,
|
||||
QLocale::Turkmen,
|
||||
QLocale::Ukrainian,
|
||||
QLocale::Urdu,
|
||||
QLocale::Uzbek,
|
||||
QLocale::Vietnamese,
|
||||
QLocale::Welsh,
|
||||
QLocale::WesternFrisian,
|
||||
QLocale::Xhosa,
|
||||
QLocale::Yiddish,
|
||||
};
|
||||
}
|
||||
|
||||
class ShowButton : public RpWidget {
|
||||
class Row final : public Ui::SettingsButton {
|
||||
public:
|
||||
Row(not_null<Ui::RpWidget*> parent, const QLocale &locale);
|
||||
|
||||
[[nodiscard]] bool filtered(const QString &query) const;
|
||||
[[nodiscard]] QLocale locale() const;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
const style::PeerListItem &_st;
|
||||
const QLocale _locale;
|
||||
const QString _status;
|
||||
const QString _titleText;
|
||||
Ui::Text::String _title;
|
||||
|
||||
};
|
||||
|
||||
Row::Row(not_null<Ui::RpWidget*> parent, const QLocale &locale)
|
||||
: SettingsButton(parent, rpl::never<QString>())
|
||||
, _st(st::inviteLinkListItem)
|
||||
, _locale(locale)
|
||||
, _status(QLocale::languageToString(locale.language()))
|
||||
, _titleText(LanguageName(locale))
|
||||
, _title(_st.nameStyle, _titleText) {
|
||||
}
|
||||
|
||||
QLocale Row::locale() const {
|
||||
return _locale;
|
||||
}
|
||||
|
||||
bool Row::filtered(const QString &query) const {
|
||||
return _status.startsWith(query, Qt::CaseInsensitive)
|
||||
|| _titleText.startsWith(query, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
int Row::resizeGetHeight(int newWidth) {
|
||||
return _st.height;
|
||||
}
|
||||
|
||||
void Row::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
|
||||
const auto paintOver = (isOver() || isDown()) && !isDisabled();
|
||||
Ui::SettingsButton::paintBg(p, e->rect(), paintOver);
|
||||
Ui::SettingsButton::paintRipple(p, 0, 0);
|
||||
Ui::SettingsButton::paintToggle(p, width());
|
||||
|
||||
const auto &color = st::windowSubTextFg;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(color);
|
||||
|
||||
const auto left = st::settingsSubsectionTitlePadding.left();
|
||||
const auto toggleRect = Ui::SettingsButton::maybeToggleRect();
|
||||
const auto right = left
|
||||
+ (toggleRect.isEmpty() ? 0 : (width() - toggleRect.x()));
|
||||
|
||||
p.setPen(_st.nameFg);
|
||||
_title.drawLeft(
|
||||
p,
|
||||
left,
|
||||
_st.namePosition.y(),
|
||||
width() - left - right,
|
||||
width() - left - right);
|
||||
|
||||
p.setPen(paintOver ? _st.statusFgOver : _st.statusFg);
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.drawTextLeft(
|
||||
left,
|
||||
_st.statusPosition.y(),
|
||||
width() - left - right,
|
||||
_status);
|
||||
}
|
||||
|
||||
class ShowButton final : public RpWidget {
|
||||
public:
|
||||
ShowButton(not_null<Ui::RpWidget*> parent);
|
||||
|
||||
@@ -125,11 +252,36 @@ rpl::producer<Qt::MouseButton> ShowButton::clicks() const {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Translate {
|
||||
|
||||
std::vector<QLocale> LocalesFromSettings() {
|
||||
const auto langs = Core::App().settings().skipTranslationForLanguages();
|
||||
if (langs.empty()) {
|
||||
return { QLocale(QLocale::English) };
|
||||
}
|
||||
return ranges::views::all(
|
||||
langs
|
||||
) | ranges::view::transform([](int langId) {
|
||||
const auto lang = QLocale::Language(langId);
|
||||
return (lang == QLocale::English)
|
||||
? QLocale(Lang::LanguageIdOrDefault(Lang::Id()))
|
||||
: (lang == QLocale::C)
|
||||
? QLocale(QLocale::English)
|
||||
: QLocale(lang);
|
||||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
} // namespace Translate
|
||||
|
||||
using namespace Translate;
|
||||
|
||||
QString LanguageName(const QLocale &locale) {
|
||||
if (locale.language() == QLocale::English
|
||||
&& (locale.country() == QLocale::UnitedStates
|
||||
|| locale.country() == QLocale::AnyCountry)) {
|
||||
return u"English"_q;
|
||||
} else if (locale.language() == QLocale::Spanish) {
|
||||
return QString::fromUtf8("\x45\x73\x70\x61\xc3\xb1\x6f\x6c");
|
||||
} else {
|
||||
const auto name = locale.nativeLanguageName();
|
||||
return name.left(1).toUpper() + name.mid(1);
|
||||
@@ -145,13 +297,7 @@ void TranslateBox(
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
|
||||
const auto container = box->verticalLayout();
|
||||
const auto settingsLang =
|
||||
Core::App().settings().skipTranslationForLanguage();
|
||||
const auto defaultId = (settingsLang == QLocale::English)
|
||||
? Lang::LanguageIdOrDefault(Lang::Id())
|
||||
: (settingsLang == QLocale::C)
|
||||
? u"en"_q
|
||||
: QLocale(settingsLang).name().mid(0, 2);
|
||||
const auto defaultId = LocalesFromSettings().front().name().mid(0, 2);
|
||||
|
||||
const auto api = box->lifetime().make_state<MTP::Sender>(
|
||||
&peer->session().mtp());
|
||||
@@ -193,7 +339,12 @@ void TranslateBox(
|
||||
original->entity()->setContextMenuHook([](auto&&) {
|
||||
});
|
||||
}
|
||||
original->entity()->setMarkedText(text);
|
||||
original->entity()->setMarkedText(
|
||||
text,
|
||||
Core::MarkedTextContext{
|
||||
.session = &peer->session(),
|
||||
.customEmojiRepaint = [=] { original->entity()->update(); },
|
||||
});
|
||||
original->setMinimalHeight(lineHeight);
|
||||
original->hide(anim::type::instant);
|
||||
|
||||
@@ -292,28 +443,104 @@ void TranslateBox(
|
||||
if (loading->toggled()) {
|
||||
return;
|
||||
}
|
||||
Ui::BoxShow(box).showBox(Box(ChooseLanguageBox, [=](QLocale locale) {
|
||||
Ui::BoxShow(box).showBox(Box(ChooseLanguageBox, [=](
|
||||
std::vector<QLocale> locales) {
|
||||
const auto &locale = locales.front();
|
||||
state->locale.fire_copy(locale);
|
||||
loading->show(anim::type::instant);
|
||||
translated->hide(anim::type::instant);
|
||||
send(locale.name().mid(0, 2));
|
||||
}));
|
||||
}, std::vector<QLocale>()));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<void(QLocale)> callback) {
|
||||
Fn<void(std::vector<QLocale>)> callback,
|
||||
std::vector<QLocale> toggled) {
|
||||
box->setMinHeight(st::boxWidth);
|
||||
box->setMaxHeight(st::boxWidth);
|
||||
box->setTitle(tr::lng_languages());
|
||||
for (const auto &lang : Languages()) {
|
||||
|
||||
const auto hasToggled = !toggled.empty();
|
||||
|
||||
const auto multiSelect = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::MultiSelect>(
|
||||
box,
|
||||
st::defaultMultiSelect,
|
||||
tr::lng_participant_filter()));
|
||||
box->setFocusCallback([=] { multiSelect->setInnerFocus(); });
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
const auto langs = [&] {
|
||||
auto langs = Languages();
|
||||
const auto current = QLocale(
|
||||
Lang::LanguageIdOrDefault(Lang::Id())).language();
|
||||
if (const auto it = ranges::find(langs, current); it != end(langs)) {
|
||||
base::reorder(langs, std::distance(begin(langs), it), 0);
|
||||
}
|
||||
return langs;
|
||||
}();
|
||||
auto rows = std::vector<not_null<Ui::SlideWrap<Row>*>>();
|
||||
rows.reserve(langs.size());
|
||||
for (const auto &lang : langs) {
|
||||
const auto locale = QLocale(lang);
|
||||
const auto button = Settings::AddButton(
|
||||
box->verticalLayout(),
|
||||
rpl::single(LanguageName(locale)),
|
||||
st::defaultSettingsButton);
|
||||
button->setClickedCallback([=] {
|
||||
callback(locale);
|
||||
const auto button = container->add(
|
||||
object_ptr<Ui::SlideWrap<Row>>(
|
||||
container,
|
||||
object_ptr<Row>(container, locale)));
|
||||
if (hasToggled) {
|
||||
button->entity()->toggleOn(
|
||||
rpl::single(ranges::contains(toggled, locale)),
|
||||
false);
|
||||
} else {
|
||||
button->entity()->setClickedCallback([=] {
|
||||
callback({ locale });
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
rows.push_back(button);
|
||||
}
|
||||
|
||||
multiSelect->setQueryChangedCallback([=](const QString &query) {
|
||||
for (const auto &row : rows) {
|
||||
const auto toggled = row->entity()->filtered(query);
|
||||
if (toggled != row->toggled()) {
|
||||
row->toggle(toggled, anim::type::instant);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
tr::lng_languages_none(),
|
||||
st::membersAbout);
|
||||
box->verticalLayout()->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &geometry) {
|
||||
const auto shown = (geometry.height() <= 0);
|
||||
label->setVisible(shown);
|
||||
if (shown) {
|
||||
label->moveToLeft(
|
||||
(geometry.width() - label->width()) / 2,
|
||||
geometry.y() + st::membersAbout.style.font->height * 4);
|
||||
label->stackUnder(box->verticalLayout());
|
||||
}
|
||||
}, label->lifetime());
|
||||
}
|
||||
|
||||
if (hasToggled) {
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
auto result = ranges::views::all(
|
||||
rows
|
||||
) | ranges::views::filter([](const auto &row) {
|
||||
return row->entity()->toggled();
|
||||
}) | ranges::views::transform([](const auto &row) {
|
||||
return row->entity()->locale();
|
||||
}) | ranges::to_vector;
|
||||
if (!result.empty()) {
|
||||
callback(std::move(result));
|
||||
}
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
@@ -342,18 +569,17 @@ bool SkipTranslate(TextWithEntities textWithEntities) {
|
||||
if (!hasLetters) {
|
||||
return true;
|
||||
}
|
||||
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
||||
const auto result = Platform::Language::Recognize(text);
|
||||
if (result.unknown) {
|
||||
return false;
|
||||
}
|
||||
const auto settingsLang =
|
||||
Core::App().settings().skipTranslationForLanguage();
|
||||
const auto skip = (settingsLang == QLocale::English)
|
||||
? QLocale(Lang::LanguageIdOrDefault(Lang::Id())).language()
|
||||
: (settingsLang == QLocale::C)
|
||||
? QLocale::English
|
||||
: settingsLang;
|
||||
return (result.locale.language() == skip);
|
||||
return ranges::any_of(LocalesFromSettings(), [&](const QLocale &l) {
|
||||
return result.locale.language() == l.language();
|
||||
});
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -10,6 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class PeerData;
|
||||
|
||||
namespace Ui {
|
||||
namespace Translate {
|
||||
[[nodiscard]] std::vector<QLocale> LocalesFromSettings();
|
||||
} // namespace Translate
|
||||
|
||||
class GenericBox;
|
||||
|
||||
@@ -26,6 +29,7 @@ void TranslateBox(
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<void(QLocale)> callback);
|
||||
Fn<void(std::vector<QLocale>)> callback,
|
||||
std::vector<QLocale> toggled);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "calls/calls_instance.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -209,7 +210,7 @@ void BoxController::Row::refreshStatus() {
|
||||
return;
|
||||
}
|
||||
auto text = [this] {
|
||||
auto time = QLocale().toString(ItemDateTime(_items.front()).time(), cTimeFormat());
|
||||
auto time = QLocale().toString(ItemDateTime(_items.front()).time(), QLocale::ShortFormat);
|
||||
auto today = QDateTime::currentDateTime().date();
|
||||
if (_date == today) {
|
||||
return tr::lng_call_box_status_today(tr::now, lt_time, time);
|
||||
|
||||
@@ -407,6 +407,7 @@ void Panel::reinitWithCall(Call *call) {
|
||||
const auto active = _call->isSharingScreen();
|
||||
_screencast->setProgress(active ? 0. : 1.);
|
||||
_screencast->setText(tr::lng_call_screencast());
|
||||
_outgoingVideoBubble->setMirrored(!active);
|
||||
}
|
||||
}, _callLifetime);
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ void VideoBubble::prepareFrame() {
|
||||
.resize = size,
|
||||
.outer = size,
|
||||
};
|
||||
const auto frame = _track->frame(request);
|
||||
const auto frame = _track->frame(request).mirrored(!_mirrored, false);
|
||||
if (_frame.width() < size.width() || _frame.height() < size.height()) {
|
||||
_frame = QImage(
|
||||
size * cIntRetinaFactor(),
|
||||
|
||||
@@ -37,6 +37,10 @@ public:
|
||||
return _content.lifetime();
|
||||
}
|
||||
|
||||
void setMirrored(bool mirrored) {
|
||||
_mirrored = mirrored;
|
||||
}
|
||||
|
||||
private:
|
||||
void setup();
|
||||
void paint();
|
||||
@@ -59,6 +63,7 @@ private:
|
||||
RectPart _corner = RectPart::None;
|
||||
bool _dragging = false;
|
||||
bool _geometryDirty = false;
|
||||
bool _mirrored = true;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/chat/group_call_bar.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/layers/layer_manager.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -35,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/round_rect.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
@@ -1244,7 +1244,6 @@ void Panel::refreshTopButton() {
|
||||
auto joinAsToggle = object_ptr<Ui::UserpicButton>(
|
||||
widget(),
|
||||
joinAs,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::groupCallJoinAsToggle);
|
||||
_joinAsToggle.destroy();
|
||||
_joinAsToggle = std::move(joinAsToggle);
|
||||
|
||||
@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "history/history_message.h" // GetErrorTextForSending.
|
||||
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
||||
#include "history/history.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_session.h"
|
||||
|
||||
@@ -35,7 +35,7 @@ rpl::producer<QString> StartsWhenText(rpl::producer<TimeId> date) {
|
||||
|
||||
const auto time = QLocale().toString(
|
||||
parsedDate.time(),
|
||||
Ui::Integration::Instance().timeFormat());
|
||||
QLocale::ShortFormat);
|
||||
auto exact = tr::lng_group_call_starts_short_date(
|
||||
lt_date,
|
||||
rpl::single(langDayOfMonthFull(dateDay.date())),
|
||||
|
||||
@@ -255,7 +255,7 @@ void BotKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pres
|
||||
bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
|
||||
if (!to || !to->definesReplyKeyboard()) {
|
||||
if (_wasForMsgId.msg) {
|
||||
_maximizeSize = _singleUse = _forceReply = false;
|
||||
_maximizeSize = _singleUse = _forceReply = _persistent = false;
|
||||
_wasForMsgId = FullMsgId();
|
||||
_placeholder = QString();
|
||||
_impl = nullptr;
|
||||
@@ -275,6 +275,7 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
|
||||
_forceReply = markupFlags & ReplyMarkupFlag::ForceReply;
|
||||
_maximizeSize = !(markupFlags & ReplyMarkupFlag::Resize);
|
||||
_singleUse = _forceReply || (markupFlags & ReplyMarkupFlag::SingleUse);
|
||||
_persistent = (markupFlags & ReplyMarkupFlag::Persistent);
|
||||
|
||||
if (const auto markup = to->Get<HistoryMessageReplyMarkup>()) {
|
||||
_placeholder = markup->data.placeholder;
|
||||
@@ -326,6 +327,10 @@ bool BotKeyboard::singleUse() const {
|
||||
return _singleUse;
|
||||
}
|
||||
|
||||
bool BotKeyboard::persistent() const {
|
||||
return _persistent;
|
||||
}
|
||||
|
||||
void BotKeyboard::updateStyle(int newWidth) {
|
||||
if (!_impl) return;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool maximizeSize() const;
|
||||
[[nodiscard]] bool singleUse() const;
|
||||
[[nodiscard]] bool persistent() const;
|
||||
|
||||
[[nodiscard]] FullMsgId forMsgId() const {
|
||||
return _wasForMsgId;
|
||||
@@ -91,6 +92,7 @@ private:
|
||||
bool _maximizeSize = false;
|
||||
bool _singleUse = false;
|
||||
bool _forceReply = false;
|
||||
bool _persistent = false;
|
||||
|
||||
QPoint _lastMousePos;
|
||||
std::unique_ptr<ReplyKeyboard> _impl;
|
||||
|
||||
@@ -438,9 +438,6 @@ EmojiListWidget::EmojiListWidget(
|
||||
resizeToWidth(width());
|
||||
}, lifetime());
|
||||
|
||||
if (_mode == Mode::EmojiStatus) {
|
||||
_emojiStatusColor = std::make_unique<Ui::Text::CustomEmojiColored>();
|
||||
}
|
||||
rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
@@ -449,12 +446,6 @@ EmojiListWidget::EmojiListWidget(
|
||||
initButton(_add, tr::lng_stickers_featured_add(tr::now), false);
|
||||
initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true);
|
||||
initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true);
|
||||
if (const auto status = _emojiStatusColor.get()) {
|
||||
status->color = anim::color(
|
||||
st::stickerPanPremium1,
|
||||
st::stickerPanPremium2,
|
||||
0.5);
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
if (!descriptor.customRecentList.empty()) {
|
||||
@@ -842,10 +833,36 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
|
||||
paint(p, {}, clip);
|
||||
}
|
||||
|
||||
void EmojiListWidget::validateEmojiPaintContext(
|
||||
const ExpandingContext &context) {
|
||||
auto value = Ui::Text::CustomEmojiPaintContext{
|
||||
.textColor = (_mode == Mode::EmojiStatus
|
||||
? anim::color(
|
||||
st::stickerPanPremium1,
|
||||
st::stickerPanPremium2,
|
||||
0.5)
|
||||
: st::windowFg->c),
|
||||
.size = QSize(_customSingleSize, _customSingleSize),
|
||||
.now = crl::now(),
|
||||
.scale = context.progress,
|
||||
.paused = paused(),
|
||||
.scaled = context.expanding,
|
||||
};
|
||||
if (!_emojiPaintContext) {
|
||||
_emojiPaintContext = std::make_unique<
|
||||
Ui::Text::CustomEmojiPaintContext
|
||||
>(std::move(value));
|
||||
} else {
|
||||
*_emojiPaintContext = std::move(value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::paint(
|
||||
QPainter &p,
|
||||
ExpandingContext context,
|
||||
QRect clip) {
|
||||
validateEmojiPaintContext(context);
|
||||
|
||||
auto fromColumn = floorclamp(
|
||||
clip.x() - _rowsLeft,
|
||||
_singleSize.width(),
|
||||
@@ -861,10 +878,7 @@ void EmojiListWidget::paint(
|
||||
fromColumn = _columnCount - fromColumn;
|
||||
toColumn = _columnCount - toColumn;
|
||||
}
|
||||
|
||||
const auto expandProgress = context.progress;
|
||||
const auto paused = this->paused();
|
||||
const auto now = crl::now();
|
||||
auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
|
||||
? &_pressed
|
||||
: &_selected);
|
||||
@@ -968,12 +982,12 @@ void EmojiListWidget::paint(
|
||||
_overBg.paint(p, QRect(tl, st::emojiPanArea));
|
||||
}
|
||||
if (info.section == int(Section::Recent)) {
|
||||
drawRecent(p, context, w, now, paused, index);
|
||||
drawRecent(p, context, w, index);
|
||||
} else if (info.section < _staticCount) {
|
||||
drawEmoji(p, context, w, _emoji[info.section][index]);
|
||||
} else {
|
||||
const auto set = info.section - _staticCount;
|
||||
drawCustom(p, context, w, now, paused, set, index);
|
||||
drawCustom(p, context, w, set, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1006,24 +1020,15 @@ void EmojiListWidget::drawRecent(
|
||||
QPainter &p,
|
||||
const ExpandingContext &context,
|
||||
QPoint position,
|
||||
crl::time now,
|
||||
bool paused,
|
||||
int index) {
|
||||
_recentPainted = true;
|
||||
auto &recent = _recent[index];
|
||||
if (const auto custom = recent.custom) {
|
||||
position += _innerPosition + _customPosition;
|
||||
const auto paintContext = Ui::Text::CustomEmoji::Context{
|
||||
.preview = st::windowBgRipple->c,
|
||||
.colored = _emojiStatusColor.get(),
|
||||
.size = QSize(_customSingleSize, _customSingleSize),
|
||||
.now = now,
|
||||
.scale = context.progress,
|
||||
.position = position,
|
||||
.paused = paused,
|
||||
.scaled = context.expanding,
|
||||
};
|
||||
custom->paint(p, paintContext);
|
||||
_emojiPaintContext->scale = context.progress;
|
||||
_emojiPaintContext->position = position
|
||||
+ _innerPosition
|
||||
+ _customPosition;
|
||||
custom->paint(p, *_emojiPaintContext);
|
||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
||||
if (_mode == Mode::EmojiStatus) {
|
||||
position += QPoint(
|
||||
@@ -1057,24 +1062,17 @@ void EmojiListWidget::drawCustom(
|
||||
QPainter &p,
|
||||
const ExpandingContext &context,
|
||||
QPoint position,
|
||||
crl::time now,
|
||||
bool paused,
|
||||
int set,
|
||||
int index) {
|
||||
position += _innerPosition + _customPosition;
|
||||
auto &custom = _custom[set];
|
||||
custom.painted = true;
|
||||
auto &entry = custom.list[index];
|
||||
entry.custom->paint(p, {
|
||||
.preview = st::windowBgRipple->c,
|
||||
.colored = _emojiStatusColor.get(),
|
||||
.size = QSize(_customSingleSize, _customSingleSize),
|
||||
.now = now,
|
||||
.scale = context.progress,
|
||||
.position = position,
|
||||
.paused = paused,
|
||||
.scaled = context.expanding,
|
||||
});
|
||||
_emojiPaintContext->scale = context.progress;
|
||||
_emojiPaintContext->position = position
|
||||
+ _innerPosition
|
||||
+ _customPosition;
|
||||
entry.custom->paint(p, *_emojiPaintContext);
|
||||
}
|
||||
|
||||
bool EmojiListWidget::checkPickerHide() {
|
||||
|
||||
@@ -38,7 +38,7 @@ enum class Section;
|
||||
} // namespace Ui::Emoji
|
||||
|
||||
namespace Ui::Text {
|
||||
struct CustomEmojiColored;
|
||||
struct CustomEmojiPaintContext;
|
||||
} // namespace Ui::Text
|
||||
|
||||
namespace Ui::CustomEmoji {
|
||||
@@ -267,8 +267,6 @@ private:
|
||||
QPainter &p,
|
||||
const ExpandingContext &context,
|
||||
QPoint position,
|
||||
crl::time now,
|
||||
bool paused,
|
||||
int index);
|
||||
void drawEmoji(
|
||||
QPainter &p,
|
||||
@@ -279,10 +277,9 @@ private:
|
||||
QPainter &p,
|
||||
const ExpandingContext &context,
|
||||
QPoint position,
|
||||
crl::time now,
|
||||
bool paused,
|
||||
int set,
|
||||
int index);
|
||||
void validateEmojiPaintContext(const ExpandingContext &context);
|
||||
[[nodiscard]] bool hasRemoveButton(int index) const;
|
||||
[[nodiscard]] QRect removeButtonRect(int index) const;
|
||||
[[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const;
|
||||
@@ -345,7 +342,7 @@ private:
|
||||
std::vector<RecentOne> _recent;
|
||||
base::flat_set<DocumentId> _recentCustomIds;
|
||||
base::flat_set<uint64> _repaintsScheduled;
|
||||
std::unique_ptr<Ui::Text::CustomEmojiColored> _emojiStatusColor;
|
||||
std::unique_ptr<Ui::Text::CustomEmojiPaintContext> _emojiPaintContext;
|
||||
bool _recentPainted = false;
|
||||
bool _grabbingChosen = false;
|
||||
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
||||
|
||||
@@ -341,8 +341,10 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) {
|
||||
Ui::StickerHoverCorners);
|
||||
}
|
||||
|
||||
const auto now = crl::now();
|
||||
const auto preview = st::windowBgOver->c;
|
||||
auto context = Ui::CustomEmoji::Context{
|
||||
.textColor = st::windowFg->c,
|
||||
.now = crl::now(),
|
||||
};
|
||||
for (auto i = from; i != till; ++i) {
|
||||
const auto &row = _rows[i];
|
||||
const auto emoji = row.emoji;
|
||||
@@ -351,11 +353,8 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) {
|
||||
const auto x = i * _oneWidth + (_oneWidth - size) / 2;
|
||||
const auto y = (_oneWidth - size) / 2;
|
||||
if (row.custom) {
|
||||
row.custom->paint(p, {
|
||||
.preview = preview,
|
||||
.now = now,
|
||||
.position = { x, y },
|
||||
});
|
||||
context.position = { x, y };
|
||||
row.custom->paint(p, context);
|
||||
} else {
|
||||
Ui::Emoji::Draw(p, emoji, esize, x, y);
|
||||
}
|
||||
|
||||
@@ -472,7 +472,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
|
||||
--i;
|
||||
mrows.push_back({ i->second });
|
||||
}
|
||||
} else if (_channel && _channel->isMegagroup()) {
|
||||
} else if (_channel
|
||||
&& _channel->isMegagroup()
|
||||
&& _channel->canViewMembers()) {
|
||||
if (_channel->lastParticipantsRequestNeeded()) {
|
||||
_channel->session().api().chatParticipants().requestLast(
|
||||
_channel);
|
||||
|
||||
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_widget.h"
|
||||
#include "history/history.h" // History::session
|
||||
#include "history/history_item.h" // HistoryItem::originalText
|
||||
#include "history/history_message.h" // DropCustomEmoji
|
||||
#include "history/history_item_helpers.h" // DropCustomEmoji
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/event_filter.h"
|
||||
|
||||
@@ -129,7 +129,8 @@ void DicePack::generateLocal(int index, const QString &name) {
|
||||
nullptr,
|
||||
SendMediaType::File,
|
||||
FileLoadTo(0, {}, 0, 0, 0),
|
||||
{});
|
||||
{},
|
||||
false);
|
||||
task.process({ .generateGoodThumbnail = false });
|
||||
const auto result = task.peekResult();
|
||||
Assert(result != nullptr);
|
||||
|
||||
@@ -605,6 +605,7 @@ void StickersListFooter::paint(
|
||||
|
||||
const auto now = crl::now();
|
||||
const auto paused = _paused();
|
||||
p.setPen(st::windowFg);
|
||||
enumerateVisibleIcons([&](const IconInfo &info) {
|
||||
paintSetIcon(p, context, info, now, paused);
|
||||
});
|
||||
@@ -1251,7 +1252,7 @@ void StickersListFooter::paintSetIcon(
|
||||
const auto y = _iconsTop + (st().footer - icon.pixh) / 2;
|
||||
if (icon.custom) {
|
||||
icon.custom->paint(p, Ui::Text::CustomEmoji::Context{
|
||||
.preview = st::windowBgRipple->c,
|
||||
.textColor = st::windowFg->c,
|
||||
.size = QSize(icon.pixw, icon.pixh),
|
||||
.now = now,
|
||||
.scale = context.progress,
|
||||
|
||||
@@ -268,7 +268,7 @@ void Application::run() {
|
||||
Ui::InitTextOptions();
|
||||
Ui::StartCachedCorners();
|
||||
Ui::Emoji::Init();
|
||||
Ui::PrepareTextSpoilerMask();
|
||||
Ui::PreloadTextSpoilerMask();
|
||||
startEmojiImageLoader();
|
||||
startSystemDarkModeViewer();
|
||||
Media::Player::start(_audio.get());
|
||||
@@ -284,9 +284,6 @@ void Application::run() {
|
||||
|
||||
DEBUG_LOG(("Application Info: inited..."));
|
||||
|
||||
cChangeDateFormat(QLocale().dateFormat(QLocale::ShortFormat));
|
||||
cChangeTimeFormat(QLocale().timeFormat(QLocale::ShortFormat));
|
||||
|
||||
DEBUG_LOG(("Application Info: starting app..."));
|
||||
|
||||
// Create mime database, so it won't be slow later.
|
||||
|
||||
@@ -76,6 +76,15 @@ std::map<int, const char*> BetaLogs() {
|
||||
"- Fix spoiler revealing in media viewer captions.\n"
|
||||
|
||||
"- Fix crash in folder editing on Linux.\n"
|
||||
},
|
||||
{
|
||||
4004002,
|
||||
"- Send photos and video files hidden by a spoiler effect.\n"
|
||||
|
||||
"- Set a public photo for those who are restricted to see "
|
||||
"your profile photo in the Privacy Settings.\n"
|
||||
|
||||
"- Bug fixes and other minor improvements.\n"
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -267,7 +267,16 @@ QByteArray Settings::serialize() const {
|
||||
<< qint32(_hardwareAcceleratedVideo ? 1 : 0)
|
||||
<< qint32(_suggestAnimatedEmoji ? 1 : 0)
|
||||
<< qint32(_cornerReaction.current() ? 1 : 0)
|
||||
<< qint32(_skipTranslationForLanguage);
|
||||
<< qint32(_translateButtonEnabled ? 1 : 0);
|
||||
|
||||
stream
|
||||
<< qint32(_skipTranslationForLanguages.size());
|
||||
for (const auto &lang : _skipTranslationForLanguages) {
|
||||
stream << quint64(lang);
|
||||
}
|
||||
|
||||
stream
|
||||
<< qint32(_rememberedDeleteMessageOnlyForYou ? 1 : 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -361,7 +370,10 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
qint32 chatQuickAction = static_cast<qint32>(_chatQuickAction);
|
||||
qint32 suggestAnimatedEmoji = _suggestAnimatedEmoji ? 1 : 0;
|
||||
qint32 cornerReaction = _cornerReaction.current() ? 1 : 0;
|
||||
qint32 skipTranslationForLanguage = _skipTranslationForLanguage;
|
||||
qint32 legacySkipTranslationForLanguage = _translateButtonEnabled ? 1 : 0;
|
||||
qint32 skipTranslationForLanguagesCount = 0;
|
||||
std::vector<int> skipTranslationForLanguages;
|
||||
qint32 rememberedDeleteMessageOnlyForYou = _rememberedDeleteMessageOnlyForYou ? 1 : 0;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
@@ -560,7 +572,19 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
stream >> cornerReaction;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> skipTranslationForLanguage;
|
||||
stream >> legacySkipTranslationForLanguage;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> skipTranslationForLanguagesCount;
|
||||
if (stream.status() == QDataStream::Ok) {
|
||||
for (auto i = 0; i != skipTranslationForLanguagesCount; ++i) {
|
||||
quint64 language;
|
||||
stream >> language;
|
||||
skipTranslationForLanguages.emplace_back(language);
|
||||
}
|
||||
}
|
||||
|
||||
stream >> rememberedDeleteMessageOnlyForYou;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
@@ -731,7 +755,19 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
}
|
||||
_suggestAnimatedEmoji = (suggestAnimatedEmoji == 1);
|
||||
_cornerReaction = (cornerReaction == 1);
|
||||
_skipTranslationForLanguage = skipTranslationForLanguage;
|
||||
{ // Parse the legacy translation setting.
|
||||
_skipTranslationForLanguages = skipTranslationForLanguages;
|
||||
if (legacySkipTranslationForLanguage == 0) {
|
||||
_translateButtonEnabled = false;
|
||||
} else if (legacySkipTranslationForLanguage == 1) {
|
||||
_translateButtonEnabled = true;
|
||||
} else {
|
||||
_translateButtonEnabled = (legacySkipTranslationForLanguage > 0);
|
||||
_skipTranslationForLanguages.push_back(
|
||||
std::abs(legacySkipTranslationForLanguage));
|
||||
}
|
||||
}
|
||||
_rememberedDeleteMessageOnlyForYou = (rememberedDeleteMessageOnlyForYou == 1);
|
||||
}
|
||||
|
||||
QString Settings::getSoundPath(const QString &key) const {
|
||||
@@ -1042,18 +1078,23 @@ float64 Settings::DefaultDialogsWidthRatio() {
|
||||
}
|
||||
|
||||
void Settings::setTranslateButtonEnabled(bool value) {
|
||||
_skipTranslationForLanguage = std::abs(_skipTranslationForLanguage)
|
||||
* (value ? 1 : -1);
|
||||
_translateButtonEnabled = value;
|
||||
}
|
||||
bool Settings::translateButtonEnabled() const {
|
||||
return _skipTranslationForLanguage > 0;
|
||||
return _translateButtonEnabled;
|
||||
}
|
||||
void Settings::setSkipTranslationForLanguage(QLocale::Language language) {
|
||||
const auto enabled = translateButtonEnabled();
|
||||
_skipTranslationForLanguage = language * (enabled ? 1 : -1);
|
||||
void Settings::setSkipTranslationForLanguages(std::vector<int> languages) {
|
||||
_skipTranslationForLanguages = std::move(languages);
|
||||
}
|
||||
QLocale::Language Settings::skipTranslationForLanguage() const {
|
||||
return QLocale::Language(std::abs(_skipTranslationForLanguage));
|
||||
std::vector<int> Settings::skipTranslationForLanguages() const {
|
||||
return _skipTranslationForLanguages;
|
||||
}
|
||||
|
||||
void Settings::setRememberedDeleteMessageOnlyForYou(bool value) {
|
||||
_rememberedDeleteMessageOnlyForYou = value;
|
||||
}
|
||||
bool Settings::rememberedDeleteMessageOnlyForYou() const {
|
||||
return _rememberedDeleteMessageOnlyForYou;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -724,8 +724,11 @@ public:
|
||||
|
||||
void setTranslateButtonEnabled(bool value);
|
||||
[[nodiscard]] bool translateButtonEnabled() const;
|
||||
void setSkipTranslationForLanguage(QLocale::Language language);
|
||||
[[nodiscard]] QLocale::Language skipTranslationForLanguage() const;
|
||||
void setSkipTranslationForLanguages(std::vector<int> languages);
|
||||
[[nodiscard]] std::vector<int> skipTranslationForLanguages() const;
|
||||
|
||||
void setRememberedDeleteMessageOnlyForYou(bool value);
|
||||
[[nodiscard]] bool rememberedDeleteMessageOnlyForYou() const;
|
||||
|
||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
||||
@@ -841,7 +844,9 @@ private:
|
||||
#endif // Q_OS_MAC
|
||||
HistoryView::DoubleClickQuickAction _chatQuickAction =
|
||||
HistoryView::DoubleClickQuickAction();
|
||||
int _skipTranslationForLanguage = -int(QLocale::English);
|
||||
bool _translateButtonEnabled = false;
|
||||
std::vector<int> _skipTranslationForLanguages;
|
||||
bool _rememberedDeleteMessageOnlyForYou = false;
|
||||
|
||||
bool _tabbedReplacedWithInfo = false; // per-window
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||
|
||||
@@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/themes/window_theme_editor_box.h" // GenerateSlug.
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "settings/settings_information.h"
|
||||
#include "settings/settings_global_ttl.h"
|
||||
#include "settings/settings_folders.h"
|
||||
#include "settings/settings_main.h"
|
||||
@@ -488,6 +489,8 @@ bool ResolveSettings(
|
||||
return ::Settings::ChangePhone::Id();
|
||||
} else if (section == u"auto_delete"_q) {
|
||||
return ::Settings::GlobalTTLId();
|
||||
} else if (section == u"information"_q) {
|
||||
return ::Settings::Information::Id();
|
||||
}
|
||||
return ::Settings::Main::Id();
|
||||
}();
|
||||
@@ -846,7 +849,7 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||
ResolvePrivatePost
|
||||
},
|
||||
{
|
||||
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete)?$"_q,
|
||||
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information)?$"_q,
|
||||
ResolveSettings
|
||||
},
|
||||
{
|
||||
|
||||
@@ -137,10 +137,6 @@ bool UiIntegration::screenIsLocked() {
|
||||
return Core::App().screenIsLocked();
|
||||
}
|
||||
|
||||
QString UiIntegration::timeFormat() {
|
||||
return cTimeFormat();
|
||||
}
|
||||
|
||||
std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
|
||||
const EntityLinkData &data,
|
||||
const std::any &context) {
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
void activationFromTopPanel() override;
|
||||
|
||||
bool screenIsLocked() override;
|
||||
QString timeFormat() override;
|
||||
|
||||
std::shared_ptr<ClickHandler> createLinkHandler(
|
||||
const EntityLinkData &data,
|
||||
|
||||