Compare commits
363 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9502120ee3 | |||
| 7f1a3320dd | |||
| 8a4fd7f182 | |||
| b0a4d15d64 | |||
| 11d0f9db03 | |||
| f024ceecdd | |||
| 08c07a0785 | |||
| b843f91b3c | |||
| 4b2c5b3321 | |||
| 2b43f2682a | |||
| 7da0124286 | |||
| feaeef6482 | |||
| 15bcfeec1d | |||
| 5fb002ab4c | |||
| f56ddbb1e0 | |||
| 6bd2a7c962 | |||
| 9d591ae806 | |||
| 7d30e3913c | |||
| 19d7dd7aa3 | |||
| 45444253fd | |||
| 308ade6a7e | |||
| 66d54ccd54 | |||
| ccf8d91f01 | |||
| 7c940a0480 | |||
| 277a2edb57 | |||
| bd742689e9 | |||
| 9ca0c3c41d | |||
| ce8650b4de | |||
| 4bce9d440e | |||
| adc691f516 | |||
| fc67a801e3 | |||
| 6a3657ca87 | |||
| 0537c5f273 | |||
| cc4a5f30b6 | |||
| b0d7c3e9b1 | |||
| 3bf7c44fc9 | |||
| 4ff4e63a11 | |||
| 72a35ba58b | |||
| b6a31979f2 | |||
| 7c710e22cc | |||
| ab58e7a225 | |||
| c9fb97cd7c | |||
| 789f3e1584 | |||
| 0fc8229be1 | |||
| a1e555267e | |||
| 0ac88c0cb5 | |||
| d43a6da62b | |||
| 940455f786 | |||
| 0f74456f30 | |||
| 7840fa6d90 | |||
| 95ccc99fee | |||
| 7b0a156bba | |||
| 0d8ae7bb37 | |||
| 9491cff1df | |||
| 51dc5d6e37 | |||
| f4c739ab92 | |||
| 0dd8ae3d77 | |||
| 7d2878d81c | |||
| bd70a05861 | |||
| 0605c7b2bc | |||
| 8e83a55143 | |||
| 4ab4eb8ef2 | |||
| d1e6150874 | |||
| 4121c99f36 | |||
| 827040f487 | |||
| 9032489786 | |||
| 8ea7bd4913 | |||
| 97b021efaf | |||
| b3f9a77ba7 | |||
| 63fdc1f876 | |||
| 17cf354c58 | |||
| c6fd8bcb99 | |||
| 1684465e04 | |||
| fe2df96953 | |||
| ee9d0cfd99 | |||
| 7b7e18e752 | |||
| 928be4151b | |||
| 37dd648686 | |||
| 93a590774e | |||
| 22b99b6d3e | |||
| 101d626d4f | |||
| 3633c19208 | |||
| e302f328f7 | |||
| f74ba95e95 | |||
| c38982d286 | |||
| fe9bac096b | |||
| 5b809c4fc6 | |||
| 4729e51e14 | |||
| 960cf7a34b | |||
| 852ab19760 | |||
| 2e45d9fc6b | |||
| 74b71b92b6 | |||
| bbc14ba74f | |||
| 45c7829cd8 | |||
| f2aa3afbbb | |||
| 909b01241b | |||
| abb58c58a0 | |||
| 700e10d32c | |||
| 4ac48d0e4a | |||
| 345b2cb835 | |||
| b962309498 | |||
| e99cb9bfb8 | |||
| 9e12e18f90 | |||
| 66fc9b38df | |||
| 5dbe429e6b | |||
| b2481ea6c1 | |||
| 86a294ce4b | |||
| a8d1eadfbf | |||
| b07d3c5403 | |||
| 892db55ae1 | |||
| 93615fef65 | |||
| 87452706ef | |||
| 3569615b21 | |||
| 86f7d09d31 | |||
| d5d1254393 | |||
| 4df90cfb9e | |||
| ec6862d31a | |||
| 6f23010382 | |||
| c672f105d3 | |||
| e60d501e4a | |||
| 0d7175058b | |||
| 3b0bd9d1d1 | |||
| bd28ac6e1f | |||
| 0c2d00c792 | |||
| 140ba653b9 | |||
| f64f008f77 | |||
| a6315bef05 | |||
| f810d7c82a | |||
| cf61dedc79 | |||
| 2ab9587f5f | |||
| 4950b52359 | |||
| 03af444735 | |||
| 7f6221b409 | |||
| 57aebdd300 | |||
| 25821ba338 | |||
| 7dda5461ad | |||
| ee0238bad8 | |||
| 451940e6a7 | |||
| 6163cc8a5a | |||
| ef859d77e9 | |||
| 0fd752657a | |||
| f1451a1de3 | |||
| fa96f25683 | |||
| 9e447383df | |||
| 5e762be32b | |||
| 75de81a3ab | |||
| d26b64a5bb | |||
| cb8d40eaf2 | |||
| ded0936bc4 | |||
| 16830a410c | |||
| 9f79dda463 | |||
| 15dc7c74d7 | |||
| f9abef9e05 | |||
| ba84499f00 | |||
| 79ce24222a | |||
| 6cb9264864 | |||
| 247a070405 | |||
| e05bb75b8a | |||
| 6a415cf232 | |||
| e8034189df | |||
| 107f329b4f | |||
| 999a13358e | |||
| 2077f51084 | |||
| 1e77a3df20 | |||
| 141a291523 | |||
| cb03d5a9d3 | |||
| b618d1e56a | |||
| 23ae638512 | |||
| eda749d7cb | |||
| 08d0186e53 | |||
| 443981ba31 | |||
| f3ed7c5e19 | |||
| dd2378b591 | |||
| b885779365 | |||
| 32b95f0d9a | |||
| 2b8eec8666 | |||
| 3f24627f54 | |||
| 0c4bca312e | |||
| e36afc675e | |||
| d754014321 | |||
| 9f73242cc5 | |||
| fae9649773 | |||
| 4b43f4cbec | |||
| b61befa210 | |||
| 5e2bc337bc | |||
| af1608cbfa | |||
| 9aa80976ff | |||
| c9cfe9e90f | |||
| 482e337762 | |||
| a0821f5a01 | |||
| 5ebdf3ed39 | |||
| 86096db02d | |||
| a93e01b896 | |||
| 0585e72c35 | |||
| c82fbefcfc | |||
| 23542a1db1 | |||
| 1bf50d60d8 | |||
| 8596b0309e | |||
| 7f6e871b26 | |||
| 8912d4d55a | |||
| 7ac849ab12 | |||
| 4cab699b04 | |||
| 03aa05e4d2 | |||
| 428a3cf0ce | |||
| 1c8b165a64 | |||
| 1869071ef7 | |||
| 33ca5ee39f | |||
| e46d5a86d3 | |||
| 2729bcac3b | |||
| d60ce41fa9 | |||
| 958db945f3 | |||
| 057f906ca4 | |||
| 8df7a45e29 | |||
| e4af1570cb | |||
| 049ebf9027 | |||
| f2f0c7df92 | |||
| bee4118513 | |||
| a1096f7ecb | |||
| dac0a6ec20 | |||
| 4327fd4c58 | |||
| 3243f57fd5 | |||
| 383c488268 | |||
| 53392d6e5d | |||
| bf48025d12 | |||
| 5e624605cf | |||
| 026490acc6 | |||
| 709ce3adb8 | |||
| a8d23489c4 | |||
| 296df113e3 | |||
| 292de83c35 | |||
| 7fe66c07e4 | |||
| 9aa8f30e2b | |||
| e3ebb30518 | |||
| 51122ab3ba | |||
| 5d96eab7b4 | |||
| 97c4e79e96 | |||
| f89bac7781 | |||
| 1d8a7f8fd3 | |||
| ef9f7ab27a | |||
| a676138745 | |||
| 7442ea7a16 | |||
| 0089cad740 | |||
| cf1fa718a8 | |||
| d6ba6ac41e | |||
| bbdd5feaa4 | |||
| 678527254b | |||
| a43943ec3d | |||
| ab3a61794b | |||
| 46bf7781aa | |||
| 99c547b625 | |||
| 6976e97de3 | |||
| f8e3e70273 | |||
| 23c9f7a957 | |||
| 2ca763cc77 | |||
| 2adc811351 | |||
| be18be4a86 | |||
| 8d7abb1b8a | |||
| 1cc5988c40 | |||
| da426ae03b | |||
| 3cebd6d923 | |||
| f1019c8ca4 | |||
| edfb7b6b24 | |||
| 157a928f5a | |||
| 5f5a2a3ef2 | |||
| 8534cf3756 | |||
| 39b90092ff | |||
| d3142ebe6d | |||
| d914c6be2e | |||
| 05c3e968df | |||
| 7756cce123 | |||
| 8287d717f8 | |||
| db9e60b4b5 | |||
| 28a79bfccb | |||
| 321490e528 | |||
| 6f752357d7 | |||
| e1f71baed6 | |||
| df377cd5bb | |||
| 691a0acdab | |||
| 5e2eda6af3 | |||
| 17cdc2b585 | |||
| c08266f81b | |||
| f90a4db569 | |||
| 22ec7a6d75 | |||
| 1b16a84810 | |||
| d778276f5d | |||
| e3030a168f | |||
| dfd07a4f4f | |||
| f9fc65d7de | |||
| 46cf7db242 | |||
| 2b94cffe7e | |||
| 2e74ad6fbe | |||
| df7dc1583d | |||
| 88e80b4fae | |||
| aea90f4b65 | |||
| 80db076f38 | |||
| 27bba8250a | |||
| 3fb0fa6892 | |||
| c3195cfcbe | |||
| 51661a872c | |||
| 64706ea103 | |||
| ec69d557dc | |||
| addd37fb1f | |||
| 9dc947ecb6 | |||
| 7d74d3da3a | |||
| aa0c56876c | |||
| 37c7b0c6d1 | |||
| 0d07d238bc | |||
| fa4e74ffef | |||
| c22d76e5be | |||
| 18850ebd83 | |||
| 17abef95eb | |||
| d135151477 | |||
| 07fd9b3074 | |||
| 0523ae705a | |||
| 9db2502cd0 | |||
| a174119877 | |||
| 569dd19932 | |||
| 530e2a1feb | |||
| de732ba692 | |||
| c6649e84a6 | |||
| e3517aceab | |||
| 6d7abd1718 | |||
| e9e493707b | |||
| c25adf8b57 | |||
| d2be10cd4e | |||
| 006ecf9a56 | |||
| a53cc52241 | |||
| 961d283325 | |||
| 4e46529eb6 | |||
| 81001e04e9 | |||
| 2fd174ab9c | |||
| 6ff5e221ea | |||
| 232077b919 | |||
| fecddb5203 | |||
| d0132c0f7b | |||
| 37d32b32f8 | |||
| 51213b499f | |||
| dcb98ce0fb | |||
| 428e90a844 | |||
| 017535cf7b | |||
| 409389a994 | |||
| ba34d92cd3 | |||
| b412ee258d | |||
| f1ffe2a641 | |||
| a13ca95894 | |||
| 0989a80a57 | |||
| d72c15e9d3 | |||
| 7084cf9526 | |||
| 67cc0ef75c | |||
| 75714cc358 | |||
| 75e454f3fd | |||
| 97afb4e01a | |||
| 8fcbf43410 | |||
| 15a834b883 | |||
| 3d8396e586 | |||
| 3257fd364a | |||
| d690af99fc | |||
| 1c28495162 | |||
| ec82d5674f | |||
| 4a22e76bdb | |||
| 527be95618 | |||
| 41985c0a5f | |||
| 522e45ce92 |
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "CentOS",
|
||||
"image": "tdesktop:centos_env",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"C_Cpp.intelliSenseEngine": "disabled",
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=${workspaceFolder}/out"
|
||||
],
|
||||
"cmake.generator": "Ninja Multi-Config",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/out"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools-extension-pack",
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"TheQtCompany.qt",
|
||||
"ms-python.python",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"eamodio.gitlens"
|
||||
]
|
||||
}
|
||||
},
|
||||
"capAdd": [
|
||||
"SYS_PTRACE"
|
||||
],
|
||||
"securityOpt": [
|
||||
"seccomp=unconfined"
|
||||
],
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/usr/src/tdesktop,type=bind,consistency=cached",
|
||||
"workspaceFolder": "/usr/src/tdesktop"
|
||||
}
|
||||
@@ -83,6 +83,7 @@ jobs:
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-u $(id -u) \
|
||||
-v $PWD:/usr/src/tdesktop \
|
||||
-e CONFIG=Debug \
|
||||
tdesktop:centos_env \
|
||||
@@ -114,8 +115,8 @@ jobs:
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
run: |
|
||||
cd $REPO_NAME/out/Debug
|
||||
sudo mkdir artifact
|
||||
sudo mv {Telegram,Updater} artifact/
|
||||
mkdir artifact
|
||||
mv {Telegram,Updater} artifact/
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
name: Upload artifact.
|
||||
|
||||
@@ -19,6 +19,7 @@ Release/
|
||||
ipch/
|
||||
.vs/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
/Telegram/log.txt
|
||||
/Telegram/data
|
||||
|
||||
@@ -1041,6 +1041,7 @@ PRIVATE
|
||||
info/global_media/info_global_media_inner_widget.h
|
||||
info/global_media/info_global_media_provider.cpp
|
||||
info/global_media/info_global_media_provider.h
|
||||
info/media/info_media_buttons.cpp
|
||||
info/media/info_media_buttons.h
|
||||
info/media/info_media_common.cpp
|
||||
info/media/info_media_common.h
|
||||
@@ -1096,8 +1097,8 @@ PRIVATE
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
info/settings/info_settings_widget.h
|
||||
info/similar_channels/info_similar_channels_widget.cpp
|
||||
info/similar_channels/info_similar_channels_widget.h
|
||||
info/similar_peers/info_similar_peers_widget.cpp
|
||||
info/similar_peers/info_similar_peers_widget.h
|
||||
info/statistics/info_statistics_common.h
|
||||
info/statistics/info_statistics_inner_widget.cpp
|
||||
info/statistics/info_statistics_inner_widget.h
|
||||
@@ -1168,8 +1169,6 @@ PRIVATE
|
||||
intro/intro_qr.h
|
||||
intro/intro_signup.cpp
|
||||
intro/intro_signup.h
|
||||
intro/intro_start.cpp
|
||||
intro/intro_start.h
|
||||
intro/intro_step.cpp
|
||||
intro/intro_step.h
|
||||
intro/intro_widget.cpp
|
||||
@@ -1217,6 +1216,8 @@ PRIVATE
|
||||
media/audio/media_audio_loader.h
|
||||
media/audio/media_audio_loaders.cpp
|
||||
media/audio/media_audio_loaders.h
|
||||
media/audio/media_audio_local_cache.cpp
|
||||
media/audio/media_audio_local_cache.h
|
||||
media/audio/media_audio_track.cpp
|
||||
media/audio/media_audio_track.h
|
||||
media/audio/media_child_ffmpeg_loader.cpp
|
||||
@@ -1285,6 +1286,8 @@ PRIVATE
|
||||
media/streaming/media_streaming_video_track.h
|
||||
media/view/media_view_group_thumbs.cpp
|
||||
media/view/media_view_group_thumbs.h
|
||||
media/view/media_view_open_common.cpp
|
||||
media/view/media_view_open_common.h
|
||||
media/view/media_view_overlay_opengl.cpp
|
||||
media/view/media_view_overlay_opengl.h
|
||||
media/view/media_view_overlay_raster.cpp
|
||||
@@ -1303,7 +1306,6 @@ PRIVATE
|
||||
media/view/media_view_playback_controls.h
|
||||
media/view/media_view_playback_progress.cpp
|
||||
media/view/media_view_playback_progress.h
|
||||
media/view/media_view_open_common.h
|
||||
media/system_media_controls_manager.h
|
||||
media/system_media_controls_manager.cpp
|
||||
menu/menu_antispam_validator.cpp
|
||||
@@ -1553,6 +1555,8 @@ PRIVATE
|
||||
settings/settings_privacy_security.h
|
||||
settings/settings_scale_preview.cpp
|
||||
settings/settings_scale_preview.h
|
||||
settings/settings_shortcuts.cpp
|
||||
settings/settings_shortcuts.h
|
||||
settings/settings_type.h
|
||||
settings/settings_websites.cpp
|
||||
settings/settings_websites.h
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 21 KiB |
@@ -1,6 +1,7 @@
|
||||
// This is a list of your own shortcuts for Telegram Desktop
|
||||
// You can see full list of commands in the 'shortcuts-default.json' file
|
||||
// Place a null value instead of a command string to switch the shortcut off
|
||||
// You can also edit them in Settings > Chat Settings > Keyboard Shortcuts.
|
||||
|
||||
[
|
||||
// {
|
||||
|
||||
|
After Width: | Height: | Size: 401 B |
|
After Width: | Height: | Size: 712 B |
|
After Width: | Height: | Size: 957 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 568 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 761 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 311 B |
|
After Width: | Height: | Size: 578 B |
|
After Width: | Height: | Size: 829 B |
|
After Width: | Height: | Size: 730 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 716 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 671 B |
|
After Width: | Height: | Size: 1004 B |
|
After Width: | Height: | Size: 370 B |
|
After Width: | Height: | Size: 712 B |
|
After Width: | Height: | Size: 926 B |
@@ -604,6 +604,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_update_fail" = "Update check failed :(";
|
||||
"lng_settings_workmode_tray" = "Show tray icon";
|
||||
"lng_settings_workmode_window" = "Show taskbar icon";
|
||||
"lng_settings_window_close" = "When window closed";
|
||||
"lng_settings_run_in_background" = "Run in the background";
|
||||
"lng_settings_quit_on_close" = "Quit the application";
|
||||
"lng_settings_close_to_taskbar" = "Close to taskbar";
|
||||
"lng_settings_monochrome_icon" = "Use monochrome icon";
|
||||
"lng_settings_window_system" = "Window title bar";
|
||||
@@ -639,6 +642,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_chat_quick_action_react" = "Send reaction with double click";
|
||||
"lng_settings_chat_corner_reaction" = "Reaction button on messages";
|
||||
|
||||
"lng_settings_shortcuts" = "Keyboard shortcuts";
|
||||
|
||||
"lng_shortcuts_reset" = "Reset to default";
|
||||
"lng_shortcuts_recording" = "Recording...";
|
||||
"lng_shortcuts_add_another" = "Add another";
|
||||
|
||||
"lng_shortcuts_close" = "Close the window";
|
||||
"lng_shortcuts_lock" = "Lock the application";
|
||||
"lng_shortcuts_minimize" = "Minimize the window";
|
||||
"lng_shortcuts_quit" = "Quit the application";
|
||||
"lng_shortcuts_media_play" = "Play the media";
|
||||
"lng_shortcuts_media_pause" = "Pause the media";
|
||||
"lng_shortcuts_media_play_pause" = "Toggle media playback";
|
||||
"lng_shortcuts_media_stop" = "Stop media playback";
|
||||
"lng_shortcuts_media_previous" = "Previous track";
|
||||
"lng_shortcuts_media_next" = "Next track";
|
||||
"lng_shortcuts_search" = "Search messages";
|
||||
"lng_shortcuts_chat_previous" = "Previous chat";
|
||||
"lng_shortcuts_chat_next" = "Next chat";
|
||||
"lng_shortcuts_chat_first" = "First chat";
|
||||
"lng_shortcuts_chat_last" = "Last chat";
|
||||
"lng_shortcuts_chat_self" = "Saved Messages";
|
||||
"lng_shortcuts_chat_pinned_n" = "Pinned chat #{index}";
|
||||
"lng_shortcuts_show_account_n" = "Account #{index}";
|
||||
"lng_shortcuts_show_all_chats" = "All Chats folder";
|
||||
"lng_shortcuts_show_folder_n" = "Folder #{index}";
|
||||
"lng_shortcuts_show_folder_last" = "Last folder";
|
||||
"lng_shortcuts_folder_next" = "Next folder";
|
||||
"lng_shortcuts_folder_previous" = "Previous folder";
|
||||
"lng_shortcuts_scheduled" = "Scheduled messages";
|
||||
"lng_shortcuts_archive" = "Archived chats";
|
||||
"lng_shortcuts_contacts" = "Contacts list";
|
||||
"lng_shortcuts_just_send" = "Just send";
|
||||
"lng_shortcuts_silent_send" = "Silent send";
|
||||
"lng_shortcuts_schedule" = "Schedule";
|
||||
"lng_shortcuts_read_chat" = "Mark chat as read";
|
||||
"lng_shortcuts_archive_chat" = "Archive chat";
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
"lng_settings_chat_message_reply_from" = "Bob Harris";
|
||||
@@ -1175,6 +1218,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_contacts" = "My contacts";
|
||||
"lng_edit_privacy_close_friends" = "Close friends";
|
||||
"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium";
|
||||
"lng_edit_privacy_paid" = "Paid";
|
||||
"lng_edit_privacy_contacts_and_miniapps" = "Contacts & Mini Apps";
|
||||
"lng_edit_privacy_nobody" = "Nobody";
|
||||
"lng_edit_privacy_premium" = "Premium users";
|
||||
@@ -1313,6 +1357,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_messages_privacy_premium_about" = "Subscribe now to change this setting and get access to other exclusive features of Telegram Premium.";
|
||||
"lng_messages_privacy_premium" = "Only subscribers of {link} can select this option.";
|
||||
"lng_messages_privacy_premium_link" = "Telegram Premium";
|
||||
"lng_messages_privacy_charge" = "Charge for messages";
|
||||
"lng_messages_privacy_charge_about" = "Charge a fee for messages from people outside your contacts or those you haven't messaged first.";
|
||||
"lng_messages_privacy_price" = "Set your price per message";
|
||||
"lng_messages_privacy_price_about" = "You will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||
"lng_messages_privacy_exceptions" = "Exceptions";
|
||||
"lng_messages_privacy_remove_fee" = "Remove Fee";
|
||||
"lng_messages_privacy_remove_about" = "Add users or entire groups who won't be charged for sending messages to you.";
|
||||
|
||||
"lng_self_destruct_title" = "Account self-destruction";
|
||||
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
|
||||
@@ -1346,6 +1397,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_common_groups#other" = "{count} groups in common";
|
||||
"lng_profile_similar_channels#one" = "{count} similar channel";
|
||||
"lng_profile_similar_channels#other" = "{count} similar channels";
|
||||
"lng_profile_similar_bots#one" = "{count} similar bot";
|
||||
"lng_profile_similar_bots#other" = "{count} similar bots";
|
||||
"lng_profile_saved_messages#one" = "{count} saved message";
|
||||
"lng_profile_saved_messages#other" = "{count} saved messages";
|
||||
"lng_profile_peer_gifts#one" = "{count} gift";
|
||||
@@ -2020,16 +2073,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_sent" = "You sent a gift for {cost}";
|
||||
"lng_action_gift_unique_sent" = "You sent a unique collectible item";
|
||||
"lng_action_gift_upgraded" = "{user} turned the gift from you into a unique collectible";
|
||||
"lng_action_gift_upgraded_channel" = "{user} turned this gift to {channel} into a unique collectible";
|
||||
"lng_action_gift_upgraded_self_channel" = "You turned this gift to {channel} into a unique collectible";
|
||||
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible";
|
||||
"lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible";
|
||||
"lng_action_gift_transferred" = "{user} transferred you a gift";
|
||||
"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_unknown" = "Someone transferred you a gift";
|
||||
"lng_action_gift_transferred_unknown_channel" = "Someone transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_self" = "You transferred a unique collectible";
|
||||
"lng_action_gift_transferred_self_channel" = "You transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_mine" = "You transferred a gift to {user}";
|
||||
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
||||
"lng_action_gift_sent_channel" = "{user} sent a gift to {name} for {cost}";
|
||||
"lng_action_gift_sent_self_channel" = "You sent a gift to {name} for {cost}";
|
||||
"lng_action_gift_self_bought" = "You bought a gift for {cost}";
|
||||
"lng_action_gift_self_subtitle" = "Saved Gift";
|
||||
"lng_action_gift_self_about#one" = "Display this gift on your page or convert it to **{count}** Star.";
|
||||
"lng_action_gift_self_about#other" = "Display this gift on your page or convert it to **{count}** Stars.";
|
||||
"lng_action_gift_self_about_unique" = "You can display this gift on your page or turn it into unique collectible and send to others.";
|
||||
"lng_action_gift_channel_about#one" = "Display this gift in channel's Gifts or convert it to **{count}** Star.";
|
||||
"lng_action_gift_channel_about#other" = "Display this gift in channel's Gifts or convert it to **{count}** Stars.";
|
||||
"lng_action_gift_channel_about_unique" = "You can display this gift in channel's Gifts or turn it into unique collectible.";
|
||||
"lng_action_gift_for_stars#one" = "{count} Star";
|
||||
"lng_action_gift_for_stars#other" = "{count} Stars";
|
||||
"lng_action_gift_got_subtitle" = "Gift from {user}";
|
||||
@@ -2038,6 +2103,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_got_upgradable_text" = "Upgrade this gift to a unique collectible.";
|
||||
"lng_action_gift_got_gift_text" = "You can keep this gift on your page.";
|
||||
"lng_action_gift_can_remove_text" = "You can remove this gift from your page.";
|
||||
"lng_action_gift_got_gift_channel" = "You can keep this gift in channel's Gifts.";
|
||||
"lng_action_gift_can_remove_channel" = "You can remove this gift from channel's Gifts.";
|
||||
"lng_action_gift_sent_subtitle" = "Gift for {user}";
|
||||
"lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star.";
|
||||
"lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars.";
|
||||
@@ -2099,6 +2166,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_boost_apply#other" = "{from} boosted the group {count} times";
|
||||
"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?";
|
||||
"lng_action_payment_refunded" = "{peer} refunded {amount}";
|
||||
"lng_action_paid_message_sent#one" = "You paid {count} Star to {action}";
|
||||
"lng_action_paid_message_sent#other" = "You paid {count} Stars to {action}";
|
||||
"lng_action_paid_message_one" = "send a message";
|
||||
"lng_action_paid_message_some#one" = "send {count} message";
|
||||
"lng_action_paid_message_some#other" = "send {count} messages";
|
||||
"lng_action_paid_message_got#one" = "You received {count} Star from {name}";
|
||||
"lng_action_paid_message_got#other" = "You received {count} Stars from {name}";
|
||||
"lng_you_paid_stars#one" = "You paid {count} Star.";
|
||||
"lng_you_paid_stars#other" = "You paid {count} Stars.";
|
||||
|
||||
"lng_similar_channels_title" = "Similar channels";
|
||||
"lng_similar_channels_view_all" = "View all";
|
||||
@@ -2108,9 +2184,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_similar_channels_premium_all_link" = "Telegram Premium";
|
||||
"lng_similar_channels_show_more" = "Show more channels";
|
||||
|
||||
"lng_similar_bots_title" = "Similar bots";
|
||||
"lng_similar_bots_premium_all#one" = "Subscribe to {link} to unlock up to **{count}** similar bot.";
|
||||
"lng_similar_bots_premium_all#other" = "Subscribe to {link} to unlock up to **{count}** similar bots.";
|
||||
"lng_similar_bots_show_more" = "Show more bots";
|
||||
|
||||
"lng_peer_gifts_title" = "Gifts";
|
||||
"lng_peer_gifts_about" = "These gifts were sent to {user} by other users.";
|
||||
"lng_peer_gifts_about_mine" = "These gifts were sent to you by other users. Click on a gift to convert it to Stars or change its privacy settings.";
|
||||
"lng_peer_gifts_notify" = "Notify About New Gifts";
|
||||
"lng_peer_gifts_notify_enabled" = "You will receive a message from Telegram when your channel receives a gift.";
|
||||
"lng_peer_gifts_filter_by_value" = "Sort by Value";
|
||||
"lng_peer_gifts_filter_by_date" = "Sort by Date";
|
||||
"lng_peer_gifts_filter_unlimited" = "Unlimited";
|
||||
"lng_peer_gifts_filter_limited" = "Limited";
|
||||
"lng_peer_gifts_filter_unique" = "Unique";
|
||||
"lng_peer_gifts_filter_saved" = "Displayed";
|
||||
"lng_peer_gifts_filter_unsaved" = "Hidden";
|
||||
|
||||
"lng_premium_gift_duration_months#one" = "for {count} month";
|
||||
"lng_premium_gift_duration_months#other" = "for {count} months";
|
||||
@@ -2373,6 +2463,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_stickers_add" = "Choose sticker set";
|
||||
"lng_group_emoji" = "Select Emoji Pack";
|
||||
"lng_group_emoji_description" = "Choose an emoji pack that will be available to all members within the group.";
|
||||
"lng_collectible_emoji" = "Collectibles";
|
||||
|
||||
"lng_premium" = "Premium";
|
||||
"lng_premium_free" = "Free";
|
||||
@@ -2517,6 +2608,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_premium_double_limits_subtitle_accounts" = "Connected Accounts";
|
||||
"lng_premium_double_limits_about_accounts#one" = "Connect {count} account with different mobile numbers";
|
||||
"lng_premium_double_limits_about_accounts#other" = "Connect {count} accounts with different mobile numbers";
|
||||
|
||||
"lng_premium_double_limits_subtitle_similar_channels" = "Similar Channel";
|
||||
"lng_premium_double_limits_about_similar_channels#one" = "View up to {count} similar channel";
|
||||
"lng_premium_double_limits_about_similar_channels#other" = "View up to {count} similar channels";
|
||||
//
|
||||
|
||||
"lng_premium_gift_title" = "Gift Telegram Premium";
|
||||
@@ -2586,6 +2681,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
|
||||
"lng_credits_summary_balance" = "Balance";
|
||||
"lng_credits_commission" = "{amount} commission";
|
||||
"lng_credits_paid_messages_fee#one" = "Fee for {count} Message";
|
||||
"lng_credits_paid_messages_fee#other" = "Fee for {count} Messages";
|
||||
"lng_credits_paid_messages_fee_about" = "You receive {percent} of the price that you charge for each incoming message. {link}";
|
||||
"lng_credits_paid_messages_fee_about_link" = "Change Fee {emoji}";
|
||||
"lng_credits_paid_messages_full" = "Full Price";
|
||||
"lng_credits_premium_gift_duration" = "Duration";
|
||||
"lng_credits_more_options" = "More Options";
|
||||
"lng_credits_balance_me" = "your balance";
|
||||
"lng_credits_buy_button" = "Buy More Stars";
|
||||
@@ -2699,6 +2800,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_credits_small_balance_reaction" = "Buy **Stars** and send them to {channel} to support their posts.";
|
||||
"lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels.";
|
||||
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
|
||||
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
|
||||
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
|
||||
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
|
||||
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
||||
"lng_credits_enough" = "You have enough stars at the moment. {link}";
|
||||
@@ -2960,11 +3063,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack.";
|
||||
"lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack.";
|
||||
|
||||
"lng_boost_channel_title_wear" = "Wear Item";
|
||||
"lng_boost_channel_needs_level_wear#one" = "Your channel needs **Level {count}** to wear collectibles.";
|
||||
"lng_boost_channel_needs_level_wear#other" = "Your channel needs **Level {count}** to wear collectibles.";
|
||||
|
||||
"lng_boost_channel_ask" = "Ask your **Premium** subscribers to boost your channel with this link:";
|
||||
"lng_boost_channel_ask_button" = "Copy Link";
|
||||
"lng_boost_channel_or" = "or";
|
||||
"lng_boost_channel_gifting" = "Boost your channel by gifting your subscribers Telegram Premium. {link}";
|
||||
"lng_boost_channel_gifting_link" = "Get boosts >";
|
||||
//"lng_boost_channel_or" = "or";
|
||||
//"lng_boost_channel_gifting" = "Boost your channel by gifting your subscribers Telegram Premium. {link}";
|
||||
//"lng_boost_channel_gifting_link" = "Get boosts >";
|
||||
"lng_boost_group_ask" = "Ask your **Premium** members to boost your group with this link:";
|
||||
//"lng_boost_group_gifting" = "Boost your group by gifting your members Telegram Premium. {link}";
|
||||
|
||||
"lng_feature_stories#one" = "**{count}** Story Per Day";
|
||||
"lng_feature_stories#other" = "**{count}** Stories Per Day";
|
||||
@@ -3226,6 +3335,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
|
||||
"lng_gift_premium_features" = "See Features >";
|
||||
"lng_gift_premium_label" = "Premium";
|
||||
"lng_gift_premium_by_stars" = "or {amount}";
|
||||
"lng_gift_stars_subtitle" = "Gift Stars";
|
||||
"lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}";
|
||||
"lng_gift_stars_link" = "What are Stars >";
|
||||
@@ -3237,12 +3347,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_send_title" = "Send a Gift";
|
||||
"lng_gift_send_message" = "Enter Message";
|
||||
"lng_gift_send_anonymous" = "Hide My Name";
|
||||
"lng_gift_send_pay_with_stars" = "Pay with {amount}";
|
||||
"lng_gift_send_stars_balance" = "Your balance is {amount}. {link}";
|
||||
"lng_gift_send_stars_balance_link" = "Get More Stars >";
|
||||
"lng_gift_send_anonymous_self" = "Hide my name and message from visitors to my profile.";
|
||||
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
|
||||
"lng_gift_send_anonymous_about_paid" = "You can hide your name from visitors to {user}'s profile. {recipient} will still see your name.";
|
||||
"lng_gift_send_anonymous_about_channel" = "You can hide your name and message from all visitors of this channel except its admins.";
|
||||
"lng_gift_send_unique" = "Make Unique for {price}";
|
||||
"lng_gift_send_unique_about" = "Enable this to let {user} turn your gift into a unique collectible. {link}";
|
||||
"lng_gift_send_unique_about_channel" = "Enable this to let the admins of {name} turn your gift into a unique collectible. {link}";
|
||||
"lng_gift_send_unique_link" = "Learn More >";
|
||||
"lng_gift_send_premium_about" = "Only {user} will see your message.";
|
||||
"lng_gift_send_limited_sold#one" = "{count} sold";
|
||||
"lng_gift_send_limited_sold#other" = "{count} sold";
|
||||
"lng_gift_send_limited_left#one" = "{count} left";
|
||||
"lng_gift_send_limited_left#other" = "{count} left";
|
||||
"lng_gift_send_button" = "Send a Gift for {cost}";
|
||||
"lng_gift_send_button_self" = "Buy a Gift for {cost}";
|
||||
"lng_gift_sent_title" = "Gift Sent!";
|
||||
@@ -3254,20 +3374,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_price_unique" = "Unique";
|
||||
"lng_gift_view_unpack" = "Unpack";
|
||||
"lng_gift_anonymous_hint" = "Only you can see the sender's name.";
|
||||
"lng_gift_anonymous_hint_channel" = "Only admins of this channel can see the sender's name.";
|
||||
"lng_gift_hidden_hint" = "This gift is hidden. Only you can see it.";
|
||||
"lng_gift_visible_hint" = "This gift is visible to visitors of your page.";
|
||||
"lng_gift_hidden_unique" = "This gift is not displayed on your page.";
|
||||
"lng_gift_visible_hint" = "This gift is visible on your page.";
|
||||
"lng_gift_hidden_hint_channel" = "This gift is hidden from visitors of your channel.";
|
||||
"lng_gift_visible_hint_channel" = "This gift is visible in your channel's Gifts.";
|
||||
"lng_gift_in_blockchain" = "This gift is in TON blockchain. {link}";
|
||||
"lng_gift_in_blockchain_link" = "View >";
|
||||
"lng_gift_visible_hide" = "Hide >";
|
||||
"lng_gift_show_on_page" = "Display on my Page";
|
||||
"lng_gift_show_on_channel" = "Display in channel's Gifts";
|
||||
"lng_gift_availability" = "Availability";
|
||||
"lng_gift_from_hidden" = "Hidden User";
|
||||
"lng_gift_visibility" = "Visibility";
|
||||
"lng_gift_visibility_shown" = "Visible on your page";
|
||||
"lng_gift_visibility_hidden" = "Not visible on your page";
|
||||
"lng_gift_visibility_show" = "show";
|
||||
"lng_gift_visibility_hide" = "hide";
|
||||
"lng_gift_self_status" = "buy yourself a gift";
|
||||
"lng_gift_self_title" = "Buy a Gift";
|
||||
"lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
|
||||
"lng_gift_channel_title" = "Send a Gift";
|
||||
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
|
||||
"lng_gift_unique_owner" = "Owner";
|
||||
"lng_gift_unique_owner_change" = "change";
|
||||
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
||||
"lng_gift_unique_status" = "Status";
|
||||
"lng_gift_unique_status_non" = "Non-Unique";
|
||||
"lng_gift_unique_status_upgrade" = "upgrade";
|
||||
@@ -3276,6 +3402,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_backdrop" = "Backdrop";
|
||||
"lng_gift_unique_symbol" = "Symbol";
|
||||
"lng_gift_unique_rarity" = "Only {percent} of such collectibles have this attribute.";
|
||||
"lng_gift_unique_availability_label" = "Quantity";
|
||||
"lng_gift_unique_availability#one" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_availability#other" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
|
||||
@@ -3291,14 +3418,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
|
||||
"lng_gift_convert_sure_confirm#one" = "Do you want to convert this gift from {user} to **{count} Star**?";
|
||||
"lng_gift_convert_sure_confirm#other" = "Do you want to convert this gift from {user} to **{count} Stars**?";
|
||||
"lng_gift_convert_sure_confirm_channel#one" = "Do you want to convert this gift to {channel} to **{count} Star**?";
|
||||
"lng_gift_convert_sure_confirm_channel#other" = "Do you want to convert this gift to {channel} to **{count} Stars**?";
|
||||
"lng_gift_convert_sure_limit#one" = "Conversion is available for the next **{count} day**.";
|
||||
"lng_gift_convert_sure_limit#other" = "Conversion is available for the next **{count} days**.";
|
||||
"lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift.";
|
||||
"lng_gift_convert_sure" = "Convert";
|
||||
"lng_gift_display_done" = "The gift is now shown on your profile page.";
|
||||
"lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts.";
|
||||
"lng_gift_display_done_hide" = "The gift is now hidden from your profile page.";
|
||||
"lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts.";
|
||||
"lng_gift_pinned_done" = "The gift will always be shown on top.";
|
||||
"lng_gift_got_stars#one" = "You got **{count} Star** for this gift.";
|
||||
"lng_gift_got_stars#other" = "You got **{count} Stars** for this gift.";
|
||||
"lng_gift_channel_got#one" = "Channel got **{count} Star** for this gift.";
|
||||
"lng_gift_channel_got#other" = "Channel got **{count} Stars** for this gift.";
|
||||
"lng_gift_sold_out_title" = "Sold Out!";
|
||||
"lng_gift_sold_out_text#one" = "All {count} gift was already sold.";
|
||||
"lng_gift_sold_out_text#other" = "All {count} gifts were already sold.";
|
||||
@@ -3309,6 +3443,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_about" = "Turn your gift into a unique collectible\nthat you can transfer or auction.";
|
||||
"lng_gift_upgrade_preview_title" = "Make Unique";
|
||||
"lng_gift_upgrade_preview_about" = "Let {name} turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_preview_about_channel" = "Let the admins of {name} turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_unique_title" = "Unique";
|
||||
"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_transferable_title" = "Transferable";
|
||||
@@ -3328,6 +3463,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
|
||||
"lng_gift_transfer_title" = "Transfer {name}";
|
||||
"lng_gift_transfer_via_blockchain" = "Send via Blockchain";
|
||||
"lng_gift_transfer_password_title" = "Two-step verification";
|
||||
"lng_gift_transfer_password_description" = "Please enter your password to transfer.";
|
||||
"lng_gift_transfer_password_about" = "You can withdraw only if you have:";
|
||||
"lng_gift_transfer_confirm_title" = "Manage with Fragment";
|
||||
"lng_gift_transfer_confirm_text" = "You can use Fragment, a third-party service, to transfer {name} to your TON account. After that, you can manage it as an NFT with any TON wallet outside Telegram.\n\nYou can also move such NFTs back to your Telegram account via Fragment.";
|
||||
"lng_gift_transfer_confirm_button" = "Open Fragment";
|
||||
"lng_gift_transfer_unlocks_days#one" = "unlocks in {count} day";
|
||||
"lng_gift_transfer_unlocks_days#other" = "unlocks in {count} days";
|
||||
"lng_gift_transfer_unlocks_hours#one" = "unlocks in {count} hour";
|
||||
@@ -3344,6 +3485,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?";
|
||||
"lng_gift_transfer_button" = "Transfer";
|
||||
"lng_gift_transfer_button_for" = "Transfer for {price}";
|
||||
"lng_gift_transfer_wear" = "Wear";
|
||||
"lng_gift_transfer_take_off" = "Take Off";
|
||||
"lng_gift_menu_show" = "Show";
|
||||
"lng_gift_menu_hide" = "Hide";
|
||||
"lng_gift_wear_title" = "Wear {name}";
|
||||
"lng_gift_wear_about" = "and get these benefits:";
|
||||
"lng_gift_wear_badge_title" = "Radiant Badge";
|
||||
"lng_gift_wear_badge_about" = "The glittering icon of this item will be displayed next to your name.";
|
||||
"lng_gift_wear_badge_about_channel" = "The glittering icon of this item will be displayed next to channel's name.";
|
||||
"lng_gift_wear_proof_title" = "Proof of Ownership";
|
||||
"lng_gift_wear_proof_about" = "Clicking the icon of this item next to your name will show its info and owner.";
|
||||
"lng_gift_wear_proof_about_channel" = "Clicking the icon of this item next to channel's name will show its info and owner.";
|
||||
"lng_gift_wear_start" = "Start Wearing";
|
||||
"lng_gift_wear_subscribe" = "Subscribe to {link} to wear collectibles.";
|
||||
"lng_gift_wear_start_toast" = "You put on {name}";
|
||||
"lng_gift_wear_end_toast" = "You took off {name}";
|
||||
|
||||
"lng_accounts_limit_title" = "Limit Reached";
|
||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
|
||||
@@ -3485,6 +3642,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join.";
|
||||
"lng_new_contact_about_status" = "This account uses {emoji} as a custom status next to its\nname. Such emoji statuses are available to all\nsubscribers of {link}.";
|
||||
"lng_new_contact_about_status_link" = "Telegram Premium";
|
||||
"lng_new_contact_not_contact" = "Not a contact";
|
||||
"lng_new_contact_phone_number" = "Phone number";
|
||||
"lng_new_contact_registration" = "Registration";
|
||||
"lng_new_contact_common_groups" = "Common groups";
|
||||
"lng_new_contact_groups#one" = "{count} group {emoji} {arrow}";
|
||||
"lng_new_contact_groups#other" = "{count} groups {emoji} {arrow}";
|
||||
"lng_new_contact_not_official" = "Not an official account";
|
||||
"lng_new_contact_updated_name" = "User updated name {when}";
|
||||
"lng_new_contact_updated_photo" = "User updated photo {when}";
|
||||
"lng_new_contact_updated_now" = "less than an hour ago";
|
||||
"lng_new_contact_updated_hours#one" = "{count} hour ago";
|
||||
"lng_new_contact_updated_hours#other" = "{count} hours ago";
|
||||
"lng_new_contact_updated_days#one" = "{count} day ago";
|
||||
"lng_new_contact_updated_days#other" = "{count} days ago";
|
||||
"lng_new_contact_updated_months#one" = "{count} month ago";
|
||||
"lng_new_contact_updated_months#other" = "{count} months ago";
|
||||
"lng_from_request_title_channel" = "Response to your join request";
|
||||
"lng_from_request_title_group" = "Response to your join request";
|
||||
"lng_from_request_body" = "You received this message because you requested to join {name} on {date}.";
|
||||
@@ -3513,6 +3686,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_send_anonymous_ph" = "Send anonymously...";
|
||||
"lng_story_reply_ph" = "Reply privately...";
|
||||
"lng_story_comment_ph" = "Comment story...";
|
||||
"lng_message_paid_ph" = "Message for {amount}";
|
||||
"lng_send_text_no" = "Text not allowed.";
|
||||
"lng_send_text_no_about" = "The admins of this group only allow sending {types}.";
|
||||
"lng_send_text_type_and_last" = "{types} and {last}";
|
||||
@@ -3759,6 +3933,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_choose_image" = "Choose an image";
|
||||
"lng_choose_file" = "Choose a file";
|
||||
"lng_choose_files" = "Choose Files";
|
||||
"lng_choose_cover" = "Choose video cover";
|
||||
"lng_choose_cover_bad" = "Can't use this file as a caption.";
|
||||
"lng_game_tag" = "Game";
|
||||
|
||||
"lng_context_new_window" = "Open in new window";
|
||||
@@ -3875,6 +4051,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_read_show" = "show when";
|
||||
"lng_context_edit_shortcut" = "Edit Shortcut";
|
||||
"lng_context_delete_shortcut" = "Delete Quick Reply";
|
||||
"lng_context_gift_send" = "Send Another Gift";
|
||||
|
||||
"lng_add_tag_about" = "Tag this message with an emoji for quick search.";
|
||||
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
|
||||
@@ -3903,6 +4080,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_disable_spoiler" = "Remove Spoiler";
|
||||
"lng_context_make_paid" = "Make This Content Paid";
|
||||
"lng_context_change_price" = "Change Price";
|
||||
"lng_context_edit_cover" = "Edit Cover";
|
||||
"lng_context_clear_cover" = "Clear Cover";
|
||||
|
||||
"lng_context_mention" = "Mention";
|
||||
"lng_context_search_from" = "Search messages";
|
||||
@@ -4032,6 +4211,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_reply_cant_forward" = "Sorry, you can't reply to a message that was sent before the group was upgraded to a supergroup. Do you wish to forward it and add your comment?";
|
||||
|
||||
"lng_share_title" = "Share to";
|
||||
"lng_share_at_time_title" = "Share at {time} to";
|
||||
"lng_share_copy_link" = "Copy share link";
|
||||
"lng_share_confirm" = "Send";
|
||||
"lng_share_wrong_user" = "This game was opened from a different user.";
|
||||
@@ -4155,7 +4335,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_save_progress" = "{ready} of {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Save As...";
|
||||
"lng_mediaview_copy" = "Copy";
|
||||
"lng_mediaview_copy_frame" = "Copy Frame";
|
||||
"lng_mediaview_forward" = "Forward";
|
||||
"lng_mediaview_share_at_time" = "Share at {time}";
|
||||
"lng_mediaview_delete" = "Delete";
|
||||
"lng_mediaview_save_to_profile" = "Post to Profile";
|
||||
"lng_mediaview_pin_story_done" = "Story pinned";
|
||||
@@ -4666,6 +4848,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_boosts_no_restrict" = "Do not restrict boosters";
|
||||
"lng_rights_boosts_about" = "Turn this on to always allow users who boosted your group to send messages and media.";
|
||||
"lng_rights_boosts_about_on" = "Choose how many boosts a user must give to the group to bypass restrictions on sending messages.";
|
||||
"lng_rights_charge_stars" = "Charge Stars for Messages";
|
||||
"lng_rights_charge_stars_about" = "If you turn this on, regular members of the group will have to pay Stars to send messages.";
|
||||
"lng_rights_charge_price" = "Set price per message";
|
||||
"lng_rights_charge_price_about" = "Your group will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||
|
||||
"lng_slowmode_enabled" = "Slow Mode is active.\nYou can send your next message in {left}.";
|
||||
"lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time.";
|
||||
@@ -4673,6 +4859,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_slowmode_seconds#one" = "{count} second";
|
||||
"lng_slowmode_seconds#other" = "{count} seconds";
|
||||
|
||||
"lng_payment_confirm_title" = "Confirm payment";
|
||||
"lng_payment_confirm_text#one" = "{name} charges **{count}** Star per message.";
|
||||
"lng_payment_confirm_text#other" = "{name} charges **{count}** Stars per message.";
|
||||
"lng_payment_confirm_amount#one" = "**{count}** Star";
|
||||
"lng_payment_confirm_amount#other" = "**{count}** Stars";
|
||||
"lng_payment_confirm_users#one" = "You selected **{count}** user who charge Stars for messages.";
|
||||
"lng_payment_confirm_users#other" = "You selected **{count}** users who charge Stars for messages.";
|
||||
"lng_payment_confirm_chats#one" = "You selected **{count}** chat where you pay Stars for messages.";
|
||||
"lng_payment_confirm_chats#other" = "You selected **{count}** chats where you pay Stars for messages.";
|
||||
"lng_payment_confirm_sure#one" = "Would you like to pay {amount} to send **{count}** message?";
|
||||
"lng_payment_confirm_sure#other" = "Would you like to pay {amount} to send **{count}** messages?";
|
||||
"lng_payment_confirm_dont_ask" = "Don't ask me again";
|
||||
"lng_payment_confirm_button#one" = "Pay for {count} Message";
|
||||
"lng_payment_confirm_button#other" = "Pay for {count} Messages";
|
||||
"lng_payment_bar_text" = "{name} must pay {cost} for each message to you.";
|
||||
"lng_payment_bar_button" = "Remove Fee";
|
||||
"lng_payment_refund_title" = "Remove Fee";
|
||||
"lng_payment_refund_text" = "Are you sure you want to allow {name} to message you for free?";
|
||||
"lng_payment_refund_also#one" = "Refund already paid {count} Star";
|
||||
"lng_payment_refund_also#other" = "Refund already paid {count} Stars";
|
||||
"lng_payment_refund_confirm" = "Confirm";
|
||||
|
||||
"lng_rights_gigagroup_title" = "Broadcast group";
|
||||
"lng_rights_gigagroup_convert" = "Convert to Broadcast Group";
|
||||
"lng_rights_gigagroup_about" = "Broadcast groups can have over 200,000 members, but only admins can send messages in them.";
|
||||
@@ -4804,6 +5012,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers.";
|
||||
"lng_send_non_premium_message_toast_link" = "Telegram Premium";
|
||||
|
||||
"lng_send_charges_stars_text" = "{user} charges {amount} for each message.";
|
||||
"lng_send_charges_stars_go" = "Buy Stars";
|
||||
|
||||
"lng_exceptions_list_title" = "Exceptions";
|
||||
"lng_removed_list_title" = "Removed users";
|
||||
|
||||
@@ -5520,6 +5731,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_view_button_iv" = "Instant View";
|
||||
"lng_view_button_stickerset" = "View stickers";
|
||||
"lng_view_button_emojipack" = "View emoji";
|
||||
"lng_view_button_collectible" = "View collectible";
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
@@ -5836,6 +6048,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_boosts_list_tab_gifts#other" = "{count} Gifts";
|
||||
|
||||
"lng_boosts_prepaid_giveaway_title" = "Prepaid giveaways";
|
||||
"lng_boosts_prepaid_giveaway_title_subtext" = "Select a giveaway you already paid for to set it up.";
|
||||
"lng_boosts_prepaid_giveaway_single" = "Prepaid giveaway";
|
||||
"lng_boosts_prepaid_giveaway_quantity#one" = "{count} Telegram Premium";
|
||||
"lng_boosts_prepaid_giveaway_quantity#other" = "{count} Telegram Premium";
|
||||
@@ -5895,6 +6108,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_channel_earn_learn_coin_link" = "https://telegram.org/blog/monetization-for-channels";
|
||||
"lng_channel_earn_chart_top_hours" = "Ad impressions";
|
||||
"lng_channel_earn_chart_revenue" = "Ad rewards";
|
||||
"lng_channel_earn_chart_overriden_detail_credits" = "Rewards in Stars";
|
||||
"lng_channel_earn_chart_overriden_detail_currency" = "Rewards in TON";
|
||||
"lng_channel_earn_chart_overriden_detail_usd" = "Rewards in USD";
|
||||
"lng_channel_earn_currency_history" = "TON Transactions";
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="5.10.3.0" />
|
||||
Version="5.12.3.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 5,10,3,0
|
||||
PRODUCTVERSION 5,10,3,0
|
||||
FILEVERSION 5,12,3,0
|
||||
PRODUCTVERSION 5,12,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop"
|
||||
VALUE "FileVersion", "5.10.3.0"
|
||||
VALUE "FileVersion", "5.12.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.10.3.0"
|
||||
VALUE "ProductVersion", "5.12.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,10,3,0
|
||||
PRODUCTVERSION 5,10,3,0
|
||||
FILEVERSION 5,12,3,0
|
||||
PRODUCTVERSION 5,12,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||
VALUE "FileVersion", "5.10.3.0"
|
||||
VALUE "FileVersion", "5.12.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.10.3.0"
|
||||
VALUE "ProductVersion", "5.12.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -217,7 +217,11 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) {
|
||||
MTP_bool(disabled)
|
||||
)).done([=] {
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).fail([=] {
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
LOG(("API Error: toggle calls %1. Hash: %2. %3.")
|
||||
.arg(disabled ? u"disabled"_q : u"enabled"_q)
|
||||
.arg(hash)
|
||||
.arg(error.type()));
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).send();
|
||||
_toggleCallsDisabledRequests.emplace(hash, id);
|
||||
|
||||
@@ -149,18 +149,14 @@ void InitFilterLinkHeader(
|
||||
iconEmoji
|
||||
).value_or(Ui::FilterIcon::Custom)).active;
|
||||
const auto isStatic = title.isStatic;
|
||||
const auto makeContext = [=](Fn<void()> repaint) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &box->peerListUiShow()->session(),
|
||||
.customEmojiRepaint = std::move(repaint),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
auto header = Ui::MakeFilterLinkHeader(box, {
|
||||
.type = type,
|
||||
.title = TitleText(type)(tr::now),
|
||||
.about = AboutText(type, title.text),
|
||||
.makeAboutContext = makeContext,
|
||||
.aboutContext = Core::TextContext({
|
||||
.session = &box->peerListUiShow()->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}),
|
||||
.folderTitle = title.text,
|
||||
.folderIcon = icon,
|
||||
.badge = (type == Ui::FilterLinkHeaderType::AddingChats
|
||||
@@ -560,16 +556,12 @@ void ShowImportToast(
|
||||
text.append('\n').append(phrase(tr::now, lt_count, added));
|
||||
}
|
||||
const auto isStatic = title.isStatic;
|
||||
const auto makeContext = [=](not_null<QWidget*> widget) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &strong->session(),
|
||||
.customEmojiRepaint = [=] { widget->update(); },
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
strong->showToast({
|
||||
.text = std::move(text),
|
||||
.textContext = makeContext,
|
||||
.textContext = Core::TextContext({
|
||||
.session = &strong->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -640,18 +632,14 @@ void ProcessFilterInvite(
|
||||
raw->setRealContentHeight(box->heightValue());
|
||||
|
||||
const auto isStatic = title.isStatic;
|
||||
const auto makeContext = [=](Fn<void()> update) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &strong->session(),
|
||||
.customEmojiRepaint = update,
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
auto owned = Ui::FilterLinkProcessButton(
|
||||
box,
|
||||
type,
|
||||
title.text,
|
||||
makeContext,
|
||||
Core::TextContext({
|
||||
.session = &strong->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}),
|
||||
std::move(badge));
|
||||
|
||||
const auto button = owned.data();
|
||||
@@ -873,18 +861,14 @@ void ProcessFilterRemove(
|
||||
}, type, title, iconEmoji, rpl::single(0), horizontalFilters);
|
||||
|
||||
const auto isStatic = title.isStatic;
|
||||
const auto makeContext = [=](Fn<void()> update) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &strong->session(),
|
||||
.customEmojiRepaint = update,
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
auto owned = Ui::FilterLinkProcessButton(
|
||||
box,
|
||||
type,
|
||||
title.text,
|
||||
makeContext,
|
||||
Core::TextContext({
|
||||
.session = &strong->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}),
|
||||
std::move(badge));
|
||||
|
||||
const auto button = owned.data();
|
||||
|
||||
@@ -211,11 +211,10 @@ void ApplyBotsList(
|
||||
Data::PeerUpdate::Flag::FullInfo);
|
||||
}
|
||||
|
||||
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
|
||||
[[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPmessages_Chats &chats) {
|
||||
auto result = ChatParticipants::Channels();
|
||||
std::vector<not_null<ChannelData*>>();
|
||||
auto result = ChatParticipants::Peers();
|
||||
chats.match([&](const auto &data) {
|
||||
const auto &list = data.vchats().v;
|
||||
result.list.reserve(list.size());
|
||||
@@ -234,10 +233,29 @@ void ApplyBotsList(
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
|
||||
[[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPmessages_Chats &chats) {
|
||||
return ParseSimilar(&channel->session(), chats);
|
||||
return ParseSimilarChannels(&channel->session(), chats);
|
||||
}
|
||||
|
||||
[[nodiscard]] ChatParticipants::Peers ParseSimilarBots(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPusers_Users &users) {
|
||||
auto result = ChatParticipants::Peers();
|
||||
users.match([&](const auto &data) {
|
||||
const auto &list = data.vusers().v;
|
||||
result.list.reserve(list.size());
|
||||
for (const auto &user : list) {
|
||||
result.list.push_back(session->data().processUser(user));
|
||||
}
|
||||
if constexpr (MTPDusers_usersSlice::Is<decltype(data)>()) {
|
||||
if (session->premiumPossible()) {
|
||||
result.more = data.vcount().v - data.vusers().v.size();
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -782,52 +800,65 @@ void ChatParticipants::unblock(
|
||||
_kickRequests.emplace(kick, requestId);
|
||||
}
|
||||
|
||||
void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
|
||||
if (!channel->isBroadcast()) {
|
||||
return;
|
||||
} else if (const auto i = _similar.find(channel); i != end(_similar)) {
|
||||
void ChatParticipants::loadSimilarPeers(not_null<PeerData*> peer) {
|
||||
if (const auto i = _similar.find(peer); i != end(_similar)) {
|
||||
if (i->second.requestId
|
||||
|| !i->second.channels.more
|
||||
|| !channel->session().premium()) {
|
||||
|| !i->second.peers.more
|
||||
|| !peer->session().premium()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
using Flag = MTPchannels_GetChannelRecommendations::Flag;
|
||||
_similar[channel].requestId = _api.request(
|
||||
MTPchannels_GetChannelRecommendations(
|
||||
MTP_flags(Flag::f_channel),
|
||||
channel->inputChannel)
|
||||
).done([=](const MTPmessages_Chats &result) {
|
||||
auto &similar = _similar[channel];
|
||||
similar.requestId = 0;
|
||||
auto parsed = ParseSimilar(channel, result);
|
||||
if (similar.channels == parsed) {
|
||||
return;
|
||||
}
|
||||
similar.channels = std::move(parsed);
|
||||
if (const auto history = channel->owner().historyLoaded(channel)) {
|
||||
if (const auto item = history->joinedMessageInstance()) {
|
||||
history->owner().requestItemResize(item);
|
||||
if (const auto channel = peer->asBroadcast()) {
|
||||
using Flag = MTPchannels_GetChannelRecommendations::Flag;
|
||||
_similar[peer].requestId = _api.request(
|
||||
MTPchannels_GetChannelRecommendations(
|
||||
MTP_flags(Flag::f_channel),
|
||||
channel->inputChannel)
|
||||
).done([=](const MTPmessages_Chats &result) {
|
||||
auto &similar = _similar[channel];
|
||||
similar.requestId = 0;
|
||||
auto parsed = ParseSimilarChannels(channel, result);
|
||||
if (similar.peers == parsed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_similarLoaded.fire_copy(channel);
|
||||
}).send();
|
||||
similar.peers = std::move(parsed);
|
||||
if (const auto history = channel->owner().historyLoaded(channel)) {
|
||||
if (const auto item = history->joinedMessageInstance()) {
|
||||
history->owner().requestItemResize(item);
|
||||
}
|
||||
}
|
||||
_similarLoaded.fire_copy(channel);
|
||||
}).send();
|
||||
} else if (const auto bot = peer->asBot()) {
|
||||
_similar[peer].requestId = _api.request(
|
||||
MTPbots_GetBotRecommendations(bot->inputUser)
|
||||
).done([=](const MTPusers_Users &result) {
|
||||
auto &similar = _similar[peer];
|
||||
similar.requestId = 0;
|
||||
auto parsed = ParseSimilarBots(&peer->session(), result);
|
||||
if (similar.peers == parsed) {
|
||||
return;
|
||||
}
|
||||
similar.peers = std::move(parsed);
|
||||
_similarLoaded.fire_copy(peer);
|
||||
}).send();
|
||||
}
|
||||
}
|
||||
|
||||
auto ChatParticipants::similar(not_null<ChannelData*> channel)
|
||||
-> const Channels & {
|
||||
const auto i = channel->isBroadcast()
|
||||
? _similar.find(channel)
|
||||
auto ChatParticipants::similar(not_null<PeerData*> peer)
|
||||
-> const Peers & {
|
||||
const auto i = (peer->isBroadcast() || peer->isBot())
|
||||
? _similar.find(peer)
|
||||
: end(_similar);
|
||||
if (i != end(_similar)) {
|
||||
return i->second.channels;
|
||||
return i->second.peers;
|
||||
}
|
||||
static const auto empty = Channels();
|
||||
static const auto empty = Peers();
|
||||
return empty;
|
||||
}
|
||||
|
||||
auto ChatParticipants::similarLoaded() const
|
||||
-> rpl::producer<not_null<ChannelData*>> {
|
||||
-> rpl::producer<not_null<PeerData*>> {
|
||||
return _similarLoaded.events();
|
||||
}
|
||||
|
||||
@@ -841,15 +872,15 @@ void ChatParticipants::loadRecommendations() {
|
||||
MTP_inputChannelEmpty())
|
||||
).done([=](const MTPmessages_Chats &result) {
|
||||
_recommendations.requestId = 0;
|
||||
auto parsed = ParseSimilar(_session, result);
|
||||
_recommendations.channels = std::move(parsed);
|
||||
_recommendations.channels.more = 0;
|
||||
auto parsed = ParseSimilarChannels(_session, result);
|
||||
_recommendations.peers = std::move(parsed);
|
||||
_recommendations.peers.more = 0;
|
||||
_recommendationsLoaded = true;
|
||||
}).send();
|
||||
}
|
||||
|
||||
const ChatParticipants::Channels &ChatParticipants::recommendations() const {
|
||||
return _recommendations.channels;
|
||||
const ChatParticipants::Peers &ChatParticipants::recommendations() const {
|
||||
return _recommendations.peers;
|
||||
}
|
||||
|
||||
rpl::producer<> ChatParticipants::recommendationsLoaded() const {
|
||||
|
||||
@@ -138,27 +138,27 @@ public:
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> participant);
|
||||
|
||||
void loadSimilarChannels(not_null<ChannelData*> channel);
|
||||
void loadSimilarPeers(not_null<PeerData*> peer);
|
||||
|
||||
struct Channels {
|
||||
std::vector<not_null<ChannelData*>> list;
|
||||
struct Peers {
|
||||
std::vector<not_null<PeerData*>> list;
|
||||
int more = 0;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Channels &,
|
||||
const Channels &) = default;
|
||||
const Peers &,
|
||||
const Peers &) = default;
|
||||
};
|
||||
[[nodiscard]] const Channels &similar(not_null<ChannelData*> channel);
|
||||
[[nodiscard]] const Peers &similar(not_null<PeerData*> peer);
|
||||
[[nodiscard]] auto similarLoaded() const
|
||||
-> rpl::producer<not_null<ChannelData*>>;
|
||||
-> rpl::producer<not_null<PeerData*>>;
|
||||
|
||||
void loadRecommendations();
|
||||
[[nodiscard]] const Channels &recommendations() const;
|
||||
[[nodiscard]] const Peers &recommendations() const;
|
||||
[[nodiscard]] rpl::producer<> recommendationsLoaded() const;
|
||||
|
||||
private:
|
||||
struct SimilarChannels {
|
||||
Channels channels;
|
||||
struct SimilarPeers {
|
||||
Peers peers;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
@@ -186,10 +186,10 @@ private:
|
||||
not_null<PeerData*>>;
|
||||
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
|
||||
|
||||
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar;
|
||||
rpl::event_stream<not_null<ChannelData*>> _similarLoaded;
|
||||
base::flat_map<not_null<PeerData*>, SimilarPeers> _similar;
|
||||
rpl::event_stream<not_null<PeerData*>> _similarLoaded;
|
||||
|
||||
SimilarChannels _recommendations;
|
||||
SimilarPeers _recommendations;
|
||||
rpl::variable<bool> _recommendationsLoaded = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ struct SendOptions {
|
||||
TimeId scheduled = 0;
|
||||
BusinessShortcutId shortcutId = 0;
|
||||
EffectId effectId = 0;
|
||||
int starsApproved = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool invertCaption = false;
|
||||
@@ -74,6 +75,7 @@ struct MessageToSend {
|
||||
struct RemoteFileInfo {
|
||||
MTPInputFile file;
|
||||
std::optional<MTPInputFile> thumb;
|
||||
std::optional<MTPInputPhoto> videoCover;
|
||||
std::vector<MTPInputDocument> attachedStickers;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,7 +90,13 @@ constexpr auto kTransactionsLimit = 100;
|
||||
? peerFromMTP(*tl.data().vstarref_peer()).value
|
||||
: 0;
|
||||
const auto incoming = (amount >= StarsAmount());
|
||||
const auto saveActorId = (reaction || !extended.empty()) && incoming;
|
||||
const auto paidMessagesCount
|
||||
= tl.data().vpaid_messages().value_or_empty();
|
||||
const auto premiumMonthsForStars
|
||||
= tl.data().vpremium_gift_months().value_or_empty();
|
||||
const auto saveActorId = (reaction
|
||||
|| !extended.empty()
|
||||
|| paidMessagesCount) && incoming;
|
||||
const auto parsedGift = stargift
|
||||
? FromTL(&peer->session(), *stargift)
|
||||
: std::optional<Data::StarGift>();
|
||||
@@ -110,9 +116,9 @@ constexpr auto kTransactionsLimit = 100;
|
||||
.bareGiftStickerId = giftStickerId,
|
||||
.bareActorId = saveActorId ? barePeerId : uint64(0),
|
||||
.uniqueGift = parsedGift ? parsedGift->unique : nullptr,
|
||||
.starrefAmount = starrefAmount,
|
||||
.starrefCommission = starrefCommission,
|
||||
.starrefRecipientId = starrefBarePeerId,
|
||||
.starrefAmount = paidMessagesCount ? StarsAmount() : starrefAmount,
|
||||
.starrefCommission = paidMessagesCount ? 0 : starrefCommission,
|
||||
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
|
||||
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
|
||||
return Data::CreditsHistoryEntry::PeerType::Peer;
|
||||
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
|
||||
@@ -138,9 +144,15 @@ constexpr auto kTransactionsLimit = 100;
|
||||
? base::unixtime::parse(tl.data().vtransaction_date()->v)
|
||||
: QDateTime(),
|
||||
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
|
||||
.paidMessagesCount = paidMessagesCount,
|
||||
.paidMessagesAmount = (paidMessagesCount
|
||||
? starrefAmount
|
||||
: StarsAmount()),
|
||||
.paidMessagesCommission = paidMessagesCount ? starrefCommission : 0,
|
||||
.starsConverted = int(nonUniqueGift
|
||||
? nonUniqueGift->vconvert_stars().v
|
||||
: 0),
|
||||
.premiumMonthsForStars = premiumMonthsForStars,
|
||||
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
|
||||
.converted = stargift && incoming,
|
||||
.stargift = stargift.has_value(),
|
||||
@@ -513,4 +525,12 @@ void EditCreditsSubscription(
|
||||
)).done(done).fail([=](const MTP::Error &e) { fail(e.type()); }).send();
|
||||
}
|
||||
|
||||
MTPInputSavedStarGift InputSavedStarGiftId(const Data::SavedStarGiftId &id) {
|
||||
return id.isUser()
|
||||
? MTP_inputSavedStarGiftUser(MTP_int(id.userMessageId().bare))
|
||||
: MTP_inputSavedStarGiftChat(
|
||||
id.chat()->input,
|
||||
MTP_long(id.chatSavedId()));
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_credits_earn.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Data {
|
||||
class SavedStarGiftId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -116,4 +120,7 @@ void EditCreditsSubscription(
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail);
|
||||
|
||||
[[nodiscard]] MTPInputSavedStarGift InputSavedStarGiftId(
|
||||
const Data::SavedStarGiftId &id);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -276,14 +276,22 @@ mtpRequestId EditTextMessage(
|
||||
takeFileReference = [=] { return photo->fileReference(); };
|
||||
} else if (const auto document = media->document()) {
|
||||
using Flag = MTPDinputMediaDocument::Flag;
|
||||
const auto videoCover = media->videoCover();
|
||||
const auto videoTimestamp = media->videoTimestamp();
|
||||
const auto flags = Flag()
|
||||
| (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
|
||||
| (spoilered ? Flag::f_spoiler : Flag());
|
||||
| (spoilered ? Flag::f_spoiler : Flag())
|
||||
| (videoTimestamp ? Flag::f_video_timestamp : Flag())
|
||||
| (videoCover ? Flag::f_video_cover : Flag());
|
||||
takeInputMedia = [=] {
|
||||
return MTP_inputMediaDocument(
|
||||
MTP_flags(flags),
|
||||
document->mtpInput(),
|
||||
(videoCover
|
||||
? videoCover->mtpInput()
|
||||
: MTPInputPhoto()),
|
||||
MTP_int(media->ttlSeconds()),
|
||||
MTP_int(videoTimestamp),
|
||||
MTPstring()); // query
|
||||
};
|
||||
takeFileReference = [=] { return document->fileReference(); };
|
||||
|
||||
@@ -13,6 +13,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Api {
|
||||
|
||||
PeerId ParsePaidReactionShownPeer(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPPaidReactionPrivacy &value) {
|
||||
return value.match([&](const MTPDpaidReactionPrivacyDefault &) {
|
||||
return session->userPeerId();
|
||||
}, [](const MTPDpaidReactionPrivacyAnonymous &) {
|
||||
return PeerId();
|
||||
}, [&](const MTPDpaidReactionPrivacyPeer &data) {
|
||||
return data.vpeer().match([&](const MTPDinputPeerSelf &) {
|
||||
return session->userPeerId();
|
||||
}, [](const MTPDinputPeerUser &data) {
|
||||
return peerFromUser(data.vuser_id());
|
||||
}, [](const MTPDinputPeerChat &data) {
|
||||
return peerFromChat(data.vchat_id());
|
||||
}, [](const MTPDinputPeerChannel &data) {
|
||||
return peerFromChannel(data.vchannel_id());
|
||||
}, [](const MTPDinputPeerUserFromMessage &data) -> PeerId {
|
||||
Unexpected("From message peer in ParsePaidReactionShownPeer.");
|
||||
}, [](const MTPDinputPeerChannelFromMessage &data) -> PeerId {
|
||||
Unexpected("From message peer in ParsePaidReactionShownPeer.");
|
||||
}, [](const MTPDinputPeerEmpty &) -> PeerId {
|
||||
Unexpected("Empty peer in ParsePaidReactionShownPeer.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
GlobalPrivacy::GlobalPrivacy(not_null<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
, _api(&api->instance()) {
|
||||
@@ -88,7 +114,8 @@ void GlobalPrivacy::updateHideReadTime(bool hide) {
|
||||
archiveAndMuteCurrent(),
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hide,
|
||||
newRequirePremiumCurrent());
|
||||
newRequirePremiumCurrent(),
|
||||
newChargeStarsCurrent());
|
||||
}
|
||||
|
||||
bool GlobalPrivacy::hideReadTimeCurrent() const {
|
||||
@@ -99,14 +126,6 @@ rpl::producer<bool> GlobalPrivacy::hideReadTime() const {
|
||||
return _hideReadTime.value();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateNewRequirePremium(bool value) {
|
||||
update(
|
||||
archiveAndMuteCurrent(),
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hideReadTimeCurrent(),
|
||||
value);
|
||||
}
|
||||
|
||||
bool GlobalPrivacy::newRequirePremiumCurrent() const {
|
||||
return _newRequirePremium.current();
|
||||
}
|
||||
@@ -115,27 +134,46 @@ rpl::producer<bool> GlobalPrivacy::newRequirePremium() const {
|
||||
return _newRequirePremium.value();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::loadPaidReactionAnonymous() {
|
||||
if (_paidReactionAnonymousLoaded) {
|
||||
int GlobalPrivacy::newChargeStarsCurrent() const {
|
||||
return _newChargeStars.current();
|
||||
}
|
||||
|
||||
rpl::producer<int> GlobalPrivacy::newChargeStars() const {
|
||||
return _newChargeStars.value();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateMessagesPrivacy(
|
||||
bool requirePremium,
|
||||
int chargeStars) {
|
||||
update(
|
||||
archiveAndMuteCurrent(),
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hideReadTimeCurrent(),
|
||||
requirePremium,
|
||||
chargeStars);
|
||||
}
|
||||
|
||||
void GlobalPrivacy::loadPaidReactionShownPeer() {
|
||||
if (_paidReactionShownPeerLoaded) {
|
||||
return;
|
||||
}
|
||||
_paidReactionAnonymousLoaded = true;
|
||||
_paidReactionShownPeerLoaded = true;
|
||||
_api.request(MTPmessages_GetPaidReactionPrivacy(
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_session->api().applyUpdates(result);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updatePaidReactionAnonymous(bool value) {
|
||||
_paidReactionAnonymous = value;
|
||||
void GlobalPrivacy::updatePaidReactionShownPeer(PeerId shownPeer) {
|
||||
_paidReactionShownPeer = shownPeer;
|
||||
}
|
||||
|
||||
bool GlobalPrivacy::paidReactionAnonymousCurrent() const {
|
||||
return _paidReactionAnonymous.current();
|
||||
PeerId GlobalPrivacy::paidReactionShownPeerCurrent() const {
|
||||
return _paidReactionShownPeer.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GlobalPrivacy::paidReactionAnonymous() const {
|
||||
return _paidReactionAnonymous.value();
|
||||
rpl::producer<PeerId> GlobalPrivacy::paidReactionShownPeer() const {
|
||||
return _paidReactionShownPeer.value();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateArchiveAndMute(bool value) {
|
||||
@@ -143,7 +181,8 @@ void GlobalPrivacy::updateArchiveAndMute(bool value) {
|
||||
value,
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hideReadTimeCurrent(),
|
||||
newRequirePremiumCurrent());
|
||||
newRequirePremiumCurrent(),
|
||||
newChargeStarsCurrent());
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateUnarchiveOnNewMessage(
|
||||
@@ -152,14 +191,16 @@ void GlobalPrivacy::updateUnarchiveOnNewMessage(
|
||||
archiveAndMuteCurrent(),
|
||||
value,
|
||||
hideReadTimeCurrent(),
|
||||
newRequirePremiumCurrent());
|
||||
newRequirePremiumCurrent(),
|
||||
newChargeStarsCurrent());
|
||||
}
|
||||
|
||||
void GlobalPrivacy::update(
|
||||
bool archiveAndMute,
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage,
|
||||
bool hideReadTime,
|
||||
bool newRequirePremium) {
|
||||
bool newRequirePremium,
|
||||
int newChargeStars) {
|
||||
using Flag = MTPDglobalPrivacySettings::Flag;
|
||||
|
||||
_api.request(_requestId).cancel();
|
||||
@@ -178,35 +219,44 @@ void GlobalPrivacy::update(
|
||||
| (hideReadTime ? Flag::f_hide_read_marks : Flag())
|
||||
| ((newRequirePremium && newRequirePremiumAllowed)
|
||||
? Flag::f_new_noncontact_peers_require_premium
|
||||
: Flag());
|
||||
: Flag())
|
||||
| Flag::f_noncontact_peers_paid_stars;
|
||||
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(
|
||||
MTP_globalPrivacySettings(MTP_flags(flags))
|
||||
MTP_globalPrivacySettings(
|
||||
MTP_flags(flags),
|
||||
MTP_long(newChargeStars))
|
||||
)).done([=](const MTPGlobalPrivacySettings &result) {
|
||||
_requestId = 0;
|
||||
apply(result);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
||||
update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {});
|
||||
update(
|
||||
archiveAndMute,
|
||||
unarchiveOnNewMessage,
|
||||
hideReadTime,
|
||||
false,
|
||||
0);
|
||||
}
|
||||
}).send();
|
||||
_archiveAndMute = archiveAndMute;
|
||||
_unarchiveOnNewMessage = unarchiveOnNewMessage;
|
||||
_hideReadTime = hideReadTime;
|
||||
_newRequirePremium = newRequirePremium;
|
||||
_newChargeStars = newChargeStars;
|
||||
}
|
||||
|
||||
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
|
||||
data.match([&](const MTPDglobalPrivacySettings &data) {
|
||||
_archiveAndMute = data.is_archive_and_mute_new_noncontact_peers();
|
||||
_unarchiveOnNewMessage = data.is_keep_archived_unmuted()
|
||||
? UnarchiveOnNewMessage::None
|
||||
: data.is_keep_archived_folders()
|
||||
? UnarchiveOnNewMessage::NotInFoldersUnmuted
|
||||
: UnarchiveOnNewMessage::AnyUnmuted;
|
||||
_hideReadTime = data.is_hide_read_marks();
|
||||
_newRequirePremium = data.is_new_noncontact_peers_require_premium();
|
||||
});
|
||||
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &settings) {
|
||||
const auto &data = settings.data();
|
||||
_archiveAndMute = data.is_archive_and_mute_new_noncontact_peers();
|
||||
_unarchiveOnNewMessage = data.is_keep_archived_unmuted()
|
||||
? UnarchiveOnNewMessage::None
|
||||
: data.is_keep_archived_folders()
|
||||
? UnarchiveOnNewMessage::NotInFoldersUnmuted
|
||||
: UnarchiveOnNewMessage::AnyUnmuted;
|
||||
_hideReadTime = data.is_hide_read_marks();
|
||||
_newRequirePremium = data.is_new_noncontact_peers_require_premium();
|
||||
_newChargeStars = data.vnoncontact_peers_paid_stars().value_or_empty();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -23,6 +23,10 @@ enum class UnarchiveOnNewMessage {
|
||||
AnyUnmuted,
|
||||
};
|
||||
|
||||
[[nodiscard]] PeerId ParsePaidReactionShownPeer(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPPaidReactionPrivacy &value);
|
||||
|
||||
class GlobalPrivacy final {
|
||||
public:
|
||||
explicit GlobalPrivacy(not_null<ApiWrap*> api);
|
||||
@@ -45,23 +49,28 @@ public:
|
||||
[[nodiscard]] bool hideReadTimeCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> hideReadTime() const;
|
||||
|
||||
void updateNewRequirePremium(bool value);
|
||||
[[nodiscard]] bool newRequirePremiumCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
|
||||
|
||||
void loadPaidReactionAnonymous();
|
||||
void updatePaidReactionAnonymous(bool value);
|
||||
[[nodiscard]] bool paidReactionAnonymousCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> paidReactionAnonymous() const;
|
||||
[[nodiscard]] int newChargeStarsCurrent() const;
|
||||
[[nodiscard]] rpl::producer<int> newChargeStars() const;
|
||||
|
||||
void updateMessagesPrivacy(bool requirePremium, int chargeStars);
|
||||
|
||||
void loadPaidReactionShownPeer();
|
||||
void updatePaidReactionShownPeer(PeerId shownPeer);
|
||||
[[nodiscard]] PeerId paidReactionShownPeerCurrent() const;
|
||||
[[nodiscard]] rpl::producer<PeerId> paidReactionShownPeer() const;
|
||||
|
||||
private:
|
||||
void apply(const MTPGlobalPrivacySettings &data);
|
||||
void apply(const MTPGlobalPrivacySettings &settings);
|
||||
|
||||
void update(
|
||||
bool archiveAndMute,
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage,
|
||||
bool hideReadTime,
|
||||
bool newRequirePremium);
|
||||
bool newRequirePremium,
|
||||
int newChargeStars);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
@@ -72,9 +81,10 @@ private:
|
||||
rpl::variable<bool> _showArchiveAndMute = false;
|
||||
rpl::variable<bool> _hideReadTime = false;
|
||||
rpl::variable<bool> _newRequirePremium = false;
|
||||
rpl::variable<bool> _paidReactionAnonymous = false;
|
||||
rpl::variable<int> _newChargeStars = 0;
|
||||
rpl::variable<PeerId> _paidReactionShownPeer = false;
|
||||
std::vector<Fn<void()>> _callbacks;
|
||||
bool _paidReactionAnonymousLoaded = false;
|
||||
bool _paidReactionShownPeerLoaded = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ MTPInputMedia PrepareUploadedDocument(
|
||||
| (info.thumb ? Flag::f_thumb : Flag())
|
||||
| (item->groupId() ? Flag::f_nosound_video : Flag())
|
||||
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag());
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
|
||||
| (info.videoCover ? Flag::f_video_cover : Flag());
|
||||
const auto document = item->media()->document();
|
||||
return MTP_inputMediaUploadedDocument(
|
||||
MTP_flags(flags),
|
||||
@@ -121,6 +122,8 @@ MTPInputMedia PrepareUploadedDocument(
|
||||
ComposeSendingDocumentAttributes(document),
|
||||
MTP_vector<MTPInputDocument>(
|
||||
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
|
||||
info.videoCover.value_or(MTPInputPhoto()),
|
||||
MTP_int(0), // video_timestamp
|
||||
MTP_int(ttlSeconds));
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ void MessagesSearch::searchRequest() {
|
||||
_requestId = _history->session().api().request(MTPmessages_Search(
|
||||
MTP_flags((fromPeer ? Flag::f_from_id : Flag())
|
||||
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
||||
| (_request.topMsgId ? Flag::f_top_msg_id : Flag())
|
||||
| (_request.tags.empty() ? Flag() : Flag::f_saved_reaction)),
|
||||
_history->peer->input,
|
||||
MTP_string(_request.query),
|
||||
@@ -111,7 +112,7 @@ void MessagesSearch::searchRequest() {
|
||||
MTP_vector_from_range(_request.tags | ranges::views::transform(
|
||||
Data::ReactionToMTP
|
||||
)),
|
||||
MTPint(), // top_msg_id
|
||||
MTP_int(_request.topMsgId), // top_msg_id
|
||||
MTP_inputMessagesFilterEmpty(),
|
||||
MTP_int(0), // min_date
|
||||
MTP_int(0), // max_date
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
QString query;
|
||||
PeerData *from = nullptr;
|
||||
std::vector<Data::ReactionId> tags;
|
||||
MsgId topMsgId;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Request &,
|
||||
|
||||
@@ -64,6 +64,10 @@ MessagesSearchMerged::MessagesSearchMerged(not_null<History*> history)
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesSearchMerged::disableMigrated() {
|
||||
_migratedSearch = std::nullopt;
|
||||
}
|
||||
|
||||
void MessagesSearchMerged::addFound(const FoundMessages &data) {
|
||||
for (const auto &message : data.messages) {
|
||||
_concatedFound.messages.push_back(message);
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
void clear();
|
||||
void search(const Request &search);
|
||||
void searchMore();
|
||||
void disableMigrated();
|
||||
|
||||
[[nodiscard]] const FoundMessages &messages() const;
|
||||
[[nodiscard]] const Request &request() const;
|
||||
|
||||
@@ -42,7 +42,7 @@ Polls::Polls(not_null<ApiWrap*> api)
|
||||
|
||||
void Polls::create(
|
||||
const PollData &data,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
Fn<void()> done,
|
||||
Fn<void()> fail) {
|
||||
_session->api().sendAction(action);
|
||||
@@ -64,6 +64,9 @@ void Polls::create(
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
}
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
@@ -76,6 +79,10 @@ void Polls::create(
|
||||
if (action.options.effectId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||
}
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||
}
|
||||
const auto sendAs = action.options.sendAs;
|
||||
if (sendAs) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||
@@ -98,7 +105,8 @@ void Polls::create(
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
|
||||
void create(
|
||||
const PollData &data,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
Fn<void()> done,
|
||||
Fn<void()> fail);
|
||||
void sendVotes(
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_text_entities.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/random.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h"
|
||||
@@ -377,15 +378,15 @@ const Data::PremiumSubscriptionOptions &Premium::subscriptionOptions() const {
|
||||
return _subscriptionOptions;
|
||||
}
|
||||
|
||||
rpl::producer<> Premium::somePremiumRequiredResolved() const {
|
||||
return _somePremiumRequiredResolved.events();
|
||||
rpl::producer<> Premium::someMessageMoneyRestrictionsResolved() const {
|
||||
return _someMessageMoneyRestrictionsResolved.events();
|
||||
}
|
||||
|
||||
void Premium::resolvePremiumRequired(not_null<UserData*> user) {
|
||||
_resolvePremiumRequiredUsers.emplace(user);
|
||||
if (!_premiumRequiredRequestScheduled
|
||||
&& _resolvePremiumRequestedUsers.empty()) {
|
||||
_premiumRequiredRequestScheduled = true;
|
||||
void Premium::resolveMessageMoneyRestrictions(not_null<UserData*> user) {
|
||||
_resolveMessageMoneyRequiredUsers.emplace(user);
|
||||
if (!_messageMoneyRequestScheduled
|
||||
&& _resolveMessageMoneyRequestedUsers.empty()) {
|
||||
_messageMoneyRequestScheduled = true;
|
||||
crl::on_main(_session, [=] {
|
||||
requestPremiumRequiredSlice();
|
||||
});
|
||||
@@ -393,50 +394,65 @@ void Premium::resolvePremiumRequired(not_null<UserData*> user) {
|
||||
}
|
||||
|
||||
void Premium::requestPremiumRequiredSlice() {
|
||||
_premiumRequiredRequestScheduled = false;
|
||||
if (!_resolvePremiumRequestedUsers.empty()
|
||||
|| _resolvePremiumRequiredUsers.empty()) {
|
||||
_messageMoneyRequestScheduled = false;
|
||||
if (!_resolveMessageMoneyRequestedUsers.empty()
|
||||
|| _resolveMessageMoneyRequiredUsers.empty()) {
|
||||
return;
|
||||
}
|
||||
constexpr auto kPerRequest = 100;
|
||||
auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers
|
||||
auto users = MTP_vector_from_range(_resolveMessageMoneyRequiredUsers
|
||||
| ranges::views::transform(&UserData::inputUser));
|
||||
if (users.v.size() > kPerRequest) {
|
||||
auto shortened = users.v;
|
||||
shortened.resize(kPerRequest);
|
||||
users = MTP_vector<MTPInputUser>(std::move(shortened));
|
||||
const auto from = begin(_resolvePremiumRequiredUsers);
|
||||
_resolvePremiumRequestedUsers = { from, from + kPerRequest };
|
||||
_resolvePremiumRequiredUsers.erase(from, from + kPerRequest);
|
||||
const auto from = begin(_resolveMessageMoneyRequiredUsers);
|
||||
_resolveMessageMoneyRequestedUsers = { from, from + kPerRequest };
|
||||
_resolveMessageMoneyRequiredUsers.erase(from, from + kPerRequest);
|
||||
} else {
|
||||
_resolvePremiumRequestedUsers
|
||||
= base::take(_resolvePremiumRequiredUsers);
|
||||
_resolveMessageMoneyRequestedUsers
|
||||
= base::take(_resolveMessageMoneyRequiredUsers);
|
||||
}
|
||||
const auto finish = [=](const QVector<MTPBool> &list) {
|
||||
constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
|
||||
constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
|
||||
constexpr auto mask = me | known;
|
||||
const auto finish = [=](const QVector<MTPRequirementToContact> &list) {
|
||||
|
||||
auto index = 0;
|
||||
for (const auto &user : base::take(_resolvePremiumRequestedUsers)) {
|
||||
const auto require = (index < list.size())
|
||||
&& mtpIsTrue(list[index++]);
|
||||
user->setFlags((user->flags() & ~mask)
|
||||
| known
|
||||
| (require ? me : UserDataFlag()));
|
||||
for (const auto &user : base::take(_resolveMessageMoneyRequestedUsers)) {
|
||||
const auto set = [&](bool requirePremium, int stars) {
|
||||
using Flag = UserDataFlag;
|
||||
constexpr auto me = Flag::RequiresPremiumToWrite;
|
||||
constexpr auto known = Flag::MessageMoneyRestrictionsKnown;
|
||||
constexpr auto hasPrem = Flag::HasRequirePremiumToWrite;
|
||||
constexpr auto hasStars = Flag::HasStarsPerMessage;
|
||||
user->setStarsPerMessage(stars);
|
||||
user->setFlags((user->flags() & ~(me | hasPrem | hasStars))
|
||||
| known
|
||||
| (requirePremium ? (me | hasPrem) : Flag())
|
||||
| (stars ? hasStars : Flag()));
|
||||
};
|
||||
if (index >= list.size()) {
|
||||
set(false, 0);
|
||||
continue;
|
||||
}
|
||||
list[index++].match([&](const MTPDrequirementToContactEmpty &) {
|
||||
set(false, 0);
|
||||
}, [&](const MTPDrequirementToContactPremium &) {
|
||||
set(true, 0);
|
||||
}, [&](const MTPDrequirementToContactPaidMessages &data) {
|
||||
set(false, data.vstars_amount().v);
|
||||
});
|
||||
}
|
||||
if (!_premiumRequiredRequestScheduled
|
||||
&& !_resolvePremiumRequiredUsers.empty()) {
|
||||
_premiumRequiredRequestScheduled = true;
|
||||
if (!_messageMoneyRequestScheduled
|
||||
&& !_resolveMessageMoneyRequiredUsers.empty()) {
|
||||
_messageMoneyRequestScheduled = true;
|
||||
crl::on_main(_session, [=] {
|
||||
requestPremiumRequiredSlice();
|
||||
});
|
||||
}
|
||||
_somePremiumRequiredResolved.fire({});
|
||||
_someMessageMoneyRestrictionsResolved.fire({});
|
||||
};
|
||||
_session->api().request(
|
||||
MTPusers_GetIsPremiumRequiredToContact(std::move(users))
|
||||
).done([=](const MTPVector<MTPBool> &result) {
|
||||
MTPusers_GetRequirementsToContact(std::move(users))
|
||||
).done([=](const MTPVector<MTPRequirementToContact> &result) {
|
||||
finish(result.v);
|
||||
}).fail([=] {
|
||||
finish({});
|
||||
@@ -463,10 +479,14 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
|
||||
for (const auto &tlOption : result.v) {
|
||||
const auto &data = tlOption.data();
|
||||
tlMapOptions[data.vusers().v].push_back(tlOption);
|
||||
if (qs(data.vcurrency()) == Ui::kCreditsCurrency) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto token = Token{ data.vusers().v, data.vmonths().v };
|
||||
_stores[token] = Store{
|
||||
.amount = data.vamount().v,
|
||||
.currency = qs(data.vcurrency()),
|
||||
.product = qs(data.vstore_product().value_or_empty()),
|
||||
.quantity = data.vstore_quantity().value_or_empty(),
|
||||
};
|
||||
@@ -475,14 +495,14 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
|
||||
}
|
||||
}
|
||||
for (const auto &[amount, tlOptions] : tlMapOptions) {
|
||||
if (amount == 1 && _optionsForOnePerson.currency.isEmpty()) {
|
||||
_optionsForOnePerson.currency = qs(
|
||||
tlOptions.front().data().vcurrency());
|
||||
if (amount == 1 && _optionsForOnePerson.currencies.empty()) {
|
||||
for (const auto &option : tlOptions) {
|
||||
_optionsForOnePerson.months.push_back(
|
||||
option.data().vmonths().v);
|
||||
_optionsForOnePerson.totalCosts.push_back(
|
||||
option.data().vamount().v);
|
||||
_optionsForOnePerson.currencies.push_back(
|
||||
qs(option.data().vcurrency()));
|
||||
}
|
||||
}
|
||||
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
|
||||
@@ -509,7 +529,7 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::applyPrepaid(
|
||||
_api.request(MTPpayments_LaunchPrepaidGiveaway(
|
||||
_peer->input,
|
||||
MTP_long(prepaidId),
|
||||
invoice.creditsAmount
|
||||
invoice.giveawayCredits
|
||||
? Payments::InvoiceCreditsGiveawayToTL(invoice)
|
||||
: Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
@@ -540,7 +560,7 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
|
||||
const auto token = Token{ users, months };
|
||||
const auto &store = _stores[token];
|
||||
return Payments::InvoicePremiumGiftCode{
|
||||
.currency = _optionsForOnePerson.currency,
|
||||
.currency = store.currency,
|
||||
.storeProduct = store.product,
|
||||
.randomId = randomId,
|
||||
.amount = store.amount,
|
||||
@@ -553,14 +573,15 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
|
||||
std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
|
||||
auto result = std::vector<GiftOptionData>();
|
||||
|
||||
if (!_optionsForOnePerson.currency.isEmpty()) {
|
||||
if (!_optionsForOnePerson.currencies.empty()) {
|
||||
const auto count = int(_optionsForOnePerson.months.size());
|
||||
result.reserve(count);
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
Assert(i < _optionsForOnePerson.totalCosts.size());
|
||||
Assert(i < _optionsForOnePerson.currencies.size());
|
||||
result.push_back({
|
||||
.cost = _optionsForOnePerson.totalCosts[i],
|
||||
.currency = _optionsForOnePerson.currency,
|
||||
.currency = _optionsForOnePerson.currencies[i],
|
||||
.months = _optionsForOnePerson.months[i],
|
||||
});
|
||||
}
|
||||
@@ -581,7 +602,7 @@ Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
|
||||
MTP_int(_optionsForOnePerson.months[i]),
|
||||
MTPstring(),
|
||||
MTPint(),
|
||||
MTP_string(_optionsForOnePerson.currency),
|
||||
MTP_string(_optionsForOnePerson.currencies[i]),
|
||||
MTP_long(_optionsForOnePerson.totalCosts[i] * amount)));
|
||||
}
|
||||
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
|
||||
@@ -694,28 +715,38 @@ rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
|
||||
};
|
||||
}
|
||||
|
||||
RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||
MessageMoneyRestriction ResolveMessageMoneyRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
History *maybeHistory) {
|
||||
const auto user = peer->asUser();
|
||||
if (!user
|
||||
|| !user->someRequirePremiumToWrite()
|
||||
|| user->session().premium()) {
|
||||
return RequirePremiumState::No;
|
||||
} else if (user->requirePremiumToWriteKnown()) {
|
||||
return user->meRequiresPremiumToWrite()
|
||||
? RequirePremiumState::Yes
|
||||
: RequirePremiumState::No;
|
||||
} else if (user->flags() & UserDataFlag::MutualContact) {
|
||||
return RequirePremiumState::No;
|
||||
} else if (!maybeHistory) {
|
||||
return RequirePremiumState::Unknown;
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
return {
|
||||
.starsPerMessage = channel->starsPerMessageChecked(),
|
||||
.known = true,
|
||||
};
|
||||
}
|
||||
const auto user = peer->asUser();
|
||||
if (!user) {
|
||||
return { .known = true };
|
||||
} else if (user->messageMoneyRestrictionsKnown()) {
|
||||
return {
|
||||
.starsPerMessage = user->starsPerMessageChecked(),
|
||||
.premiumRequired = (user->requiresPremiumToWrite()
|
||||
&& !user->session().premium()),
|
||||
.known = true,
|
||||
};
|
||||
} else if (user->hasStarsPerMessage()) {
|
||||
return {};
|
||||
} else if (!user->hasRequirePremiumToWrite()) {
|
||||
return { .known = true };
|
||||
} else if (user->flags() & UserDataFlag::MutualContact) {
|
||||
return { .known = true };
|
||||
} else if (!maybeHistory) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto update = [&](bool require) {
|
||||
using Flag = UserDataFlag;
|
||||
constexpr auto known = Flag::RequirePremiumToWriteKnown;
|
||||
constexpr auto me = Flag::MeRequiresPremiumToWrite;
|
||||
constexpr auto known = Flag::MessageMoneyRestrictionsKnown;
|
||||
constexpr auto me = Flag::RequiresPremiumToWrite;
|
||||
user->setFlags((user->flags() & ~me)
|
||||
| known
|
||||
| (require ? me : Flag()));
|
||||
@@ -727,16 +758,19 @@ RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||
const auto item = view->data();
|
||||
if (!item->out() && !item->isService()) {
|
||||
update(false);
|
||||
return RequirePremiumState::No;
|
||||
return { .known = true };
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user->isContact() // Here we know, that we're not in his contacts.
|
||||
&& maybeHistory->loadedAtTop() // And no incoming messages.
|
||||
&& maybeHistory->loadedAtBottom()) {
|
||||
update(true);
|
||||
return {
|
||||
.premiumRequired = !user->session().premium(),
|
||||
.known = true,
|
||||
};
|
||||
}
|
||||
return RequirePremiumState::Unknown;
|
||||
return {};
|
||||
}
|
||||
|
||||
rpl::producer<DocumentData*> RandomHelloStickerValue(
|
||||
@@ -805,8 +839,14 @@ std::optional<Data::StarGift> FromTL(
|
||||
auto result = Data::StarGift{
|
||||
.id = uint64(data.vid().v),
|
||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||
.id = data.vid().v,
|
||||
.slug = qs(data.vslug()),
|
||||
.title = qs(data.vtitle()),
|
||||
.ownerId = peerFromUser(UserId(data.vowner_id().v)),
|
||||
.ownerAddress = qs(data.vowner_address().value_or_empty()),
|
||||
.ownerName = qs(data.vowner_name().value_or_empty()),
|
||||
.ownerId = (data.vowner_id()
|
||||
? peerFromMTP(*data.vowner_id())
|
||||
: PeerId()),
|
||||
.number = data.vnum().v,
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
@@ -829,9 +869,9 @@ std::optional<Data::StarGift> FromTL(
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<Data::UserStarGift> FromTL(
|
||||
not_null<UserData*> to,
|
||||
const MTPuserStarGift &gift) {
|
||||
std::optional<Data::SavedStarGift> FromTL(
|
||||
not_null<PeerData*> to,
|
||||
const MTPsavedStarGift &gift) {
|
||||
const auto session = &to->session();
|
||||
const auto &data = gift.data();
|
||||
auto parsed = FromTL(session, data.vgift());
|
||||
@@ -841,8 +881,12 @@ std::optional<Data::UserStarGift> FromTL(
|
||||
unique->starsForTransfer = data.vtransfer_stars().value_or(-1);
|
||||
unique->exportAt = data.vcan_export_at().value_or_empty();
|
||||
}
|
||||
return Data::UserStarGift{
|
||||
using Id = Data::SavedStarGiftId;
|
||||
return Data::SavedStarGift{
|
||||
.info = std::move(*parsed),
|
||||
.manageId = (to->isUser()
|
||||
? Id::User(data.vmsg_id().value_or_empty())
|
||||
: Id::Chat(to, data.vsaved_id().value_or_empty())),
|
||||
.message = (data.vmessage()
|
||||
? TextWithEntities{
|
||||
.text = qs(data.vmessage()->data().vtext()),
|
||||
@@ -855,12 +899,12 @@ std::optional<Data::UserStarGift> FromTL(
|
||||
.starsUpgradedBySender = int64(
|
||||
data.vupgrade_stars().value_or_empty()),
|
||||
.fromId = (data.vfrom_id()
|
||||
? peerFromUser(data.vfrom_id()->v)
|
||||
? peerFromMTP(*data.vfrom_id())
|
||||
: PeerId()),
|
||||
.messageId = data.vmsg_id().value_or_empty(),
|
||||
.date = data.vdate().v,
|
||||
.upgradable = data.is_can_upgrade(),
|
||||
.anonymous = data.is_name_hidden(),
|
||||
.pinned = data.is_pinned_to_top(),
|
||||
.hidden = data.is_unsaved(),
|
||||
.mine = to->isSelf(),
|
||||
};
|
||||
@@ -910,11 +954,9 @@ Data::UniqueGiftOriginalDetails FromTL(
|
||||
auto result = Data::UniqueGiftOriginalDetails();
|
||||
result.date = data.vdate().v;
|
||||
result.senderId = data.vsender_id()
|
||||
? peerFromUser(
|
||||
UserId(data.vsender_id().value_or_empty()))
|
||||
? peerFromMTP(*data.vsender_id())
|
||||
: PeerId();
|
||||
result.recipientId = peerFromUser(
|
||||
UserId(data.vrecipient_id().v));
|
||||
result.recipientId = peerFromMTP(data.vrecipient_id());
|
||||
result.message = data.vmessage()
|
||||
? ParseTextWithEntities(session, *data.vmessage())
|
||||
: TextWithEntities();
|
||||
|
||||
@@ -116,8 +116,9 @@ public:
|
||||
[[nodiscard]] auto subscriptionOptions() const
|
||||
-> const Data::PremiumSubscriptionOptions &;
|
||||
|
||||
[[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const;
|
||||
void resolvePremiumRequired(not_null<UserData*> user);
|
||||
[[nodiscard]] auto someMessageMoneyRestrictionsResolved() const
|
||||
-> rpl::producer<>;
|
||||
void resolveMessageMoneyRestrictions(not_null<UserData*> user);
|
||||
|
||||
private:
|
||||
void reloadPromo();
|
||||
@@ -166,10 +167,10 @@ private:
|
||||
|
||||
Data::PremiumSubscriptionOptions _subscriptionOptions;
|
||||
|
||||
rpl::event_stream<> _somePremiumRequiredResolved;
|
||||
base::flat_set<not_null<UserData*>> _resolvePremiumRequiredUsers;
|
||||
base::flat_set<not_null<UserData*>> _resolvePremiumRequestedUsers;
|
||||
bool _premiumRequiredRequestScheduled = false;
|
||||
rpl::event_stream<> _someMessageMoneyRestrictionsResolved;
|
||||
base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequiredUsers;
|
||||
base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequestedUsers;
|
||||
bool _messageMoneyRequestScheduled = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -208,6 +209,7 @@ private:
|
||||
};
|
||||
struct Store final {
|
||||
uint64 amount = 0;
|
||||
QString currency;
|
||||
QString product;
|
||||
int quantity = 0;
|
||||
};
|
||||
@@ -218,7 +220,7 @@ private:
|
||||
struct {
|
||||
std::vector<int> months;
|
||||
std::vector<int64> totalCosts;
|
||||
QString currency;
|
||||
std::vector<QString> currencies;
|
||||
} _optionsForOnePerson;
|
||||
|
||||
std::vector<int> _availablePresets;
|
||||
@@ -244,12 +246,20 @@ private:
|
||||
|
||||
};
|
||||
|
||||
enum class RequirePremiumState {
|
||||
Unknown,
|
||||
Yes,
|
||||
No,
|
||||
struct MessageMoneyRestriction {
|
||||
int starsPerMessage = 0;
|
||||
bool premiumRequired = false;
|
||||
bool known = false;
|
||||
|
||||
explicit operator bool() const {
|
||||
return starsPerMessage != 0 || premiumRequired;
|
||||
}
|
||||
|
||||
friend inline bool operator==(
|
||||
const MessageMoneyRestriction &,
|
||||
const MessageMoneyRestriction &) = default;
|
||||
};
|
||||
[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||
[[nodiscard]] MessageMoneyRestriction ResolveMessageMoneyRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
History *maybeHistory);
|
||||
|
||||
@@ -259,9 +269,9 @@ enum class RequirePremiumState {
|
||||
[[nodiscard]] std::optional<Data::StarGift> FromTL(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPstarGift &gift);
|
||||
[[nodiscard]] std::optional<Data::UserStarGift> FromTL(
|
||||
not_null<UserData*> to,
|
||||
const MTPuserStarGift &gift);
|
||||
[[nodiscard]] std::optional<Data::SavedStarGift> FromTL(
|
||||
not_null<PeerData*> to,
|
||||
const MTPsavedStarGift &gift);
|
||||
|
||||
[[nodiscard]] Data::UniqueGiftModel FromTL(
|
||||
not_null<Main::Session*> session,
|
||||
|
||||
@@ -26,7 +26,7 @@ Data::PremiumSubscriptionOption CreateSubscriptionOption(
|
||||
}();
|
||||
return {
|
||||
.duration = Ui::FormatTTL(months * 86400 * 31),
|
||||
.discount = discount
|
||||
.discount = (discount > 0)
|
||||
? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount)
|
||||
: QString(),
|
||||
.costPerMonth = Ui::FillAmountAndCurrency(
|
||||
|
||||
@@ -24,15 +24,26 @@ template<typename Option>
|
||||
if (tlOpts.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
auto monthlyAmountPerCurrency = base::flat_map<QString, int>();
|
||||
auto result = Data::PremiumSubscriptionOptions();
|
||||
const auto monthlyAmount = [&] {
|
||||
const auto monthlyAmount = [&](const QString ¤cy) -> int {
|
||||
const auto it = monthlyAmountPerCurrency.find(currency);
|
||||
if (it != end(monthlyAmountPerCurrency)) {
|
||||
return it->second;
|
||||
}
|
||||
const auto &min = ranges::min_element(
|
||||
tlOpts,
|
||||
ranges::less(),
|
||||
[](const Option &o) { return o.data().vamount().v; }
|
||||
[&](const Option &o) {
|
||||
return currency == qs(o.data().vcurrency())
|
||||
? o.data().vamount().v
|
||||
: std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
)->data();
|
||||
return min.vamount().v / float64(min.vmonths().v);
|
||||
}();
|
||||
const auto monthly = min.vamount().v / float64(min.vmonths().v);
|
||||
monthlyAmountPerCurrency.emplace(currency, monthly);
|
||||
return monthly;
|
||||
};
|
||||
result.reserve(tlOpts.size());
|
||||
for (const auto &tlOption : tlOpts) {
|
||||
const auto &option = tlOption.data();
|
||||
@@ -45,7 +56,7 @@ template<typename Option>
|
||||
const auto currency = qs(option.vcurrency());
|
||||
result.push_back(CreateSubscriptionOption(
|
||||
months,
|
||||
monthlyAmount,
|
||||
monthlyAmount(currency),
|
||||
amount,
|
||||
currency,
|
||||
botUrl));
|
||||
|
||||
@@ -95,7 +95,9 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? session->user()->name()
|
||||
: QString();
|
||||
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (action.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
@@ -111,6 +113,10 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||
}
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
histories.sendPreparedMessage(
|
||||
@@ -129,7 +135,8 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
api->sendMessageFail(error, peer, randomId);
|
||||
@@ -160,7 +167,7 @@ void SendExistingMedia(
|
||||
? (*localMessageId)
|
||||
: session->data().nextLocalMessageId());
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
const auto &action = message.action;
|
||||
auto &action = message.action;
|
||||
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
@@ -190,7 +197,9 @@ void SendExistingMedia(
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
|
||||
}
|
||||
const auto captionText = caption.text;
|
||||
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (action.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
@@ -206,6 +215,10 @@ void SendExistingMedia(
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
@@ -216,6 +229,7 @@ void SendExistingMedia(
|
||||
.replyTo = action.replyTo,
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = starsPaid,
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
.effectId = action.options.effectId,
|
||||
}, media, caption);
|
||||
@@ -240,7 +254,8 @@ void SendExistingMedia(
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (error.code() == 400
|
||||
@@ -272,7 +287,9 @@ void SendExistingDocument(
|
||||
return MTP_inputMediaDocument(
|
||||
MTP_flags(0),
|
||||
document->mtpInput(),
|
||||
MTPInputPhoto(), // video_cover
|
||||
MTPint(), // ttl_seconds
|
||||
MTPint(), // video_timestamp
|
||||
MTPstring()); // query
|
||||
};
|
||||
SendExistingMedia(
|
||||
@@ -339,7 +356,7 @@ bool SendDice(MessageToSend &message) {
|
||||
message.action.generateLocal = true;
|
||||
|
||||
|
||||
const auto &action = message.action;
|
||||
auto &action = message.action;
|
||||
api->sendAction(action);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
@@ -378,6 +395,13 @@ bool SendDice(MessageToSend &message) {
|
||||
flags |= MessageFlag::InvertMedia;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||
}
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
@@ -388,6 +412,7 @@ bool SendDice(MessageToSend &message) {
|
||||
.replyTo = action.replyTo,
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = starsPaid,
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
.effectId = action.options.effectId,
|
||||
}, TextWithEntities(), MTP_messageMediaDice(
|
||||
@@ -409,7 +434,8 @@ bool SendDice(MessageToSend &message) {
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
@@ -547,9 +573,12 @@ void SendConfirmedFile(
|
||||
using Flag = MTPDmessageMediaDocument::Flag;
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(Flag::f_document
|
||||
| (file->spoiler ? Flag::f_spoiler : Flag())),
|
||||
| (file->spoiler ? Flag::f_spoiler : Flag())
|
||||
| (file->videoCover ? Flag::f_video_cover : Flag())),
|
||||
file->document,
|
||||
MTPVector<MTPDocument>(), // alt_documents
|
||||
file->videoCover ? file->videoCover->photo : MTPPhoto(),
|
||||
MTPint(), // video_timestamp
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::Audio) {
|
||||
const auto ttlSeconds = file->to.options.ttlSeconds;
|
||||
@@ -557,9 +586,12 @@ void SendConfirmedFile(
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(Flag::f_document
|
||||
| Flag::f_voice
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())
|
||||
| (file->videoCover ? Flag::f_video_cover : Flag())),
|
||||
file->document,
|
||||
MTPVector<MTPDocument>(), // alt_documents
|
||||
file->videoCover ? file->videoCover->photo : MTPPhoto(),
|
||||
MTPint(), // video_timestamp
|
||||
MTP_int(ttlSeconds));
|
||||
} else if (file->type == SendMediaType::Round) {
|
||||
using Flag = MTPDmessageMediaDocument::Flag;
|
||||
@@ -571,6 +603,8 @@ void SendConfirmedFile(
|
||||
| (file->spoiler ? Flag::f_spoiler : Flag())),
|
||||
file->document,
|
||||
MTPVector<MTPDocument>(), // alt_documents
|
||||
MTPPhoto(), // video_cover
|
||||
MTPint(), // video_timestamp
|
||||
MTP_int(ttlSeconds));
|
||||
} else {
|
||||
Unexpected("Type in sendFilesConfirmed.");
|
||||
@@ -600,6 +634,9 @@ void SendConfirmedFile(
|
||||
.replyTo = file->to.replyTo,
|
||||
.date = NewMessageDate(file->to.options),
|
||||
.shortcutId = file->to.options.shortcutId,
|
||||
.starsPaid = std::min(
|
||||
history->peer->starsPerMessageChecked(),
|
||||
file->to.options.starsApproved),
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
.groupedId = groupId,
|
||||
.effectId = file->to.options.effectId,
|
||||
|
||||
@@ -1227,7 +1227,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
MTPint(), // quick_reply_shortcut_id
|
||||
MTPlong(), // effect
|
||||
MTPFactCheck(),
|
||||
MTPint()), // report_delivery_until_date
|
||||
MTPint(), // report_delivery_until_date
|
||||
MTPlong()), // paid_message_stars
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
@@ -1265,7 +1266,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
MTPint(), // quick_reply_shortcut_id
|
||||
MTPlong(), // effect
|
||||
MTPFactCheck(),
|
||||
MTPint()), // report_delivery_until_date
|
||||
MTPint(), // report_delivery_until_date
|
||||
MTPlong()), // paid_message_stars
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
@@ -2717,8 +2719,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updatePaidReactionPrivacy: {
|
||||
const auto &data = update.c_updatePaidReactionPrivacy();
|
||||
_session->api().globalPrivacy().updatePaidReactionAnonymous(
|
||||
mtpIsTrue(data.vprivate()));
|
||||
_session->api().globalPrivacy().updatePaidReactionShownPeer(
|
||||
Api::ParsePaidReactionShownPeer(_session, data.vprivate()));
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
@@ -210,6 +210,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
|
||||
case Key::About: return MTP_inputPrivacyKeyAbout();
|
||||
case Key::Birthday: return MTP_inputPrivacyKeyBirthday();
|
||||
case Key::GiftsAutoSave: return MTP_inputPrivacyKeyStarGiftsAutoSave();
|
||||
case Key::NoPaidMessages: return MTP_inputPrivacyKeyNoPaidMessages();
|
||||
}
|
||||
Unexpected("Key in Api::UserPrivacy::KetToTL.");
|
||||
}
|
||||
@@ -241,6 +242,8 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
|
||||
case mtpc_inputPrivacyKeyBirthday: return Key::Birthday;
|
||||
case mtpc_privacyKeyStarGiftsAutoSave:
|
||||
case mtpc_inputPrivacyKeyStarGiftsAutoSave: return Key::GiftsAutoSave;
|
||||
case mtpc_privacyKeyNoPaidMessages:
|
||||
case mtpc_inputPrivacyKeyNoPaidMessages: return Key::NoPaidMessages;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
About,
|
||||
Birthday,
|
||||
GiftsAutoSave,
|
||||
NoPaidMessages,
|
||||
};
|
||||
enum class Option {
|
||||
Everyone,
|
||||
|
||||
@@ -148,6 +148,16 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
||||
action.replaceMediaOf);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatVideoTimestamp(TimeId seconds) {
|
||||
const auto minutes = seconds / 60;
|
||||
const auto hours = minutes / 60;
|
||||
return hours
|
||||
? u"%1h%2m%3s"_q.arg(hours).arg(minutes % 60).arg(seconds % 60)
|
||||
: minutes
|
||||
? u"%1m%2s"_q.arg(minutes).arg(seconds % 60)
|
||||
: QString::number(seconds);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
@@ -536,6 +546,7 @@ void ApiWrap::sendMessageFail(
|
||||
uint64 randomId,
|
||||
FullMsgId itemId) {
|
||||
const auto show = ShowForPeer(peer);
|
||||
const auto paidStarsPrefix = u"ALLOW_PAYMENT_REQUIRED_"_q;
|
||||
if (show && error == u"PEER_FLOOD"_q) {
|
||||
show->showBox(
|
||||
Ui::MakeInformBox(
|
||||
@@ -590,6 +601,19 @@ void ApiWrap::sendMessageFail(
|
||||
if (show) {
|
||||
show->showToast(tr::lng_error_schedule_limit(tr::now));
|
||||
}
|
||||
} else if (error.startsWith(paidStarsPrefix)) {
|
||||
if (show) {
|
||||
show->showToast(
|
||||
u"Payment requirements changed. Please, try again."_q);
|
||||
}
|
||||
if (const auto stars = error.mid(paidStarsPrefix.size()).toInt()) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
user->setStarsPerMessage(stars);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
channel->setStarsPerMessage(stars);
|
||||
}
|
||||
}
|
||||
peer->updateFull();
|
||||
}
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
Assert(randomId != 0);
|
||||
@@ -740,7 +764,8 @@ void ApiWrap::finalizeMessageDataRequest(
|
||||
QString ApiWrap::exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext,
|
||||
bool forceNonPublicLink) {
|
||||
bool forceNonPublicLink,
|
||||
std::optional<TimeId> videoTimestamp) {
|
||||
Expects(item->history()->peer->isChannel());
|
||||
|
||||
const auto itemId = item->fullId();
|
||||
@@ -792,19 +817,6 @@ QString ApiWrap::exportDirectMessageLink(
|
||||
: linkThreadId
|
||||
? (QString::number(linkThreadId.bare) + '/' + post)
|
||||
: post);
|
||||
if (linkChannel->hasUsername()
|
||||
&& !forceNonPublicLink
|
||||
&& !linkChannel->isMegagroup()
|
||||
&& !linkCommentId
|
||||
&& !linkThreadId) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
return u"https://telesco.pe/"_q + query;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return session().createInternalLinkFull(query);
|
||||
};
|
||||
if (forceNonPublicLink) {
|
||||
@@ -826,7 +838,14 @@ QString ApiWrap::exportDirectMessageLink(
|
||||
_unlikelyMessageLinks.emplace_or_assign(itemId, link);
|
||||
}
|
||||
}).send();
|
||||
return current;
|
||||
const auto addTimestamp = channel->hasUsername()
|
||||
&& !inRepliesContext
|
||||
&& videoTimestamp.has_value();
|
||||
const auto addedSeparator = (current.indexOf('?') >= 0) ? '&' : '?';
|
||||
const auto addedTimestamp = addTimestamp
|
||||
? (addedSeparator + u"t="_q + FormatVideoTimestamp(*videoTimestamp))
|
||||
: QString();
|
||||
return current + addedTimestamp;
|
||||
}
|
||||
|
||||
QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
|
||||
@@ -1812,7 +1831,7 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
|
||||
_channelAmInRequests.emplace(channel, requestId);
|
||||
|
||||
using Flag = ChannelDataFlag;
|
||||
chatParticipants().loadSimilarChannels(channel);
|
||||
chatParticipants().loadSimilarPeers(channel);
|
||||
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (!settings->collapseSimilarChannels) {
|
||||
@@ -3337,7 +3356,7 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
|
||||
void ApiWrap::forwardMessages(
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
FnMut<void()> &&successCallback) {
|
||||
Expects(!draft.items.empty());
|
||||
|
||||
@@ -3412,9 +3431,17 @@ void ApiWrap::forwardMessages(
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
const auto idsCopy = localIds;
|
||||
const auto scheduled = action.options.scheduled;
|
||||
const auto starsPaid = std::min(
|
||||
action.options.starsApproved,
|
||||
int(ids.size() * peer->starsPerMessageChecked()));
|
||||
auto oneFlags = sendFlags;
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
oneFlags |= SendFlag::f_allow_paid_stars;
|
||||
}
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
history->sendRequestId = request(MTPmessages_ForwardMessages(
|
||||
MTP_flags(sendFlags),
|
||||
MTP_flags(oneFlags),
|
||||
forwardFrom->input,
|
||||
MTP_vector<MTPint>(ids),
|
||||
MTP_vector<MTPlong>(randomIds),
|
||||
@@ -3422,7 +3449,9 @@ void ApiWrap::forwardMessages(
|
||||
MTP_int(topMsgId),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||
MTPint(), // video_timestamp
|
||||
MTP_long(starsPaid)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
if (!scheduled) {
|
||||
this->updates().checkForSentToScheduled(result);
|
||||
@@ -3474,6 +3503,7 @@ void ApiWrap::forwardMessages(
|
||||
.replyTo = { .topicRootId = topMsgId },
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = action.options.starsApproved,
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
|
||||
// forwarded messages don't have effects
|
||||
@@ -3567,6 +3597,7 @@ void ApiWrap::sendSharedContact(
|
||||
.replyTo = action.replyTo,
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = action.options.starsApproved,
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
.effectId = action.options.effectId,
|
||||
}, TextWithEntities(), MTP_messageMediaContact(
|
||||
@@ -3623,6 +3654,18 @@ void ApiWrap::editMedia(
|
||||
file.path,
|
||||
file.content,
|
||||
std::move(file.information),
|
||||
(file.videoCover
|
||||
? std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
file.videoCover->path,
|
||||
file.videoCover->content,
|
||||
std::move(file.videoCover->information),
|
||||
nullptr,
|
||||
SendMediaType::Photo,
|
||||
to,
|
||||
TextWithTags(),
|
||||
false)
|
||||
: nullptr),
|
||||
type,
|
||||
to,
|
||||
caption,
|
||||
@@ -3664,6 +3707,19 @@ void ApiWrap::sendFiles(
|
||||
file.path,
|
||||
file.content,
|
||||
std::move(file.information),
|
||||
(file.videoCover
|
||||
? std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
file.videoCover->path,
|
||||
file.videoCover->content,
|
||||
std::move(file.videoCover->information),
|
||||
nullptr,
|
||||
SendMediaType::Photo,
|
||||
to,
|
||||
TextWithTags(),
|
||||
false,
|
||||
nullptr)
|
||||
: nullptr),
|
||||
uploadWithType,
|
||||
to,
|
||||
caption,
|
||||
@@ -3689,11 +3745,13 @@ void ApiWrap::sendFile(
|
||||
auto caption = TextWithTags();
|
||||
const auto spoiler = false;
|
||||
const auto information = nullptr;
|
||||
const auto videoCover = nullptr;
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
QString(),
|
||||
fileContent,
|
||||
information,
|
||||
videoCover,
|
||||
type,
|
||||
to,
|
||||
caption,
|
||||
@@ -3911,6 +3969,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
|
||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||
}
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars;
|
||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||
}
|
||||
lastMessage = history->addNewLocalMessage({
|
||||
.id = newId.msg,
|
||||
.flags = flags,
|
||||
@@ -3918,6 +3984,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
.replyTo = action.replyTo,
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = starsPaid,
|
||||
.postAuthor = NewMessagePostAuthor(action),
|
||||
.effectId = action.options.effectId,
|
||||
}, sending, media);
|
||||
@@ -3968,7 +4035,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
mtpShortcut,
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), done, fail);
|
||||
} else {
|
||||
histories.sendPreparedMessage(
|
||||
@@ -3986,7 +4054,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
mtpShortcut,
|
||||
MTP_long(action.options.effectId)
|
||||
MTP_long(action.options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), done, fail);
|
||||
}
|
||||
isFirst = false;
|
||||
@@ -4051,7 +4120,7 @@ void ApiWrap::sendBotStart(
|
||||
void ApiWrap::sendInlineResult(
|
||||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void(bool)> done) {
|
||||
sendAction(action);
|
||||
@@ -4091,6 +4160,13 @@ void ApiWrap::sendInlineResult(
|
||||
if (action.options.hideViaBot) {
|
||||
sendFlags |= SendFlag::f_hide_via;
|
||||
}
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
action.options.starsApproved);
|
||||
if (starsPaid) {
|
||||
action.options.starsApproved -= starsPaid;
|
||||
sendFlags |= SendFlag::f_allow_paid_stars;
|
||||
}
|
||||
|
||||
const auto sendAs = action.options.sendAs;
|
||||
if (sendAs) {
|
||||
@@ -4105,6 +4181,7 @@ void ApiWrap::sendInlineResult(
|
||||
.replyTo = action.replyTo,
|
||||
.date = NewMessageDate(action.options),
|
||||
.shortcutId = action.options.shortcutId,
|
||||
.starsPaid = starsPaid,
|
||||
.viaBotId = ((bot && !action.options.hideViaBot)
|
||||
? peerToUser(bot->id)
|
||||
: UserId()),
|
||||
@@ -4128,7 +4205,8 @@ void ApiWrap::sendInlineResult(
|
||||
MTP_string(data->getId()),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId)
|
||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
@@ -4208,16 +4286,29 @@ void ApiWrap::uploadAlbumMedia(
|
||||
return;
|
||||
}
|
||||
const auto &fields = document->c_document();
|
||||
const auto mtpCover = data.vvideo_cover();
|
||||
const auto cover = (mtpCover && mtpCover->type() == mtpc_photo)
|
||||
? &(mtpCover->c_photo())
|
||||
: (const MTPDphoto*)nullptr;
|
||||
using Flag = MTPDinputMediaDocument::Flag;
|
||||
const auto flags = Flag()
|
||||
| (data.vttl_seconds() ? Flag::f_ttl_seconds : Flag())
|
||||
| (spoiler ? Flag::f_spoiler : Flag());
|
||||
| (spoiler ? Flag::f_spoiler : Flag())
|
||||
| (data.vvideo_timestamp() ? Flag::f_video_timestamp : Flag())
|
||||
| (cover ? Flag::f_video_cover : Flag());
|
||||
const auto media = MTP_inputMediaDocument(
|
||||
MTP_flags(flags),
|
||||
MTP_inputDocument(
|
||||
fields.vid(),
|
||||
fields.vaccess_hash(),
|
||||
fields.vfile_reference()),
|
||||
(cover
|
||||
? MTP_inputPhoto(
|
||||
cover->vid(),
|
||||
cover->vaccess_hash(),
|
||||
cover->vfile_reference())
|
||||
: MTPInputPhoto()),
|
||||
MTP_int(data.vvideo_timestamp().value_or_empty()),
|
||||
MTP_int(data.vttl_seconds().value_or_empty()),
|
||||
MTPstring()); // query
|
||||
sendAlbumWithUploaded(item, groupId, media);
|
||||
@@ -4252,6 +4343,7 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
|
||||
const auto history = item->history();
|
||||
const auto replyTo = item->replyTo();
|
||||
const auto peer = history->peer;
|
||||
|
||||
auto caption = item->originalText();
|
||||
TextUtilities::Trim(caption);
|
||||
@@ -4261,6 +4353,12 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
Api::ConvertOption::SkipLocal);
|
||||
|
||||
const auto updateRecentStickers = Api::HasAttachedStickers(media);
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
options.starsApproved);
|
||||
if (starsPaid) {
|
||||
options.starsApproved -= starsPaid;
|
||||
}
|
||||
|
||||
using Flag = MTPmessages_SendMedia::Flag;
|
||||
const auto flags = Flag(0)
|
||||
@@ -4273,10 +4371,10 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
||||
| (options.effectId ? Flag::f_effect : Flag(0))
|
||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0));
|
||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
||||
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
const auto itemId = item->fullId();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
@@ -4300,7 +4398,8 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
MTP_int(options.scheduled),
|
||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
||||
MTP_long(options.effectId)
|
||||
MTP_long(options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (done) done(true);
|
||||
if (updateRecentStickers) {
|
||||
@@ -4321,7 +4420,7 @@ void ApiWrap::sendMultiPaidMedia(
|
||||
Expects(album->options.price > 0);
|
||||
|
||||
const auto groupId = album->groupId;
|
||||
const auto &options = album->options;
|
||||
auto &options = album->options;
|
||||
const auto randomId = album->items.front().randomId;
|
||||
auto medias = album->items | ranges::view::transform([](
|
||||
const SendingAlbum::Item &part) {
|
||||
@@ -4331,6 +4430,7 @@ void ApiWrap::sendMultiPaidMedia(
|
||||
|
||||
const auto history = item->history();
|
||||
const auto replyTo = item->replyTo();
|
||||
const auto peer = history->peer;
|
||||
|
||||
auto caption = item->originalText();
|
||||
TextUtilities::Trim(caption);
|
||||
@@ -4338,6 +4438,12 @@ void ApiWrap::sendMultiPaidMedia(
|
||||
_session,
|
||||
caption.entities,
|
||||
Api::ConvertOption::SkipLocal);
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
options.starsApproved);
|
||||
if (starsPaid) {
|
||||
options.starsApproved -= starsPaid;
|
||||
}
|
||||
|
||||
using Flag = MTPmessages_SendMedia::Flag;
|
||||
const auto flags = Flag(0)
|
||||
@@ -4350,10 +4456,10 @@ void ApiWrap::sendMultiPaidMedia(
|
||||
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
||||
| (options.effectId ? Flag::f_effect : Flag(0))
|
||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0));
|
||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
||||
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
const auto itemId = item->fullId();
|
||||
album->sent = true;
|
||||
histories.sendPreparedMessage(
|
||||
@@ -4376,7 +4482,8 @@ void ApiWrap::sendMultiPaidMedia(
|
||||
MTP_int(options.scheduled),
|
||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
||||
MTP_long(options.effectId)
|
||||
MTP_long(options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (const auto album = _sendingAlbums.take(groupId)) {
|
||||
const auto copy = (*album)->items;
|
||||
@@ -4472,6 +4579,12 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
const auto history = sample->history();
|
||||
const auto replyTo = sample->replyTo();
|
||||
const auto sendAs = album->options.sendAs;
|
||||
const auto starsPaid = std::min(
|
||||
history->peer->starsPerMessageChecked() * int(medias.size()),
|
||||
album->options.starsApproved);
|
||||
if (starsPaid) {
|
||||
album->options.starsApproved -= starsPaid;
|
||||
}
|
||||
using Flag = MTPmessages_SendMultiMedia::Flag;
|
||||
const auto flags = Flag(0)
|
||||
| (replyTo ? Flag::f_reply_to : Flag(0))
|
||||
@@ -4484,7 +4597,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
? Flag::f_quick_reply_shortcut
|
||||
: Flag(0))
|
||||
| (album->options.effectId ? Flag::f_effect : Flag(0))
|
||||
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0));
|
||||
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0))
|
||||
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
||||
auto &histories = history->owner().histories();
|
||||
const auto peer = history->peer;
|
||||
album->sent = true;
|
||||
@@ -4500,7 +4614,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
MTP_int(album->options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||
Data::ShortcutIdToMTP(_session, album->options.shortcutId),
|
||||
MTP_long(album->options.effectId)
|
||||
MTP_long(album->options.effectId),
|
||||
MTP_long(starsPaid)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
_sendingAlbums.remove(groupId);
|
||||
|
||||
|
||||
@@ -166,7 +166,8 @@ public:
|
||||
QString exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext,
|
||||
bool forceNonPublicLink = false);
|
||||
bool forceNonPublicLink = false,
|
||||
std::optional<TimeId> videoTimestamp = {});
|
||||
QString exportDirectStoryLink(not_null<Data::Story*> item);
|
||||
|
||||
void requestContacts();
|
||||
@@ -305,7 +306,7 @@ public:
|
||||
void finishForwarding(const SendAction &action);
|
||||
void forwardMessages(
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
@@ -367,7 +368,7 @@ public:
|
||||
void sendInlineResult(
|
||||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendAction &action,
|
||||
SendAction action,
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
void sendMessageFail(
|
||||
|
||||
@@ -240,7 +240,7 @@ AyuGramSettings::AyuGramSettings() {
|
||||
#endif
|
||||
;
|
||||
simpleQuotesAndReplies = true;
|
||||
replaceBottomInfoWithIcons = false;
|
||||
replaceBottomInfoWithIcons = true;
|
||||
deletedMark = "🧹";
|
||||
editedMark = Core::IsAppLaunched() ? tr::lng_edited(tr::now) : QString("edited");
|
||||
recentStickersCount = 100;
|
||||
|
||||
@@ -43,18 +43,20 @@ bool ResolveUser(
|
||||
return true;
|
||||
}
|
||||
|
||||
searchById(userId,
|
||||
&controller->session(),
|
||||
[=](const QString &title, UserData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
return;
|
||||
}
|
||||
searchById(
|
||||
userId,
|
||||
&controller->session(),
|
||||
[=](const QString &title, UserData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
return;
|
||||
}
|
||||
|
||||
Core::App().hideMediaView();
|
||||
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
|
||||
});
|
||||
Core::App().hideMediaView();
|
||||
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ void map(not_null<HistoryItem*> item, AyuMessageBase &message) {
|
||||
message.fromId = item->from()->id.value & PeerId::kChatTypeMask;
|
||||
if (item->topic()) {
|
||||
message.topicId = item->topicRootId().bare;
|
||||
} else {
|
||||
message.topicId = 0;
|
||||
}
|
||||
message.messageId = item->id.bare;
|
||||
message.date = item->date();
|
||||
|
||||
@@ -247,8 +247,8 @@ QImage addPadding(const QImage &original) {
|
||||
}
|
||||
|
||||
QImage paddedImage(
|
||||
original.width() + 2 * st::messageShotPadding,
|
||||
original.height() + 2 * st::messageShotPadding,
|
||||
original.width() + 2 * st::messageShotPadding * style::DevicePixelRatio(),
|
||||
original.height() + 2 * st::messageShotPadding * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied
|
||||
);
|
||||
paddedImage.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
@@ -352,6 +352,7 @@ QImage Make(not_null<QWidget*> box, const ShotConfig &config) {
|
||||
height += view->resizeGetHeight(width);
|
||||
}
|
||||
|
||||
width *= style::DevicePixelRatio();
|
||||
height *= style::DevicePixelRatio();
|
||||
|
||||
// create the image
|
||||
|
||||
@@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.48.0"
|
||||
#define SQLITE_VERSION_NUMBER 3048000
|
||||
#define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
|
||||
#define SQLITE_VERSION "3.49.1"
|
||||
#define SQLITE_VERSION_NUMBER 3049001
|
||||
#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -2211,7 +2211,15 @@ struct sqlite3_mem_methods {
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
**
|
||||
** These constants are the available integer configuration options that
|
||||
** can be passed as the second argument to the [sqlite3_db_config()] interface.
|
||||
** can be passed as the second parameter to the [sqlite3_db_config()] interface.
|
||||
**
|
||||
** The [sqlite3_db_config()] interface is a var-args functions. It takes a
|
||||
** variable number of parameters, though always at least two. The number of
|
||||
** parameters passed into sqlite3_db_config() depends on which of these
|
||||
** constants is given as the second parameter. This documentation page
|
||||
** refers to parameters beyond the second as "arguments". Thus, when this
|
||||
** page says "the N-th argument" it means "the N-th parameter past the
|
||||
** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
|
||||
**
|
||||
** New configuration options may be added in future releases of SQLite.
|
||||
** Existing configuration options might be discontinued. Applications
|
||||
@@ -2223,8 +2231,14 @@ struct sqlite3_mem_methods {
|
||||
** <dl>
|
||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||
** <dd> ^This option takes three additional arguments that determine the
|
||||
** [lookaside memory allocator] configuration for the [database connection].
|
||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||
** configuration of the lookaside memory allocator within a database
|
||||
** connection.
|
||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||
** in the [DBCONFIG arguments|usual format].
|
||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||
** should have a total of five parameters.
|
||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
||||
** pointer to a memory buffer to use for lookaside memory.
|
||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
||||
@@ -2247,7 +2261,8 @@ struct sqlite3_mem_methods {
|
||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||
** <dd> ^This option is used to enable or disable the enforcement of
|
||||
** [foreign key constraints]. There should be two additional arguments.
|
||||
** [foreign key constraints]. This is the same setting that is
|
||||
** enabled or disabled by the [PRAGMA foreign_keys] statement.
|
||||
** The first argument is an integer which is 0 to disable FK enforcement,
|
||||
** positive to enable FK enforcement or negative to leave FK enforcement
|
||||
** unchanged. The second parameter is a pointer to an integer into which
|
||||
@@ -2269,13 +2284,13 @@ struct sqlite3_mem_methods {
|
||||
** <p>Originally this option disabled all triggers. ^(However, since
|
||||
** SQLite version 3.35.0, TEMP triggers are still allowed even if
|
||||
** this option is off. So, in other words, this option now only disables
|
||||
** triggers in the main database schema or in the schemas of ATTACH-ed
|
||||
** triggers in the main database schema or in the schemas of [ATTACH]-ed
|
||||
** databases.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
||||
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable views,
|
||||
** positive to enable views or negative to leave the setting unchanged.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
@@ -2294,7 +2309,7 @@ struct sqlite3_mem_methods {
|
||||
** <dd> ^This option is used to enable or disable the
|
||||
** [fts3_tokenizer()] function which is part of the
|
||||
** [FTS3] full-text search engine extension.
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
||||
** positive to enable fts3_tokenizer() or negative to leave the setting
|
||||
** unchanged.
|
||||
@@ -2309,7 +2324,7 @@ struct sqlite3_mem_methods {
|
||||
** interface independently of the [load_extension()] SQL function.
|
||||
** The [sqlite3_enable_load_extension()] API enables or disables both the
|
||||
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** When the first argument to this interface is 1, then only the C-API is
|
||||
** enabled and the SQL function remains disabled. If the first argument to
|
||||
** this interface is 0, then both the C-API and the SQL function are disabled.
|
||||
@@ -2323,23 +2338,30 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
||||
** <dd> ^This option is used to change the name of the "main" database
|
||||
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
||||
** which will become the new schema name in place of "main". ^SQLite
|
||||
** does not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into this DBCONFIG option is unchanged
|
||||
** until after the database connection closes.
|
||||
** schema. This option does not follow the
|
||||
** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format].
|
||||
** This option takes exactly one additional argument so that the
|
||||
** [sqlite3_db_config()] call has a total of three parameters. The
|
||||
** extra argument must be a pointer to a constant UTF8 string which
|
||||
** will become the new schema name in place of "main". ^SQLite does
|
||||
** not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
|
||||
** is unchanged until after the database connection closes.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
|
||||
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
|
||||
** <dd> Usually, when a database in wal mode is closed or detached from a
|
||||
** database handle, SQLite checks if this will mean that there are now no
|
||||
** connections at all to the database. If so, it performs a checkpoint
|
||||
** operation before closing the connection. This option may be used to
|
||||
** override this behavior. The first parameter passed to this operation
|
||||
** is an integer - positive to disable checkpoints-on-close, or zero (the
|
||||
** default) to enable them, and negative to leave the setting unchanged.
|
||||
** The second parameter is a pointer to an integer
|
||||
** <dd> Usually, when a database in [WAL mode] is closed or detached from a
|
||||
** database handle, SQLite checks if if there are other connections to the
|
||||
** same database, and if there are no other database connection (if the
|
||||
** connection being closed is the last open connection to the database),
|
||||
** then SQLite performs a [checkpoint] before closing the connection and
|
||||
** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can
|
||||
** be used to override that behavior. The first argument passed to this
|
||||
** operation (the third parameter to [sqlite3_db_config()]) is an integer
|
||||
** which is positive to disable checkpoints-on-close, or zero (the default)
|
||||
** to enable them, and negative to leave the setting unchanged.
|
||||
** The second argument (the fourth parameter) is a pointer to an integer
|
||||
** into which is written 0 or 1 to indicate whether checkpoints-on-close
|
||||
** have been disabled - 0 if they are not disabled, 1 if they are.
|
||||
** </dd>
|
||||
@@ -2500,7 +2522,7 @@ struct sqlite3_mem_methods {
|
||||
** statistics. For statistics to be collected, the flag must be set on
|
||||
** the database handle both when the SQL statement is prepared and when it
|
||||
** is stepped. The flag is set (collection of statistics is enabled)
|
||||
** by default. This option takes two arguments: an integer and a pointer to
|
||||
** by default. <p>This option takes two arguments: an integer and a pointer to
|
||||
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the statement scanstatus option. If the second argument
|
||||
** is not NULL, then the value of the statement scanstatus setting after
|
||||
@@ -2514,7 +2536,7 @@ struct sqlite3_mem_methods {
|
||||
** in which tables and indexes are scanned so that the scans start at the end
|
||||
** and work toward the beginning rather than starting at the beginning and
|
||||
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
|
||||
** same as setting [PRAGMA reverse_unordered_selects]. This option takes
|
||||
** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes
|
||||
** two arguments which are an integer and a pointer to an integer. The first
|
||||
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
|
||||
** reverse scan order flag, respectively. If the second argument is not NULL,
|
||||
@@ -2523,7 +2545,76 @@ struct sqlite3_mem_methods {
|
||||
** first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
|
||||
** the ability of the [ATTACH DATABASE] SQL command to create a new database
|
||||
** file if the database filed named in the ATTACH command does not already
|
||||
** exist. This ability of ATTACH to create a new database is enabled by
|
||||
** default. Applications can disable or reenable the ability for ATTACH to
|
||||
** create new database files using this DBCONFIG option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the attach-create flag, respectively. If the second
|
||||
** argument is not NULL, then 0 or 1 is written into the integer that the
|
||||
** second argument points to depending on if the attach-create flag is set
|
||||
** after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
|
||||
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
|
||||
** This capability is enabled by default. Applications can disable or
|
||||
** reenable this capability using the current DBCONFIG option. If the
|
||||
** the this capability is disabled, the [ATTACH] command will still work,
|
||||
** but the database will be opened read-only. If this option is disabled,
|
||||
** then the ability to create a new database using [ATTACH] is also disabled,
|
||||
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
|
||||
** option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the ability to ATTACH another database for writing,
|
||||
** respectively. If the second argument is not NULL, then 0 or 1 is written
|
||||
** into the integer to which the second argument points, depending on whether
|
||||
** the ability to ATTACH a read/write database is enabled or disabled
|
||||
** after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
|
||||
** ability to include comments in SQL text. Comments are enabled by default.
|
||||
** An application can disable or reenable comments in SQL text using this
|
||||
** DBCONFIG option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the ability to use comments in SQL text,
|
||||
** respectively. If the second argument is not NULL, then 0 or 1 is written
|
||||
** into the integer that the second argument points to depending on if
|
||||
** comments are allowed in SQL text after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
**
|
||||
** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
|
||||
**
|
||||
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
|
||||
** overall call to [sqlite3_db_config()] has a total of four parameters.
|
||||
** The first argument (the third parameter to sqlite3_db_config()) is a integer.
|
||||
** The second argument is a pointer to an integer. If the first argument is 1,
|
||||
** then the option becomes enabled. If the first integer argument is 0, then the
|
||||
** option is disabled. If the first argument is -1, then the option setting
|
||||
** is unchanged. The second argument, the pointer to an integer, may be NULL.
|
||||
** If the second argument is not NULL, then a value of 0 or 1 is written into
|
||||
** the integer to which the second argument points, depending on whether the
|
||||
** setting is disabled or enabled after applying any changes specified by
|
||||
** the first argument.
|
||||
**
|
||||
** <p>While most SQLITE_DBCONFIG options use the argument format
|
||||
** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
|
||||
** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
|
||||
** documentation of those exceptional options for details.
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
@@ -2545,7 +2636,10 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
|
||||
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
|
||||
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
|
||||
#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||
@@ -10748,8 +10842,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
|
||||
/*
|
||||
** CAPI3REF: Serialize a database
|
||||
**
|
||||
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
|
||||
** that is a serialization of the S database on [database connection] D.
|
||||
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
|
||||
** memory that is a serialization of the S database on
|
||||
** [database connection] D. If S is a NULL pointer, the main database is used.
|
||||
** If P is not a NULL pointer, then the size of the database in bytes
|
||||
** is written into *P.
|
||||
**
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
using "ui/colors.palette";
|
||||
using "ui/widgets/widgets.style";
|
||||
using "dialogs/dialogs.style";
|
||||
|
||||
ayuGhostIcon: icon {{ "ayu/ghost", menuIconColor }};
|
||||
ayuMenuIcon: icon {{ "ayu/ayu_menu", menuIconColor }};
|
||||
@@ -40,3 +41,14 @@ editedIconPadding: margins(0px, 0px, 0px, 0px);
|
||||
|
||||
deletedIcon: icon {{ "ayu/trash_bin", windowFg }};
|
||||
deletedIconPadding: margins(0px, 0px, 0px, 0px);
|
||||
|
||||
dialogsExteraOfficialIcon: ThreeStateIcon {
|
||||
icon: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBg }};
|
||||
over: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBgOver }};
|
||||
active: icon {{ "ayu/dialogs_extera_official", dialogsVerifiedIconBgActive }};
|
||||
}
|
||||
dialogsExteraSupporterIcon: ThreeStateIcon {
|
||||
icon: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBg }};
|
||||
over: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBgOver }};
|
||||
active: icon {{ "ayu/dialogs_extera_supporter", dialogsVerifiedIconBgActive }};
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ void MessageShotBox::setupContent() {
|
||||
|
||||
updatePreview();
|
||||
|
||||
const auto boxWidth = imageView->getImage().width() + (st::boxPadding.left() + st::boxPadding.right()) * 4;
|
||||
const auto boxWidth = imageView->getImage().width() / style::DevicePixelRatio() + (st::boxPadding.left() + st::boxPadding.right()) * 4;
|
||||
|
||||
boxClosing() | rpl::start_with_next(
|
||||
[=]
|
||||
|
||||
@@ -27,7 +27,8 @@ void ImageView::setImage(const QImage &image) {
|
||||
this->prevImage = this->image;
|
||||
this->image = image;
|
||||
|
||||
setMinimumSize(image.size().grownBy(st::imageViewInnerPadding));
|
||||
const auto size = image.size() / style::DevicePixelRatio();
|
||||
setMinimumSize(size.grownBy(st::imageViewInnerPadding));
|
||||
|
||||
if (this->animation.animating()) {
|
||||
this->animation.stop();
|
||||
@@ -67,7 +68,7 @@ void ImageView::paintEvent(QPaintEvent *e) {
|
||||
const auto brush = QBrush(AyuFeatures::MessageShot::makeDefaultBackgroundColor());
|
||||
|
||||
QPainterPath path;
|
||||
path.addRoundedRect(rect(), st::bubbleRadiusLarge, st::bubbleRadiusLarge);
|
||||
path.addRoundedRect(rect(), st::roundRadiusLarge, st::roundRadiusLarge);
|
||||
|
||||
p.fillPath(path, brush);
|
||||
|
||||
@@ -75,10 +76,10 @@ void ImageView::paintEvent(QPaintEvent *e) {
|
||||
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
|
||||
|
||||
const auto resizedRect = QRect(
|
||||
(realRect.width() - prevImage.width()) / 2 + st::imageViewInnerPadding.left(),
|
||||
(realRect.height() - prevImage.height()) / 2 + st::imageViewInnerPadding.top(),
|
||||
prevImage.width(),
|
||||
prevImage.height());
|
||||
(realRect.width() - prevImage.width() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.left(),
|
||||
(realRect.height() - prevImage.height() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.top(),
|
||||
prevImage.width() / style::DevicePixelRatio(),
|
||||
prevImage.height() / style::DevicePixelRatio());
|
||||
|
||||
const auto opacity = 1.0 - animation.value(1.0);
|
||||
p.setOpacity(opacity);
|
||||
@@ -90,10 +91,10 @@ void ImageView::paintEvent(QPaintEvent *e) {
|
||||
const auto realRect = rect().marginsRemoved(st::imageViewInnerPadding);
|
||||
|
||||
const auto resizedRect = QRect(
|
||||
(realRect.width() - image.width()) / 2 + st::imageViewInnerPadding.left(),
|
||||
(realRect.height() - image.height()) / 2 + st::imageViewInnerPadding.top(),
|
||||
image.width(),
|
||||
image.height());
|
||||
(realRect.width() - image.width() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.left(),
|
||||
(realRect.height() - image.height() / style::DevicePixelRatio()) / 2 + st::imageViewInnerPadding.top(),
|
||||
image.width() / style::DevicePixelRatio(),
|
||||
image.height() / style::DevicePixelRatio());
|
||||
|
||||
const auto opacity = animation.value(1.0);
|
||||
p.setOpacity(opacity);
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "data/data_session.h"
|
||||
#include "history/view/history_view_context_menu.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
namespace AyuUi {
|
||||
@@ -58,13 +59,14 @@ void AddDeletedMessagesActions(PeerData *peerData,
|
||||
return;
|
||||
}
|
||||
|
||||
addCallback(tr::ayu_ViewDeletedMenuText(tr::now),
|
||||
[=]
|
||||
{
|
||||
sessionController->session().tryResolveWindow()
|
||||
->showSection(std::make_shared<MessageHistory::SectionMemento>(peerData, nullptr, topicId));
|
||||
},
|
||||
&st::menuIconArchive);
|
||||
addCallback(
|
||||
tr::ayu_ViewDeletedMenuText(tr::now),
|
||||
[=]
|
||||
{
|
||||
sessionController->session().tryResolveWindow()
|
||||
->showSection(std::make_shared<MessageHistory::SectionMemento>(peerData, nullptr, topicId));
|
||||
},
|
||||
&st::menuIconArchive);
|
||||
}
|
||||
|
||||
void AddJumpToBeginningAction(PeerData *peerData,
|
||||
@@ -147,16 +149,38 @@ void AddJumpToBeginningAction(PeerData *peerData,
|
||||
&st::ayuMenuIconToBeginning);
|
||||
}
|
||||
|
||||
void AddOpenChannelAction(PeerData *peerData,
|
||||
not_null<Window::SessionController*> sessionController,
|
||||
const Window::PeerMenuCallback &addCallback) {
|
||||
if (!peerData || !peerData->isMegagroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto chat = peerData->asMegagroup()->linkedChat();
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
addCallback(
|
||||
tr::lng_context_open_channel(tr::now),
|
||||
[=]
|
||||
{
|
||||
sessionController->showPeerHistory(chat, Window::SectionShow::Way::Forward);
|
||||
},
|
||||
&st::menuIconChannel);
|
||||
}
|
||||
|
||||
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||
if (AyuMessages::hasRevisions(item)) {
|
||||
menu->addAction(tr::ayu_EditsHistoryMenuText(tr::now),
|
||||
[=]
|
||||
{
|
||||
item->history()->session().tryResolveWindow()
|
||||
->showSection(
|
||||
std::make_shared<MessageHistory::SectionMemento>(item->history()->peer, item, 0));
|
||||
},
|
||||
&st::ayuEditsHistoryIcon);
|
||||
menu->addAction(
|
||||
tr::ayu_EditsHistoryMenuText(tr::now),
|
||||
[=]
|
||||
{
|
||||
item->history()->session().tryResolveWindow()
|
||||
->showSection(
|
||||
std::make_shared<MessageHistory::SectionMemento>(item->history()->peer, item, 0));
|
||||
},
|
||||
&st::ayuEditsHistoryIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,15 +195,16 @@ void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||
}
|
||||
|
||||
const auto history = item->history();
|
||||
menu->addAction(tr::ayu_ContextHideMessage(tr::now),
|
||||
[=]()
|
||||
{
|
||||
item->destroy();
|
||||
history->requestChatListMessage();
|
||||
menu->addAction(
|
||||
tr::ayu_ContextHideMessage(tr::now),
|
||||
[=]()
|
||||
{
|
||||
item->destroy();
|
||||
history->requestChatListMessage();
|
||||
|
||||
AyuState::hide(item);
|
||||
},
|
||||
&st::menuIconClear);
|
||||
AyuState::hide(item);
|
||||
},
|
||||
&st::menuIconClear);
|
||||
}
|
||||
|
||||
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||
@@ -432,31 +457,77 @@ void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||
return;
|
||||
}
|
||||
|
||||
menu->addAction(tr::ayu_ReadUntilMenuText(tr::now),
|
||||
[=]()
|
||||
menu->addAction(
|
||||
tr::ayu_ReadUntilMenuText(tr::now),
|
||||
[=]()
|
||||
{
|
||||
readHistory(item);
|
||||
if (item->media() && item->media()->ttlSeconds() <= 0 && item->unsupportedTTL() <= 0 && !item->out() && item
|
||||
->isUnreadMedia()) {
|
||||
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
item->history()->session().api().request(MTPchannels_ReadMessageContents(
|
||||
channel->inputChannel,
|
||||
ids
|
||||
)).send();
|
||||
} else {
|
||||
item->history()->session().api().request(MTPmessages_ReadMessageContents(
|
||||
ids
|
||||
)).done([=](const MTPmessages_AffectedMessages &result)
|
||||
{
|
||||
readHistory(item);
|
||||
if (item->media() && !item->media()->ttlSeconds()) {
|
||||
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
item->history()->session().api().request(MTPchannels_ReadMessageContents(
|
||||
channel->inputChannel,
|
||||
ids
|
||||
)).send();
|
||||
} else {
|
||||
item->history()->session().api().request(MTPmessages_ReadMessageContents(
|
||||
ids
|
||||
)).done([=](const MTPmessages_AffectedMessages &result)
|
||||
{
|
||||
item->history()->session().api().applyAffectedMessages(
|
||||
item->history()->peer,
|
||||
result);
|
||||
}).send();
|
||||
}
|
||||
item->markContentsRead();
|
||||
}
|
||||
},
|
||||
&st::menuIconShowInChat);
|
||||
item->history()->session().api().applyAffectedMessages(
|
||||
item->history()->peer,
|
||||
result);
|
||||
}).send();
|
||||
}
|
||||
item->markContentsRead();
|
||||
}
|
||||
},
|
||||
&st::menuIconShowInChat);
|
||||
}
|
||||
|
||||
void AddBurnAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||
if (!item->media() || item->media()->ttlSeconds() <= 0 && item->unsupportedTTL() <= 0 || item->out() ||
|
||||
!item->isUnreadMedia()) {
|
||||
return;
|
||||
}
|
||||
|
||||
menu->addAction(
|
||||
tr::ayu_ExpireMediaContextMenuText(tr::now),
|
||||
[=]()
|
||||
{
|
||||
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
|
||||
const auto callback = [=]()
|
||||
{
|
||||
if (const auto window = Core::App().activeWindow()) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
controller->showToast(tr::lng_box_ok(tr::now));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
item->history()->session().api().request(MTPchannels_ReadMessageContents(
|
||||
channel->inputChannel,
|
||||
ids
|
||||
)).done([=]()
|
||||
{
|
||||
callback();
|
||||
}).send();
|
||||
} else {
|
||||
item->history()->session().api().request(MTPmessages_ReadMessageContents(
|
||||
ids
|
||||
)).done([=](const MTPmessages_AffectedMessages &result)
|
||||
{
|
||||
item->history()->session().api().applyAffectedMessages(
|
||||
item->history()->peer,
|
||||
result);
|
||||
callback();
|
||||
}).send();
|
||||
}
|
||||
item->markContentsRead();
|
||||
},
|
||||
&st::menuIconTTLAny);
|
||||
}
|
||||
|
||||
} // namespace AyuUi
|
||||
|
||||
@@ -25,10 +25,15 @@ void AddJumpToBeginningAction(PeerData *peerData,
|
||||
not_null<Window::SessionController*> sessionController,
|
||||
const Window::PeerMenuCallback &addCallback);
|
||||
|
||||
void AddOpenChannelAction(PeerData *peerData,
|
||||
not_null<Window::SessionController*> sessionController,
|
||||
const Window::PeerMenuCallback &addCallback);
|
||||
|
||||
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
void AddMessageDetailsAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
void AddBurnAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||
|
||||
}
|
||||
|
||||
@@ -218,59 +218,73 @@ ActionStickerPackAuthor::ActionStickerPackAuthor(not_null<Menu::Menu*> menu,
|
||||
}
|
||||
|
||||
void ActionStickerPackAuthor::searchAuthor(ID authorId) {
|
||||
const auto pointer = Ui::MakeWeak(this);
|
||||
searchById(authorId,
|
||||
_session,
|
||||
[=](const QString &username, UserData *user)
|
||||
{
|
||||
if (!pointer) {
|
||||
LOG(("ContextActionStickerAuthor: searchById callback after destruction"));
|
||||
return;
|
||||
}
|
||||
if (username.isEmpty() && !user) {
|
||||
_subText = QString(tr::ayu_MessageDetailsPackOwnerNotFoundPC(tr::now));
|
||||
setClickedCallback(
|
||||
[=]
|
||||
{
|
||||
QGuiApplication::clipboard()->setText(QString::number(authorId));
|
||||
if (const auto window = _session->tryResolveWindow()) {
|
||||
if (const auto mainWidget = window->widget()->sessionController()) {
|
||||
mainWidget->showToast(tr::ayu_IDCopiedToast(tr::now));
|
||||
}
|
||||
}
|
||||
});
|
||||
const auto session = _session;
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
|
||||
crl::on_main(
|
||||
[=]
|
||||
{
|
||||
update();
|
||||
});
|
||||
return;
|
||||
}
|
||||
searchById(
|
||||
authorId,
|
||||
session,
|
||||
[session, weak, authorId](const QString &username, UserData *user)
|
||||
{
|
||||
if (!weak) {
|
||||
LOG(("ContextActionStickerAuthor: searchById callback after destruction"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto title = username.isEmpty() ? user ? user->name() : QString() : username;
|
||||
const auto callback = [=]
|
||||
{
|
||||
if (user) {
|
||||
if (const auto window = _session->tryResolveWindow()) {
|
||||
if (const auto mainWidget = window->widget()->sessionController()) {
|
||||
mainWidget->showPeer(user);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QGuiApplication::clipboard()->setText(title);
|
||||
}
|
||||
};
|
||||
const auto strong = weak.data();
|
||||
if (!strong) {
|
||||
LOG(("ContextActionStickerAuthor: weak.data() returned null"));
|
||||
return;
|
||||
}
|
||||
|
||||
setClickedCallback(callback);
|
||||
if (username.isEmpty() && !user) {
|
||||
strong->_subText = QString(tr::ayu_MessageDetailsPackOwnerNotFoundPC(tr::now));
|
||||
strong->setClickedCallback(
|
||||
[authorId, session]
|
||||
{
|
||||
QGuiApplication::clipboard()->setText(QString::number(authorId));
|
||||
if (const auto window = session->tryResolveWindow()) {
|
||||
if (const auto mainWidget = window->widget()->sessionController()) {
|
||||
mainWidget->showToast(tr::ayu_IDCopiedToast(tr::now));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_subText = QString(title);
|
||||
crl::on_main(
|
||||
[=]
|
||||
{
|
||||
update();
|
||||
});
|
||||
});
|
||||
crl::on_main(
|
||||
[weak]
|
||||
{
|
||||
if (const auto strongInner = weak.data()) {
|
||||
strongInner->update();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto title = username.isEmpty() ? (user ? user->name() : QString()) : username;
|
||||
const auto callback = [user, title, session]
|
||||
{
|
||||
if (user) {
|
||||
if (const auto window = session->tryResolveWindow()) {
|
||||
if (const auto mainWidget = window->widget()->sessionController()) {
|
||||
mainWidget->showPeer(user);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QGuiApplication::clipboard()->setText(title);
|
||||
}
|
||||
};
|
||||
|
||||
strong->setClickedCallback(callback);
|
||||
strong->_subText = QString(title);
|
||||
crl::on_main(
|
||||
[weak]
|
||||
{
|
||||
if (const auto strongInner = weak.data()) {
|
||||
strongInner->update();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -702,7 +702,7 @@ void InnerWidget::addMessages(Direction direction, const std::vector<AyuMessageB
|
||||
? message.fakeId // viewing edited history
|
||||
: message.messageId; // viewing deleted messages
|
||||
if (_messageIds.find(id) != _messageIds.end()) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
auto count = 0;
|
||||
const auto addOne = [&](
|
||||
|
||||
@@ -48,6 +48,13 @@
|
||||
|
||||
class PainterHighQualityEnabler;
|
||||
|
||||
rpl::producer<QString> asBeta(rpl::producer<QString> text) {
|
||||
return std::move(text) | rpl::map([=](const QString &val)
|
||||
{
|
||||
return val + " β";
|
||||
});
|
||||
}
|
||||
|
||||
not_null<Ui::RpWidget*> AddInnerToggle(not_null<Ui::VerticalLayout*> container,
|
||||
const style::SettingsButton &st,
|
||||
std::vector<not_null<Ui::AbstractCheckView*>> innerCheckViews,
|
||||
@@ -1277,7 +1284,7 @@ void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
||||
updateLabel(settings->recentStickersCount);
|
||||
|
||||
slider->setPseudoDiscrete(
|
||||
100 + 1,
|
||||
200 + 1,
|
||||
// thx tg
|
||||
[=](int amount)
|
||||
{
|
||||
@@ -1461,10 +1468,7 @@ void SetupMarks(not_null<Ui::VerticalLayout*> container) {
|
||||
|
||||
AddButtonWithIcon(
|
||||
container,
|
||||
tr::ayu_ReplaceMarksWithIcons() | rpl::map([=](QString val)
|
||||
{
|
||||
return val + " β";
|
||||
}),
|
||||
tr::ayu_ReplaceMarksWithIcons(),
|
||||
st::settingsButtonNoIcon
|
||||
)->toggleOn(
|
||||
rpl::single(settings->replaceBottomInfoWithIcons)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/ayu_state.h"
|
||||
#include "ayu/data/messages_storage.h"
|
||||
|
||||
Main::Session *getSession(ID userId) {
|
||||
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
||||
@@ -85,6 +86,15 @@ bool isSupporterPeer(ID peerId) {
|
||||
return RCManager::getInstance().supporters().contains(peerId);
|
||||
}
|
||||
|
||||
rpl::producer<Info::Profile::Badge::Content> ExteraBadgeTypeFromPeer(not_null<PeerData*> peer) {
|
||||
if (isExteraPeer(getBareID(peer))) {
|
||||
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::Extera });
|
||||
} else if (isSupporterPeer(getBareID(peer))) {
|
||||
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::ExteraSupporter });
|
||||
}
|
||||
return rpl::single(Info::Profile::Badge::Content{Info::Profile::BadgeType::None });
|
||||
}
|
||||
|
||||
bool isMessageHidden(const not_null<HistoryItem*> item) {
|
||||
if (AyuState::isHidden(item)) {
|
||||
return true;
|
||||
@@ -494,6 +504,28 @@ int getScheduleTime(int64 sumSize) {
|
||||
return time;
|
||||
}
|
||||
|
||||
bool isMessageSavable(const not_null<HistoryItem *> item) {
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
|
||||
if (!settings->saveDeletedMessages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto possiblyBot = item->history()->peer->asUser()) {
|
||||
return !possiblyBot->isBot() || (settings->saveForBots && possiblyBot->isBot());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void processMessageDelete(not_null<HistoryItem*> item) {
|
||||
if (!isMessageSavable(item)) {
|
||||
item->destroy();
|
||||
} else {
|
||||
item->setDeleted();
|
||||
AyuMessages::addDeletedMessage(item);
|
||||
}
|
||||
}
|
||||
|
||||
void resolveUser(ID userId, const QString &username, Main::Session *session, const Callback &callback) {
|
||||
auto normalized = username.trimmed().toLower();
|
||||
if (normalized.isEmpty()) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "core/application.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "info/profile/info_profile_badge.h"
|
||||
#include "main/main_domain.h"
|
||||
|
||||
using Callback = Fn<void(const QString &, UserData *)>;
|
||||
@@ -23,6 +24,8 @@ ID getBareID(not_null<PeerData*> peer);
|
||||
bool isExteraPeer(ID peerId);
|
||||
bool isSupporterPeer(ID peerId);
|
||||
|
||||
rpl::producer<Info::Profile::Badge::Content> ExteraBadgeTypeFromPeer(not_null<PeerData*> peer);
|
||||
|
||||
bool isMessageHidden(not_null<HistoryItem*> item);
|
||||
|
||||
void MarkAsReadChatList(not_null<Dialogs::MainList*> list);
|
||||
@@ -45,6 +48,9 @@ QString getPeerDC(not_null<PeerData*> peer);
|
||||
|
||||
int getScheduleTime(int64 sumSize);
|
||||
|
||||
bool isMessageSavable(not_null<HistoryItem *> item);
|
||||
void processMessageDelete(not_null<HistoryItem *> item);
|
||||
|
||||
void searchById(ID userId, Main::Session *session, bool retry, const Callback &callback);
|
||||
void searchById(ID userId, Main::Session *session, const Callback &callback);
|
||||
|
||||
|
||||
@@ -25,17 +25,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_session_controller_link_info.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
rpl::producer<TextWithEntities> Text1() {
|
||||
return tr::lng_about_text1(
|
||||
lt_api_link,
|
||||
tr::lng_about_text1_api(
|
||||
) | Ui::Text::ToLink("https://core.telegram.org/api"),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
rpl::producer<TextWithEntities> Text2() {
|
||||
rpl::producer<TextWithEntities> Text() {
|
||||
return tr::lng_about_text2(
|
||||
lt_gpl_link,
|
||||
rpl::single(Ui::Text::Link(
|
||||
@@ -48,34 +45,33 @@ rpl::producer<TextWithEntities> Text2() {
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
rpl::producer<TextWithEntities> Text3() {
|
||||
return tr::lng_about_text3(
|
||||
lt_faq_link,
|
||||
tr::lng_about_text3_faq() | Ui::Text::ToLink(telegramFaqLink()),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AboutBox::AboutBox(QWidget *parent)
|
||||
AboutBox::AboutBox(QWidget *parent, Window::SessionController* controller)
|
||||
: _version(this, tr::lng_about_version(tr::now, lt_version, currentVersionText()), st::aboutVersionLink)
|
||||
, _text1(this, Text1(), st::aboutLabel)
|
||||
, _text2(this, Text2(), st::aboutLabel)
|
||||
, _text3(this, Text3(), st::aboutLabel) {
|
||||
, _text(this, Text(), st::aboutLabel)
|
||||
, _controller(controller) {
|
||||
}
|
||||
|
||||
void AboutBox::prepare() {
|
||||
setTitle(rpl::single(u"AyuGram Desktop"_q));
|
||||
|
||||
addButton(tr::lng_close(), [this] { closeBox(); });
|
||||
addLeftButton(
|
||||
rpl::single(QString("@ayugramchat")),
|
||||
[this, controller = _controller]
|
||||
{
|
||||
closeBox();
|
||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||
.usernameOrId = QString("ayugramchat"),
|
||||
});
|
||||
});
|
||||
|
||||
_text1->setLinksTrusted();
|
||||
_text2->setLinksTrusted();
|
||||
_text3->setLinksTrusted();
|
||||
_text->setLinksTrusted();
|
||||
|
||||
_version->setClickedCallback([this] { showVersionHistory(); });
|
||||
|
||||
setDimensions(st::aboutWidth, st::aboutTextTop + _text1->height() + st::aboutSkip + _text2->height() + st::aboutSkip + _text3->height());
|
||||
setDimensions(st::aboutWidth, st::aboutTextTop + _text->height());
|
||||
}
|
||||
|
||||
void AboutBox::resizeEvent(QResizeEvent *e) {
|
||||
@@ -85,42 +81,12 @@ void AboutBox::resizeEvent(QResizeEvent *e) {
|
||||
- st::boxPadding.left()
|
||||
- st::boxPadding.right();
|
||||
_version->moveToLeft(st::boxPadding.left(), st::aboutVersionTop);
|
||||
_text1->resizeToWidth(available);
|
||||
_text1->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
|
||||
_text2->resizeToWidth(available);
|
||||
_text2->moveToLeft(st::boxPadding.left(), _text1->y() + _text1->height() + st::aboutSkip);
|
||||
_text3->resizeToWidth(available);
|
||||
_text3->moveToLeft(st::boxPadding.left(), _text2->y() + _text2->height() + st::aboutSkip);
|
||||
_text->resizeToWidth(available);
|
||||
_text->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
|
||||
}
|
||||
|
||||
void AboutBox::showVersionHistory() {
|
||||
if (cRealAlphaVersion()) {
|
||||
auto url = u"https://tdesktop.com/"_q;
|
||||
if (Platform::IsWindows32Bit()) {
|
||||
url += u"win/%1.zip"_q;
|
||||
} else if (Platform::IsWindows64Bit()) {
|
||||
url += u"win64/%1.zip"_q;
|
||||
} else if (Platform::IsWindowsARM64()) {
|
||||
url += u"winarm/%1.zip"_q;
|
||||
} else if (Platform::IsMac()) {
|
||||
url += u"mac/%1.zip"_q;
|
||||
} else if (Platform::IsLinux()) {
|
||||
url += u"linux/%1.tar.xz"_q;
|
||||
} else {
|
||||
Unexpected("Platform value.");
|
||||
}
|
||||
url = url.arg(u"talpha%1_%2"_q.arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
|
||||
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
|
||||
getDelegate()->show(
|
||||
Ui::MakeInformBox(
|
||||
"The link to the current private alpha "
|
||||
"version of Telegram Desktop was copied to the clipboard."),
|
||||
Ui::LayerOption::CloseOther);
|
||||
} else {
|
||||
File::OpenUrl(Core::App().changelogLink());
|
||||
}
|
||||
File::OpenUrl(Core::App().changelogLink());
|
||||
}
|
||||
|
||||
void AboutBox::keyPressEvent(QKeyEvent *e) {
|
||||
@@ -131,23 +97,6 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
QString telegramFaqLink() {
|
||||
const auto result = u"https://telegram.org/faq"_q;
|
||||
const auto langpacked = [&](const char *language) {
|
||||
return result + '/' + language;
|
||||
};
|
||||
const auto current = Lang::Id();
|
||||
for (const auto language : { "de", "es", "it", "ko" }) {
|
||||
if (current.startsWith(QLatin1String(language))) {
|
||||
return langpacked(language);
|
||||
}
|
||||
}
|
||||
if (current.startsWith(u"pt-br"_q)) {
|
||||
return langpacked("br");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString currentVersionText() {
|
||||
auto result = QString::fromLatin1(AppVersionStr);
|
||||
if (cAlphaVersion()) {
|
||||
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class LinkButton;
|
||||
class FlatLabel;
|
||||
@@ -16,7 +20,7 @@ class FlatLabel;
|
||||
|
||||
class AboutBox : public Ui::BoxContent {
|
||||
public:
|
||||
AboutBox(QWidget*);
|
||||
AboutBox(QWidget*, Window::SessionController* controller);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -28,9 +32,8 @@ private:
|
||||
void showVersionHistory();
|
||||
|
||||
object_ptr<Ui::LinkButton> _version;
|
||||
object_ptr<Ui::FlatLabel> _text1;
|
||||
object_ptr<Ui::FlatLabel> _text2;
|
||||
object_ptr<Ui::FlatLabel> _text3;
|
||||
object_ptr<Ui::FlatLabel> _text;
|
||||
Window::SessionController* _controller;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1078,7 +1078,9 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
? tr::lng_background_other_group(tr::now)
|
||||
: forChannel()
|
||||
? tr::lng_background_other_channel(tr::now)
|
||||
: (_forPeer && !_fromMessageId)
|
||||
: (_forPeer
|
||||
&& !_fromMessageId
|
||||
&& !_forPeer->starsPerMessageChecked())
|
||||
? tr::lng_background_other_info(
|
||||
tr::now,
|
||||
lt_user,
|
||||
|
||||
@@ -1122,3 +1122,10 @@ profileQrBackgroundRadius: 12px;
|
||||
profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }};
|
||||
profileQrBackgroundMargins: margins(36px, 12px, 36px, 12px);
|
||||
profileQrBackgroundPadding: margins(0px, 24px, 0px, 24px);
|
||||
|
||||
foldersMenu: PopupMenu(popupMenuWithIcons) {
|
||||
maxHeight: 320px;
|
||||
menu: Menu(menuWithIcons) {
|
||||
itemPadding: margins(54px, 8px, 44px, 8px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,13 +172,6 @@ void ChangeFilterById(
|
||||
const auto account = not_null(&history->session().account());
|
||||
if (const auto controller = Core::App().windowFor(account)) {
|
||||
const auto isStatic = name.isStatic;
|
||||
const auto textContext = [=](not_null<QWidget*> widget) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &history->session(),
|
||||
.customEmojiRepaint = [=] { widget->update(); },
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
controller->showToast({
|
||||
.text = (add
|
||||
? tr::lng_filters_toast_add
|
||||
@@ -189,7 +182,10 @@ void ChangeFilterById(
|
||||
lt_folder,
|
||||
Ui::Text::Wrapped(name.text, EntityType::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
.textContext = textContext,
|
||||
.textContext = Core::TextContext({
|
||||
.session = &history->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
@@ -290,19 +286,17 @@ void FillChooseFilterMenu(
|
||||
const auto title = filter.title();
|
||||
auto item = base::make_unique_q<FilterAction>(
|
||||
menu.get(),
|
||||
st::foldersMenu,
|
||||
menu->st().menu,
|
||||
Ui::Menu::CreateAction(
|
||||
menu.get(),
|
||||
Ui::Text::FixAmpersandInAction(title.text.text),
|
||||
std::move(callback)),
|
||||
contains ? &st::mediaPlayerMenuCheck : nullptr,
|
||||
contains ? &st::mediaPlayerMenuCheck : nullptr);
|
||||
const auto context = Core::MarkedTextContext{
|
||||
item->setMarkedText(title.text, QString(), Core::TextContext({
|
||||
.session = &history->session(),
|
||||
.customEmojiRepaint = [raw = item.get()] { raw->update(); },
|
||||
.customEmojiLoopLimit = title.isStatic ? -1 : 0,
|
||||
};
|
||||
item->setMarkedText(title.text, QString(), context);
|
||||
}));
|
||||
|
||||
item->setIcon(Icon(showColors ? filter : filter.withColorIndex({})));
|
||||
const auto action = menu->addAction(std::move(item));
|
||||
|
||||
@@ -817,13 +817,15 @@ CreatePollBox::CreatePollBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
PollData::Flags chosen,
|
||||
PollData::Flags disabled,
|
||||
rpl::producer<int> starsRequired,
|
||||
Api::SendType sendType,
|
||||
SendMenu::Details sendMenuDetails)
|
||||
: _controller(controller)
|
||||
, _chosen(chosen)
|
||||
, _disabled(disabled)
|
||||
, _sendType(sendType)
|
||||
, _sendMenuDetails([result = sendMenuDetails] { return result; }) {
|
||||
, _sendMenuDetails([result = sendMenuDetails] { return result; })
|
||||
, _starsRequired(std::move(starsRequired)) {
|
||||
}
|
||||
|
||||
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
|
||||
@@ -1226,10 +1228,11 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
_sendMenuDetails());
|
||||
};
|
||||
const auto submit = addButton(
|
||||
(isNormal
|
||||
? tr::lng_polls_create_button()
|
||||
: tr::lng_schedule_button()),
|
||||
tr::lng_polls_create_button(),
|
||||
[=] { isNormal ? send({}) : schedule(); });
|
||||
submit->setText(PaidSendButtonText(_starsRequired.value(), isNormal
|
||||
? tr::lng_polls_create_button()
|
||||
: tr::lng_schedule_button()));
|
||||
const auto sendMenuDetails = [=] {
|
||||
collectError();
|
||||
return (*error) ? SendMenu::Details() : _sendMenuDetails();
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
not_null<Window::SessionController*> controller,
|
||||
PollData::Flags chosen,
|
||||
PollData::Flags disabled,
|
||||
rpl::producer<int> starsRequired,
|
||||
Api::SendType sendType,
|
||||
SendMenu::Details sendMenuDetails);
|
||||
|
||||
@@ -76,6 +77,7 @@ private:
|
||||
const PollData::Flags _disabled = PollData::Flags();
|
||||
const Api::SendType _sendType = Api::SendType();
|
||||
const Fn<SendMenu::Details()> _sendMenuDetails;
|
||||
rpl::variable<int> _starsRequired;
|
||||
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
|
||||
Fn<void()> _setInnerFocus;
|
||||
Fn<rpl::producer<bool>()> _dataIsValidValue;
|
||||
|
||||
@@ -467,13 +467,16 @@ void EditCaptionBox::rebuildPreview() {
|
||||
}
|
||||
} else {
|
||||
const auto &file = _preparedList.files.front();
|
||||
|
||||
const auto isVideoFile = file.isVideoFile();
|
||||
const auto media = Ui::SingleMediaPreview::Create(
|
||||
this,
|
||||
st::defaultComposeControls,
|
||||
gifPaused,
|
||||
file,
|
||||
[] { return true; },
|
||||
[=](Ui::AttachActionType type) {
|
||||
return (type != Ui::AttachActionType::EditCover)
|
||||
|| isVideoFile;
|
||||
},
|
||||
Ui::AttachControls::Type::EditOnly);
|
||||
_isPhoto = (media && media->isPhoto());
|
||||
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
||||
@@ -719,7 +722,7 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
controller->uiShow(),
|
||||
&_preparedList.files.front(),
|
||||
st::sendMediaPreviewSize,
|
||||
[=] { rebuildPreview(); });
|
||||
[=](bool ok) { if (ok) rebuildPreview(); });
|
||||
} else {
|
||||
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
|
||||
Ui::PreparedList &&list) {
|
||||
|
||||
@@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
@@ -21,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "settings/settings_privacy_controllers.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -42,6 +45,8 @@ namespace {
|
||||
|
||||
constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
|
||||
constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value;
|
||||
constexpr auto kStarsMin = 1;
|
||||
constexpr auto kDefaultChargeStars = 10;
|
||||
|
||||
using Exceptions = Api::UserPrivacy::Exceptions;
|
||||
|
||||
@@ -452,6 +457,143 @@ auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeChargeStarsSlider(
|
||||
QWidget *parent,
|
||||
not_null<const style::MediaSlider*> sliderStyle,
|
||||
not_null<const style::FlatLabel*> labelStyle,
|
||||
int valuesCount,
|
||||
Fn<int(int)> valueByIndex,
|
||||
int value,
|
||||
int maxValue,
|
||||
Fn<void(int)> valueProgress,
|
||||
Fn<void(int)> valueFinished) {
|
||||
auto result = object_ptr<Ui::VerticalLayout>(parent);
|
||||
const auto raw = result.data();
|
||||
|
||||
const auto labels = raw->add(object_ptr<Ui::RpWidget>(raw));
|
||||
const auto min = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(kStarsMin),
|
||||
*labelStyle);
|
||||
const auto max = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(maxValue),
|
||||
*labelStyle);
|
||||
const auto current = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(value),
|
||||
*labelStyle);
|
||||
min->setTextColorOverride(st::windowSubTextFg->c);
|
||||
max->setTextColorOverride(st::windowSubTextFg->c);
|
||||
const auto slider = raw->add(object_ptr<Ui::MediaSliderWheelless>(
|
||||
raw,
|
||||
*sliderStyle));
|
||||
labels->resize(
|
||||
labels->width(),
|
||||
current->height() + st::defaultVerticalListSkip);
|
||||
struct State {
|
||||
int indexMin = 0;
|
||||
int index = 0;
|
||||
};
|
||||
const auto state = raw->lifetime().make_state<State>();
|
||||
const auto updateByIndex = [=] {
|
||||
const auto outer = labels->width();
|
||||
const auto minWidth = min->width();
|
||||
const auto maxWidth = max->width();
|
||||
const auto currentWidth = current->width();
|
||||
if (minWidth + maxWidth + currentWidth > outer) {
|
||||
return;
|
||||
}
|
||||
|
||||
min->moveToLeft(0, 0, outer);
|
||||
max->moveToRight(0, 0, outer);
|
||||
current->moveToLeft((outer - current->width()) / 2, 0, outer);
|
||||
};
|
||||
const auto updateByValue = [=](int value) {
|
||||
current->setText(
|
||||
tr::lng_action_gift_for_stars(tr::now, lt_count, value));
|
||||
|
||||
state->index = 0;
|
||||
auto maxIndex = valuesCount - 1;
|
||||
while (state->index < maxIndex) {
|
||||
const auto mid = (state->index + maxIndex) / 2;
|
||||
const auto midValue = valueByIndex(mid);
|
||||
if (midValue == value) {
|
||||
state->index = mid;
|
||||
break;
|
||||
} else if (midValue < value) {
|
||||
state->index = mid + 1;
|
||||
} else {
|
||||
maxIndex = mid - 1;
|
||||
}
|
||||
}
|
||||
updateByIndex();
|
||||
};
|
||||
const auto progress = [=](int value) {
|
||||
updateByValue(value);
|
||||
valueProgress(value);
|
||||
};
|
||||
const auto finished = [=](int value) {
|
||||
updateByValue(value);
|
||||
valueFinished(value);
|
||||
};
|
||||
style::PaletteChanged() | rpl::start_with_next([=] {
|
||||
min->setTextColorOverride(st::windowSubTextFg->c);
|
||||
max->setTextColorOverride(st::windowSubTextFg->c);
|
||||
}, raw->lifetime());
|
||||
updateByValue(value);
|
||||
state->indexMin = 0;
|
||||
|
||||
slider->setPseudoDiscrete(
|
||||
valuesCount,
|
||||
valueByIndex,
|
||||
value,
|
||||
progress,
|
||||
finished,
|
||||
state->indexMin);
|
||||
slider->resize(slider->width(), sliderStyle->seekSize.height());
|
||||
|
||||
raw->widthValue() | rpl::start_with_next([=](int width) {
|
||||
labels->resizeToWidth(width);
|
||||
updateByIndex();
|
||||
}, slider->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditNoPaidMessagesExceptions(
|
||||
not_null<Window::SessionController*> window,
|
||||
const Api::UserPrivacy::Rule &value) {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
|
||||
&window->session(),
|
||||
tr::lng_messages_privacy_remove_fee(),
|
||||
value.always,
|
||||
std::optional<SpecialRowType>());
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
auto copy = value;
|
||||
auto &setTo = copy.always;
|
||||
setTo.peers = box->collectSelectedRows();
|
||||
setTo.premiums = false;
|
||||
setTo.miniapps = false;
|
||||
auto &removeFrom = copy.never;
|
||||
for (const auto peer : setTo.peers) {
|
||||
removeFrom.peers.erase(
|
||||
ranges::remove(removeFrom.peers, peer),
|
||||
end(removeFrom.peers));
|
||||
}
|
||||
window->session().api().userPrivacy().save(
|
||||
Api::UserPrivacy::Key::NoPaidMessages,
|
||||
copy);
|
||||
box->closeBox();
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
};
|
||||
window->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EditPrivacyController::hasOption(Option option) const {
|
||||
@@ -812,19 +954,27 @@ void EditMessagesPrivacyBox(
|
||||
|
||||
constexpr auto kOptionAll = 0;
|
||||
constexpr auto kOptionPremium = 1;
|
||||
constexpr auto kOptionCharge = 2;
|
||||
|
||||
const auto session = &controller->session();
|
||||
const auto allowed = [=] {
|
||||
return controller->session().premium()
|
||||
|| controller->session().appConfig().newRequirePremiumFree();
|
||||
return session->premium()
|
||||
|| session->appConfig().newRequirePremiumFree();
|
||||
};
|
||||
const auto privacy = &controller->session().api().globalPrivacy();
|
||||
const auto privacy = &session->api().globalPrivacy();
|
||||
const auto inner = box->verticalLayout();
|
||||
inner->add(object_ptr<Ui::PlainShadow>(box));
|
||||
|
||||
Ui::AddSkip(inner, st::messagePrivacyTopSkip);
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_messages_privacy_subtitle());
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
|
||||
privacy->newRequirePremiumCurrent() ? kOptionPremium : kOptionAll);
|
||||
(!allowed()
|
||||
? kOptionAll
|
||||
: privacy->newRequirePremiumCurrent()
|
||||
? kOptionPremium
|
||||
: privacy->newChargeStarsCurrent()
|
||||
? kOptionCharge
|
||||
: kOptionAll));
|
||||
inner->add(
|
||||
object_ptr<Ui::Radiobutton>(
|
||||
inner,
|
||||
@@ -846,6 +996,92 @@ void EditMessagesPrivacyBox(
|
||||
0,
|
||||
st::messagePrivacyBottomSkip));
|
||||
|
||||
Ui::AddDividerText(inner, tr::lng_messages_privacy_about());
|
||||
|
||||
const auto available = session->appConfig().paidMessagesAvailable();
|
||||
|
||||
const auto charged = available
|
||||
? inner->add(
|
||||
object_ptr<Ui::Radiobutton>(
|
||||
inner,
|
||||
group,
|
||||
kOptionCharge,
|
||||
tr::lng_messages_privacy_charge(tr::now),
|
||||
st::messagePrivacyCheck),
|
||||
st::settingsSendTypePadding + style::margins(
|
||||
0,
|
||||
st::messagePrivacyBottomSkip,
|
||||
0,
|
||||
st::messagePrivacyBottomSkip))
|
||||
: nullptr;
|
||||
|
||||
struct State {
|
||||
rpl::variable<int> stars;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
const auto savedValue = privacy->newChargeStarsCurrent();
|
||||
|
||||
if (available) {
|
||||
Ui::AddDividerText(inner, tr::lng_messages_privacy_charge_about());
|
||||
|
||||
const auto chargeWrap = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
inner,
|
||||
object_ptr<Ui::VerticalLayout>(inner)));
|
||||
const auto chargeInner = chargeWrap->entity();
|
||||
|
||||
Ui::AddSkip(chargeInner);
|
||||
|
||||
state->stars = SetupChargeSlider(
|
||||
chargeInner,
|
||||
session->user(),
|
||||
savedValue);
|
||||
|
||||
Ui::AddSkip(chargeInner);
|
||||
Ui::AddSubsectionTitle(
|
||||
chargeInner,
|
||||
tr::lng_messages_privacy_exceptions());
|
||||
|
||||
const auto key = Api::UserPrivacy::Key::NoPaidMessages;
|
||||
session->api().userPrivacy().reload(key);
|
||||
auto label = session->api().userPrivacy().value(
|
||||
key
|
||||
) | rpl::map([=](const Api::UserPrivacy::Rule &value) {
|
||||
using namespace Settings;
|
||||
const auto always = ExceptionUsersCount(value.always.peers);
|
||||
return always
|
||||
? tr::lng_edit_privacy_exceptions_count(
|
||||
tr::now,
|
||||
lt_count,
|
||||
always)
|
||||
: QString();
|
||||
});
|
||||
|
||||
const auto exceptions = Settings::AddButtonWithLabel(
|
||||
chargeInner,
|
||||
tr::lng_messages_privacy_remove_fee(),
|
||||
std::move(label),
|
||||
st::settingsButtonNoIcon);
|
||||
|
||||
const auto shower = exceptions->lifetime().make_state<rpl::lifetime>();
|
||||
exceptions->setClickedCallback([=] {
|
||||
*shower = session->api().userPrivacy().value(
|
||||
key
|
||||
) | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](const Api::UserPrivacy::Rule &value) {
|
||||
EditNoPaidMessagesExceptions(controller, value);
|
||||
});
|
||||
});
|
||||
Ui::AddSkip(chargeInner);
|
||||
Ui::AddDividerText(
|
||||
chargeInner,
|
||||
tr::lng_messages_privacy_remove_about());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
chargeWrap->toggleOn(group->value() | rpl::map(_1 == kOptionCharge));
|
||||
chargeWrap->finishAnimating();
|
||||
}
|
||||
using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
|
||||
const auto toast = std::make_shared<WeakToast>();
|
||||
const auto showToast = [=] {
|
||||
@@ -875,19 +1111,20 @@ void EditMessagesPrivacyBox(
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
if (!allowed()) {
|
||||
CreateRadiobuttonLock(restricted, st::messagePrivacyCheck);
|
||||
if (charged) {
|
||||
CreateRadiobuttonLock(charged, st::messagePrivacyCheck);
|
||||
}
|
||||
|
||||
group->setChangedCallback([=](int value) {
|
||||
if (value == kOptionPremium) {
|
||||
if (value == kOptionPremium || value == kOptionCharge) {
|
||||
group->setValue(kOptionAll);
|
||||
showToast();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ui::AddDividerText(inner, tr::lng_messages_privacy_about());
|
||||
if (!allowed()) {
|
||||
Ui::AddSkip(inner);
|
||||
Settings::AddButtonWithIcon(
|
||||
inner,
|
||||
@@ -907,8 +1144,12 @@ void EditMessagesPrivacyBox(
|
||||
} else {
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
if (allowed()) {
|
||||
privacy->updateNewRequirePremium(
|
||||
group->current() == kOptionPremium);
|
||||
const auto value = group->current();
|
||||
const auto premiumRequired = (value == kOptionPremium);
|
||||
const auto chargeStars = (value == kOptionCharge)
|
||||
? state->stars.current()
|
||||
: 0;
|
||||
privacy->updateMessagesPrivacy(premiumRequired, chargeStars);
|
||||
box->closeBox();
|
||||
} else {
|
||||
showToast();
|
||||
@@ -919,3 +1160,78 @@ void EditMessagesPrivacyBox(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<int> SetupChargeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
int savedValue) {
|
||||
struct State {
|
||||
rpl::variable<int> stars;
|
||||
};
|
||||
const auto group = !peer->isUser();
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
const auto chargeStars = savedValue ? savedValue : kDefaultChargeStars;
|
||||
state->stars = chargeStars;
|
||||
|
||||
Ui::AddSubsectionTitle(container, group
|
||||
? tr::lng_rights_charge_price()
|
||||
: tr::lng_messages_privacy_price());
|
||||
|
||||
auto values = std::vector<int>();
|
||||
const auto maxStars = peer->session().appConfig().paidMessageStarsMax();
|
||||
if (chargeStars < kStarsMin) {
|
||||
values.push_back(chargeStars);
|
||||
}
|
||||
for (auto i = kStarsMin; i < std::min(100, maxStars); ++i) {
|
||||
values.push_back(i);
|
||||
}
|
||||
for (auto i = 100; i < std::min(1000, maxStars); i += 10) {
|
||||
if (i < chargeStars + 10 && chargeStars < i) {
|
||||
values.push_back(chargeStars);
|
||||
}
|
||||
values.push_back(i);
|
||||
}
|
||||
for (auto i = 1000; i < maxStars + 1; i += 100) {
|
||||
if (i < chargeStars + 100 && chargeStars < i) {
|
||||
values.push_back(chargeStars);
|
||||
}
|
||||
values.push_back(i);
|
||||
}
|
||||
const auto valuesCount = int(values.size());
|
||||
const auto setStars = [=](int value) {
|
||||
state->stars = value;
|
||||
};
|
||||
container->add(
|
||||
MakeChargeStarsSlider(
|
||||
container,
|
||||
&st::settingsScale,
|
||||
&st::settingsScaleLabel,
|
||||
valuesCount,
|
||||
[=](int index) { return values[index]; },
|
||||
chargeStars,
|
||||
maxStars,
|
||||
setStars,
|
||||
setStars),
|
||||
st::boxRowPadding);
|
||||
|
||||
const auto skip = 2 * st::defaultVerticalListSkip;
|
||||
Ui::AddSkip(container, skip);
|
||||
|
||||
auto dollars = state->stars.value() | rpl::map([=](int stars) {
|
||||
const auto ratio = peer->session().appConfig().starsWithdrawRate();
|
||||
const auto dollars = int(base::SafeRound(stars * ratio));
|
||||
return '~' + Ui::FillAmountAndCurrency(dollars, u"USD"_q);
|
||||
});
|
||||
const auto percent = peer->session().appConfig().paidMessageCommission();
|
||||
Ui::AddDividerText(
|
||||
container,
|
||||
(group
|
||||
? tr::lng_rights_charge_price_about
|
||||
: tr::lng_messages_privacy_price_about)(
|
||||
lt_percent,
|
||||
rpl::single(QString::number(percent / 10.) + '%'),
|
||||
lt_amount,
|
||||
std::move(dollars)));
|
||||
|
||||
return state->stars.value();
|
||||
}
|
||||
|
||||
@@ -169,3 +169,8 @@ private:
|
||||
void EditMessagesPrivacyBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
[[nodiscard]] rpl::producer<int> SetupChargeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
int savedValue);
|
||||
|
||||
@@ -441,13 +441,10 @@ void EditFilterBox(
|
||||
using namespace Window;
|
||||
return window->isGifPausedAtLeastFor(GifPauseReason::Layer);
|
||||
};
|
||||
name->setCustomTextContext([=](Fn<void()> repaint) {
|
||||
return std::any(Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = std::move(repaint),
|
||||
.customEmojiLoopLimit = value ? -1 : 0,
|
||||
});
|
||||
}, [paused] {
|
||||
name->setCustomTextContext(Core::TextContext({
|
||||
.session = session,
|
||||
.customEmojiLoopLimit = value ? -1 : 0,
|
||||
}), [paused] {
|
||||
return On(PowerSaving::kEmojiChat) || paused();
|
||||
}, [paused] {
|
||||
return On(PowerSaving::kChatSpoiler) || paused();
|
||||
@@ -609,10 +606,7 @@ void EditFilterBox(
|
||||
float64 alpha = 1.;
|
||||
};
|
||||
const auto tag = preview->lifetime().make_state<TagState>();
|
||||
tag->context.textContext = Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [] {},
|
||||
};
|
||||
tag->context.textContext = Core::TextContext({ .session = session });
|
||||
preview->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(preview);
|
||||
p.setOpacity(tag->alpha);
|
||||
|
||||
@@ -163,10 +163,10 @@ ExceptionRow::ExceptionRow(
|
||||
st::defaultTextStyle,
|
||||
filters,
|
||||
kMarkupTextOptions,
|
||||
Core::MarkedTextContext{
|
||||
Core::TextContext({
|
||||
.session = &history->session(),
|
||||
.customEmojiRepaint = repaint,
|
||||
});
|
||||
.repaint = repaint,
|
||||
}));
|
||||
} else if (peer()->isSelf()) {
|
||||
setCustomStatus(tr::lng_saved_forward_here(tr::now));
|
||||
}
|
||||
|
||||
@@ -537,13 +537,6 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
|
||||
verticalLayout->add(std::move(icon.widget));
|
||||
|
||||
const auto isStatic = _filterTitle.isStatic;
|
||||
const auto makeContext = [=](Fn<void()> update) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &_window->session(),
|
||||
.customEmojiRepaint = update,
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
};
|
||||
};
|
||||
verticalLayout->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
verticalLayout,
|
||||
@@ -559,7 +552,10 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
|
||||
Ui::Text::WithEntities)),
|
||||
st::settingsFilterDividerLabel,
|
||||
st::defaultPopupMenu,
|
||||
makeContext)),
|
||||
Core::TextContext({
|
||||
.session = &_window->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}))),
|
||||
st::filterLinkDividerLabelPadding);
|
||||
|
||||
verticalLayout->geometryValue(
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "api/api_credits.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "core/ui_integration.h" // TextContext.
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
@@ -67,14 +68,9 @@ void GiftCreditsBox(
|
||||
2.);
|
||||
{
|
||||
Ui::AddSkip(content);
|
||||
const auto arrow = Ui::Text::SingleCustomEmoji(
|
||||
peer->owner().customEmojiManager().registerInternalEmoji(
|
||||
st::topicButtonArrow,
|
||||
st::channelEarnLearnArrowMargins,
|
||||
true));
|
||||
auto link = tr::lng_credits_box_history_entry_gift_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(arrow),
|
||||
rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Ui::Text::RichLangValue
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(
|
||||
@@ -92,7 +88,7 @@ void GiftCreditsBox(
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
{ .session = &peer->session() },
|
||||
Core::TextContext({ .session = &peer->session() }),
|
||||
st::creditsBoxAbout)),
|
||||
st::boxRowPadding);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace Api {
|
||||
struct GiftCode;
|
||||
} // namespace Api
|
||||
|
||||
namespace ChatHelpers {
|
||||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Data {
|
||||
struct Boost;
|
||||
struct CreditsHistoryEntry;
|
||||
@@ -21,6 +25,14 @@ struct GiveawayResults;
|
||||
struct SubscriptionEntry;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Settings {
|
||||
struct CreditsEntryBoxStyleOverrides;
|
||||
} // namespace Settings
|
||||
|
||||
namespace Ui {
|
||||
class GenericBox;
|
||||
class VerticalLayout;
|
||||
@@ -54,29 +66,37 @@ void ResolveGiveawayInfo(
|
||||
std::optional<Data::GiveawayStart> start,
|
||||
std::optional<Data::GiveawayResults> results);
|
||||
|
||||
[[nodiscard]] QString TonAddressUrl(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &address);
|
||||
|
||||
void AddStarGiftTable(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry,
|
||||
Fn<void(bool)> toggleVisibility,
|
||||
Fn<void()> convertToStars,
|
||||
Fn<void()> startUpgrade);
|
||||
void AddCreditsHistoryEntryTable(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
void AddSubscriptionEntryTable(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::SubscriptionEntry &s);
|
||||
void AddSubscriberEntryTable(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
not_null<PeerData*> peer,
|
||||
TimeId date);
|
||||
|
||||
void AddCreditsBoostTable(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::Boost &boost);
|
||||
|
||||
@@ -72,6 +72,16 @@ ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
|
||||
if (peer != item->history()->peer) {
|
||||
return {};
|
||||
}
|
||||
{
|
||||
const auto author = item->author();
|
||||
if (author == peer) {
|
||||
return {};
|
||||
} else if (const auto channel = author->asChannel()) {
|
||||
if (channel->linkedChat() == peer) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!item->suggestBanReport()) {
|
||||
result.allCanBan = false;
|
||||
}
|
||||
@@ -443,10 +453,7 @@ void CreateModerateMessagesBox(
|
||||
) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||
raw->setMarkedText(
|
||||
Ui::Text::Link(text, u"internal:"_q),
|
||||
Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [=] { raw->update(); },
|
||||
});
|
||||
Core::TextContext({ .session = session }));
|
||||
}, label->lifetime());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
@@ -1154,8 +1154,7 @@ RecoverBox::RecoverBox(
|
||||
rpl::single(Ui::Text::WrapEmailPattern(pattern)),
|
||||
Ui::Text::WithEntities),
|
||||
st::termsContent,
|
||||
st::defaultPopupMenu,
|
||||
[=](Fn<void()> update) { return CommonTextContext{ std::move(update) }; })
|
||||
st::defaultPopupMenu)
|
||||
, _closeParent(std::move(closeParent)) {
|
||||
_patternLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (_cloudFields.pendingResetDate != 0 || !session) {
|
||||
|
||||
@@ -39,6 +39,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <xxhash.h> // XXH64.
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
// AyuGram includes
|
||||
#include "styles/style_ayu_icons.h"
|
||||
|
||||
|
||||
[[nodiscard]] PeerListRowId UniqueRowIdFromString(const QString &d) {
|
||||
return XXH64(d.data(), d.size() * sizeof(ushort), 0);
|
||||
}
|
||||
@@ -800,6 +804,12 @@ int PeerListRow::paintNameIconGetWidth(
|
||||
.verified = &(selected
|
||||
? st::dialogsVerifiedIconOver
|
||||
: st::dialogsVerifiedIcon),
|
||||
.exteraOfficial = &(selected
|
||||
? st::dialogsExteraOfficialIcon.over
|
||||
: st::dialogsExteraOfficialIcon.icon),
|
||||
.exteraSupporter = &(selected
|
||||
? st::dialogsExteraSupporterIcon.over
|
||||
: st::dialogsExteraSupporterIcon.icon),
|
||||
.premium = &(selected
|
||||
? st::dialogsPremiumIcon.over
|
||||
: st::dialogsPremiumIcon.icon),
|
||||
@@ -873,6 +883,7 @@ void PeerListRow::paintUserpic(
|
||||
} else if (const auto callback = generatePaintUserpicCallback(false)) {
|
||||
callback(p, x, y, outerWidth, st.photoSize);
|
||||
}
|
||||
paintUserpicOverlay(p, st, x, y, outerWidth);
|
||||
}
|
||||
|
||||
// Emulates Ui::RoundImageCheckbox::paint() in a checked state.
|
||||
@@ -912,7 +923,13 @@ void PeerListRow::paintDisabledCheckUserpic(
|
||||
|
||||
p.setPen(userpicBorderPen);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawEllipse(userpicEllipse);
|
||||
if (peer()->forum()) {
|
||||
const auto radius = userpicDiameter
|
||||
* Ui::ForumUserpicRadiusMultiplier();
|
||||
p.drawRoundedRect(userpicEllipse, radius, radius);
|
||||
} else {
|
||||
p.drawEllipse(userpicEllipse);
|
||||
}
|
||||
|
||||
p.setPen(iconBorderPen);
|
||||
p.setBrush(st.disabledCheckFg);
|
||||
|
||||
@@ -95,6 +95,13 @@ public:
|
||||
[[nodiscard]] virtual QString generateShortName();
|
||||
[[nodiscard]] virtual auto generatePaintUserpicCallback(
|
||||
bool forceRound) -> PaintRoundImageCallback;
|
||||
virtual void paintUserpicOverlay(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth) {
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> &;
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "api/api_premium.h" // MessageMoneyRestriction.
|
||||
#include "base/random.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
#include "settings/settings_premium.h"
|
||||
@@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "payments/ui/payments_reaction_box.h"
|
||||
#include "ui/effects/outline_segments.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "window/window_separate_id.h"
|
||||
@@ -275,40 +276,71 @@ bool PeerListGlobalSearchController::isLoading() {
|
||||
return _timer.isActive() || _requestId;
|
||||
}
|
||||
|
||||
struct RecipientRow::Restriction {
|
||||
Api::MessageMoneyRestriction value;
|
||||
RestrictionBadgeCache cache;
|
||||
};
|
||||
|
||||
RecipientRow::RecipientRow(
|
||||
not_null<PeerData*> peer,
|
||||
const style::PeerListItem *maybeLockedSt,
|
||||
History *maybeHistory)
|
||||
: PeerListRow(peer)
|
||||
, _maybeHistory(maybeHistory)
|
||||
, _resolvePremiumRequired(maybeLockedSt != nullptr) {
|
||||
if (maybeLockedSt
|
||||
&& (Api::ResolveRequiresPremiumToWrite(peer, maybeHistory)
|
||||
== Api::RequirePremiumState::Yes)) {
|
||||
_lockedSt = maybeLockedSt;
|
||||
, _maybeLockedSt(maybeLockedSt) {
|
||||
if (_maybeLockedSt) {
|
||||
setRestriction(Api::ResolveMessageMoneyRestrictions(
|
||||
peer,
|
||||
maybeHistory));
|
||||
}
|
||||
}
|
||||
|
||||
PaintRoundImageCallback RecipientRow::generatePaintUserpicCallback(
|
||||
bool forceRound) {
|
||||
auto result = PeerListRow::generatePaintUserpicCallback(forceRound);
|
||||
if (const auto st = _lockedSt) {
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
result(p, x, y, outerWidth, size);
|
||||
PaintPremiumRequiredLock(p, st, x, y, outerWidth, size);
|
||||
};
|
||||
Api::MessageMoneyRestriction RecipientRow::restriction() const {
|
||||
return _restriction
|
||||
? _restriction->value
|
||||
: Api::MessageMoneyRestriction();
|
||||
}
|
||||
|
||||
void RecipientRow::setRestriction(Api::MessageMoneyRestriction restriction) {
|
||||
if (!restriction) {
|
||||
_restriction = nullptr;
|
||||
return;
|
||||
} else if (!_restriction) {
|
||||
_restriction = std::make_unique<Restriction>();
|
||||
}
|
||||
_restriction->value = restriction;
|
||||
}
|
||||
|
||||
void RecipientRow::paintUserpicOverlay(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth) {
|
||||
if (const auto &r = _restriction) {
|
||||
PaintRestrictionBadge(
|
||||
p,
|
||||
_maybeLockedSt,
|
||||
r->value.starsPerMessage,
|
||||
r->cache,
|
||||
x,
|
||||
y,
|
||||
outerWidth,
|
||||
st.photoSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RecipientRow::refreshLock(
|
||||
not_null<const style::PeerListItem*> maybeLockedSt) {
|
||||
if (const auto user = peer()->asUser()) {
|
||||
const auto locked = _resolvePremiumRequired
|
||||
&& (Api::ResolveRequiresPremiumToWrite(user, _maybeHistory)
|
||||
== Api::RequirePremiumState::Yes);
|
||||
if (this->locked() != locked) {
|
||||
setLocked(locked ? maybeLockedSt.get() : nullptr);
|
||||
using Restriction = Api::MessageMoneyRestriction;
|
||||
const auto r = _maybeLockedSt
|
||||
? Api::ResolveMessageMoneyRestrictions(
|
||||
user,
|
||||
_maybeHistory)
|
||||
: Restriction();
|
||||
if ((_restriction ? _restriction->value : Restriction()) != r) {
|
||||
setRestriction(r);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -318,22 +350,30 @@ bool RecipientRow::refreshLock(
|
||||
void RecipientRow::preloadUserpic() {
|
||||
PeerListRow::preloadUserpic();
|
||||
|
||||
if (!_resolvePremiumRequired) {
|
||||
if (!_maybeLockedSt) {
|
||||
return;
|
||||
} else if (Api::ResolveRequiresPremiumToWrite(peer(), _maybeHistory)
|
||||
== Api::RequirePremiumState::Unknown) {
|
||||
const auto user = peer()->asUser();
|
||||
user->session().api().premium().resolvePremiumRequired(user);
|
||||
}
|
||||
const auto peer = this->peer();
|
||||
const auto known = Api::ResolveMessageMoneyRestrictions(
|
||||
peer,
|
||||
_maybeHistory).known;
|
||||
if (known) {
|
||||
return;
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
const auto api = &user->session().api();
|
||||
api->premium().resolveMessageMoneyRestrictions(user);
|
||||
} else if (const auto group = peer->asChannel()) {
|
||||
group->updateFull();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackPremiumRequiredChanges(
|
||||
void TrackMessageMoneyRestrictionsChanges(
|
||||
not_null<PeerListController*> controller,
|
||||
rpl::lifetime &lifetime) {
|
||||
const auto session = &controller->session();
|
||||
rpl::merge(
|
||||
Data::AmPremiumValue(session) | rpl::to_empty,
|
||||
session->api().premium().somePremiumRequiredResolved()
|
||||
session->api().premium().someMessageMoneyRestrictionsResolved()
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto st = &controller->computeListSt().item;
|
||||
const auto delegate = controller->delegate();
|
||||
@@ -726,7 +766,7 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
|
||||
return std::make_unique<PeerListRow>(user);
|
||||
}
|
||||
|
||||
RecipientPremiumRequiredError WritePremiumRequiredError(
|
||||
RecipientMoneyRestrictionError WriteMoneyRestrictionError(
|
||||
not_null<UserData*> user) {
|
||||
return {
|
||||
.text = tr::lng_send_non_premium_message_toast(
|
||||
@@ -759,7 +799,7 @@ ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
, _session(args.session)
|
||||
, _callback(std::move(args.callback))
|
||||
, _filter(std::move(args.filter))
|
||||
, _premiumRequiredError(std::move(args.premiumRequiredError)) {
|
||||
, _moneyRestrictionError(std::move(args.moneyRestrictionError)) {
|
||||
}
|
||||
|
||||
Main::Session &ChooseRecipientBoxController::session() const {
|
||||
@@ -769,14 +809,17 @@ Main::Session &ChooseRecipientBoxController::session() const {
|
||||
void ChooseRecipientBoxController::prepareViewHook() {
|
||||
delegate()->peerListSetTitle(tr::lng_forward_choose());
|
||||
|
||||
if (_premiumRequiredError) {
|
||||
TrackPremiumRequiredChanges(this, lifetime());
|
||||
if (_moneyRestrictionError) {
|
||||
TrackMessageMoneyRestrictionsChanges(this, lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
bool ChooseRecipientBoxController::showLockedError(
|
||||
not_null<PeerListRow*> row) {
|
||||
return RecipientRow::ShowLockedError(this, row, _premiumRequiredError);
|
||||
return RecipientRow::ShowLockedError(
|
||||
this,
|
||||
row,
|
||||
_moneyRestrictionError);
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
@@ -836,8 +879,9 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
bool RecipientRow::ShowLockedError(
|
||||
not_null<PeerListController*> controller,
|
||||
not_null<PeerListRow*> row,
|
||||
Fn<RecipientPremiumRequiredError(not_null<UserData*>)> error) {
|
||||
if (!static_cast<RecipientRow*>(row.get())->locked()) {
|
||||
Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error) {
|
||||
const auto recipient = static_cast<RecipientRow*>(row.get());
|
||||
if (!recipient->restriction().premiumRequired) {
|
||||
return false;
|
||||
}
|
||||
::Settings::ShowPremiumPromoToast(
|
||||
@@ -860,15 +904,15 @@ auto ChooseRecipientBoxController::createRow(
|
||||
: ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|
||||
|| peer->isRepliesChat()
|
||||
|| peer->isVerifyCodes()
|
||||
|| (peer->isUser() && (_premiumRequiredError
|
||||
? !peer->asUser()->canSendIgnoreRequirePremium()
|
||||
|| (peer->isUser() && (_moneyRestrictionError
|
||||
? !peer->asUser()->canSendIgnoreMoneyRestrictions()
|
||||
: !Data::CanSendAnything(peer))));
|
||||
if (skip) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<Row>(
|
||||
history,
|
||||
_premiumRequiredError ? &computeListSt().item : nullptr);
|
||||
_moneyRestrictionError ? &computeListSt().item : nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1093,25 +1137,61 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
|
||||
return skip ? nullptr : std::make_unique<Row>(topic);
|
||||
};
|
||||
|
||||
void PaintPremiumRequiredLock(
|
||||
void PaintRestrictionBadge(
|
||||
Painter &p,
|
||||
not_null<const style::PeerListItem*> st,
|
||||
int stars,
|
||||
RestrictionBadgeCache &cache,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto paletteVersion = style::PaletteVersion();
|
||||
const auto good = !cache.badge.isNull()
|
||||
&& (cache.stars == stars)
|
||||
&& (cache.paletteVersion == paletteVersion);
|
||||
const auto &check = st->checkbox.check;
|
||||
auto pen = check.border->p;
|
||||
pen.setWidthF(check.width);
|
||||
p.setPen(pen);
|
||||
p.setBrush(st::premiumButtonBg2);
|
||||
const auto &icon = st::stickersPremiumLock;
|
||||
const auto width = icon.width();
|
||||
const auto height = icon.height();
|
||||
const auto rect = QRect(
|
||||
QPoint(x + size - width, y + size - height),
|
||||
icon.size());
|
||||
p.drawEllipse(rect);
|
||||
icon.paintInCenter(p, rect);
|
||||
const auto add = check.width;
|
||||
if (!good) {
|
||||
cache.stars = stars;
|
||||
cache.paletteVersion = paletteVersion;
|
||||
if (stars) {
|
||||
const auto text = (stars >= 1000)
|
||||
? (QString::number(stars / 1000) + 'K')
|
||||
: QString::number(stars);
|
||||
cache.badge = Ui::GenerateSmallBadgeImage(
|
||||
text,
|
||||
st::paidReactTopStarIcon,
|
||||
check.bgActive->c,
|
||||
st::premiumButtonFg->c,
|
||||
&check);
|
||||
} else {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto &icon = st::stickersPremiumLock;
|
||||
const auto width = icon.width();
|
||||
const auto height = icon.height();
|
||||
const auto rect = QRect(
|
||||
QPoint(x + size - width, y + size - height),
|
||||
icon.size());
|
||||
const auto added = QMargins(add, add, add, add);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
cache.badge = QImage(
|
||||
(rect + added).size() * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.badge.setDevicePixelRatio(ratio);
|
||||
cache.badge.fill(Qt::transparent);
|
||||
const auto inner = QRect(add, add, rect.width(), rect.height());
|
||||
auto q = QPainter(&cache.badge);
|
||||
auto pen = check.border->p;
|
||||
pen.setWidthF(check.width);
|
||||
q.setPen(pen);
|
||||
q.setBrush(st::premiumButtonBg2);
|
||||
q.drawEllipse(inner);
|
||||
icon.paintInCenter(q, inner);
|
||||
}
|
||||
}
|
||||
const auto cached = cache.badge.size() / cache.badge.devicePixelRatio();
|
||||
const auto left = x + size + add - cached.width();
|
||||
const auto top = stars ? (y - add) : (y + size + add - cached.height());
|
||||
p.drawImage(left, top, cache.badge);
|
||||
}
|
||||
|
||||