Compare commits
333 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e675dc1ef1 | ||
|
|
8cb980a791 | ||
|
|
785372f5d0 | ||
|
|
aa6495a257 | ||
|
|
7993c6207a | ||
|
|
6af93b3497 | ||
|
|
153fb3e579 | ||
|
|
80d4c3affe | ||
|
|
9a1d9deea5 | ||
|
|
3b7cdb5748 | ||
|
|
f542a026ec | ||
|
|
629314cfa2 | ||
|
|
c320917069 | ||
|
|
9276f3dab8 | ||
|
|
1316d14f7a | ||
|
|
0737034ea6 | ||
|
|
9a54473e03 | ||
|
|
991fe491c5 | ||
|
|
6e606f3bb6 | ||
|
|
d8a0497a7e | ||
|
|
fc4682d77e | ||
|
|
a507edb67a | ||
|
|
bd8b90055e | ||
|
|
371ba40a50 | ||
|
|
a8d8b5be28 | ||
|
|
3e428faa2e | ||
|
|
3887fbc437 | ||
|
|
a4b0443047 | ||
|
|
a73ff8f5d7 | ||
|
|
0c8400212e | ||
|
|
dcfc3431f5 | ||
|
|
be60052b2d | ||
|
|
fcf88a9d1f | ||
|
|
9e7a4e59a8 | ||
|
|
29e30d2d00 | ||
|
|
efc06b7951 | ||
|
|
c15f89c260 | ||
|
|
9dcea2cdef | ||
|
|
080ab26804 | ||
|
|
d58f312342 | ||
|
|
b5bba2e4e2 | ||
|
|
26f287fae0 | ||
|
|
fb33951c94 | ||
|
|
421e7a960b | ||
|
|
fe987e3f22 | ||
|
|
d3071acad2 | ||
|
|
959f23f475 | ||
|
|
820d367e78 | ||
|
|
6ff3d9f975 | ||
|
|
da2f1b3d97 | ||
|
|
0b2b3416d7 | ||
|
|
1c514b23ea | ||
|
|
592c471f58 | ||
|
|
fdee5f9227 | ||
|
|
9d313d2827 | ||
|
|
55fd9c5091 | ||
|
|
0dd45de254 | ||
|
|
d7f2385275 | ||
|
|
1336a7149b | ||
|
|
67d99355ca | ||
|
|
1953cc2f8a | ||
|
|
da941e4837 | ||
|
|
b5a412929e | ||
|
|
3fcb7c90e0 | ||
|
|
b772697c59 | ||
|
|
a088791752 | ||
|
|
ba9f5d138a | ||
|
|
41192e5acd | ||
|
|
a21c73facd | ||
|
|
9d4840c0de | ||
|
|
ff352d7647 | ||
|
|
fdb29a756a | ||
|
|
34a2c5c8ce | ||
|
|
fdf4129e5e | ||
|
|
45cacc94c0 | ||
|
|
e62bdd286d | ||
|
|
969b051c8f | ||
|
|
3b79d944af | ||
|
|
cddb2a4bac | ||
|
|
0c514c4b0a | ||
|
|
435e4f2783 | ||
|
|
68ad56db79 | ||
|
|
ab0f12cf1c | ||
|
|
fba0f0c49b | ||
|
|
03c08ad95f | ||
|
|
c6aba83a6b | ||
|
|
f13a2517cd | ||
|
|
7ec1af5e50 | ||
|
|
bd8cdcb520 | ||
|
|
7dbb4a0959 | ||
|
|
f6e501a431 | ||
|
|
57dc0c632e | ||
|
|
e6c2aa8021 | ||
|
|
7384cd3463 | ||
|
|
83ec449890 | ||
|
|
15f72ca6c1 | ||
|
|
afd6121cbb | ||
|
|
dd3c8430bf | ||
|
|
d5ea0149e8 | ||
|
|
6d215d3729 | ||
|
|
1ac051a812 | ||
|
|
c497e9ca9c | ||
|
|
ed356a1fc5 | ||
|
|
290e8bb449 | ||
|
|
079c36abf3 | ||
|
|
fb0eee01f2 | ||
|
|
bc96bdf7a9 | ||
|
|
18bf5c0ee2 | ||
|
|
3078a94404 | ||
|
|
af6ca8d4f1 | ||
|
|
aa5f9467f2 | ||
|
|
b3f9b16eb2 | ||
|
|
443745e3fc | ||
|
|
fcf9ca3982 | ||
|
|
8dc27339b4 | ||
|
|
da1e784803 | ||
|
|
46ebbdb547 | ||
|
|
850746b0ba | ||
|
|
5d76415a5d | ||
|
|
ee8f997c14 | ||
|
|
60aef7871a | ||
|
|
d6ee5b3456 | ||
|
|
88d1a502a5 | ||
|
|
c8ed8e0e5f | ||
|
|
99564d3d44 | ||
|
|
aac91a19ca | ||
|
|
97d8aa0a0d | ||
|
|
76330eaaa6 | ||
|
|
06d1644baa | ||
|
|
ef3ed760b1 | ||
|
|
ec0380b250 | ||
|
|
c6bc7c3de1 | ||
|
|
6e491913d6 | ||
|
|
48fb410bc7 | ||
|
|
602ba5bba9 | ||
|
|
1cd02fc3c9 | ||
|
|
dec47eafb8 | ||
|
|
579b20fff7 | ||
|
|
b4a9705564 | ||
|
|
72354f52d4 | ||
|
|
70e5f752ba | ||
|
|
53beb6f562 | ||
|
|
ed895ace66 | ||
|
|
0cba9e4a22 | ||
|
|
5314833c82 | ||
|
|
b92b8e56cb | ||
|
|
b497e5ea21 | ||
|
|
92bf925fd0 | ||
|
|
eab23df174 | ||
|
|
ce17904dcc | ||
|
|
fd2edb51e9 | ||
|
|
6d3f9017fa | ||
|
|
f0177bc6cc | ||
|
|
50b27b51a1 | ||
|
|
4074a558e7 | ||
|
|
f9173ea849 | ||
|
|
3a967bbbfe | ||
|
|
7aea54ad8f | ||
|
|
4910a60499 | ||
|
|
5356f6cd2c | ||
|
|
08ba277327 | ||
|
|
a292f8a34e | ||
|
|
7cdf20a7c5 | ||
|
|
adaa1d0c55 | ||
|
|
306179ca7c | ||
|
|
c86496add1 | ||
|
|
ad2f9438a2 | ||
|
|
fe41fbd7e9 | ||
|
|
df5602d203 | ||
|
|
fcc4503791 | ||
|
|
9ddbc75638 | ||
|
|
dc8b693f1d | ||
|
|
659a7622be | ||
|
|
a4856e4436 | ||
|
|
c9390dc02a | ||
|
|
bdd79aa2f2 | ||
|
|
ad70942d0e | ||
|
|
c8ae7c7402 | ||
|
|
8175fd19de | ||
|
|
4207995ef0 | ||
|
|
00df4625e2 | ||
|
|
79f592a84f | ||
|
|
ad7bc6326d | ||
|
|
17623640b3 | ||
|
|
d55ff7aa4a | ||
|
|
113d9742f4 | ||
|
|
acbc248f23 | ||
|
|
4f4d0bef25 | ||
|
|
ffa8a94180 | ||
|
|
51cead1445 | ||
|
|
29663a410a | ||
|
|
4824b26afd | ||
|
|
5751d29c47 | ||
|
|
9b7329b378 | ||
|
|
38f5cda800 | ||
|
|
c1b2d7ed4c | ||
|
|
89d0a71591 | ||
|
|
791addd0ee | ||
|
|
d4255bbfe4 | ||
|
|
ead427793b | ||
|
|
16e189a2ce | ||
|
|
6997e165c6 | ||
|
|
9b0cae9c97 | ||
|
|
b68ffcd75d | ||
|
|
d92580b8fc | ||
|
|
b8bdca8921 | ||
|
|
9fccdf21cc | ||
|
|
92a4b27e65 | ||
|
|
24843e3acd | ||
|
|
8561893e2e | ||
|
|
6695eda1be | ||
|
|
04d06e5b12 | ||
|
|
58b8eb8e96 | ||
|
|
eec4b72d9a | ||
|
|
3999bca823 | ||
|
|
6a7f030ee7 | ||
|
|
2c0b5b3210 | ||
|
|
9348039313 | ||
|
|
0d985b5745 | ||
|
|
d4cdd67128 | ||
|
|
f258b054e8 | ||
|
|
032e6c57e9 | ||
|
|
377b86372b | ||
|
|
24d3bcb590 | ||
|
|
2c50f7b18c | ||
|
|
3aa7f4dd62 | ||
|
|
bacc30e296 | ||
|
|
3c739912ba | ||
|
|
ae3496d6a4 | ||
|
|
f3c664859d | ||
|
|
57b752c232 | ||
|
|
2781fe2c34 | ||
|
|
5d43073efa | ||
|
|
3722e55b67 | ||
|
|
065d2e2ac9 | ||
|
|
b77756ce12 | ||
|
|
1e8dfb7315 | ||
|
|
83008fa358 | ||
|
|
f167cdad6f | ||
|
|
d15173e09d | ||
|
|
001c46f68a | ||
|
|
09ab83836f | ||
|
|
e5f2d83548 | ||
|
|
9cab06e17d | ||
|
|
8268e9f872 | ||
|
|
b2302d35fe | ||
|
|
5e82433693 | ||
|
|
abdd126dcf | ||
|
|
f3662f4873 | ||
|
|
dd52c53ec0 | ||
|
|
b13471672d | ||
|
|
ba2f92906b | ||
|
|
405d8c327d | ||
|
|
1401b19994 | ||
|
|
4724ef91f6 | ||
|
|
d7fdd80f55 | ||
|
|
9f652b0d3f | ||
|
|
3b3792ef75 | ||
|
|
c90f879c96 | ||
|
|
73e56b0340 | ||
|
|
2201159da5 | ||
|
|
eaf679916a | ||
|
|
388fe6adfb | ||
|
|
c88140e256 | ||
|
|
87b228b256 | ||
|
|
4832e3b3ab | ||
|
|
7b5781b845 | ||
|
|
2c1933bdb8 | ||
|
|
f9bf68461d | ||
|
|
8f1a8f909d | ||
|
|
4b859828c8 | ||
|
|
13103ad0e6 | ||
|
|
2d69329dc9 | ||
|
|
3fe403117a | ||
|
|
6147994713 | ||
|
|
0fbb2e77fe | ||
|
|
1350fa64f3 | ||
|
|
70ba28bbd5 | ||
|
|
16649a9661 | ||
|
|
8eb6d2e36b | ||
|
|
8b580de47e | ||
|
|
5b1a9fb3e1 | ||
|
|
2a6e2fa353 | ||
|
|
16669402cf | ||
|
|
073c1a4d0f | ||
|
|
4c276e2357 | ||
|
|
889529718f | ||
|
|
acb092f9f2 | ||
|
|
9cd194e60e | ||
|
|
9c3173e4bb | ||
|
|
62b624c390 | ||
|
|
1ee9122660 | ||
|
|
6cb01998cc | ||
|
|
0e6c899cee | ||
|
|
09a6df1fdc | ||
|
|
43bdfbf170 | ||
|
|
bcfe4ac4a3 | ||
|
|
c67c53bcdf | ||
|
|
330c6a31a2 | ||
|
|
aa674a0ea5 | ||
|
|
e01910ac68 | ||
|
|
eac13b31f1 | ||
|
|
d4a2c4a151 | ||
|
|
8fc35e45e4 | ||
|
|
2931e754c3 | ||
|
|
4e7a5eeffb | ||
|
|
6fcafeff5b | ||
|
|
3165ef9f0b | ||
|
|
ec992ae552 | ||
|
|
f0a36d6460 | ||
|
|
ffb024a5f7 | ||
|
|
140dcb033b | ||
|
|
06d9d04787 | ||
|
|
a7c29991db | ||
|
|
e3d79d46f7 | ||
|
|
476a864be2 | ||
|
|
fc3810fd7f | ||
|
|
f5bff22bb8 | ||
|
|
157b4c43ee | ||
|
|
f2d2826fc7 | ||
|
|
cfa12fb0cc | ||
|
|
d9318c9935 | ||
|
|
5132fd5010 | ||
|
|
89879e355d | ||
|
|
278ab5ebaf | ||
|
|
39294a7fe1 | ||
|
|
7727cdff92 | ||
|
|
8bd0ff7925 | ||
|
|
4f948699c0 | ||
|
|
cd00d41cca | ||
|
|
749fb52113 | ||
|
|
4975254cc1 | ||
|
|
b36063e086 |
2
.github/workflows/docker.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
cd Telegram/build/docker/centos_env
|
||||
poetry install
|
||||
DEBUG= poetry run gen_dockerfile | docker buildx build -t $IMAGE_TAG -
|
||||
DEBUG= LTO= poetry run gen_dockerfile | docker buildx build -t $IMAGE_TAG -
|
||||
|
||||
- name: Push the Docker image.
|
||||
if: ${{ github.ref_name == github.event.repository.default_branch }}
|
||||
|
||||
2
.github/workflows/mac.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
macos:
|
||||
name: MacOS
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-12
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
2
.github/workflows/win.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
|
||||
|
||||
- name: Python installs.
|
||||
run: py -m pip install pywin32
|
||||
run: pip3 install --upgrade pywin32 six
|
||||
|
||||
- name: Set up environment paths.
|
||||
shell: bash
|
||||
|
||||
6
.gitmodules
vendored
@@ -91,3 +91,9 @@
|
||||
[submodule "Telegram/ThirdParty/wayland-protocols"]
|
||||
path = Telegram/ThirdParty/wayland-protocols
|
||||
url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git
|
||||
[submodule "Telegram/ThirdParty/kimageformats"]
|
||||
path = Telegram/ThirdParty/kimageformats
|
||||
url = https://github.com/KDE/kimageformats.git
|
||||
[submodule "Telegram/ThirdParty/kcoreaddons"]
|
||||
path = Telegram/ThirdParty/kcoreaddons
|
||||
url = https://github.com/KDE/kcoreaddons.git
|
||||
|
||||
@@ -38,6 +38,8 @@ get_filename_component(cmake_helpers_loc "cmake" REALPATH)
|
||||
|
||||
include(cmake/variables.cmake)
|
||||
include(cmake/nice_target_sources.cmake)
|
||||
include(cmake/target_compile_options_if_exists.cmake)
|
||||
include(cmake/target_link_options_if_exists.cmake)
|
||||
include(cmake/target_link_static_libraries.cmake)
|
||||
include(cmake/target_link_frameworks.cmake)
|
||||
include(cmake/init_target.cmake)
|
||||
|
||||
@@ -48,7 +48,6 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
set_target_properties(Telegram PROPERTIES AUTOMOC ON)
|
||||
target_prepare_qrc(Telegram)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
@@ -113,6 +112,7 @@ PRIVATE
|
||||
api/api_chat_participants.h
|
||||
api/api_cloud_password.cpp
|
||||
api/api_cloud_password.h
|
||||
api/api_common.cpp
|
||||
api/api_common.h
|
||||
api/api_confirm_phone.cpp
|
||||
api/api_confirm_phone.h
|
||||
@@ -162,6 +162,8 @@ PRIVATE
|
||||
api/api_unread_things.h
|
||||
api/api_updates.cpp
|
||||
api/api_updates.h
|
||||
api/api_user_names.cpp
|
||||
api/api_user_names.h
|
||||
api/api_user_privacy.cpp
|
||||
api/api_user_privacy.h
|
||||
api/api_views.cpp
|
||||
@@ -178,6 +180,8 @@ PRIVATE
|
||||
boxes/peers/add_participants_box.h
|
||||
boxes/peers/edit_contact_box.cpp
|
||||
boxes/peers/edit_contact_box.h
|
||||
boxes/peers/edit_forum_topic_box.cpp
|
||||
boxes/peers/edit_forum_topic_box.h
|
||||
boxes/peers/edit_linked_chat_box.cpp
|
||||
boxes/peers/edit_linked_chat_box.h
|
||||
boxes/peers/edit_participant_box.cpp
|
||||
@@ -199,6 +203,8 @@ PRIVATE
|
||||
boxes/peers/edit_peer_requests_box.h
|
||||
boxes/peers/edit_peer_type_box.cpp
|
||||
boxes/peers/edit_peer_type_box.h
|
||||
boxes/peers/edit_peer_usernames_list.cpp
|
||||
boxes/peers/edit_peer_usernames_list.h
|
||||
boxes/peers/peer_short_info_box.cpp
|
||||
boxes/peers/peer_short_info_box.h
|
||||
boxes/peers/prepare_short_info_box.cpp
|
||||
@@ -247,8 +253,6 @@ PRIVATE
|
||||
boxes/local_storage_box.h
|
||||
boxes/max_invite_box.cpp
|
||||
boxes/max_invite_box.h
|
||||
boxes/mute_settings_box.cpp
|
||||
boxes/mute_settings_box.h
|
||||
boxes/peer_list_box.cpp
|
||||
boxes/peer_list_box.h
|
||||
boxes/peer_list_controllers.cpp
|
||||
@@ -467,6 +471,12 @@ PRIVATE
|
||||
data/data_emoji_statuses.h
|
||||
data/data_folder.cpp
|
||||
data/data_folder.h
|
||||
data/data_forum.cpp
|
||||
data/data_forum.h
|
||||
data/data_forum_icons.cpp
|
||||
data/data_forum_icons.h
|
||||
data/data_forum_topic.cpp
|
||||
data/data_forum_topic.h
|
||||
data/data_file_click_handler.cpp
|
||||
data/data_file_click_handler.h
|
||||
data/data_file_origin.cpp
|
||||
@@ -533,12 +543,16 @@ PRIVATE
|
||||
data/data_sponsored_messages.h
|
||||
data/data_streaming.cpp
|
||||
data/data_streaming.h
|
||||
data/data_thread.cpp
|
||||
data/data_thread.h
|
||||
data/data_types.cpp
|
||||
data/data_types.h
|
||||
data/data_user.cpp
|
||||
data/data_user.h
|
||||
data/data_user_photos.cpp
|
||||
data/data_user_photos.h
|
||||
data/data_user_names.cpp
|
||||
data/data_user_names.h
|
||||
data/data_wall_paper.cpp
|
||||
data/data_wall_paper.h
|
||||
data/data_web_page.cpp
|
||||
@@ -611,6 +625,8 @@ PRIVATE
|
||||
history/view/controls/history_view_compose_controls.h
|
||||
history/view/controls/history_view_compose_search.cpp
|
||||
history/view/controls/history_view_compose_search.h
|
||||
history/view/controls/history_view_forward_panel.cpp
|
||||
history/view/controls/history_view_forward_panel.h
|
||||
history/view/controls/history_view_ttl_button.cpp
|
||||
history/view/controls/history_view_ttl_button.h
|
||||
history/view/controls/history_view_voice_record_bar.cpp
|
||||
@@ -668,8 +684,6 @@ PRIVATE
|
||||
history/view/media/history_view_web_page.h
|
||||
history/view/reactions/history_view_reactions.cpp
|
||||
history/view/reactions/history_view_reactions.h
|
||||
history/view/reactions/history_view_reactions_animation.cpp
|
||||
history/view/reactions/history_view_reactions_animation.h
|
||||
history/view/reactions/history_view_reactions_button.cpp
|
||||
history/view/reactions/history_view_reactions_button.h
|
||||
history/view/reactions/history_view_reactions_list.cpp
|
||||
@@ -686,6 +700,8 @@ PRIVATE
|
||||
history/view/history_view_contact_status.h
|
||||
history/view/history_view_context_menu.cpp
|
||||
history/view/history_view_context_menu.h
|
||||
history/view/history_view_corner_buttons.cpp
|
||||
history/view/history_view_corner_buttons.h
|
||||
history/view/history_view_cursor_state.cpp
|
||||
history/view/history_view_cursor_state.h
|
||||
history/view/history_view_element.cpp
|
||||
@@ -960,6 +976,8 @@ PRIVATE
|
||||
media/view/media_view_playback_progress.cpp
|
||||
media/view/media_view_playback_progress.h
|
||||
media/view/media_view_open_common.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_mute.cpp
|
||||
menu/menu_mute.h
|
||||
menu/menu_send.cpp
|
||||
@@ -1227,9 +1245,13 @@ PRIVATE
|
||||
ui/chat/choose_send_as.h
|
||||
ui/chat/choose_theme_controller.cpp
|
||||
ui/chat/choose_theme_controller.h
|
||||
ui/effects/emoji_fly_animation.cpp
|
||||
ui/effects/emoji_fly_animation.h
|
||||
ui/effects/message_sending_animation_common.h
|
||||
ui/effects/message_sending_animation_controller.cpp
|
||||
ui/effects/message_sending_animation_controller.h
|
||||
ui/effects/reaction_fly_animation.cpp
|
||||
ui/effects/reaction_fly_animation.h
|
||||
ui/effects/send_action_animations.cpp
|
||||
ui/effects/send_action_animations.h
|
||||
ui/image/image.cpp
|
||||
@@ -1456,7 +1478,6 @@ else()
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_glibmm
|
||||
desktop-app::external_glib
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1577,6 +1598,8 @@ if (WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
target_prepare_qrc(Telegram)
|
||||
|
||||
if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT build_winstore)
|
||||
add_executable(Updater WIN32)
|
||||
init_non_host_target(Updater)
|
||||
@@ -1657,7 +1680,7 @@ endif()
|
||||
|
||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
include(GNUInstallDirs)
|
||||
configure_file("../lib/xdg/telegramdesktop.metainfo.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" @ONLY)
|
||||
configure_file("../lib/xdg/telegramdesktop.metainfo.xml" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png")
|
||||
@@ -1667,6 +1690,6 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png")
|
||||
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png")
|
||||
install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.metainfo.xml")
|
||||
install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo")
|
||||
endif()
|
||||
|
||||
18
Telegram/Resources/art/topic_icons/blue.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic blue</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#4BB7FF" offset="0%"></stop>
|
||||
<stop stop-color="#015EC1" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.39588%" id="linearGradient-2">
|
||||
<stop stop-color="#0888DF" offset="0%"></stop>
|
||||
<stop stop-color="#0042AC" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-blue" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.47368421 C52.6535116,4.47368421 62.3029461,8.44172846 69.286057,14.8715044 C76.2233062,21.2590526 80.5263158,30.0798831 80.5263158,39.8304382 C80.5263158,49.5809933 76.2233062,58.4018239 69.286057,64.7893721 C62.3029461,71.219148 52.6535116,75.1871923 42,75.1871923 C37.5267059,75.1871923 33.2308553,74.4877643 29.2348341,73.2001187 C29.0182529,73.1303293 28.8019654,73.0586229 28.5862413,72.9852481 C28.3948519,73.106612 28.2000814,73.2292009 28.0019297,73.3530173 C26.1800899,74.4914063 24.5768234,75.4183854 23.1902273,76.1332316 C19.9632993,77.7968433 15.8838664,78.895322 10.968708,79.4573148 L10.3331597,79.5269374 C10.8475353,78.5381033 11.3155893,77.6262907 11.7364879,76.7897918 L12.0856904,76.0911971 C13.1548056,73.9371347 13.8747553,72.3343394 14.2288664,71.2486595 C14.8154652,69.4501884 15.1095499,67.6701625 15.2363061,66.0328103 C15.2549358,65.7921643 15.2704685,65.5479168 15.2812363,65.2994157 C15.1072303,65.1457013 14.9345381,64.9902431 14.7631844,64.8330639 C7.79678435,58.4429296 3.47368421,49.6037286 3.47368421,39.8304382 C3.47368421,30.0798831 7.77669379,21.2590526 14.713943,14.8715044 C21.6970539,8.44172846 31.3464884,4.47368421 42,4.47368421 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.94736842" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#71D0FF" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
18
Telegram/Resources/art/topic_icons/gray.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic gray</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#A5A5A5" offset="0%"></stop>
|
||||
<stop stop-color="#616161" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.39588%" id="linearGradient-2">
|
||||
<stop stop-color="#737373" offset="0%"></stop>
|
||||
<stop stop-color="#565656" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-gray" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.47368421 C52.6535116,4.47368421 62.3029461,8.44172846 69.286057,14.8715044 C76.2233062,21.2590526 80.5263158,30.0798831 80.5263158,39.8304382 C80.5263158,49.5809933 76.2233062,58.4018239 69.286057,64.7893721 C62.3029461,71.219148 52.6535116,75.1871923 42,75.1871923 C37.5267059,75.1871923 33.2308553,74.4877643 29.2348341,73.2001187 C29.0182529,73.1303293 28.8019654,73.0586229 28.5862413,72.9852481 C28.3948519,73.106612 28.2000814,73.2292009 28.0019297,73.3530173 C26.1800899,74.4914063 24.5768234,75.4183854 23.1902273,76.1332316 C19.9632993,77.7968433 15.8838664,78.895322 10.968708,79.4573148 L10.3331597,79.5269374 C10.8475353,78.5381033 11.3155893,77.6262907 11.7364879,76.7897918 L12.0856904,76.0911971 C13.1548056,73.9371347 13.8747553,72.3343394 14.2288664,71.2486595 C14.8154652,69.4501884 15.1095499,67.6701625 15.2363061,66.0328103 C15.2549358,65.7921643 15.2704685,65.5479168 15.2812363,65.2994157 C15.1072303,65.1457013 14.9345381,64.9902431 14.7631844,64.8330639 C7.79678435,58.4429296 3.47368421,49.6037286 3.47368421,39.8304382 C3.47368421,30.0798831 7.77669379,21.2590526 14.713943,14.8715044 C21.6970539,8.44172846 31.3464884,4.47368421 42,4.47368421 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.94736842" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#B8B8B8" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
18
Telegram/Resources/art/topic_icons/green.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic green</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.7635421%" id="linearGradient-1">
|
||||
<stop stop-color="#97E334" offset="0%"></stop>
|
||||
<stop stop-color="#11B411" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="98.9250576%" id="linearGradient-2">
|
||||
<stop stop-color="#48AF18" offset="0%"></stop>
|
||||
<stop stop-color="#05951A" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-green" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.42105263 C52.6675181,4.42105263 62.3294728,8.39460913 69.3217075,14.8327858 C76.2697184,21.230243 80.5789474,30.0648871 80.5789474,39.8304382 C80.5789474,49.5959894 76.2697184,58.4306335 69.3217075,64.8280906 C62.3294728,71.2662674 52.6675181,75.2398239 42,75.2398239 C37.5210466,75.2398239 33.2197662,74.5394876 29.2186919,73.2502137 C29.0098956,73.1829329 28.8013719,73.1138726 28.5929684,73.0432995 C28.4083865,73.1602808 28.2206704,73.2783974 28.0298198,73.3976517 C26.2065565,74.5369301 24.6020235,75.4646079 23.2143446,76.1800123 C19.9826132,77.8461004 15.8972513,78.9467661 10.9744394,79.5096334 L10.3380323,79.5793501 C12.3422829,75.5502987 13.657562,72.8305079 14.1788292,71.2323391 C14.7640488,69.4380965 15.0573738,67.6622454 15.1838316,66.0287479 C15.2017691,65.7970433 15.21683,65.561992 15.2283048,65.3228731 C15.0601712,65.1741519 14.8932645,65.0238038 14.727607,64.8718496 C7.75040024,58.4718025 3.42105263,49.6187586 3.42105263,39.8304382 C3.42105263,30.0648871 7.7302816,21.230243 14.6782925,14.8327858 C21.6705272,8.39460913 31.3324819,4.42105263 42,4.42105263 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.84210526" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#C2FF71" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
18
Telegram/Resources/art/topic_icons/red.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic red</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#FF714C" offset="0%"></stop>
|
||||
<stop stop-color="#C61505" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="98.6056043%" id="linearGradient-2">
|
||||
<stop stop-color="#E12F1F" offset="0%"></stop>
|
||||
<stop stop-color="#B40101" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-red" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.42105263 C52.6675181,4.42105263 62.3294728,8.39460913 69.3217075,14.8327858 C76.2697184,21.230243 80.5789474,30.0648871 80.5789474,39.8304382 C80.5789474,49.5959894 76.2697184,58.4306335 69.3217075,64.8280906 C62.3294728,71.2662674 52.6675181,75.2398239 42,75.2398239 C37.5210466,75.2398239 33.2197662,74.5394876 29.2186919,73.2502137 C29.0098956,73.1829329 28.8013719,73.1138726 28.5929684,73.0432995 C28.4083865,73.1602808 28.2206704,73.2783974 28.0298198,73.3976517 C26.2065565,74.5369301 24.6020235,75.4646079 23.2143446,76.1800123 C19.9826132,77.8461004 15.8972513,78.9467661 10.9744394,79.5096334 L10.3380323,79.5793501 C12.3422829,75.5502987 13.657562,72.8305079 14.1788292,71.2323391 C14.7640488,69.4380965 15.0573738,67.6622454 15.1838316,66.0287479 C15.2017691,65.7970433 15.21683,65.561992 15.2283048,65.3228731 C15.0601712,65.1741519 14.8932645,65.0238038 14.727607,64.8718496 C7.75040024,58.4718025 3.42105263,49.6187586 3.42105263,39.8304382 C3.42105263,30.0648871 7.7302816,21.230243 14.6782925,14.8327858 C21.6705272,8.39460913 31.3324819,4.42105263 42,4.42105263 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.84210526" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#FFB47D" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
18
Telegram/Resources/art/topic_icons/rose.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic rose</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="4.31422203%" x2="50%" y2="99.6023762%" id="linearGradient-1">
|
||||
<stop stop-color="#FF7999" offset="0%"></stop>
|
||||
<stop stop-color="#E4215A" offset="98.5968743%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="96.4024371%" id="linearGradient-2">
|
||||
<stop stop-color="#F83B72" offset="0%"></stop>
|
||||
<stop stop-color="#BA0940" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-rose" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.42105263 C52.6675181,4.42105263 62.3294728,8.39460913 69.3217075,14.8327858 C76.2697184,21.230243 80.5789474,30.0648871 80.5789474,39.8304382 C80.5789474,49.5959894 76.2697184,58.4306335 69.3217075,64.8280906 C62.3294728,71.2662674 52.6675181,75.2398239 42,75.2398239 C37.5210466,75.2398239 33.2197662,74.5394876 29.2186919,73.2502137 C29.0098956,73.1829329 28.8013719,73.1138726 28.5929684,73.0432995 C28.4083865,73.1602808 28.2206704,73.2783974 28.0298198,73.3976517 C26.2065565,74.5369301 24.6020235,75.4646079 23.2143446,76.1800123 C19.9826132,77.8461004 15.8972513,78.9467661 10.9744394,79.5096334 L10.3380323,79.5793501 C12.3422829,75.5502987 13.657562,72.8305079 14.1788292,71.2323391 C14.7640488,69.4380965 15.0573738,67.6622454 15.1838316,66.0287479 C15.2017691,65.7970433 15.21683,65.561992 15.2283048,65.3228731 C15.0601712,65.1741519 14.8932645,65.0238038 14.727607,64.8718496 C7.75040024,58.4718025 3.42105263,49.6187586 3.42105263,39.8304382 C3.42105263,30.0648871 7.7302816,21.230243 14.6782925,14.8327858 C21.6705272,8.39460913 31.3324819,4.42105263 42,4.42105263 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.84210526" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#FFC7D6" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
18
Telegram/Resources/art/topic_icons/violet.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic violet</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.7635421%" id="linearGradient-1">
|
||||
<stop stop-color="#E57AFF" offset="0%"></stop>
|
||||
<stop stop-color="#A438BB" offset="99.8550522%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.39588%" id="linearGradient-2">
|
||||
<stop stop-color="#B239D1" offset="0%"></stop>
|
||||
<stop stop-color="#7C279A" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-violet" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.42105263 C52.6675181,4.42105263 62.3294728,8.39460913 69.3217075,14.8327858 C76.2697184,21.230243 80.5789474,30.0648871 80.5789474,39.8304382 C80.5789474,49.5959894 76.2697184,58.4306335 69.3217075,64.8280906 C62.3294728,71.2662674 52.6675181,75.2398239 42,75.2398239 C37.5210466,75.2398239 33.2197662,74.5394876 29.2186919,73.2502137 C29.0098956,73.1829329 28.8013719,73.1138726 28.5929684,73.0432995 C28.4083865,73.1602808 28.2206704,73.2783974 28.0298198,73.3976517 C26.2065565,74.5369301 24.6020235,75.4646079 23.2143446,76.1800123 C19.9826132,77.8461004 15.8972513,78.9467661 10.9744394,79.5096334 L10.3380323,79.5793501 C12.3422829,75.5502987 13.657562,72.8305079 14.1788292,71.2323391 C14.7640488,69.4380965 15.0573738,67.6622454 15.1838316,66.0287479 C15.2017691,65.7970433 15.21683,65.561992 15.2283048,65.3228731 C15.0601712,65.1741519 14.8932645,65.0238038 14.727607,64.8718496 C7.75040024,58.4718025 3.42105263,49.6187586 3.42105263,39.8304382 C3.42105263,30.0648871 7.7302816,21.230243 14.6782925,14.8327858 C21.6705272,8.39460913 31.3324819,4.42105263 42,4.42105263 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.84210526" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#F5BDFF" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
18
Telegram/Resources/art/topic_icons/yellow.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="84px" height="84px" viewBox="0 0 84 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>topic yellow</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#FFDB5C" offset="0%"></stop>
|
||||
<stop stop-color="#EA5800" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.0141482%" id="linearGradient-2">
|
||||
<stop stop-color="#F2A807" offset="0%"></stop>
|
||||
<stop stop-color="#D93A00" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="topic-yellow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M42,4.42105263 C52.6675181,4.42105263 62.3294728,8.39460913 69.3217075,14.8327858 C76.2697184,21.230243 80.5789474,30.0648871 80.5789474,39.8304382 C80.5789474,49.5959894 76.2697184,58.4306335 69.3217075,64.8280906 C62.3294728,71.2662674 52.6675181,75.2398239 42,75.2398239 C37.5210466,75.2398239 33.2197662,74.5394876 29.2186919,73.2502137 C29.0098956,73.1829329 28.8013719,73.1138726 28.5929684,73.0432995 C28.4083865,73.1602808 28.2206704,73.2783974 28.0298198,73.3976517 C26.2065565,74.5369301 24.6020235,75.4646079 23.2143446,76.1800123 C19.9826132,77.8461004 15.8972513,78.9467661 10.9744394,79.5096334 L10.3380323,79.5793501 C12.3422829,75.5502987 13.657562,72.8305079 14.1788292,71.2323391 C14.7640488,69.4380965 15.0573738,67.6622454 15.1838316,66.0287479 C15.2017691,65.7970433 15.21683,65.561992 15.2283048,65.3228731 C15.0601712,65.1741519 14.8932645,65.0238038 14.727607,64.8718496 C7.75040024,58.4718025 3.42105263,49.6187586 3.42105263,39.8304382 C3.42105263,30.0648871 7.7302816,21.230243 14.6782925,14.8327858 C21.6705272,8.39460913 31.3324819,4.42105263 42,4.42105263 Z" id="Combined-Shape-Copy-2" stroke="url(#linearGradient-2)" stroke-width="2.84210526" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M9.68078613,24.6137047 C9.8721537,24.8136848 10.1894036,24.8206666 10.3893837,24.629299 C10.3964827,24.6225057 10.4033805,24.6155051 10.410082,24.6083194 C20.5178445,13.7276637 31.3141669,8.50123177 42.7990494,8.92902374 C54.2584365,9.35586606 64.9235425,15.3681505 74.7943671,26.9658769 C75.0309355,27.243826 75.4426222,27.2904538 75.7353592,27.0724506 C76.0315877,26.8518473 76.1075038,26.440096 75.9094038,26.1283693 C67.7821181,13.3374534 56.7453333,6.69089625 42.7990494,6.18869781 C28.8220513,5.68539338 17.7581791,11.5492352 9.60743269,23.7802233 C9.4336795,24.0409463 9.46416665,24.3873362 9.68078613,24.6137047 Z" id="Path-22" fill="#F9FF71" opacity="0.37491644"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/dialogs/dialogs_forum.png
Normal file
|
After Width: | Height: | Size: 469 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_forum@2x.png
Normal file
|
After Width: | Height: | Size: 902 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_forum@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow.png
Normal file
|
After Width: | Height: | Size: 250 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@2x.png
Normal file
|
After Width: | Height: | Size: 293 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_topic_arrow@3x.png
Normal file
|
After Width: | Height: | Size: 416 B |
BIN
Telegram/Resources/icons/menu/video_chat.png
Normal file
|
After Width: | Height: | Size: 557 B |
BIN
Telegram/Resources/icons/menu/video_chat@2x.png
Normal file
|
After Width: | Height: | Size: 1022 B |
BIN
Telegram/Resources/icons/menu/video_chat@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/settings/topics.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
Telegram/Resources/icons/settings/topics@2x.png
Normal file
|
After Width: | Height: | Size: 461 B |
BIN
Telegram/Resources/icons/settings/topics@3x.png
Normal file
|
After Width: | Height: | Size: 701 B |
@@ -407,6 +407,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_username_link" = "This link opens a chat with you:";
|
||||
"lng_username_copied" = "Link copied to clipboard.";
|
||||
|
||||
"lng_usernames_edit" = "click to edit";
|
||||
"lng_usernames_active" = "active";
|
||||
"lng_usernames_non_active" = "non active";
|
||||
"lng_usernames_subtitle" = "Usernames order";
|
||||
"lng_usernames_activate_error#one" = "Sorry, you can't activate more than **{count}** usernames.";
|
||||
"lng_usernames_activate_error#other" = "Sorry, you can't activate more than **{count}** usernames.";
|
||||
"lng_usernames_activate_description" = "Do you want to show this username on your info page?";
|
||||
"lng_usernames_activate_confirm" = "Show";
|
||||
"lng_channel_usernames_subtitle" = "Links order";
|
||||
"lng_usernames_deactivate_description" = "Do you want to hide this username from your info page?";
|
||||
"lng_usernames_deactivate_confirm" = "Hide";
|
||||
"lng_usernames_description" = "Drag and drop links to change the order in which they will be displayed on your info page.";
|
||||
|
||||
"lng_channel_usernames_activate_description" = "Do you want to show this link on the channel info page?";
|
||||
"lng_channel_usernames_deactivate_description" = "Do you want to hide this link from the channel info page?";
|
||||
"lng_channel_usernames_deactivate_error" = "Sorry, you can't deactivate this link from the channel info page. ";
|
||||
"lng_channel_usernames_description" = "Drag and drop links to change the order in which they will be displayed on the channel info page.";
|
||||
|
||||
"lng_bio_title" = "Edit your bio";
|
||||
"lng_bio_placeholder" = "Bio";
|
||||
"lng_bio_about" = "You can add a few lines about yourself. Anyone who opens your profile will see this text.";
|
||||
@@ -1128,6 +1146,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_mobile_label" = "Mobile";
|
||||
"lng_info_mobile_hidden" = "Hidden";
|
||||
"lng_info_username_label" = "Username";
|
||||
"lng_info_usernames_label" = "also";
|
||||
"lng_info_bio_label" = "Bio";
|
||||
"lng_info_link_label" = "Link";
|
||||
"lng_info_location_label" = "Location";
|
||||
@@ -1136,6 +1155,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_bot_title" = "Bot Info";
|
||||
"lng_info_group_title" = "Group Info";
|
||||
"lng_info_channel_title" = "Channel Info";
|
||||
"lng_info_topic_title" = "Topic Info";
|
||||
"lng_profile_enable_notifications" = "Notifications";
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_info_add_as_contact" = "Add to contacts";
|
||||
@@ -1284,6 +1304,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_report_message_title" = "Report message";
|
||||
"lng_report_profile_photo_title" = "Report profile photo";
|
||||
"lng_report_profile_video_title" = "Report profile video";
|
||||
"lng_report_group_photo_title" = "Report group photo";
|
||||
"lng_report_group_video_title" = "Report group video";
|
||||
"lng_report_channel_photo_title" = "Report channel photo";
|
||||
"lng_report_channel_video_title" = "Report channel video";
|
||||
"lng_report_please_select_messages" = "Please select messages to report.";
|
||||
"lng_report_select_messages" = "Select messages";
|
||||
"lng_report_messages_none" = "Select Messages";
|
||||
@@ -1484,6 +1508,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topic closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
"lng_action_topic_created" = "«{topic}» was created";
|
||||
"lng_action_topic_closed" = "«{topic}» was closed";
|
||||
"lng_action_topic_reopened" = "«{topic}» was reopened";
|
||||
"lng_action_topic_placeholder" = "topic";
|
||||
"lng_action_topic_renamed" = "{from} renamed the {link} to «{title}»";
|
||||
"lng_action_topic_icon_changed" = "{from} changed the {link} icon to {emoji}";
|
||||
"lng_action_topic_icon_removed" = "{from} removed the {link} icon";
|
||||
|
||||
"lng_premium_gift_duration_months#one" = "for {count} month";
|
||||
"lng_premium_gift_duration_months#other" = "for {count} months";
|
||||
@@ -1640,10 +1674,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edited" = "edited";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
"lng_sent_date" = "Sent: {date}";
|
||||
"lng_views_tooltip#one" = "Views: {count}";
|
||||
"lng_views_tooltip#other" = "Views: {count}";
|
||||
"lng_forwards_tooltip#one" = "Forwards: {count}";
|
||||
"lng_forwards_tooltip#other" = "Forwards: {count}";
|
||||
"lng_imported" = "imported";
|
||||
"lng_admin_badge" = "admin";
|
||||
"lng_owner_badge" = "owner";
|
||||
"lng_channel_badge" = "channel";
|
||||
"lng_topic_author_badge" = "Topic Author";
|
||||
"lng_fast_reply" = "Reply";
|
||||
"lng_cancel_edit_post_sure" = "Cancel editing?";
|
||||
"lng_cancel_edit_post_yes" = "Yes";
|
||||
@@ -1985,6 +2024,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_replies_header#one" = "{count} reply";
|
||||
"lng_replies_header#other" = "{count} replies";
|
||||
"lng_replies_header_none" = "Replies";
|
||||
"lng_replies_view_topic" = "View in Topic";
|
||||
"lng_comments_header#one" = "{count} comment";
|
||||
"lng_comments_header#other" = "{count} comments";
|
||||
"lng_comments_header_none" = "Comments";
|
||||
@@ -2009,6 +2049,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dialogs_text_from_wrapped" = "{from}:";
|
||||
"lng_dialogs_text_media" = "{media_part} {caption}";
|
||||
"lng_dialogs_text_media_wrapped" = "{media},";
|
||||
"lng_dialogs_text_from_in_topic" = "{from} {topic}";
|
||||
"lng_dialogs_show_all_chats" = "Show all chats";
|
||||
"lng_dialogs_hide_muted_chats" = "Hide muted chats";
|
||||
"lng_dialogs_skip_archive_in_search" = "Skip results from archive";
|
||||
@@ -2116,6 +2157,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_send_message" = "Send message";
|
||||
"lng_context_view_group" = "View group info";
|
||||
"lng_context_view_channel" = "View channel info";
|
||||
"lng_context_view_topic" = "View topic info";
|
||||
"lng_context_hide_psa" = "Hide this announcement";
|
||||
"lng_context_pin_to_top" = "Pin to top";
|
||||
"lng_context_unpin_from_top" = "Unpin from top";
|
||||
@@ -2182,6 +2224,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_send_now_selected" = "Send selected now";
|
||||
"lng_context_reschedule_selected" = "Reschedule Selected";
|
||||
"lng_context_delete_selected" = "Delete Selected";
|
||||
"lng_context_save_images_selected" = "Save Selected";
|
||||
"lng_context_save_documents_selected" = "Download Selected";
|
||||
"lng_context_clear_selection" = "Clear Selection";
|
||||
"lng_context_seen_loading" = "Loading...";
|
||||
"lng_context_seen_text#one" = "{count} Seen";
|
||||
@@ -2362,6 +2406,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_mediaview_report_profile_photo" = "Report";
|
||||
|
||||
"lng_mediaview_saved_to" = "Image was saved to your {downloads} folder";
|
||||
"lng_mediaview_saved_images_to" = "Images were saved to your {downloads} folder";
|
||||
"lng_mediaview_downloads" = "Downloads";
|
||||
"lng_mediaview_video_loading" = "Loading - {percent}";
|
||||
"lng_mediaview_playback_speed" = "Playback speed: {speed}";
|
||||
@@ -2449,8 +2494,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration.";
|
||||
"lng_payments_webview_install_edge" = "Please install {link}.";
|
||||
"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkit2gtk-5.0/webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
|
||||
"lng_payments_webview_switch_mutter" = "Qt's window embedding doesn't work well with Mutter window manager. Please switch to another window manager or desktop environment.";
|
||||
"lng_payments_webview_switch_wayland" = "There is no way to embed WebView window on Wayland. Please switch to X11.";
|
||||
"lng_payments_webview_update_windows" = "Please update your system to Windows 8.1 or later.";
|
||||
"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
@@ -2767,6 +2810,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_menu_start_group_call" = "Start video chat";
|
||||
"lng_menu_start_group_call_scheduled" = "Schedule video chat";
|
||||
"lng_menu_start_group_call_with" = "Stream with...";
|
||||
"lng_menu_start_group_call_join" = "Join video chat";
|
||||
"lng_menu_start_group_call_options" = "Video chat";
|
||||
"lng_menu_start_group_call_channel" = "Start live stream";
|
||||
"lng_menu_start_group_call_scheduled_channel" = "Schedule live stream";
|
||||
"lng_menu_start_group_call_with_channel" = "Stream with...";
|
||||
@@ -2852,6 +2897,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_group_invite_link" = "Invite users via link";
|
||||
"lng_rights_group_invite" = "Add users";
|
||||
"lng_rights_group_pin" = "Pin messages";
|
||||
"lng_rights_group_topics" = "Manage topics";
|
||||
"lng_rights_group_add_topics" = "Create topics";
|
||||
"lng_rights_group_manage_calls" = "Manage voice chats";
|
||||
"lng_rights_group_delete" = "Delete messages";
|
||||
"lng_rights_group_anonymous" = "Remain Anonymous";
|
||||
@@ -2937,6 +2984,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_filter_voice_chats_channel" = "Live stream";
|
||||
"lng_admin_log_filter_invite_links" = "Invite links";
|
||||
"lng_admin_log_filter_members_removed" = "Leaving members";
|
||||
"lng_admin_log_filter_topics" = "Topics";
|
||||
"lng_admin_log_filter_all_admins" = "All users and admins";
|
||||
"lng_admin_log_about" = "What is this?";
|
||||
"lng_admin_log_about_text" = "This is a list of all service actions taken by the group's members and admins in the last 48 hours.";
|
||||
@@ -2961,6 +3009,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_changed_link_channel" = "{from} changed channel link:";
|
||||
"lng_admin_log_removed_link_channel" = "{from} removed channel link";
|
||||
"lng_admin_log_previous_link" = "Previous link";
|
||||
"lng_admin_log_reordered_link_group" = "{from} reordered group links:";
|
||||
"lng_admin_log_reordered_link_channel" = "{from} reordered channel links:";
|
||||
"lng_admin_log_previous_links_order" = "Previous order";
|
||||
"lng_admin_log_activated_link" = "{from} activated @{link} username";
|
||||
"lng_admin_log_deactivated_link" = "{from} deactivated @{link} username";
|
||||
"lng_admin_log_changed_photo_group" = "{from} changed group photo";
|
||||
"lng_admin_log_changed_photo_channel" = "{from} changed channel photo";
|
||||
"lng_admin_log_removed_photo_group" = "{from} removed group photo";
|
||||
@@ -3042,6 +3095,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_invite_link_label" = "Name: {previous} -> {limit}";
|
||||
"lng_admin_log_invite_link_request_needed" = "Now admin approval is required to join.";
|
||||
"lng_admin_log_invite_link_request_not_needed" = "Now admin approval is not required to join.";
|
||||
"lng_admin_log_topics_enabled" = "{from} enabled topics";
|
||||
"lng_admin_log_topics_disabled" = "{from} disabled topics";
|
||||
"lng_admin_log_topics_created" = "{from} created topic {topic}";
|
||||
"lng_admin_log_topics_changed" = "{from} changed topic {topic} to {new_topic}";
|
||||
"lng_admin_log_topics_closed" = "{from} closed topic {topic}";
|
||||
"lng_admin_log_topics_reopened" = "{from} reopened topic {topic}";
|
||||
"lng_admin_log_topics_deleted" = "{from} deleted topic {topic}";
|
||||
"lng_admin_log_topics_pinned" = "{from} pinned topic {topic}";
|
||||
"lng_admin_log_topics_unpinned" = "{from} unpinned topic {topic}";
|
||||
"lng_admin_log_restricted_forever" = "indefinitely";
|
||||
"lng_admin_log_restricted_until" = "until {date}";
|
||||
"lng_admin_log_banned_view_messages" = "Read messages";
|
||||
@@ -3058,6 +3120,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_admin_invite_users" = "Add members";
|
||||
"lng_admin_log_admin_invite_link" = "Invite users via link";
|
||||
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
||||
"lng_admin_log_admin_manage_topics" = "Manage topics";
|
||||
"lng_admin_log_admin_create_topics" = "Create topics";
|
||||
"lng_admin_log_admin_manage_calls" = "Manage voice chats";
|
||||
"lng_admin_log_admin_manage_calls_channel" = "Manage live streams";
|
||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||
@@ -3470,6 +3534,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ringtones_error_max_size" = "Sorry, but your file is too big. The maximum size for ringtones is {size}.";
|
||||
"lng_ringtones_error_max_duration" = "Sorry, but your file is too long. The maximum duration for ringtones is {duration}.";
|
||||
|
||||
"lng_forum_topic_new" = "New Topic";
|
||||
"lng_forum_topic_edit" = "Edit Topic";
|
||||
"lng_forum_topic_title" = "Topic Title";
|
||||
"lng_forum_topic_close" = "Close Topic";
|
||||
"lng_forum_topic_reopen" = "Reopen Topic";
|
||||
"lng_forum_topic_closed" = "This topic is now closed.";
|
||||
"lng_forum_topic_delete" = "Delete";
|
||||
"lng_forum_topic_delete_sure" = "Are you sure you want to delete this topic?";
|
||||
"lng_forum_topic_created_title_my" = "Almost done!";
|
||||
"lng_forum_topic_created_body_my" = "Send the first message\nto start this topic.";
|
||||
"lng_forum_topic_created_title" = "Topic started!";
|
||||
"lng_forum_topic_created_body" = "Send a message to open\nthe discussion.";
|
||||
"lng_forum_topics_switch" = "Topics";
|
||||
"lng_forum_topics_not_enough#one" = "Only groups with more than **{count} member** can have topics enabled.";
|
||||
"lng_forum_topics_not_enough#other" = "Only groups with more than **{count} members** can have topics enabled.";
|
||||
"lng_forum_topics_no_discussion" = "Topics can't be enabled in discussion groups at the moment.";
|
||||
"lng_forum_choose_title_and_icon" = "Choose title and icon for your topic";
|
||||
"lng_forum_replies_only" = "You can reply to messages in topics.";
|
||||
"lng_forum_no_topics" = "No topics currently created in this forum.";
|
||||
"lng_forum_create_topic" = "Create topic";
|
||||
"lng_forum_discard_sure" = "Are you sure you want to discard this topic?";
|
||||
"lng_forum_view_as_messages" = "View as Messages";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
<file alias="icons/settings/dino.svg">../../icons/settings/dino.svg</file>
|
||||
<file alias="icons/settings/star.svg">../../icons/settings/star.svg</file>
|
||||
<file alias="icons/settings/starmini.svg">../../icons/settings/starmini.svg</file>
|
||||
<file alias="topic_icons/blue.svg">../../art/topic_icons/blue.svg</file>
|
||||
<file alias="topic_icons/yellow.svg">../../art/topic_icons/yellow.svg</file>
|
||||
<file alias="topic_icons/violet.svg">../../art/topic_icons/violet.svg</file>
|
||||
<file alias="topic_icons/green.svg">../../art/topic_icons/green.svg</file>
|
||||
<file alias="topic_icons/rose.svg">../../art/topic_icons/rose.svg</file>
|
||||
<file alias="topic_icons/red.svg">../../art/topic_icons/red.svg</file>
|
||||
<file alias="topic_icons/gray.svg">../../art/topic_icons/gray.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons">
|
||||
<file alias="calls/hands.lottie">../../icons/calls/hands.lottie</file>
|
||||
|
||||
@@ -110,7 +110,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
userEmpty#d3bc4b7a id:long = User;
|
||||
user#5d99adee flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus = User;
|
||||
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
@@ -125,7 +125,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#29562865 id:long = Chat;
|
||||
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chatForbidden#6592a1a7 id:long title:string = Chat;
|
||||
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> = Chat;
|
||||
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
|
||||
@@ -192,6 +192,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
|
||||
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
|
||||
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
||||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#b18a431c flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = MessageAction;
|
||||
|
||||
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
@@ -220,6 +222,7 @@ inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
||||
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
||||
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
||||
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
|
||||
inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer;
|
||||
|
||||
inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings;
|
||||
|
||||
@@ -298,7 +301,7 @@ updateUserTyping#c01e857f user_id:long action:SendMessageAction = Update;
|
||||
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;
|
||||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
|
||||
updateUserName#c3f202e0 user_id:long first_name:string last_name:string username:string = Update;
|
||||
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
|
||||
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||
@@ -333,7 +336,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
|
||||
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
|
||||
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
|
||||
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
|
||||
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
|
||||
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update;
|
||||
updateReadFeaturedStickers#571d2742 = Update;
|
||||
updateRecentStickers#9a422c20 = Update;
|
||||
updateConfig#a229dd06 = Update;
|
||||
@@ -349,7 +352,7 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
|
||||
updateLangPackTooLong#46560264 lang_code:string = Update;
|
||||
updateLangPack#56022f4d difference:LangPackDifference = Update;
|
||||
updateFavedStickers#e511996d = Update;
|
||||
updateChannelReadMessagesContents#44bdd535 channel_id:long messages:Vector<int> = Update;
|
||||
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update;
|
||||
updateContactsReset#7084a7be = Update;
|
||||
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
|
||||
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
|
||||
@@ -386,7 +389,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
|
||||
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
|
||||
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
|
||||
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
|
||||
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update;
|
||||
updateAttachMenuBots#17b7a20b = Update;
|
||||
updateWebViewResultSent#1592b79d query_id:long = Update;
|
||||
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
|
||||
@@ -398,6 +401,7 @@ updateRecentEmojiStatuses#30f443db = Update;
|
||||
updateRecentReactions#6f7863f4 = Update;
|
||||
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
|
||||
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
|
||||
updateChannelPinnedTopic#f694b0ae flags:# channel_id:long topic_id:flags.0?int = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -470,6 +474,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||
notifyUsers#b4c83b4c = NotifyPeer;
|
||||
notifyChats#c007cec3 = NotifyPeer;
|
||||
notifyBroadcasts#d612e8ef = NotifyPeer;
|
||||
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
|
||||
|
||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||
@@ -588,10 +593,11 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
|
||||
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
|
||||
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
||||
|
||||
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
||||
|
||||
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
|
||||
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
|
||||
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
|
||||
|
||||
botCommand#c27ac8c7 command:string description:string = BotCommand;
|
||||
@@ -770,7 +776,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
|
||||
|
||||
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
|
||||
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
|
||||
stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = StickerSetCovered;
|
||||
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = StickerSetCovered;
|
||||
|
||||
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
|
||||
|
||||
@@ -952,12 +958,18 @@ channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatI
|
||||
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
|
||||
|
||||
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter;
|
||||
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
|
||||
|
||||
popularContact#5ce14175 client_id:long importers:int = PopularContact;
|
||||
|
||||
@@ -1115,9 +1127,9 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||
|
||||
statsURL#47a971e0 url:string = StatsURL;
|
||||
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights;
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights;
|
||||
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true until_date:int = ChatBannedRights;
|
||||
|
||||
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
|
||||
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
@@ -1248,7 +1260,7 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
|
||||
|
||||
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
|
||||
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
|
||||
|
||||
@@ -1316,9 +1328,10 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
|
||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
|
||||
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
||||
|
||||
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
||||
|
||||
@@ -1448,6 +1461,15 @@ sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer
|
||||
messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
|
||||
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
|
||||
|
||||
stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword;
|
||||
|
||||
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
|
||||
|
||||
forumTopicDeleted#23f109b id:int = ForumTopic;
|
||||
forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;
|
||||
|
||||
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1538,7 +1560,7 @@ account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?I
|
||||
account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
|
||||
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
|
||||
account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool;
|
||||
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
|
||||
account.getTheme#3a5869ec format:string theme:InputTheme = Theme;
|
||||
account.getThemes#7206e458 format:string hash:long = account.Themes;
|
||||
account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool;
|
||||
account.getContentSettings#8b9b4dae = account.ContentSettings;
|
||||
@@ -1558,6 +1580,8 @@ account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool;
|
||||
account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses;
|
||||
account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
|
||||
account.clearRecentEmojiStatuses#18201aae = Bool;
|
||||
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
|
||||
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||
@@ -1594,9 +1618,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
|
||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMessage#1cc20387 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMedia#7547c966 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.forwardMessages#c661bbc4 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
|
||||
@@ -1639,14 +1663,14 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendInlineBotResult#d3fbdccb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getAllDrafts#6a3f8d65 = Updates;
|
||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||
@@ -1672,10 +1696,10 @@ messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
|
||||
messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates;
|
||||
messages.getFavedStickers#4f1aaa9 hash:long = messages.FavedStickers;
|
||||
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
|
||||
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
|
||||
messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
|
||||
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendMultiMedia#b6f11a1c flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
|
||||
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
|
||||
@@ -1692,7 +1716,7 @@ messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
|
||||
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
|
||||
messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
|
||||
messages.getSearchCounters#ae7cc1 flags:# peer:InputPeer top_msg_id:flags.0?int filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
|
||||
messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
|
||||
messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
|
||||
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
|
||||
@@ -1710,7 +1734,7 @@ messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messag
|
||||
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
|
||||
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
|
||||
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
|
||||
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
||||
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
||||
messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages;
|
||||
messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed;
|
||||
@@ -1741,14 +1765,14 @@ messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:C
|
||||
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
|
||||
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
|
||||
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
|
||||
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
|
||||
messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
|
||||
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
|
||||
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
|
||||
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
|
||||
messages.requestWebView#fc87a53c flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
|
||||
messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
|
||||
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
|
||||
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
|
||||
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
|
||||
@@ -1846,6 +1870,16 @@ channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
|
||||
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
|
||||
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
|
||||
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
|
||||
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
|
||||
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
|
||||
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
|
||||
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics;
|
||||
channels.editForumTopic#6c883e2d flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = Updates;
|
||||
channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinned:Bool = Updates;
|
||||
channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@@ -1924,4 +1958,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
||||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 146
|
||||
// LAYER 148
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.2.0.0" />
|
||||
Version="4.3.1.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,2,0,0
|
||||
PRODUCTVERSION 4,2,0,0
|
||||
FILEVERSION 4,3,1,0
|
||||
PRODUCTVERSION 4,3,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "4.2.0.0"
|
||||
VALUE "FileVersion", "4.3.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.2.0.0"
|
||||
VALUE "ProductVersion", "4.3.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,2,0,0
|
||||
PRODUCTVERSION 4,2,0,0
|
||||
FILEVERSION 4,3,1,0
|
||||
PRODUCTVERSION 4,3,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "4.2.0.0"
|
||||
VALUE "FileVersion", "4.3.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.2.0.0"
|
||||
VALUE "ProductVersion", "4.3.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -307,7 +307,7 @@ void updateRegistry() {
|
||||
SYSTEMTIME stLocalTime;
|
||||
GetLocalTime(&stLocalTime);
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (const BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
|
||||
wsprintf(nameStr, L"Telegram Desktop");
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (const BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(publisherStr, L"Telegram FZ-LLC");
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (const BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
|
||||
@@ -25,7 +25,7 @@ AttachedStickers::AttachedStickers(not_null<ApiWrap*> api)
|
||||
void AttachedStickers::request(
|
||||
not_null<Window::SessionController*> controller,
|
||||
MTPmessages_GetAttachedStickers &&mtpRequest) {
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
_api.request(_requestId).cancel();
|
||||
_requestId = _api.request(
|
||||
std::move(mtpRequest)
|
||||
|
||||
@@ -79,12 +79,12 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
const auto nowDate = now.date();
|
||||
const auto lastDate = lastTime.date();
|
||||
if (lastDate == nowDate) {
|
||||
result.active = lastTime.toString(cTimeFormat());
|
||||
result.active = QLocale().toString(lastTime, cTimeFormat());
|
||||
} else if (lastDate.year() == nowDate.year()
|
||||
&& lastDate.weekNumber() == nowDate.weekNumber()) {
|
||||
result.active = langDayOfWeek(lastDate);
|
||||
} else {
|
||||
result.active = lastDate.toString(cDateFormat());
|
||||
result.active = QLocale().toString(lastDate, cDateFormat());
|
||||
}
|
||||
}
|
||||
result.location = country;
|
||||
|
||||
@@ -45,6 +45,7 @@ void SendBotCallbackData(
|
||||
int row,
|
||||
int column,
|
||||
std::optional<Core::CloudPasswordResult> password,
|
||||
Fn<void()> done = nullptr,
|
||||
Fn<void(const QString &)> handleError = nullptr) {
|
||||
if (!item->isRegular()) {
|
||||
return;
|
||||
@@ -56,11 +57,7 @@ void SendBotCallbackData(
|
||||
const auto bot = item->getMessageBot();
|
||||
const auto fullId = item->fullId();
|
||||
const auto getButton = [=] {
|
||||
return HistoryMessageMarkupButton::Get(
|
||||
owner,
|
||||
fullId,
|
||||
row,
|
||||
column);
|
||||
return HistoryMessageMarkupButton::Get(owner, fullId, row, column);
|
||||
};
|
||||
const auto button = getButton();
|
||||
if (!button || button->requestId) {
|
||||
@@ -83,7 +80,7 @@ void SendBotCallbackData(
|
||||
if (withPassword) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_password;
|
||||
}
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto show = std::make_shared<Window::Show>(controller);
|
||||
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
|
||||
MTP_flags(flags),
|
||||
@@ -92,6 +89,11 @@ void SendBotCallbackData(
|
||||
MTP_bytes(sendData),
|
||||
password ? password->result : MTP_inputCheckPasswordEmpty()
|
||||
)).done([=](const MTPmessages_BotCallbackAnswer &result) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
@@ -139,6 +141,11 @@ void SendBotCallbackData(
|
||||
show->hideLayer();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (handleError) {
|
||||
handleError(error.type());
|
||||
}
|
||||
});
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
@@ -148,9 +155,6 @@ void SendBotCallbackData(
|
||||
button->requestId = 0;
|
||||
owner->requestItemRepaint(item);
|
||||
}
|
||||
if (handleError) {
|
||||
handleError(error.type());
|
||||
}
|
||||
}).send();
|
||||
|
||||
session->changes().messageUpdated(
|
||||
@@ -202,9 +206,10 @@ void SendBotCallbackDataWithPassword(
|
||||
return;
|
||||
}
|
||||
api->cloudPassword().reload();
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto show = std::make_shared<Window::Show>(controller);
|
||||
SendBotCallbackData(controller, item, row, column, std::nullopt, [=](const QString &error) {
|
||||
SendBotCallbackData(controller, item, row, column, {}, {}, [=](
|
||||
const QString &error) {
|
||||
auto box = PrePasswordErrorBox(
|
||||
error,
|
||||
session,
|
||||
@@ -250,7 +255,11 @@ void SendBotCallbackDataWithPassword(
|
||||
if (!strongController) {
|
||||
return;
|
||||
}
|
||||
SendBotCallbackData(strongController, item, row, column, result, [=](const QString &error) {
|
||||
SendBotCallbackData(strongController, item, row, column, result, [=] {
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
}, [=](const QString &error) {
|
||||
if (*box) {
|
||||
(*box)->handleCustomCheckError(error);
|
||||
}
|
||||
@@ -351,6 +360,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
case ButtonType::RequestPhone: {
|
||||
HideSingleUseKeyboard(controller, item);
|
||||
const auto itemId = item->id;
|
||||
const auto topicRootId = item->topicRootId();
|
||||
const auto history = item->history();
|
||||
controller->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_bot_share_phone(),
|
||||
@@ -362,6 +372,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
auto action = Api::SendAction(history);
|
||||
action.clearDraft = false;
|
||||
action.replyTo = itemId;
|
||||
action.topicRootId = topicRootId;
|
||||
history->session().api().shareContact(
|
||||
history->session().user(),
|
||||
action);
|
||||
@@ -381,10 +392,12 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
}
|
||||
}
|
||||
const auto replyToId = MsgId(0);
|
||||
const auto topicRootId = MsgId(0);
|
||||
Window::PeerMenuCreatePoll(
|
||||
controller,
|
||||
item->history()->peer,
|
||||
replyToId,
|
||||
topicRootId,
|
||||
chosen,
|
||||
disabled);
|
||||
} break;
|
||||
@@ -414,7 +427,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
}();
|
||||
if (!fastSwitchDone) {
|
||||
controller->content()->inlineSwitchLayer('@'
|
||||
+ bot->username
|
||||
+ bot->username()
|
||||
+ ' '
|
||||
+ QString::fromUtf8(button->data));
|
||||
}
|
||||
@@ -437,7 +450,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
if (const auto bot = item->getMessageBot()) {
|
||||
bot->session().attachWebView().request(
|
||||
controller,
|
||||
bot,
|
||||
Api::SendAction(bot->owner().history(bot)),
|
||||
bot,
|
||||
{ .text = button->text, .url = button->data });
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/painter.h"
|
||||
#include "core/application.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -106,16 +108,27 @@ void CheckChatInvite(
|
||||
const QString &hash,
|
||||
ChannelData *invitePeekChannel) {
|
||||
const auto session = &controller->session();
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
session->api().checkChatInvite(hash, [=](const MTPChatInvite &result) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
Core::App().hideMediaView();
|
||||
result.match([=](const MTPDchatInvite &data) {
|
||||
const auto strongController = weak.get();
|
||||
if (!strongController) {
|
||||
return;
|
||||
const auto show = [&](not_null<PeerData*> chat) {
|
||||
if (const auto forum = chat->forum()) {
|
||||
strong->openForum(
|
||||
forum->channel(),
|
||||
Window::SectionShow::Way::Forward);
|
||||
} else {
|
||||
strong->showPeerHistory(
|
||||
chat,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
};
|
||||
result.match([=](const MTPDchatInvite &data) {
|
||||
const auto isGroup = !data.is_broadcast();
|
||||
const auto box = strongController->show(Box<ConfirmInviteBox>(
|
||||
const auto box = strong->show(Box<ConfirmInviteBox>(
|
||||
session,
|
||||
data,
|
||||
invitePeekChannel,
|
||||
@@ -138,21 +151,13 @@ void CheckChatInvite(
|
||||
if (const auto channel = chat->asChannel()) {
|
||||
channel->clearInvitePeek();
|
||||
}
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showPeerHistory(
|
||||
chat,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
show(chat);
|
||||
}
|
||||
}, [=](const MTPDchatInvitePeek &data) {
|
||||
if (const auto chat = session->data().processChat(data.vchat())) {
|
||||
if (const auto channel = chat->asChannel()) {
|
||||
channel->setInvitePeek(hash, data.vexpires().v);
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showPeerHistory(
|
||||
chat,
|
||||
Window::SectionShow::Way::Forward);
|
||||
}
|
||||
show(chat);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
23
Telegram/SourceFiles/api/api_common.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_common.h"
|
||||
|
||||
#include "data/data_thread.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
SendAction::SendAction(
|
||||
not_null<Data::Thread*> thread,
|
||||
SendOptions options)
|
||||
: history(thread->owningHistory())
|
||||
, options(options)
|
||||
, replyTo(thread->topicRootId())
|
||||
, topicRootId(replyTo) {
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class History;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct SendOptions {
|
||||
@@ -28,15 +32,13 @@ enum class SendType {
|
||||
|
||||
struct SendAction {
|
||||
explicit SendAction(
|
||||
not_null<History*> history,
|
||||
SendOptions options = SendOptions())
|
||||
: history(history)
|
||||
, options(options) {
|
||||
}
|
||||
not_null<Data::Thread*> thread,
|
||||
SendOptions options = SendOptions());
|
||||
|
||||
not_null<History*> history;
|
||||
SendOptions options;
|
||||
MsgId replyTo = 0;
|
||||
MsgId topicRootId = 0;
|
||||
bool clearDraft = true;
|
||||
bool generateLocal = true;
|
||||
MsgId replaceMediaOf = 0;
|
||||
|
||||
@@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kSearchPerPage = 50;
|
||||
|
||||
[[nodiscard]] MessageIdsList HistoryItemsFromTL(
|
||||
not_null<Data::Session*> data,
|
||||
const QVector<MTPMessage> &messages) {
|
||||
@@ -94,7 +96,7 @@ void MessagesSearch::searchRequest() {
|
||||
MTP_int(0), // max_date
|
||||
MTP_int(_offsetId), // offset_id
|
||||
MTP_int(0), // add_offset
|
||||
MTP_int(SearchPerPage),
|
||||
MTP_int(kSearchPerPage),
|
||||
MTP_int(0), // max_id
|
||||
MTP_int(0), // min_id
|
||||
MTP_long(0) // hash
|
||||
|
||||
@@ -42,16 +42,20 @@ void Polls::create(
|
||||
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto topicRootId = action.replyTo ? action.topicRootId : 0;
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (action.replyTo) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto clearCloudDraft = action.clearDraft;
|
||||
if (clearCloudDraft) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||
history->clearLocalDraft();
|
||||
history->clearCloudDraft();
|
||||
history->startSavingCloudDraft();
|
||||
history->clearLocalDraft(topicRootId);
|
||||
history->clearCloudDraft(topicRootId);
|
||||
history->startSavingCloudDraft(topicRootId);
|
||||
}
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
if (silentPost) {
|
||||
@@ -65,47 +69,43 @@ void Polls::create(
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||
}
|
||||
auto &histories = history->owner().histories();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
const auto replyTo = action.replyTo;
|
||||
history->sendRequestId = _api.request(MTPmessages_SendMedia(
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
action.replyTo,
|
||||
topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
PollDataToInputMedia(&data),
|
||||
MTP_string(),
|
||||
MTP_long(base::RandomValue<uint64>()),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
)).done([=](
|
||||
const MTPUpdates &result,
|
||||
const MTP::Response &response) mutable {
|
||||
_session->updates().applyUpdates(result);
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
_session->changes().historyUpdated(
|
||||
history,
|
||||
(action.options.scheduled
|
||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||
: Data::HistoryUpdate::Flag::MessageSent));
|
||||
done();
|
||||
finish();
|
||||
}).fail([=](
|
||||
const MTP::Error &error,
|
||||
const MTP::Response &response) mutable {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
fail();
|
||||
finish();
|
||||
}).afterRequest(history->sendRequestId
|
||||
).send();
|
||||
return history->sendRequestId;
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
_session->changes().historyUpdated(
|
||||
history,
|
||||
(action.options.scheduled
|
||||
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||
: Data::HistoryUpdate::Flag::MessageSent));
|
||||
done();
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (clearCloudDraft) {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
fail();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,9 @@ void SendExistingMedia(
|
||||
if (message.action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (message.action.topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, message.action.options);
|
||||
@@ -118,7 +121,6 @@ void SendExistingMedia(
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
|
||||
}
|
||||
const auto replyTo = message.action.replyTo;
|
||||
const auto captionText = caption.text;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
@@ -133,7 +135,7 @@ void SendExistingMedia(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
replyTo,
|
||||
message.action.replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
@@ -141,15 +143,19 @@ void SendExistingMedia(
|
||||
caption,
|
||||
HistoryMessageMarkupData());
|
||||
|
||||
auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
auto &histories = history->owner().histories();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
const auto usedFileReference = media->fileReference();
|
||||
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||
const auto usedFileReference = media->fileReference();
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
message.action.replyTo,
|
||||
message.action.topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
inputMedia(),
|
||||
MTP_string(captionText),
|
||||
MTP_long(randomId),
|
||||
@@ -157,26 +163,20 @@ void SendExistingMedia(
|
||||
sentEntities,
|
||||
MTP_int(message.action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
finish();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
api->refreshFileReference(origin, [=](const auto &result) {
|
||||
if (media->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
finish();
|
||||
}).afterRequest(history->sendRequestId
|
||||
).send();
|
||||
return history->sendRequestId;
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
api->refreshFileReference(origin, [=](const auto &result) {
|
||||
if (media->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
});
|
||||
};
|
||||
performRequest(performRequest);
|
||||
@@ -273,6 +273,9 @@ bool SendDice(MessageToSend &message) {
|
||||
if (message.action.replyTo) {
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
if (message.action.topicRootId) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_top_msg_id;
|
||||
}
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(message.action);
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
@@ -293,7 +296,6 @@ bool SendDice(MessageToSend &message) {
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? session->user()->name()
|
||||
: QString();
|
||||
const auto replyTo = message.action.replyTo;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
@@ -314,13 +316,16 @@ bool SendDice(MessageToSend &message) {
|
||||
TextWithEntities(),
|
||||
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
|
||||
HistoryMessageMarkupData());
|
||||
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||
histories.sendPreparedMessage(
|
||||
history,
|
||||
message.action.replyTo,
|
||||
message.action.topicRootId,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
Data::Histories::TopicRootPlaceholder(),
|
||||
MTP_inputMediaDice(MTP_string(emoji)),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
@@ -328,15 +333,9 @@ bool SendDice(MessageToSend &message) {
|
||||
MTP_vector<MTPMessageEntity>(),
|
||||
MTP_int(message.action.options.scheduled),
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
finish();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
finish();
|
||||
}).afterRequest(history->sendRequestId
|
||||
).send();
|
||||
return history->sendRequestId;
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
});
|
||||
api->finishForwarding(message.action);
|
||||
return true;
|
||||
@@ -356,9 +355,9 @@ void SendConfirmedFile(
|
||||
&& (file->to.replaceMediaOf != 0);
|
||||
const auto newId = FullMsgId(
|
||||
file->to.peer,
|
||||
isEditing
|
||||
(isEditing
|
||||
? file->to.replaceMediaOf
|
||||
: session->data().nextLocalMessageId());
|
||||
: session->data().nextLocalMessageId()));
|
||||
const auto groupId = file->album ? file->album->groupId : uint64(0);
|
||||
if (file->album) {
|
||||
const auto proj = [](const SendingAlbum::Item &item) {
|
||||
@@ -369,18 +368,29 @@ void SendConfirmedFile(
|
||||
|
||||
it->msgId = newId;
|
||||
}
|
||||
session->uploader().upload(newId, file);
|
||||
|
||||
const auto itemToEdit = isEditing
|
||||
? session->data().message(newId)
|
||||
: nullptr;
|
||||
|
||||
const auto history = session->data().history(file->to.peer);
|
||||
const auto peer = history->peer;
|
||||
|
||||
if (!isEditing) {
|
||||
const auto histories = &session->data().histories();
|
||||
file->to.replyTo = histories->convertTopicReplyTo(
|
||||
history,
|
||||
file->to.replyTo);
|
||||
file->to.topicRootId = histories->convertTopicReplyTo(
|
||||
history,
|
||||
file->to.topicRootId);
|
||||
}
|
||||
|
||||
session->uploader().upload(newId, file);
|
||||
|
||||
auto action = SendAction(history, file->to.options);
|
||||
action.clearDraft = false;
|
||||
action.replyTo = file->to.replyTo;
|
||||
action.topicRootId = file->to.topicRootId;
|
||||
action.generateLocal = true;
|
||||
session->api().sendAction(action);
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ using namespace TextUtilities;
|
||||
|
||||
[[nodiscard]] QString CustomEmojiEntityData(
|
||||
const MTPDmessageEntityCustomEmoji &data) {
|
||||
return Data::SerializeCustomEmojiId({
|
||||
.id = data.vdocument_id().v,
|
||||
});
|
||||
return Data::SerializeCustomEmojiId(data.vdocument_id().v);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> CustomEmojiEntity(
|
||||
@@ -31,13 +29,13 @@ using namespace TextUtilities;
|
||||
MTPint length,
|
||||
const QString &data) {
|
||||
const auto parsed = Data::ParseCustomEmojiData(data);
|
||||
if (!parsed.id) {
|
||||
if (!parsed) {
|
||||
return {};
|
||||
}
|
||||
return MTP_messageEntityCustomEmoji(
|
||||
offset,
|
||||
length,
|
||||
MTP_long(parsed.id));
|
||||
MTP_long(parsed));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPMessageEntity> MentionNameEntity(
|
||||
|
||||
@@ -66,7 +66,7 @@ void ToggleFavedSticker(
|
||||
if (faved && !document->sticker()) {
|
||||
return;
|
||||
}
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
auto done = [=] {
|
||||
document->owner().stickers().setFaved(weak.get(), document, faved);
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -30,6 +31,9 @@ void Transcribes::toggle(not_null<HistoryItem*> item) {
|
||||
_session->data().requestItemResize(item);
|
||||
} else if (!i->second.requestId) {
|
||||
i->second.shown = !i->second.shown;
|
||||
if (i->second.roundview) {
|
||||
_session->data().requestItemViewRefresh(item);
|
||||
}
|
||||
_session->data().requestItemResize(item);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +59,9 @@ void Transcribes::apply(const MTPDupdateTranscribedAudio &update) {
|
||||
j->second.result = text;
|
||||
j->second.pending = update.is_pending();
|
||||
if (const auto item = _session->data().message(i->second)) {
|
||||
if (j->second.roundview) {
|
||||
_session->data().requestItemViewRefresh(item);
|
||||
}
|
||||
_session->data().requestItemResize(item);
|
||||
}
|
||||
}
|
||||
@@ -63,29 +70,41 @@ void Transcribes::load(not_null<HistoryItem*> item) {
|
||||
if (!item->isHistoryEntry() || item->isLocal()) {
|
||||
return;
|
||||
}
|
||||
const auto toggleRound = [](not_null<HistoryItem*> item, Entry &entry) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
entry.roundview = true;
|
||||
document->owner().requestItemViewRefresh(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto id = item->fullId();
|
||||
const auto requestId = _api.request(MTPmessages_TranscribeAudio(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id)
|
||||
)).done([=](const MTPmessages_TranscribedAudio &result) {
|
||||
result.match([&](const MTPDmessages_transcribedAudio &data) {
|
||||
auto &entry = _map[id];
|
||||
entry.requestId = 0;
|
||||
entry.pending = data.is_pending();
|
||||
entry.result = qs(data.vtext());
|
||||
_ids.emplace(data.vtranscription_id().v, id);
|
||||
if (const auto item = _session->data().message(id)) {
|
||||
_session->data().requestItemResize(item);
|
||||
}
|
||||
});
|
||||
const auto &data = result.data();
|
||||
auto &entry = _map[id];
|
||||
entry.requestId = 0;
|
||||
entry.pending = data.is_pending();
|
||||
entry.result = qs(data.vtext());
|
||||
_ids.emplace(data.vtranscription_id().v, id);
|
||||
if (const auto item = _session->data().message(id)) {
|
||||
toggleRound(item, entry);
|
||||
_session->data().requestItemResize(item);
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = _map[id];
|
||||
entry.requestId = 0;
|
||||
entry.pending = false;
|
||||
entry.failed = true;
|
||||
if (error.type() == qstr("MSG_VOICE_TOO_LONG")) {
|
||||
if (error.type() == u"MSG_VOICE_TOO_LONG"_q) {
|
||||
entry.toolong = true;
|
||||
} else if (const auto item = _session->data().message(id)) {
|
||||
}
|
||||
if (const auto item = _session->data().message(id)) {
|
||||
toggleRound(item, entry);
|
||||
_session->data().requestItemResize(item);
|
||||
}
|
||||
}).send();
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
bool failed = false;
|
||||
bool toolong = false;
|
||||
bool pending = false;
|
||||
bool roundview = false;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history.h"
|
||||
@@ -28,23 +29,22 @@ constexpr auto kNextRequestLimit = 100;
|
||||
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
|
||||
}
|
||||
|
||||
bool UnreadThings::trackMentions(PeerData *peer) const {
|
||||
bool UnreadThings::trackMentions(Data::Thread *thread) const {
|
||||
const auto peer = thread ? thread->peer().get() : nullptr;
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
bool UnreadThings::trackReactions(PeerData *peer) const {
|
||||
return trackMentions(peer) || (peer && peer->isUser());
|
||||
bool UnreadThings::trackReactions(Data::Thread *thread) const {
|
||||
const auto peer = thread ? thread->peer().get() : nullptr;
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnough(History *history) {
|
||||
if (!history) {
|
||||
return;
|
||||
void UnreadThings::preloadEnough(Data::Thread *thread) {
|
||||
if (trackMentions(thread)) {
|
||||
preloadEnoughMentions(thread);
|
||||
}
|
||||
if (trackMentions(history->peer)) {
|
||||
preloadEnoughMentions(history);
|
||||
}
|
||||
if (trackReactions(history->peer)) {
|
||||
preloadEnoughReactions(history);
|
||||
if (trackReactions(thread)) {
|
||||
preloadEnoughReactions(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,76 +63,99 @@ void UnreadThings::mediaAndMentionsRead(
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughMentions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadMentions().count();
|
||||
const auto loadedCount = history->unreadMentions().loadedCount();
|
||||
void UnreadThings::preloadEnoughMentions(not_null<Data::Thread*> thread) {
|
||||
const auto fullCount = thread->unreadMentions().count();
|
||||
const auto loadedCount = thread->unreadMentions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestMentions(history, loadedCount);
|
||||
requestMentions(thread, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughReactions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadReactions().count();
|
||||
const auto loadedCount = history->unreadReactions().loadedCount();
|
||||
void UnreadThings::preloadEnoughReactions(not_null<Data::Thread*> thread) {
|
||||
const auto fullCount = thread->unreadReactions().count();
|
||||
const auto loadedCount = thread->unreadReactions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestReactions(history, loadedCount);
|
||||
requestReactions(thread, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::requestMentions(not_null<History*> history, int loaded) {
|
||||
if (_mentionsRequests.contains(history)) {
|
||||
void UnreadThings::cancelRequests(not_null<Data::Thread*> thread) {
|
||||
if (const auto requestId = _mentionsRequests.take(thread)) {
|
||||
_api->request(*requestId).cancel();
|
||||
}
|
||||
if (const auto requestId = _reactionsRequests.take(thread)) {
|
||||
_api->request(*requestId).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::requestMentions(
|
||||
not_null<Data::Thread*> thread,
|
||||
int loaded) {
|
||||
if (_mentionsRequests.contains(thread)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = std::max(
|
||||
history->unreadMentions().maxLoaded(),
|
||||
thread->unreadMentions().maxLoaded(),
|
||||
MsgId(1));
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto history = thread->owningHistory();
|
||||
const auto topic = thread->asTopic();
|
||||
using Flag = MTPmessages_GetUnreadMentions::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
|
||||
MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
|
||||
history->peer->input,
|
||||
MTP_int(topic ? topic->rootId() : 0),
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_mentionsRequests.remove(history);
|
||||
history->unreadMentions().addSlice(result, loaded);
|
||||
_mentionsRequests.remove(thread);
|
||||
thread->unreadMentions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_mentionsRequests.remove(history);
|
||||
_mentionsRequests.remove(thread);
|
||||
}).send();
|
||||
_mentionsRequests.emplace(history, requestId);
|
||||
_mentionsRequests.emplace(thread, requestId);
|
||||
}
|
||||
|
||||
void UnreadThings::requestReactions(not_null<History*> history, int loaded) {
|
||||
if (_reactionsRequests.contains(history)) {
|
||||
void UnreadThings::requestReactions(
|
||||
not_null<Data::Thread*> thread,
|
||||
int loaded) {
|
||||
if (_reactionsRequests.contains(thread)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = loaded
|
||||
? std::max(history->unreadReactions().maxLoaded(), MsgId(1))
|
||||
? std::max(thread->unreadReactions().maxLoaded(), MsgId(1))
|
||||
: MsgId(1);
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto history = thread->owningHistory();
|
||||
const auto topic = thread->asTopic();
|
||||
using Flag = MTPmessages_GetUnreadReactions::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
||||
MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
|
||||
history->peer->input,
|
||||
MTP_int(topic ? topic->rootId() : 0),
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_reactionsRequests.remove(history);
|
||||
history->unreadReactions().addSlice(result, loaded);
|
||||
_reactionsRequests.remove(thread);
|
||||
thread->unreadReactions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_reactionsRequests.remove(history);
|
||||
_reactionsRequests.remove(thread);
|
||||
}).send();
|
||||
_reactionsRequests.emplace(history, requestId);
|
||||
_reactionsRequests.emplace(thread, requestId);
|
||||
}
|
||||
|
||||
} // namespace UnreadThings
|
||||
|
||||
@@ -7,37 +7,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class History;
|
||||
class ApiWrap;
|
||||
class PeerData;
|
||||
class ChannelData;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
class UnreadThings final {
|
||||
public:
|
||||
explicit UnreadThings(not_null<ApiWrap*> api);
|
||||
|
||||
[[nodiscard]] bool trackMentions(PeerData *peer) const;
|
||||
[[nodiscard]] bool trackReactions(PeerData *peer) const;
|
||||
[[nodiscard]] bool trackMentions(Data::Thread *thread) const;
|
||||
[[nodiscard]] bool trackReactions(Data::Thread *thread) const;
|
||||
|
||||
void preloadEnough(History *history);
|
||||
void preloadEnough(Data::Thread *thread);
|
||||
|
||||
void mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
private:
|
||||
void preloadEnoughMentions(not_null<History*> history);
|
||||
void preloadEnoughReactions(not_null<History*> history);
|
||||
void cancelRequests(not_null<Data::Thread*> thread);
|
||||
|
||||
void requestMentions(not_null<History*> history, int loaded);
|
||||
void requestReactions(not_null<History*> history, int loaded);
|
||||
private:
|
||||
void preloadEnoughMentions(not_null<Data::Thread*> thread);
|
||||
void preloadEnoughReactions(not_null<Data::Thread*> thread);
|
||||
|
||||
void requestMentions(not_null<Data::Thread*> thread, int loaded);
|
||||
void requestReactions(not_null<Data::Thread*> thread, int loaded);
|
||||
|
||||
const not_null<ApiWrap*> _api;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _reactionsRequests;
|
||||
base::flat_map<not_null<Data::Thread*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<Data::Thread*>, mtpRequestId> _reactionsRequests;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_updates.h"
|
||||
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_user_names.h"
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "api/api_ringtones.h"
|
||||
#include "api/api_text_entities.h"
|
||||
@@ -33,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
@@ -393,6 +395,9 @@ void Updates::channelDifferenceDone(
|
||||
data.vmessages().v,
|
||||
QVector<MTPDialog>(1, data.vdialog()));
|
||||
session().data().channelDifferenceTooLong(channel);
|
||||
if (const auto forum = channel->forum()) {
|
||||
forum->reloadTopics();
|
||||
}
|
||||
}, [&](const MTPDupdates_channelDifference &data) {
|
||||
feedChannelDifference(data);
|
||||
channel->ptsInit(data.vpts().v);
|
||||
@@ -940,6 +945,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
if (!isOnline) { // Went offline, so we need to save message draft to the cloud.
|
||||
api().saveCurrentDraftToCloud();
|
||||
session().data().maybeStopWatchForOffline(self);
|
||||
}
|
||||
|
||||
_lastSetOnline = ms;
|
||||
@@ -1534,7 +1540,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
const auto randomId = d.vrandom_id().v;
|
||||
if (const auto id = session().data().messageIdByRandomId(randomId)) {
|
||||
const auto newId = d.vid().v;
|
||||
if (const auto local = session().data().message(id)) {
|
||||
auto &owner = session().data();
|
||||
if (const auto local = owner.message(id)) {
|
||||
if (local->isScheduled()) {
|
||||
session().data().scheduledMessages().apply(d, local);
|
||||
} else {
|
||||
@@ -1552,6 +1559,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
local->setRealId(d.vid().v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
owner.histories().checkTopicCreated(id, newId);
|
||||
}
|
||||
session().data().unregisterMessageRandomId(randomId);
|
||||
}
|
||||
@@ -1703,11 +1712,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
if (const auto history = session().data().historyLoaded(id)) {
|
||||
history->setUnreadMark(data.is_unread());
|
||||
}
|
||||
}, [&](const MTPDdialogPeerFolder &dialog) {
|
||||
//const auto id = dialog.vfolder_id().v; // #TODO archive
|
||||
//if (const auto folder = session().data().folderLoaded(id)) {
|
||||
// folder->setUnreadMark(data.is_unread());
|
||||
//}
|
||||
}, [](const MTPDdialogPeerFolder &dialog) {
|
||||
});
|
||||
} break;
|
||||
|
||||
@@ -1852,6 +1857,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
session().data().maybeStopWatchForOffline(user);
|
||||
}
|
||||
if (UserId(d.vuser_id()) == session().userId()) {
|
||||
if (d.vstatus().type() == mtpc_userStatusOffline
|
||||
@@ -1869,21 +1875,23 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateUserName: {
|
||||
auto &d = update.c_updateUserName();
|
||||
if (auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
if (!user->isContact()) {
|
||||
user->setName(
|
||||
TextUtilities::SingleLine(qs(d.vfirst_name())),
|
||||
TextUtilities::SingleLine(qs(d.vlast_name())),
|
||||
user->nameOrPhone,
|
||||
TextUtilities::SingleLine(qs(d.vusername())));
|
||||
} else {
|
||||
user->setName(
|
||||
TextUtilities::SingleLine(user->firstName),
|
||||
TextUtilities::SingleLine(user->lastName),
|
||||
user->nameOrPhone,
|
||||
TextUtilities::SingleLine(qs(d.vusername())));
|
||||
}
|
||||
const auto &d = update.c_updateUserName();
|
||||
if (const auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
const auto contact = user->isContact();
|
||||
const auto first = contact
|
||||
? user->firstName
|
||||
: qs(d.vfirst_name());
|
||||
const auto last = contact ? user->lastName : qs(d.vlast_name());
|
||||
// #TODO usernames
|
||||
const auto username = d.vusernames().v.isEmpty()
|
||||
? QString()
|
||||
: qs(d.vusernames().v.front().data().vusername());
|
||||
user->setName(
|
||||
TextUtilities::SingleLine(first),
|
||||
TextUtilities::SingleLine(last),
|
||||
user->nameOrPhone,
|
||||
TextUtilities::SingleLine(username));
|
||||
user->setUsernames(Api::Usernames::FromTL(d.vusernames()));
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1949,7 +1957,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|| user->phone().isEmpty())
|
||||
? QString()
|
||||
: Ui::FormatPhone(user->phone())),
|
||||
user->username);
|
||||
user->username());
|
||||
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
@@ -2203,7 +2211,9 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
const auto history = channel->owner().history(channel);
|
||||
history->requestChatListMessage();
|
||||
if (!history->unreadCountKnown()) {
|
||||
if (!history->folderKnown()
|
||||
|| (!history->unreadCountKnown()
|
||||
&& !history->peer->isForum())) {
|
||||
history->owner().histories().requestDialogEntry(history);
|
||||
}
|
||||
if (!channel->amCreator()) {
|
||||
@@ -2243,40 +2253,34 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateReadChannelDiscussionInbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionInbox();
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto id = FullMsgId(
|
||||
peerFromChannel(d.vchannel_id()),
|
||||
d.vtop_msg_id().v);
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(peerId, msgId);
|
||||
const auto unreadCount = item
|
||||
? session().data().countUnreadRepliesLocally(item, readTillId)
|
||||
: std::nullopt;
|
||||
session().data().updateRepliesReadTill({ id, readTillId, false });
|
||||
const auto item = session().data().message(id);
|
||||
if (item) {
|
||||
item->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
item->setCommentsInboxReadTill(readTillId);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
post->setCommentsInboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
if (const auto broadcastId = d.vbroadcast_id()) {
|
||||
if (const auto post = session().data().message(
|
||||
peerFromChannel(*broadcastId),
|
||||
d.vbroadcast_post()->v)) {
|
||||
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
post->setCommentsInboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelDiscussionOutbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionOutbox();
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto id = FullMsgId(
|
||||
peerFromChannel(d.vchannel_id()),
|
||||
d.vtop_msg_id().v);
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(peerId, msgId);
|
||||
if (item) {
|
||||
item->setRepliesOutboxReadTill(readTillId);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
post->setRepliesOutboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
session().data().updateRepliesReadTill({ id, readTillId, true });
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelAvailableMessages: {
|
||||
@@ -2289,6 +2293,23 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelPinnedTopic: {
|
||||
const auto &d = update.c_updateChannelPinnedTopic();
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
const auto rootId = d.vtopic_id().value_or_empty();
|
||||
if (const auto topic = peer->forumTopicFor(rootId)) {
|
||||
session().data().setChatPinned(topic, 0, true);
|
||||
} else if (const auto forum = peer->forum()) {
|
||||
if (rootId) {
|
||||
forum->requestTopic(rootId);
|
||||
} else {
|
||||
forum->unpinTopic();
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
// Pinned message.
|
||||
case mtpc_updatePinnedMessages: {
|
||||
const auto &d = update.c_updatePinnedMessages();
|
||||
@@ -2445,12 +2466,14 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
case mtpc_updateDraftMessage: {
|
||||
const auto &data = update.c_updateDraftMessage();
|
||||
const auto peerId = peerFromMTP(data.vpeer());
|
||||
const auto topicRootId = data.vtop_msg_id().value_or_empty();
|
||||
data.vdraft().match([&](const MTPDdraftMessage &data) {
|
||||
Data::ApplyPeerCloudDraft(&session(), peerId, data);
|
||||
Data::ApplyPeerCloudDraft(&session(), peerId, topicRootId, data);
|
||||
}, [&](const MTPDdraftMessageEmpty &data) {
|
||||
Data::ClearPeerCloudDraft(
|
||||
&session(),
|
||||
peerId,
|
||||
topicRootId,
|
||||
data.vdate().value_or_empty());
|
||||
});
|
||||
} break;
|
||||
|
||||
242
Telegram/SourceFiles/api/api_user_names.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_user_names.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] Data::Username UsernameFromTL(const MTPUsername &username) {
|
||||
return {
|
||||
.username = qs(username.data().vusername()),
|
||||
.active = username.data().is_active(),
|
||||
.editable = username.data().is_editable(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Usernames::Usernames(not_null<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
rpl::producer<Data::Usernames> Usernames::loadUsernames(
|
||||
not_null<PeerData*> peer) const {
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto push = [consumer](
|
||||
const auto &usernames,
|
||||
const auto &username) {
|
||||
if (usernames) {
|
||||
if (usernames->v.empty()) {
|
||||
// Probably will never happen.
|
||||
consumer.put_next({});
|
||||
} else {
|
||||
auto parsed = FromTL(*usernames);
|
||||
if ((parsed.size() == 1)
|
||||
&& username
|
||||
&& (parsed.front().username == qs(*username))) {
|
||||
// Probably will never happen.
|
||||
consumer.put_next({});
|
||||
} else {
|
||||
consumer.put_next(std::move(parsed));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
consumer.put_next({});
|
||||
}
|
||||
};
|
||||
|
||||
const auto requestUser = [&](const MTPInputUser &data) {
|
||||
_session->api().request(MTPusers_GetUsers(
|
||||
MTP_vector<MTPInputUser>(1, data)
|
||||
)).done([=](const MTPVector<MTPUser> &result) {
|
||||
result.v.front().match([&](const MTPDuser &data) {
|
||||
push(data.vusernames(), data.vusername());
|
||||
consumer.put_done();
|
||||
}, [&](const MTPDuserEmpty&) {
|
||||
consumer.put_next({});
|
||||
consumer.put_done();
|
||||
});
|
||||
}).send();
|
||||
};
|
||||
const auto requestChannel = [&](const MTPInputChannel &data) {
|
||||
_session->api().request(MTPchannels_GetChannels(
|
||||
MTP_vector<MTPInputChannel>(1, data)
|
||||
)).done([=](const MTPmessages_Chats &result) {
|
||||
result.match([&](const auto &data) {
|
||||
data.vchats().v.front().match([&](const MTPDchannel &c) {
|
||||
push(c.vusernames(), c.vusername());
|
||||
consumer.put_done();
|
||||
}, [&](auto &&) {
|
||||
consumer.put_next({});
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}).send();
|
||||
};
|
||||
if (peer->isSelf()) {
|
||||
requestUser(MTP_inputUserSelf());
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
requestUser(user->inputUser);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
requestChannel(channel->inputChannel);
|
||||
}
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
rpl::producer<rpl::no_value, Usernames::Error> Usernames::toggle(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &username,
|
||||
bool active) {
|
||||
const auto peerId = peer->id;
|
||||
const auto it = _toggleRequests.find(peerId);
|
||||
const auto found = (it != end(_toggleRequests));
|
||||
auto &entry = (!found
|
||||
? _toggleRequests.emplace(
|
||||
peerId,
|
||||
Entry{ .usernames = { username } }).first
|
||||
: it)->second;
|
||||
if (ranges::contains(entry.usernames, username)) {
|
||||
if (found) {
|
||||
return entry.done.events();
|
||||
}
|
||||
} else {
|
||||
entry.usernames.push_back(username);
|
||||
}
|
||||
|
||||
const auto pop = [=](Error error) {
|
||||
const auto it = _toggleRequests.find(peerId);
|
||||
if (it != end(_toggleRequests)) {
|
||||
auto &list = it->second.usernames;
|
||||
list.erase(ranges::remove(list, username), end(list));
|
||||
if (list.empty()) {
|
||||
if (error == Error::Unknown) {
|
||||
it->second.done.fire_done();
|
||||
} else if (error == Error::TooMuch) {
|
||||
it->second.done.fire_error_copy(error);
|
||||
}
|
||||
_toggleRequests.remove(peerId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto done = [=] {
|
||||
pop(Error::Unknown);
|
||||
};
|
||||
const auto fail = [=](const MTP::Error &error) {
|
||||
const auto type = error.type();
|
||||
if (type == u"USERNAMES_ACTIVE_TOO_MUCH"_q) {
|
||||
pop(Error::TooMuch);
|
||||
} else {
|
||||
pop(Error::Unknown);
|
||||
}
|
||||
};
|
||||
|
||||
if (peer->isSelf()) {
|
||||
_api.request(MTPaccount_ToggleUsername(
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
_api.request(MTPchannels_ToggleUsername(
|
||||
channel->inputChannel,
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
} else {
|
||||
return rpl::never<rpl::no_value, Error>();
|
||||
}
|
||||
return entry.done.events();
|
||||
}
|
||||
|
||||
rpl::producer<> Usernames::reorder(
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<QString> &usernames) {
|
||||
const auto peerId = peer->id;
|
||||
const auto it = _reorderRequests.find(peerId);
|
||||
if (it != end(_reorderRequests)) {
|
||||
_api.request(it->second).cancel();
|
||||
_reorderRequests.erase(peerId);
|
||||
}
|
||||
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
auto tlUsernames = ranges::views::all(
|
||||
usernames
|
||||
) | ranges::views::transform([](const QString &username) {
|
||||
return MTP_string(username);
|
||||
}) | ranges::to<QVector<MTPstring>>;
|
||||
|
||||
const auto finish = [=] {
|
||||
if (_reorderRequests.contains(peerId)) {
|
||||
_reorderRequests.erase(peerId);
|
||||
}
|
||||
consumer.put_done();
|
||||
};
|
||||
if (usernames.empty()) {
|
||||
crl::on_main([=] { consumer.put_done(); });
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
if (peer->isSelf()) {
|
||||
const auto requestId = _api.request(MTPaccount_ReorderUsernames(
|
||||
MTP_vector<MTPstring>(std::move(tlUsernames))
|
||||
)).done(finish).fail(finish).send();
|
||||
_reorderRequests.emplace(peerId, requestId);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
const auto requestId = _api.request(MTPchannels_ReorderUsernames(
|
||||
channel->inputChannel,
|
||||
MTP_vector<MTPstring>(std::move(tlUsernames))
|
||||
)).done(finish).fail(finish).send();
|
||||
_reorderRequests.emplace(peerId, requestId);
|
||||
}
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
Data::Usernames Usernames::FromTL(const MTPVector<MTPUsername> &usernames) {
|
||||
return ranges::views::all(
|
||||
usernames.v
|
||||
) | ranges::views::transform(UsernameFromTL) | ranges::to_vector;
|
||||
}
|
||||
|
||||
void Usernames::requestToCache(not_null<PeerData*> peer) {
|
||||
_tinyCache = {};
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (user->usernames().empty()) {
|
||||
return;
|
||||
}
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
if (channel->usernames().empty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
*lifetime = loadUsernames(
|
||||
peer
|
||||
) | rpl::start_with_next([=, id = peer->id](Data::Usernames usernames) {
|
||||
_tinyCache = std::make_pair(id, std::move(usernames));
|
||||
lifetime->destroy();
|
||||
});
|
||||
}
|
||||
|
||||
Data::Usernames Usernames::cacheFor(PeerId id) {
|
||||
return (_tinyCache.first == id) ? _tinyCache.second : Data::Usernames();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
63
Telegram/SourceFiles/api/api_user_names.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_user_names.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
class PeerData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Api {
|
||||
|
||||
class Usernames final {
|
||||
public:
|
||||
enum class Error {
|
||||
TooMuch,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
explicit Usernames(not_null<ApiWrap*> api);
|
||||
|
||||
[[nodiscard]] rpl::producer<Data::Usernames> loadUsernames(
|
||||
not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] rpl::producer<rpl::no_value, Error> toggle(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &username,
|
||||
bool active);
|
||||
[[nodiscard]] rpl::producer<> reorder(
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<QString> &usernames);
|
||||
|
||||
void requestToCache(not_null<PeerData*> peer);
|
||||
[[nodiscard]] Data::Usernames cacheFor(PeerId id);
|
||||
|
||||
static Data::Usernames FromTL(const MTPVector<MTPUsername> &usernames);
|
||||
|
||||
private:
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
|
||||
using Key = PeerId;
|
||||
struct Entry final {
|
||||
rpl::event_stream<rpl::no_value, Error> done;
|
||||
std::vector<QString> usernames;
|
||||
};
|
||||
base::flat_map<Key, Entry> _toggleRequests;
|
||||
base::flat_map<Key, mtpRequestId> _reorderRequests;
|
||||
// Used for a seamless display of usernames list.
|
||||
std::pair<Key, Data::Usernames> _tinyCache;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -562,7 +562,7 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
}
|
||||
const auto type = DetectSeenType(item);
|
||||
const auto unseen = (type == Ui::WhoReadType::Seen)
|
||||
? item->unread()
|
||||
? item->unread(item->history())
|
||||
: item->isUnreadMedia();
|
||||
if (unseen) {
|
||||
return false;
|
||||
@@ -571,7 +571,7 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
const auto peer = history->peer;
|
||||
const auto chat = peer->asChat();
|
||||
const auto megagroup = peer->asMegagroup();
|
||||
if (!chat && !megagroup) {
|
||||
if ((!chat && !megagroup) || peer->isForum()) {
|
||||
return false;
|
||||
}
|
||||
const auto &appConfig = peer->session().account().appConfig();
|
||||
|
||||
@@ -32,6 +32,8 @@ class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
enum class DefaultNotify;
|
||||
enum class StickersType : uchar;
|
||||
class ForumTopic;
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
@@ -75,6 +77,7 @@ class UnreadThings;
|
||||
class Ringtones;
|
||||
class Transcribes;
|
||||
class Premium;
|
||||
class Usernames;
|
||||
|
||||
namespace details {
|
||||
|
||||
@@ -143,10 +146,6 @@ public:
|
||||
void registerModifyRequest(const QString &key, mtpRequestId requestId);
|
||||
void clearModifyRequest(const QString &key);
|
||||
|
||||
void applyNotifySettings(
|
||||
MTPInputNotifyPeer peer,
|
||||
const MTPPeerNotifySettings &settings);
|
||||
|
||||
void saveCurrentDraftToCloud();
|
||||
|
||||
void savePinnedOrder(Data::Folder *folder);
|
||||
@@ -219,8 +218,8 @@ public:
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> from);
|
||||
|
||||
void requestWebPageDelayed(WebPageData *page);
|
||||
void clearWebPageRequest(WebPageData *page);
|
||||
void requestWebPageDelayed(not_null<WebPageData*> page);
|
||||
void clearWebPageRequest(not_null<WebPageData*> page);
|
||||
void clearWebPageRequests();
|
||||
|
||||
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
||||
@@ -244,9 +243,10 @@ public:
|
||||
void leaveChannel(not_null<ChannelData*> channel);
|
||||
|
||||
void requestNotifySettings(const MTPInputNotifyPeer &peer);
|
||||
void updateNotifySettingsDelayed(not_null<const Data::Thread*> thread);
|
||||
void updateNotifySettingsDelayed(not_null<const PeerData*> peer);
|
||||
void updateDefaultNotifySettingsDelayed(Data::DefaultNotify type);
|
||||
void saveDraftToCloudDelayed(not_null<History*> history);
|
||||
void updateNotifySettingsDelayed(Data::DefaultNotify type);
|
||||
void saveDraftToCloudDelayed(not_null<Data::Thread*> thread);
|
||||
|
||||
static int OnlineTillFromStatus(
|
||||
const MTPUserStatus &status,
|
||||
@@ -265,12 +265,10 @@ public:
|
||||
using SliceType = Data::LoadDirection;
|
||||
void requestSharedMedia(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId,
|
||||
Storage::SharedMediaType type,
|
||||
MsgId messageId,
|
||||
SliceType slice);
|
||||
void requestSharedMediaCount(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type);
|
||||
|
||||
void readFeaturedSetDelayed(uint64 setId);
|
||||
|
||||
@@ -372,6 +370,7 @@ public:
|
||||
[[nodiscard]] Api::Ringtones &ringtones();
|
||||
[[nodiscard]] Api::Transcribes &transcribes();
|
||||
[[nodiscard]] Api::Premium &premium();
|
||||
[[nodiscard]] Api::Usernames &usernames();
|
||||
|
||||
void updatePrivacyLastSeens();
|
||||
|
||||
@@ -455,16 +454,19 @@ private:
|
||||
|
||||
void resolveJumpToHistoryDate(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId,
|
||||
const QDate &date,
|
||||
Fn<void(not_null<PeerData*>, MsgId)> callback);
|
||||
template <typename Callback>
|
||||
void requestMessageAfterDate(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId,
|
||||
const QDate &date,
|
||||
Callback &&callback);
|
||||
|
||||
void sharedMediaDone(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId,
|
||||
SharedMediaType type,
|
||||
Api::SearchResult &&parsed);
|
||||
|
||||
@@ -524,7 +526,7 @@ private:
|
||||
not_null<ChannelData*> channel);
|
||||
void migrateFail(not_null<PeerData*> peer, const QString &error);
|
||||
|
||||
not_null<Main::Session*> _session;
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
base::flat_map<QString, int> _modifyRequests;
|
||||
|
||||
@@ -542,14 +544,32 @@ private:
|
||||
not_null<History*>,
|
||||
std::pair<mtpRequestId,Fn<void()>>> _historyArchivedRequests;
|
||||
|
||||
QMap<WebPageData*, mtpRequestId> _webPagesPending;
|
||||
base::flat_map<not_null<WebPageData*>, mtpRequestId> _webPagesPending;
|
||||
base::Timer _webPagesTimer;
|
||||
|
||||
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
|
||||
struct StickerSetRequest {
|
||||
uint64 accessHash = 0;
|
||||
mtpRequestId id = 0;
|
||||
};
|
||||
base::flat_map<uint64, StickerSetRequest> _stickerSetRequests;
|
||||
|
||||
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||
base::flat_map<PeerId, mtpRequestId> _notifySettingRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::flat_map<
|
||||
not_null<ChannelData*>,
|
||||
mtpRequestId> _channelAmInRequests;
|
||||
|
||||
struct NotifySettingsKey {
|
||||
PeerId peerId = 0;
|
||||
MsgId topicRootId = 0;
|
||||
|
||||
friend inline constexpr auto operator<=>(
|
||||
NotifySettingsKey,
|
||||
NotifySettingsKey) = default;
|
||||
};
|
||||
base::flat_map<NotifySettingsKey, mtpRequestId> _notifySettingRequests;
|
||||
|
||||
base::flat_map<
|
||||
base::weak_ptr<Data::Thread>,
|
||||
mtpRequestId> _draftsSaveRequestIds;
|
||||
base::Timer _draftsSaveTimer;
|
||||
|
||||
base::flat_set<mtpRequestId> _stickerSetDisenableRequests;
|
||||
@@ -579,11 +599,18 @@ private:
|
||||
mtpRequestId _contactsRequestId = 0;
|
||||
mtpRequestId _contactsStatusesRequestId = 0;
|
||||
|
||||
base::flat_set<std::tuple<
|
||||
not_null<PeerData*>,
|
||||
SharedMediaType,
|
||||
MsgId,
|
||||
SliceType>> _sharedMediaRequests;
|
||||
struct SharedMediaRequest {
|
||||
not_null<PeerData*> peer;
|
||||
MsgId topicRootId = 0;
|
||||
SharedMediaType mediaType = {};
|
||||
MsgId aroundId = 0;
|
||||
SliceType sliceType = {};
|
||||
|
||||
friend inline auto operator<=>(
|
||||
const SharedMediaRequest&,
|
||||
const SharedMediaRequest&) = default;
|
||||
};
|
||||
base::flat_set<SharedMediaRequest> _sharedMediaRequests;
|
||||
|
||||
std::unique_ptr<DialogsLoadState> _dialogsLoadState;
|
||||
TimeId _dialogsLoadTill = 0;
|
||||
@@ -604,9 +631,11 @@ private:
|
||||
TimeId _topPromotionNextRequestTime = TimeId(0);
|
||||
base::Timer _topPromotionTimer;
|
||||
|
||||
base::flat_set<not_null<const PeerData*>> _updateNotifySettingsPeers;
|
||||
base::flat_set<Data::DefaultNotify> _updateNotifySettingsDefaults;
|
||||
base::Timer _updateNotifySettingsTimer;
|
||||
base::flat_set<not_null<const Data::ForumTopic*>> _updateNotifyTopics;
|
||||
base::flat_set<not_null<const PeerData*>> _updateNotifyPeers;
|
||||
base::flat_set<Data::DefaultNotify> _updateNotifyDefaults;
|
||||
base::Timer _updateNotifyTimer;
|
||||
rpl::lifetime _updateNotifyQueueLifetime;
|
||||
|
||||
std::map<
|
||||
Data::FileOrigin,
|
||||
@@ -650,6 +679,7 @@ private:
|
||||
const std::unique_ptr<Api::Ringtones> _ringtones;
|
||||
const std::unique_ptr<Api::Transcribes> _transcribes;
|
||||
const std::unique_ptr<Api::Premium> _premium;
|
||||
const std::unique_ptr<Api::Usernames> _usernames;
|
||||
|
||||
mtpRequestId _wallPaperRequestId = 0;
|
||||
QString _wallPaperSlug;
|
||||
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/unread_badge.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
@@ -269,7 +270,7 @@ void AddContactBox::setInnerFocus() {
|
||||
void AddContactBox::paintEvent(QPaintEvent *e) {
|
||||
BoxContent::paintEvent(e);
|
||||
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
if (_retrying) {
|
||||
p.setPen(st::boxTextFg);
|
||||
p.setFont(st::boxTextFont);
|
||||
@@ -846,7 +847,7 @@ SetupChannelBox::SetupChannelBox(
|
||||
this,
|
||||
st::setupChannelLink,
|
||||
nullptr,
|
||||
channel->username,
|
||||
channel->username(),
|
||||
channel->session().createInternalLink(QString()))
|
||||
, _checkTimer([=] { check(); }) {
|
||||
}
|
||||
@@ -1175,7 +1176,7 @@ void SetupChannelBox::check() {
|
||||
)).done([=](const MTPBool &result) {
|
||||
_checkRequestId = 0;
|
||||
_errorText = (mtpIsTrue(result)
|
||||
|| _checkUsername == _channel->username)
|
||||
|| _checkUsername == _channel->username())
|
||||
? QString()
|
||||
: tr::lng_create_channel_link_occupied(tr::now);
|
||||
_goodText = _errorText.isEmpty()
|
||||
@@ -1237,7 +1238,7 @@ SetupChannelBox::UsernameResult SetupChannelBox::parseError(
|
||||
|
||||
void SetupChannelBox::updateFail(UsernameResult result) {
|
||||
if ((result == UsernameResult::Ok)
|
||||
|| (_sentUsername == _channel->username)) {
|
||||
|| (_sentUsername == _channel->username())) {
|
||||
_channel->setName(
|
||||
TextUtilities::SingleLine(_channel->name()),
|
||||
TextUtilities::SingleLine(_sentUsername));
|
||||
@@ -1271,7 +1272,7 @@ void SetupChannelBox::checkFail(UsernameResult result) {
|
||||
_errorText = tr::lng_create_channel_link_invalid(tr::now);
|
||||
update();
|
||||
} else if ((result == UsernameResult::Occupied)
|
||||
&& _checkUsername != _channel->username) {
|
||||
&& _checkUsername != _channel->username()) {
|
||||
_errorText = tr::lng_create_channel_link_occupied(tr::now);
|
||||
update();
|
||||
} else {
|
||||
@@ -1445,7 +1446,7 @@ void EditNameBox::saveSelfFail(const QString &error) {
|
||||
TextUtilities::SingleLine(_first->getLastText().trimmed()),
|
||||
TextUtilities::SingleLine(_last->getLastText().trimmed()),
|
||||
QString(),
|
||||
TextUtilities::SingleLine(_user->username));
|
||||
TextUtilities::SingleLine(_user->username()));
|
||||
closeBox();
|
||||
} else if (error == "FIRSTNAME_INVALID") {
|
||||
_first->setFocus();
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
Group,
|
||||
Channel,
|
||||
Megagroup,
|
||||
Forum,
|
||||
};
|
||||
GroupInfoBox(
|
||||
QWidget*,
|
||||
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
void requestPapers();
|
||||
void sortPapers();
|
||||
void paintPaper(
|
||||
Painter &p,
|
||||
QPainter &p,
|
||||
const Paper &paper,
|
||||
int column,
|
||||
int row) const;
|
||||
@@ -289,7 +289,7 @@ void BackgroundBox::Inner::resizeToContentAndPreload() {
|
||||
|
||||
void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
|
||||
if (_papers.empty()) {
|
||||
p.setFont(st::noContactsFont);
|
||||
@@ -361,7 +361,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::paintPaper(
|
||||
Painter &p,
|
||||
QPainter &p,
|
||||
const Paper &paper,
|
||||
int column,
|
||||
int row) const {
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
|
||||
@@ -25,13 +25,6 @@ UserpicButton {
|
||||
uploadIconPosition: point;
|
||||
}
|
||||
|
||||
FeedUserpicButton {
|
||||
size: size;
|
||||
innerSize: pixels;
|
||||
innerPosition: point;
|
||||
innerPart: UserpicButton;
|
||||
}
|
||||
|
||||
countryRowHeight: 36px;
|
||||
countryRowNameFont: semiboldFont;
|
||||
countryRowNameFg: boxTextFg;
|
||||
@@ -69,12 +62,6 @@ defaultUserpicButton: UserpicButton {
|
||||
uploadIcon: defaultUploadUserpicIcon;
|
||||
uploadIconPosition: point(-1px, 1px);
|
||||
}
|
||||
defaultFeedUserpicButton: FeedUserpicButton {
|
||||
size: size(76px, 76px);
|
||||
innerSize: 76px;
|
||||
innerPosition: point(-1px, -1px);
|
||||
innerPart: defaultUserpicButton;
|
||||
}
|
||||
|
||||
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
|
||||
align: align(center);
|
||||
@@ -660,7 +647,7 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
|
||||
textFg: windowActiveTextFg;
|
||||
}
|
||||
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
|
||||
rightsRankMargin: margins(0px, 16px, 0px, 20px);
|
||||
rightsRankMargin: margins(0px, 7px, 0px, 20px);
|
||||
|
||||
mutePhotoButton: UserpicButton(defaultUserpicButton) {
|
||||
size: size(40px, 40px);
|
||||
|
||||
@@ -148,7 +148,7 @@ void FillChooseFilterMenu(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<History*> history) {
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
const auto validator = ChooseFilterValidator(history);
|
||||
for (const auto &filter : history->owner().chatsFilters().list()) {
|
||||
const auto id = filter.id();
|
||||
|
||||
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/painter.h"
|
||||
#include "boxes/abstract_box.h" // Ui::show().
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/observer.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Ui {
|
||||
template <typename Enum>
|
||||
|
||||
@@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/scroll_content_shadow.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -72,7 +73,7 @@ QCursor EditColorBox::Picker::generateCursor() {
|
||||
cursor.setDevicePixelRatio(cRetinaFactor());
|
||||
cursor.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&cursor);
|
||||
auto p = QPainter(&cursor);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
@@ -102,7 +103,7 @@ EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
|
||||
preparePalette();
|
||||
|
||||
@@ -360,7 +361,7 @@ void EditColorBox::Slider::prepareMinSize() {
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
auto to = rect().marginsRemoved(QMargins(st::colorSliderSkip, st::colorSliderSkip, st::colorSliderSkip, st::colorSliderSkip));
|
||||
Ui::Shadow::paint(p, to, width(), st::defaultRoundShadow);
|
||||
if (_type == Type::Opacity) {
|
||||
@@ -573,7 +574,7 @@ public:
|
||||
|
||||
protected:
|
||||
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(Painter &p) override;
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
void wheelEvent(QWheelEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
@@ -632,7 +633,7 @@ void EditColorBox::Field::correctValue(const QString &was, int wasCursor, QStrin
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Field::paintAdditionalPlaceholder(Painter &p) {
|
||||
void EditColorBox::Field::paintAdditionalPlaceholder(QPainter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
auto inner = QRect(_st.textMargins.right(), _st.textMargins.top(), width() - 2 * _st.textMargins.right(), height() - _st.textMargins.top() - _st.textMargins.bottom());
|
||||
@@ -696,7 +697,7 @@ public:
|
||||
|
||||
protected:
|
||||
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(Painter &p) override;
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
};
|
||||
|
||||
@@ -737,7 +738,7 @@ void EditColorBox::ResultField::correctValue(const QString &was, int wasCursor,
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
|
||||
void EditColorBox::ResultField::paintAdditionalPlaceholder(QPainter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/panel_animation.h"
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/filter_icon_panel.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h" // Data::AmPremiumValue.
|
||||
@@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/painter.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -125,7 +126,7 @@ QString TypeRow::generateShortName() {
|
||||
|
||||
PaintRoundImageCallback TypeRow::generatePaintUserpicCallback() {
|
||||
const auto flag = this->flag();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
|
||||
PaintFilterChatsTypeIcon(p, flag, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
@@ -241,7 +242,7 @@ auto TypeController::rowSelectionChanges() const
|
||||
}
|
||||
|
||||
void PaintFilterChatsTypeIcon(
|
||||
Painter &p,
|
||||
QPainter &p,
|
||||
Data::ChatFilter::Flag flag,
|
||||
int x,
|
||||
int y,
|
||||
|
||||
@@ -28,7 +28,7 @@ class Painter;
|
||||
|
||||
[[nodiscard]] QString FilterChatsTypeName(Data::ChatFilter::Flag flag);
|
||||
void PaintFilterChatsTypeIcon(
|
||||
Painter &p,
|
||||
QPainter &p,
|
||||
Data::ChatFilter::Flag flag,
|
||||
int x,
|
||||
int y,
|
||||
|
||||
@@ -108,7 +108,7 @@ void GiftBox(
|
||||
|
||||
top->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
Painter p(top);
|
||||
auto p = QPainter(top);
|
||||
|
||||
p.fillRect(r, Qt::transparent);
|
||||
stars->paint(p);
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -372,8 +373,8 @@ void Rows::ensureRippleBySelection(not_null<Row*> row, Selection selected) {
|
||||
const auto menu = v::is<MenuSelection>(selected);
|
||||
const auto menuArea = menuToggleArea(row);
|
||||
auto mask = menu
|
||||
? Ui::RippleAnimation::ellipseMask(menuArea.size())
|
||||
: Ui::RippleAnimation::rectMask({ width(), row->height });
|
||||
? Ui::RippleAnimation::EllipseMask(menuArea.size())
|
||||
: Ui::RippleAnimation::RectMask({ width(), row->height });
|
||||
ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
st::defaultRippleAnimation,
|
||||
std::move(mask),
|
||||
|
||||
@@ -242,7 +242,7 @@ void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
|
||||
if (!_progress || true) {
|
||||
return;
|
||||
}
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
const auto padding = st::localStorageRowPadding;
|
||||
const auto height = st::localStorageRowHeight;
|
||||
const auto bottom = height - padding.bottom() - _description->height();
|
||||
@@ -595,12 +595,3 @@ void LocalStorageBox::save() {
|
||||
_session->data().cache().updateSettings(update);
|
||||
closeBox();
|
||||
}
|
||||
|
||||
void LocalStorageBox::paintEvent(QPaintEvent *e) {
|
||||
BoxContent::paintEvent(e);
|
||||
|
||||
Painter p(this);
|
||||
|
||||
p.setFont(st::boxTextFont);
|
||||
p.setPen(st::windowFg);
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ public:
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
class Row;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
This code is in Public Domain, see license terms in .github/CONTRIBUTING.md
|
||||
Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
*/
|
||||
#include "boxes/mute_settings_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kForeverHours = 24 * 365;
|
||||
|
||||
} // namespace
|
||||
|
||||
MuteSettingsBox::MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer)
|
||||
: _peer(peer) {
|
||||
}
|
||||
|
||||
void MuteSettingsBox::prepare() {
|
||||
setTitle(tr::lng_disable_notifications_from_tray());
|
||||
auto y = 0;
|
||||
|
||||
object_ptr<Ui::FlatLabel> info(this, st::boxLabel);
|
||||
info->setText(tr::lng_mute_box_tip(tr::now));
|
||||
info->moveToLeft(st::boxPadding.left(), y);
|
||||
y += info->height() + st::boxLittleSkip;
|
||||
|
||||
const auto icon = object_ptr<Ui::UserpicButton>(
|
||||
this,
|
||||
_peer,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::mutePhotoButton);
|
||||
icon->setPointerCursor(false);
|
||||
icon->moveToLeft(st::boxPadding.left(), y);
|
||||
|
||||
object_ptr<Ui::FlatLabel> title(this, st::muteChatTitle);
|
||||
title->setText(_peer->name());
|
||||
title->moveToLeft(
|
||||
st::boxPadding.left() + st::muteChatTitleLeft,
|
||||
y + (icon->height() / 2) - (title->height() / 2));
|
||||
// the icon is always higher than this chat title
|
||||
y += icon->height() + st::boxMediumSkip;
|
||||
|
||||
// in fact, this is mute only for 1 year
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>(kForeverHours);
|
||||
y += st::boxOptionListPadding.top();
|
||||
for (const auto hours : { 1, 4, 18, 72, kForeverHours }) {
|
||||
const auto text = [&] {
|
||||
if (hours < 24) {
|
||||
return tr::lng_mute_duration_hours(tr::now, lt_count, hours);
|
||||
} else if (hours < kForeverHours) {
|
||||
return tr::lng_mute_duration_days(tr::now, lt_count, hours / 24);
|
||||
} else {
|
||||
return tr::lng_mute_duration_forever(tr::now);
|
||||
}
|
||||
}();
|
||||
object_ptr<Ui::Radiobutton> option(this, group, hours, text);
|
||||
option->moveToLeft(st::boxPadding.left(), y);
|
||||
y += option->heightNoMargins() + st::boxOptionListSkip;
|
||||
}
|
||||
y += st::boxOptionListPadding.bottom()
|
||||
- st::boxOptionListSkip
|
||||
+ st::defaultCheckbox.margin.bottom();
|
||||
|
||||
_save = [=] {
|
||||
const auto muteForSeconds = group->value() * 3600;
|
||||
_peer->owner().notifySettings().update(
|
||||
_peer,
|
||||
{ .period = muteForSeconds });
|
||||
closeBox();
|
||||
};
|
||||
addButton(tr::lng_box_ok(), _save);
|
||||
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWidth, y);
|
||||
}
|
||||
|
||||
void MuteSettingsBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||
if (_save) {
|
||||
_save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vi: ts=4 tw=80
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
This code is in Public Domain, see license terms in .github/CONTRIBUTING.md
|
||||
Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
/* This class implements a dialog-box with radio-buttons for pick duration of
|
||||
* turning off notifications from a chat. The widget is opened by a context menu
|
||||
* in the left list of dialogues. */
|
||||
class MuteSettingsBox : public Ui::BoxContent {
|
||||
public:
|
||||
MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
not_null<PeerData*> _peer;
|
||||
Fn<void()> _save;
|
||||
|
||||
};
|
||||
// vi: ts=4 tw=80
|
||||
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/sent_code_field.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "passport/passport_encryption.h"
|
||||
#include "passport/passport_panel_edit_contact.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
|
||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "data/data_peer_values.h"
|
||||
@@ -37,11 +38,11 @@ PaintRoundImageCallback PaintUserpicCallback(
|
||||
bool respectSavedMessagesChat) {
|
||||
if (respectSavedMessagesChat) {
|
||||
if (peer->isSelf()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
return [](QPainter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
} else if (peer->isRepliesChat()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
return [](QPainter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
@@ -206,7 +207,7 @@ void PeerListBox::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
void PeerListBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
|
||||
const auto &bg = (_controller->listSt()
|
||||
? *_controller->listSt()
|
||||
@@ -330,6 +331,14 @@ void PeerListController::peerListSearchAddRow(not_null<PeerData*> peer) {
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListController::peerListSearchAddRow(PeerListRowId id) {
|
||||
if (auto row = delegate()->peerListFindRow(id)) {
|
||||
delegate()->peerListAppendFoundRow(row);
|
||||
} else if (auto row = createSearchRow(id)) {
|
||||
delegate()->peerListAppendSearchRow(std::move(row));
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListController::peerListSearchRefreshRows() {
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
@@ -358,7 +367,8 @@ void PeerListController::setSearchNoResultsText(const QString &text) {
|
||||
if (text.isEmpty()) {
|
||||
setSearchNoResults(nullptr);
|
||||
} else {
|
||||
setSearchNoResults(object_ptr<Ui::FlatLabel>(nullptr, text, st::membersAbout));
|
||||
setSearchNoResults(
|
||||
object_ptr<Ui::FlatLabel>(nullptr, text, st::membersAbout));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +378,14 @@ base::unique_qptr<Ui::PopupMenu> PeerListController::rowContextMenu(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> PeerListController::createSearchRow(
|
||||
PeerListRowId id) {
|
||||
if (const auto peer = session().data().peerLoaded(PeerId(id))) {
|
||||
return createSearchRow(peer);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListState> PeerListController::saveState() const {
|
||||
return delegate()->peerListSaveState();
|
||||
}
|
||||
@@ -647,6 +665,18 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
auto PeerListRow::generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> & {
|
||||
return peer()->nameFirstLetters();
|
||||
}
|
||||
|
||||
auto PeerListRow::generateNameWords() const
|
||||
-> const base::flat_set<QString> & {
|
||||
return peer()->nameWords();
|
||||
}
|
||||
|
||||
|
||||
void PeerListRow::invalidatePixmapsCache() {
|
||||
if (_checkbox) {
|
||||
_checkbox->invalidateCache();
|
||||
@@ -672,7 +702,7 @@ int PeerListRow::paintNameIconGetWidth(
|
||||
nameLeft,
|
||||
nameTop,
|
||||
availableWidth,
|
||||
st::msgNameStyle.font->height),
|
||||
st::semiboldFont->height),
|
||||
nameWidth,
|
||||
outerWidth,
|
||||
{
|
||||
@@ -817,10 +847,16 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
|
||||
void PeerListRow::createCheckbox(
|
||||
const style::RoundImageCheckbox &st,
|
||||
Fn<void()> updateCallback) {
|
||||
const auto generateRadius = [=] {
|
||||
return (!special() && peer()->isForum())
|
||||
? ImageRoundRadius::Large
|
||||
: ImageRoundRadius::Ellipse;
|
||||
};
|
||||
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(
|
||||
st,
|
||||
std::move(updateCallback),
|
||||
generatePaintUserpicCallback());
|
||||
generatePaintUserpicCallback(),
|
||||
generateRadius);
|
||||
}
|
||||
|
||||
void PeerListRow::setCheckedInternal(bool checked, anim::type animated) {
|
||||
@@ -982,12 +1018,12 @@ bool PeerListContent::addingToSearchIndex() const {
|
||||
}
|
||||
|
||||
void PeerListContent::addToSearchIndex(not_null<PeerListRow*> row) {
|
||||
if (row->isSearchResult() || row->special()) {
|
||||
if (row->isSearchResult()) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeFromSearchIndex(row);
|
||||
row->setNameFirstLetters(row->peer()->nameFirstLetters());
|
||||
row->setNameFirstLetters(row->generateNameFirstLetters());
|
||||
for (auto ch : row->nameFirstLetters()) {
|
||||
_searchIndex[ch].push_back(row);
|
||||
}
|
||||
@@ -1409,7 +1445,7 @@ void PeerListContent::mousePressEvent(QMouseEvent *e) {
|
||||
row->addRipple(_st.item, _controller->customRowRippleMaskGenerator(), point, std::move(updateCallback));
|
||||
} else {
|
||||
const auto maskGenerator = [&] {
|
||||
return Ui::RippleAnimation::rectMask(
|
||||
return Ui::RippleAnimation::RectMask(
|
||||
QSize(width(), _rowHeight));
|
||||
};
|
||||
row->addRipple(_st.item, maskGenerator, point, std::move(updateCallback));
|
||||
@@ -1812,9 +1848,9 @@ void PeerListContent::searchQueryChanged(QString query) {
|
||||
}
|
||||
if (minimalList) {
|
||||
auto searchWordInNames = [](
|
||||
not_null<PeerData*> peer,
|
||||
not_null<PeerListRow*> row,
|
||||
const QString &searchWord) {
|
||||
for (auto &nameWord : peer->nameWords()) {
|
||||
for (auto &nameWord : row->generateNameWords()) {
|
||||
if (nameWord.startsWith(searchWord)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1822,9 +1858,9 @@ void PeerListContent::searchQueryChanged(QString query) {
|
||||
return false;
|
||||
};
|
||||
auto allSearchWordsInNames = [&](
|
||||
not_null<PeerData*> peer) {
|
||||
not_null<PeerListRow*> row) {
|
||||
for (const auto &searchWord : searchWordsList) {
|
||||
if (!searchWordInNames(peer, searchWord)) {
|
||||
if (!searchWordInNames(row, searchWord)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1833,7 +1869,7 @@ void PeerListContent::searchQueryChanged(QString query) {
|
||||
|
||||
_filterResults.reserve(minimalList->size());
|
||||
for (const auto &row : *minimalList) {
|
||||
if (!row->special() && allSearchWordsInNames(row->peer())) {
|
||||
if (allSearchWordsInNames(row)) {
|
||||
_filterResults.push_back(row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,11 @@ public:
|
||||
[[nodiscard]] virtual auto generatePaintUserpicCallback()
|
||||
-> PaintRoundImageCallback;
|
||||
|
||||
[[nodiscard]] virtual auto generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> &;
|
||||
[[nodiscard]] virtual auto generateNameWords() const
|
||||
-> const base::flat_set<QString> &;
|
||||
|
||||
void setCustomStatus(const QString &status, bool active = false);
|
||||
void clearCustomStatus();
|
||||
|
||||
@@ -360,6 +365,7 @@ private:
|
||||
class PeerListSearchDelegate {
|
||||
public:
|
||||
virtual void peerListSearchAddRow(not_null<PeerData*> peer) = 0;
|
||||
virtual void peerListSearchAddRow(PeerListRowId id) = 0;
|
||||
virtual void peerListSearchRefreshRows() = 0;
|
||||
virtual ~PeerListSearchDelegate() = default;
|
||||
|
||||
@@ -470,6 +476,7 @@ public:
|
||||
not_null<PeerData*> peer) {
|
||||
return nullptr;
|
||||
}
|
||||
virtual std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id);
|
||||
virtual std::unique_ptr<PeerListRow> createRestoredRow(
|
||||
not_null<PeerData*> peer) {
|
||||
return nullptr;
|
||||
@@ -494,6 +501,7 @@ public:
|
||||
void search(const QString &query);
|
||||
|
||||
void peerListSearchAddRow(not_null<PeerData*> peer) override;
|
||||
void peerListSearchAddRow(PeerListRowId id) override;
|
||||
void peerListSearchRefreshRows() override;
|
||||
|
||||
[[nodiscard]] virtual bool respectSavedMessagesChat() const {
|
||||
|
||||
@@ -11,61 +11,40 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/random.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "window/window_session_controller.h" // showAddContact()
|
||||
#include "base/unixtime.h"
|
||||
#include "facades.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000);
|
||||
constexpr auto kSearchPerPage = 50;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Not used for now.
|
||||
//
|
||||
//MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
|
||||
//, _st(st) {
|
||||
// resize(_st.width, _st.height);
|
||||
// setCursor(style::cur_pointer);
|
||||
//}
|
||||
//
|
||||
//void MembersAddButton::paintEvent(QPaintEvent *e) {
|
||||
// Painter p(this);
|
||||
//
|
||||
// auto ms = crl::now();
|
||||
// auto over = isOver();
|
||||
// auto down = isDown();
|
||||
//
|
||||
// ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
|
||||
// paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
|
||||
// ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width());
|
||||
//}
|
||||
//
|
||||
//QImage MembersAddButton::prepareRippleMask() const {
|
||||
// return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
|
||||
//}
|
||||
//
|
||||
//QPoint MembersAddButton::prepareRippleStartPosition() const {
|
||||
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
|
||||
//}
|
||||
|
||||
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||
not_null<Window::SessionController*> sessionController) {
|
||||
using Mode = ContactsBoxController::SortMode;
|
||||
@@ -313,7 +292,8 @@ QString ChatsListBoxController::emptyBoxText() const {
|
||||
return tr::lng_contacts_not_found(tr::now);
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(not_null<PeerData*> peer) {
|
||||
std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(
|
||||
not_null<PeerData*> peer) {
|
||||
return createRow(peer->owner().history(peer));
|
||||
}
|
||||
|
||||
@@ -480,8 +460,8 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
|
||||
|
||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<void(not_null<PeerData*>)> callback,
|
||||
Fn<bool(not_null<PeerData*>)> filter)
|
||||
FnMut<void(not_null<Data::Thread*>)> callback,
|
||||
Fn<bool(not_null<Data::Thread*>)> filter)
|
||||
: ChatsListBoxController(session)
|
||||
, _session(session)
|
||||
, _callback(std::move(callback))
|
||||
@@ -497,10 +477,52 @@ void ChooseRecipientBoxController::prepareViewHook() {
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
auto weak = base::make_weak(this);
|
||||
auto guard = base::make_weak(this);
|
||||
const auto peer = row->peer();
|
||||
if (const auto forum = peer->forum()) {
|
||||
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
auto callback = [=](not_null<Data::ForumTopic*> topic) {
|
||||
const auto exists = guard.get();
|
||||
if (!exists) {
|
||||
if (*weak) {
|
||||
(*weak)->closeBox();
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto onstack = std::move(_callback);
|
||||
onstack(topic);
|
||||
if (guard) {
|
||||
_callback = std::move(onstack);
|
||||
} else if (*weak) {
|
||||
(*weak)->closeBox();
|
||||
}
|
||||
};
|
||||
const auto filter = [=](not_null<Data::ForumTopic*> topic) {
|
||||
return guard && (!_filter || _filter(topic));
|
||||
};
|
||||
auto owned = Box<PeerListBox>(
|
||||
std::make_unique<ChooseTopicBoxController>(
|
||||
forum,
|
||||
std::move(callback),
|
||||
filter),
|
||||
[=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
forum->destroyed(
|
||||
) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
});
|
||||
*weak = owned.data();
|
||||
delegate()->peerListShowBox(std::move(owned));
|
||||
return;
|
||||
}
|
||||
const auto history = peer->owner().history(peer);
|
||||
auto callback = std::move(_callback);
|
||||
callback(row->peer());
|
||||
if (weak) {
|
||||
callback(history);
|
||||
if (guard) {
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
}
|
||||
@@ -509,8 +531,220 @@ auto ChooseRecipientBoxController::createRow(
|
||||
not_null<History*> history) -> std::unique_ptr<Row> {
|
||||
const auto peer = history->peer;
|
||||
const auto skip = _filter
|
||||
? !_filter(peer)
|
||||
? !_filter(history)
|
||||
: ((peer->isBroadcast() && !peer->canWrite())
|
||||
|| peer->isRepliesChat());
|
||||
return skip ? nullptr : std::make_unique<Row>(history);
|
||||
}
|
||||
|
||||
ChooseTopicSearchController::ChooseTopicSearchController(
|
||||
not_null<Data::Forum*> forum)
|
||||
: _forum(forum)
|
||||
, _api(&forum->session().mtp())
|
||||
, _timer([=] { searchOnServer(); }) {
|
||||
}
|
||||
|
||||
void ChooseTopicSearchController::searchQuery(const QString &query) {
|
||||
if (_query != query) {
|
||||
_query = query;
|
||||
_api.request(base::take(_requestId)).cancel();
|
||||
_offsetDate = 0;
|
||||
_offsetId = 0;
|
||||
_offsetTopicId = 0;
|
||||
_allLoaded = false;
|
||||
if (!_query.isEmpty()) {
|
||||
_timer.callOnce(AutoSearchTimeout);
|
||||
} else {
|
||||
_timer.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseTopicSearchController::searchOnServer() {
|
||||
_requestId = _api.request(MTPchannels_GetForumTopics(
|
||||
MTP_flags(MTPchannels_GetForumTopics::Flag::f_q),
|
||||
_forum->channel()->inputChannel,
|
||||
MTP_string(_query),
|
||||
MTP_int(_offsetDate),
|
||||
MTP_int(_offsetId),
|
||||
MTP_int(_offsetTopicId),
|
||||
MTP_int(kSearchPerPage)
|
||||
)).done([=](const MTPmessages_ForumTopics &result) {
|
||||
_requestId = 0;
|
||||
const auto savedTopicId = _offsetTopicId;
|
||||
const auto byCreation = result.data().is_order_by_create_date();
|
||||
_forum->applyReceivedTopics(result, [&](
|
||||
not_null<Data::ForumTopic*> topic) {
|
||||
_offsetTopicId = topic->rootId();
|
||||
if (byCreation) {
|
||||
_offsetDate = topic->creationDate();
|
||||
if (const auto last = topic->lastServerMessage()) {
|
||||
_offsetId = last->id;
|
||||
}
|
||||
} else if (const auto last = topic->lastServerMessage()) {
|
||||
_offsetId = last->id;
|
||||
_offsetDate = last->date();
|
||||
}
|
||||
delegate()->peerListSearchAddRow(topic->rootId().bare);
|
||||
});
|
||||
if (_offsetTopicId != savedTopicId) {
|
||||
delegate()->peerListSearchRefreshRows();
|
||||
} else {
|
||||
_allLoaded = true;
|
||||
}
|
||||
}).fail([=] {
|
||||
_allLoaded = true;
|
||||
}).send();
|
||||
}
|
||||
|
||||
bool ChooseTopicSearchController::isLoading() {
|
||||
return _timer.isActive() || _requestId;
|
||||
}
|
||||
|
||||
bool ChooseTopicSearchController::loadMoreRows() {
|
||||
if (!isLoading()) {
|
||||
searchOnServer();
|
||||
}
|
||||
return !_allLoaded;
|
||||
}
|
||||
|
||||
ChooseTopicBoxController::Row::Row(not_null<Data::ForumTopic*> topic)
|
||||
: PeerListRow(topic->rootId().bare)
|
||||
, _topic(topic) {
|
||||
}
|
||||
|
||||
QString ChooseTopicBoxController::Row::generateName() {
|
||||
return _topic->title();
|
||||
}
|
||||
|
||||
QString ChooseTopicBoxController::Row::generateShortName() {
|
||||
return _topic->title();
|
||||
}
|
||||
|
||||
auto ChooseTopicBoxController::Row::generatePaintUserpicCallback()
|
||||
-> PaintRoundImageCallback {
|
||||
return [=](
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) {
|
||||
auto view = std::shared_ptr<Data::CloudImageView>();
|
||||
p.translate(x, y);
|
||||
_topic->paintUserpic(p, view, {
|
||||
.st = &st::forumTopicRow,
|
||||
.now = crl::now(),
|
||||
.width = outerWidth,
|
||||
.paused = false,
|
||||
});
|
||||
p.translate(-x, -y);
|
||||
};
|
||||
}
|
||||
|
||||
auto ChooseTopicBoxController::Row::generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> & {
|
||||
return _topic->chatListFirstLetters();
|
||||
}
|
||||
|
||||
auto ChooseTopicBoxController::Row::generateNameWords() const
|
||||
-> const base::flat_set<QString> & {
|
||||
return _topic->chatListNameWords();
|
||||
}
|
||||
|
||||
ChooseTopicBoxController::ChooseTopicBoxController(
|
||||
not_null<Data::Forum*> forum,
|
||||
FnMut<void(not_null<Data::ForumTopic*>)> callback,
|
||||
Fn<bool(not_null<Data::ForumTopic*>)> filter)
|
||||
: PeerListController(std::make_unique<ChooseTopicSearchController>(forum))
|
||||
, _forum(forum)
|
||||
, _callback(std::move(callback))
|
||||
, _filter(std::move(filter)) {
|
||||
setStyleOverrides(&st::chooseTopicList);
|
||||
|
||||
_forum->chatsListChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshRows();
|
||||
}, lifetime());
|
||||
|
||||
_forum->topicDestroyed(
|
||||
) | rpl::start_with_next([=](not_null<Data::ForumTopic*> topic) {
|
||||
const auto id = PeerListRowId(topic->rootId().bare);
|
||||
if (const auto row = delegate()->peerListFindRow(id)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
Main::Session &ChooseTopicBoxController::session() const {
|
||||
return _forum->session();
|
||||
}
|
||||
|
||||
void ChooseTopicBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto weak = base::make_weak(this);
|
||||
auto onstack = base::take(_callback);
|
||||
onstack(static_cast<Row*>(row.get())->topic());
|
||||
if (weak) {
|
||||
_callback = std::move(onstack);
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseTopicBoxController::prepare() {
|
||||
delegate()->peerListSetTitle(tr::lng_forward_choose());
|
||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
refreshRows(true);
|
||||
|
||||
session().changes().entryUpdates(
|
||||
Data::EntryUpdate::Flag::Repaint
|
||||
) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
|
||||
if (const auto topic = update.entry->asTopic()) {
|
||||
if (topic->forum() == _forum) {
|
||||
const auto id = topic->rootId().bare;
|
||||
if (const auto row = delegate()->peerListFindRow(id)) {
|
||||
delegate()->peerListUpdateRow(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void ChooseTopicBoxController::refreshRows(bool initial) {
|
||||
auto added = false;
|
||||
for (const auto &row : _forum->topicsList()->indexed()->all()) {
|
||||
if (const auto topic = row->topic()) {
|
||||
const auto id = topic->rootId().bare;
|
||||
auto already = delegate()->peerListFindRow(id);
|
||||
if (initial || !already) {
|
||||
if (auto created = createRow(topic)) {
|
||||
delegate()->peerListAppendRow(std::move(created));
|
||||
added = true;
|
||||
}
|
||||
} else if (already->isSearchResult()) {
|
||||
delegate()->peerListAppendFoundRow(already);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (added) {
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseTopicBoxController::loadMoreRows() {
|
||||
_forum->requestTopics();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ChooseTopicBoxController::createSearchRow(
|
||||
PeerListRowId id) {
|
||||
if (const auto topic = _forum->topicFor(MsgId(id))) {
|
||||
return std::make_unique<Row>(topic);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
|
||||
-> std::unique_ptr<Row> {
|
||||
const auto skip = _filter ? !_filter(topic) : !topic->canWrite();
|
||||
return skip ? nullptr : std::make_unique<Row>(topic);
|
||||
};
|
||||
|
||||
@@ -12,25 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
// Not used for now.
|
||||
//
|
||||
//class MembersAddButton : public Ui::RippleButton {
|
||||
//public:
|
||||
// MembersAddButton(QWidget *parent, const style::TwoIconButton &st);
|
||||
//
|
||||
//protected:
|
||||
// void paintEvent(QPaintEvent *e) override;
|
||||
//
|
||||
// QImage prepareRippleMask() const override;
|
||||
// QPoint prepareRippleStartPosition() const override;
|
||||
//
|
||||
//private:
|
||||
// const style::TwoIconButton &_st;
|
||||
//
|
||||
//};
|
||||
|
||||
class History;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
@@ -110,7 +99,8 @@ public:
|
||||
std::unique_ptr<PeerListSearchController> searchController);
|
||||
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(
|
||||
not_null<PeerData*> peer) override final;
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<Row> createRow(not_null<History*> history) = 0;
|
||||
@@ -173,8 +163,8 @@ class ChooseRecipientBoxController
|
||||
public:
|
||||
ChooseRecipientBoxController(
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<void(not_null<PeerData*>)> callback,
|
||||
Fn<bool(not_null<PeerData*>)> filter = nullptr);
|
||||
FnMut<void(not_null<Data::Thread*>)> callback,
|
||||
Fn<bool(not_null<Data::Thread*>)> filter = nullptr);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
@@ -189,7 +179,80 @@ protected:
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
FnMut<void(not_null<PeerData*>)> _callback;
|
||||
Fn<bool(not_null<PeerData*>)> _filter;
|
||||
FnMut<void(not_null<Data::Thread*>)> _callback;
|
||||
Fn<bool(not_null<Data::Thread*>)> _filter;
|
||||
|
||||
};
|
||||
|
||||
class ChooseTopicSearchController : public PeerListSearchController {
|
||||
public:
|
||||
explicit ChooseTopicSearchController(not_null<Data::Forum*> forum);
|
||||
|
||||
void searchQuery(const QString &query) override;
|
||||
bool isLoading() override;
|
||||
bool loadMoreRows() override;
|
||||
|
||||
private:
|
||||
void searchOnServer();
|
||||
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
|
||||
|
||||
const not_null<Data::Forum*> _forum;
|
||||
MTP::Sender _api;
|
||||
base::Timer _timer;
|
||||
QString _query;
|
||||
mtpRequestId _requestId = 0;
|
||||
TimeId _offsetDate = 0;
|
||||
MsgId _offsetId = 0;
|
||||
MsgId _offsetTopicId = 0;
|
||||
bool _allLoaded = false;
|
||||
|
||||
};
|
||||
|
||||
class ChooseTopicBoxController final
|
||||
: public PeerListController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
ChooseTopicBoxController(
|
||||
not_null<Data::Forum*> forum,
|
||||
FnMut<void(not_null<Data::ForumTopic*>)> callback,
|
||||
Fn<bool(not_null<Data::ForumTopic*>)> filter = nullptr);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
void prepare() override;
|
||||
void loadMoreRows() override;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override;
|
||||
|
||||
private:
|
||||
class Row final : public PeerListRow {
|
||||
public:
|
||||
explicit Row(not_null<Data::ForumTopic*> topic);
|
||||
|
||||
[[nodiscard]] not_null<Data::ForumTopic*> topic() const {
|
||||
return _topic;
|
||||
}
|
||||
|
||||
QString generateName() override;
|
||||
QString generateShortName() override;
|
||||
PaintRoundImageCallback generatePaintUserpicCallback() override;
|
||||
|
||||
auto generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> & override;
|
||||
auto generateNameWords() const
|
||||
-> const base::flat_set<QString> & override;
|
||||
|
||||
private:
|
||||
const not_null<Data::ForumTopic*> _topic;
|
||||
|
||||
};
|
||||
|
||||
void refreshRows(bool initial = false);
|
||||
[[nodiscard]] std::unique_ptr<Row> createRow(
|
||||
not_null<Data::ForumTopic*> topic);
|
||||
|
||||
const not_null<Data::Forum*> _forum;
|
||||
FnMut<void(not_null<Data::ForumTopic*>)> _callback;
|
||||
Fn<bool(not_null<Data::ForumTopic*>)> _filter;
|
||||
|
||||
};
|
||||
|
||||
@@ -278,7 +278,7 @@ void PeerListsBox::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
void PeerListsBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto p = QPainter(this);
|
||||
|
||||
const auto &bg = (firstController()->listSt()
|
||||
? *firstController()->listSt()
|
||||
|
||||
@@ -57,43 +57,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
void ShareBotGame(
|
||||
not_null<UserData*> bot,
|
||||
not_null<PeerData*> chat,
|
||||
const QString &shortName) {
|
||||
const auto history = chat->owner().history(chat);
|
||||
auto &histories = history->owner().histories();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
const auto api = &chat->session().api();
|
||||
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||
MTP_flags(0),
|
||||
chat->input,
|
||||
MTP_int(0),
|
||||
MTP_inputMediaGame(
|
||||
MTP_inputGameShortName(
|
||||
bot->inputUser,
|
||||
MTP_string(shortName))),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(0), // schedule_date
|
||||
MTPInputPeer() // send_as
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
finish();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
api->sendMessageFail(error, chat);
|
||||
finish();
|
||||
}).afterRequest(
|
||||
history->sendRequestId
|
||||
).send();
|
||||
return history->sendRequestId;
|
||||
});
|
||||
}
|
||||
|
||||
Controller::Controller(
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<not_null<PeerData*>> add,
|
||||
@@ -167,9 +130,7 @@ AddBotToGroupBoxController::AddBotToGroupBoxController(
|
||||
Scope scope,
|
||||
const QString &token,
|
||||
ChatAdminRights requestedRights)
|
||||
: ChatsListBoxController((scope == Scope::ShareGame)
|
||||
? std::make_unique<PeerListGlobalSearchController>(&bot->session())
|
||||
: nullptr)
|
||||
: ChatsListBoxController(std::unique_ptr<PeerListSearchController>())
|
||||
, _controller(controller)
|
||||
, _bot(bot)
|
||||
, _scope(scope)
|
||||
@@ -187,42 +148,7 @@ Main::Session &AddBotToGroupBoxController::session() const {
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
if (sharingBotGame()) {
|
||||
shareBotGame(row->peer());
|
||||
} else {
|
||||
addBotToGroup(row->peer());
|
||||
}
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
|
||||
auto send = crl::guard(this, [
|
||||
bot = _bot,
|
||||
controller = _controller,
|
||||
chat,
|
||||
token = _token] {
|
||||
ShareBotGame(bot, chat, token);
|
||||
using Way = Window::SectionShow::Way;
|
||||
controller->hideLayer();
|
||||
controller->showPeerHistory(chat, Way::ClearStack, ShowAtUnreadMsgId);
|
||||
});
|
||||
auto confirmText = [chat] {
|
||||
if (chat->isUser()) {
|
||||
return tr::lng_bot_sure_share_game(
|
||||
tr::now,
|
||||
lt_user,
|
||||
chat->name());
|
||||
}
|
||||
return tr::lng_bot_sure_share_game_group(
|
||||
tr::now,
|
||||
lt_group,
|
||||
chat->name());
|
||||
}();
|
||||
_controller->show(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = confirmText,
|
||||
.confirmed = std::move(send),
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
addBotToGroup(row->peer());
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::requestExistingRights(
|
||||
@@ -343,13 +269,6 @@ auto AddBotToGroupBoxController::createRow(not_null<History*> history)
|
||||
|
||||
bool AddBotToGroupBoxController::needToCreateRow(
|
||||
not_null<PeerData*> peer) const {
|
||||
if (sharingBotGame()) {
|
||||
if (!peer->canWrite()
|
||||
|| peer->amRestricted(ChatRestriction::SendGames)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (const auto chat = peer->asChat()) {
|
||||
if (onlyAdminToGroup()) {
|
||||
return chat->canAddAdmins();
|
||||
@@ -376,14 +295,10 @@ bool AddBotToGroupBoxController::needToCreateRow(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AddBotToGroupBoxController::sharingBotGame() const {
|
||||
return (_scope == Scope::ShareGame);
|
||||
}
|
||||
|
||||
QString AddBotToGroupBoxController::emptyBoxText() const {
|
||||
return !session().data().chatsListLoaded()
|
||||
? tr::lng_contacts_loading(tr::now)
|
||||
: (sharingBotGame() || _adminToChannel)
|
||||
: _adminToChannel
|
||||
? tr::lng_bot_no_chats(tr::now)
|
||||
: tr::lng_bot_no_groups(tr::now);
|
||||
}
|
||||
@@ -391,7 +306,7 @@ QString AddBotToGroupBoxController::emptyBoxText() const {
|
||||
QString AddBotToGroupBoxController::noResultsText() const {
|
||||
return !session().data().chatsListLoaded()
|
||||
? tr::lng_contacts_loading(tr::now)
|
||||
: (sharingBotGame() || _adminToChannel)
|
||||
: _adminToChannel
|
||||
? tr::lng_bot_chats_not_found(tr::now)
|
||||
: tr::lng_bot_groups_not_found(tr::now);
|
||||
}
|
||||
@@ -465,7 +380,7 @@ bool AddBotToGroupBoxController::onlyAdminToChannel() const {
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::prepareViewHook() {
|
||||
delegate()->peerListSetTitle((sharingBotGame() || _adminToChannel)
|
||||
delegate()->peerListSetTitle(_adminToChannel
|
||||
? tr::lng_bot_choose_chat()
|
||||
: tr::lng_bot_choose_group());
|
||||
if ((_adminToGroup && !onlyAdminToGroup())
|
||||
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
None,
|
||||
GroupAdmin,
|
||||
ChannelAdmin,
|
||||
ShareGame,
|
||||
All,
|
||||
};
|
||||
static void Start(
|
||||
@@ -50,11 +49,9 @@ private:
|
||||
[[nodiscard]] bool onlyAdminToChannel() const;
|
||||
|
||||
bool needToCreateRow(not_null<PeerData*> peer) const;
|
||||
bool sharingBotGame() const;
|
||||
QString noResultsText() const;
|
||||
void updateLabels();
|
||||
|
||||
void shareBotGame(not_null<PeerData*> chat);
|
||||
void addBotToGroup(not_null<PeerData*> chat);
|
||||
void requestExistingRights(not_null<ChannelData*> channel);
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ void SendRequest(
|
||||
first,
|
||||
last,
|
||||
user->nameOrPhone,
|
||||
user->username);
|
||||
user->username());
|
||||
user->session().api().applyUpdates(result);
|
||||
if (const auto settings = user->settings()) {
|
||||
const auto flags = PeerSetting::AddContact
|
||||
|
||||
542
Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peers/edit_forum_topic_box.h"
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/effects/emoji_fly_animation.h"
|
||||
#include "ui/abstract_button.h"
|
||||
#include "ui/color_int_conversion.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_icons.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "base/random.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "chat_helpers/emoji_list_widget.h"
|
||||
#include "chat_helpers/stickers_list_footer.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/view/history_view_replies_section.h"
|
||||
#include "history/view/history_view_sticker_toast.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwindow.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultIconId = DocumentId(0x7FFF'FFFF'FFFF'FFFFULL);
|
||||
|
||||
struct DefaultIcon {
|
||||
QString title;
|
||||
int32 colorId = 0;
|
||||
};
|
||||
|
||||
class DefaultIconEmoji final : public Ui::Text::CustomEmoji {
|
||||
public:
|
||||
DefaultIconEmoji(
|
||||
rpl::producer<DefaultIcon> value,
|
||||
Fn<void()> repaint);
|
||||
|
||||
QString entityData() override;
|
||||
|
||||
void paint(QPainter &p, const Context &context) override;
|
||||
void unload() override;
|
||||
bool ready() override;
|
||||
bool readyInDefaultState() override;
|
||||
|
||||
private:
|
||||
DefaultIcon _icon = {};
|
||||
QImage _image;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
DefaultIconEmoji::DefaultIconEmoji(
|
||||
rpl::producer<DefaultIcon> value,
|
||||
Fn<void()> repaint) {
|
||||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
QString DefaultIconEmoji::entityData() {
|
||||
return u"topic_icon:%1"_q.arg(_icon.colorId);
|
||||
}
|
||||
|
||||
void DefaultIconEmoji::paint(QPainter &p, const Context &context) {
|
||||
if (_image.isNull()) {
|
||||
_image = Data::ForumTopicIconFrame(
|
||||
_icon.colorId,
|
||||
_icon.title,
|
||||
st::defaultForumTopicIcon);
|
||||
}
|
||||
const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
|
||||
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
|
||||
const auto skip = (customSize - st::defaultForumTopicIcon.size) / 2;
|
||||
p.drawImage(context.position + QPoint(skip, skip), _image);
|
||||
}
|
||||
|
||||
void DefaultIconEmoji::unload() {
|
||||
_image = QImage();
|
||||
}
|
||||
|
||||
bool DefaultIconEmoji::ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefaultIconEmoji::readyInDefaultState() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] int EditIconSize() {
|
||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||
return Data::FrameSizeFromTag(tag) / style::DevicePixelRatio();
|
||||
}
|
||||
|
||||
[[nodiscard]] int32 ChooseNextColorId(
|
||||
int32 currentId,
|
||||
std::vector<int32> &otherIds) {
|
||||
if (otherIds.size() == 1 && otherIds.front() == currentId) {
|
||||
otherIds = Data::ForumTopicColorIds();
|
||||
}
|
||||
const auto i = ranges::find(otherIds, currentId);
|
||||
if (i != end(otherIds)) {
|
||||
otherIds.erase(i);
|
||||
}
|
||||
return otherIds.empty()
|
||||
? currentId
|
||||
: otherIds[base::RandomIndex(otherIds.size())];
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::AbstractButton*> EditIconButton(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
rpl::producer<DefaultIcon> defaultIcon,
|
||||
rpl::producer<DocumentId> iconId,
|
||||
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame) {
|
||||
using namespace Info::Profile;
|
||||
struct State {
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> icon;
|
||||
QImage defaultIcon;
|
||||
};
|
||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||
const auto size = EditIconSize();
|
||||
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
||||
result->show();
|
||||
const auto state = result->lifetime().make_state<State>();
|
||||
|
||||
std::move(
|
||||
iconId
|
||||
) | rpl::start_with_next([=](DocumentId id) {
|
||||
const auto owner = &controller->session().data();
|
||||
state->icon = id
|
||||
? owner->customEmojiManager().create(
|
||||
id,
|
||||
[=] { result->update(); },
|
||||
tag)
|
||||
: nullptr;
|
||||
result->update();
|
||||
}, result->lifetime());
|
||||
|
||||
std::move(
|
||||
defaultIcon
|
||||
) | rpl::start_with_next([=](DefaultIcon icon) {
|
||||
state->defaultIcon = Data::ForumTopicIconFrame(
|
||||
icon.colorId,
|
||||
icon.title,
|
||||
st::largeForumTopicIcon);
|
||||
result->update();
|
||||
}, result->lifetime());
|
||||
|
||||
result->resize(size, size);
|
||||
result->paintRequest(
|
||||
) | rpl::filter([=] {
|
||||
return !paintIconFrame(result);
|
||||
}) | rpl::start_with_next([=](QRect clip) {
|
||||
auto args = Ui::Text::CustomEmoji::Context{
|
||||
.preview = st::windowBgOver->c,
|
||||
.now = crl::now(),
|
||||
.paused = controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer),
|
||||
};
|
||||
auto p = QPainter(result);
|
||||
if (state->icon) {
|
||||
state->icon->paint(p, args);
|
||||
} else {
|
||||
const auto skip = (size - st::largeForumTopicIcon.size) / 2;
|
||||
p.drawImage(skip, skip, state->defaultIcon);
|
||||
}
|
||||
}, result->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct IconSelector {
|
||||
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame;
|
||||
rpl::producer<DocumentId> iconIdValue;
|
||||
};
|
||||
|
||||
[[nodiscard]] IconSelector AddIconSelector(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
not_null<Window::SessionController*> controller,
|
||||
rpl::producer<DefaultIcon> defaultIcon,
|
||||
rpl::producer<int> coverHeight,
|
||||
DocumentId iconId,
|
||||
Fn<void(object_ptr<Ui::RpWidget>)> placeFooter) {
|
||||
using namespace ChatHelpers;
|
||||
|
||||
struct State {
|
||||
std::unique_ptr<Ui::EmojiFlyAnimation> animation;
|
||||
std::unique_ptr<HistoryView::StickerToast> toast;
|
||||
rpl::variable<DocumentId> iconId;
|
||||
QPointer<QWidget> button;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>(State{
|
||||
.iconId = iconId,
|
||||
.button = button.get(),
|
||||
});
|
||||
|
||||
const auto manager = &controller->session().data().customEmojiManager();
|
||||
|
||||
auto factory = [=](DocumentId id, Fn<void()> repaint)
|
||||
-> std::unique_ptr<Ui::Text::CustomEmoji> {
|
||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||
if (id == kDefaultIconId) {
|
||||
return std::make_unique<DefaultIconEmoji>(
|
||||
rpl::duplicate(defaultIcon),
|
||||
repaint);
|
||||
}
|
||||
return manager->create(id, std::move(repaint), tag);
|
||||
};
|
||||
|
||||
const auto icons = &controller->session().data().forumIcons();
|
||||
const auto body = box->verticalLayout();
|
||||
Settings::AddSkip(body);
|
||||
const auto recent = [=] {
|
||||
auto list = icons->list();
|
||||
list.insert(begin(list), kDefaultIconId);
|
||||
return list;
|
||||
};
|
||||
const auto selector = body->add(
|
||||
object_ptr<EmojiListWidget>(body, EmojiListDescriptor{
|
||||
.session = &controller->session(),
|
||||
.mode = EmojiListWidget::Mode::TopicIcon,
|
||||
.controller = controller,
|
||||
.paused = Window::PausedIn(
|
||||
controller,
|
||||
Window::GifPauseReason::Layer),
|
||||
.customRecentList = recent(),
|
||||
.customRecentFactory = std::move(factory),
|
||||
.st = &st::reactPanelEmojiPan,
|
||||
}),
|
||||
st::reactPanelEmojiPan.padding);
|
||||
|
||||
icons->requestDefaultIfUnknown();
|
||||
icons->defaultUpdates(
|
||||
) | rpl::start_with_next([=] {
|
||||
selector->provideRecent(recent());
|
||||
}, selector->lifetime());
|
||||
|
||||
placeFooter(selector->createFooter());
|
||||
|
||||
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(box.get());
|
||||
shadow->show();
|
||||
|
||||
rpl::combine(
|
||||
rpl::duplicate(coverHeight),
|
||||
selector->widthValue()
|
||||
) | rpl::start_with_next([=](int top, int width) {
|
||||
shadow->setGeometry(0, top, width, st::lineWidth);
|
||||
}, shadow->lifetime());
|
||||
|
||||
selector->refreshEmoji();
|
||||
|
||||
selector->scrollToRequests(
|
||||
) | rpl::start_with_next([=](int y) {
|
||||
box->scrollToY(y);
|
||||
shadow->update();
|
||||
}, selector->lifetime());
|
||||
|
||||
rpl::combine(
|
||||
box->heightValue(),
|
||||
std::move(coverHeight),
|
||||
rpl::mappers::_1 - rpl::mappers::_2
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
selector->setMinimalHeight(selector->width(), height);
|
||||
}, body->lifetime());
|
||||
|
||||
const auto showToast = [=](not_null<DocumentData*> document) {
|
||||
if (!state->toast) {
|
||||
state->toast = std::make_unique<HistoryView::StickerToast>(
|
||||
controller,
|
||||
controller->widget()->bodyWidget(),
|
||||
[=] { state->toast = nullptr; });
|
||||
}
|
||||
state->toast->showFor(
|
||||
document,
|
||||
HistoryView::StickerToast::Section::TopicIcon);
|
||||
};
|
||||
|
||||
selector->customChosen(
|
||||
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||
const auto owner = &controller->session().data();
|
||||
const auto document = data.document;
|
||||
const auto id = document->id;
|
||||
const auto custom = (id != kDefaultIconId);
|
||||
const auto premium = custom
|
||||
&& !ranges::contains(document->owner().forumIcons().list(), id);
|
||||
if (premium && !controller->session().premium()) {
|
||||
showToast(document);
|
||||
return;
|
||||
}
|
||||
const auto body = controller->window().widget()->bodyWidget();
|
||||
if (state->button && custom) {
|
||||
const auto &from = data.messageSendingFrom;
|
||||
auto args = Ui::ReactionFlyAnimationArgs{
|
||||
.id = { { id } },
|
||||
.flyIcon = from.frame,
|
||||
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
||||
};
|
||||
state->animation = std::make_unique<Ui::EmojiFlyAnimation>(
|
||||
body,
|
||||
&owner->reactions(),
|
||||
std::move(args),
|
||||
[=] { state->animation->repaint(); },
|
||||
Data::CustomEmojiSizeTag::Large);
|
||||
}
|
||||
state->iconId = id;
|
||||
}, selector->lifetime());
|
||||
|
||||
auto paintIconFrame = [=](not_null<Ui::RpWidget*> button) {
|
||||
if (!state->animation) {
|
||||
return false;
|
||||
} else if (state->animation->paintBadgeFrame(button)) {
|
||||
return true;
|
||||
}
|
||||
InvokeQueued(state->animation->layer(), [=] {
|
||||
state->animation = nullptr;
|
||||
});
|
||||
return false;
|
||||
};
|
||||
return {
|
||||
.paintIconFrame = std::move(paintIconFrame),
|
||||
.iconIdValue = state->iconId.value(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void NewForumTopicBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> forum) {
|
||||
EditForumTopicBox(box, controller, forum, MsgId(0));
|
||||
}
|
||||
|
||||
void EditForumTopicBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> forum,
|
||||
MsgId rootId) {
|
||||
const auto creating = !rootId;
|
||||
const auto topic = (!creating && forum->peer->forum())
|
||||
? forum->peer->forum()->topicFor(rootId)
|
||||
: nullptr;
|
||||
const auto created = topic && !topic->creating();
|
||||
box->setTitle(creating
|
||||
? tr::lng_forum_topic_new()
|
||||
: tr::lng_forum_topic_edit());
|
||||
|
||||
box->setMaxHeight(st::editTopicMaxHeight);
|
||||
|
||||
struct State {
|
||||
rpl::variable<DefaultIcon> defaultIcon;
|
||||
rpl::variable<DocumentId> iconId = 0;
|
||||
std::vector<int32> otherColorIds;
|
||||
mtpRequestId requestId = 0;
|
||||
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto &colors = Data::ForumTopicColorIds();
|
||||
state->iconId = topic ? topic->iconId() : 0;
|
||||
state->otherColorIds = colors;
|
||||
state->defaultIcon = DefaultIcon{
|
||||
topic ? topic->title() : QString(),
|
||||
topic ? topic->colorId() : ChooseNextColorId(0, state->otherColorIds)
|
||||
};
|
||||
|
||||
const auto top = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::VerticalLayout>(box));
|
||||
|
||||
const auto title = top->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::defaultInputField,
|
||||
tr::lng_forum_topic_title(),
|
||||
topic ? topic->title() : QString()),
|
||||
st::editTopicTitleMargin);
|
||||
box->setFocusCallback([=] {
|
||||
title->setFocusFast();
|
||||
});
|
||||
|
||||
const auto paintIconFrame = [=](not_null<Ui::RpWidget*> widget) {
|
||||
return state->paintIconFrame(widget);
|
||||
};
|
||||
const auto icon = EditIconButton(
|
||||
title->parentWidget(),
|
||||
controller,
|
||||
state->defaultIcon.value(),
|
||||
state->iconId.value(),
|
||||
paintIconFrame);
|
||||
|
||||
title->geometryValue(
|
||||
) | rpl::start_with_next([=](QRect geometry) {
|
||||
icon->move(
|
||||
st::editTopicIconPosition.x(),
|
||||
st::editTopicIconPosition.y());
|
||||
}, icon->lifetime());
|
||||
|
||||
state->iconId.value(
|
||||
) | rpl::start_with_next([=](DocumentId iconId) {
|
||||
icon->setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents,
|
||||
created || (iconId != 0));
|
||||
}, box->lifetime());
|
||||
|
||||
icon->setClickedCallback([=] {
|
||||
const auto current = state->defaultIcon.current();
|
||||
state->defaultIcon = DefaultIcon{
|
||||
current.title,
|
||||
ChooseNextColorId(current.colorId, state->otherColorIds),
|
||||
};
|
||||
});
|
||||
base::qt_signal_producer(
|
||||
title,
|
||||
&Ui::InputField::changed
|
||||
) | rpl::start_with_next([=] {
|
||||
state->defaultIcon = DefaultIcon{
|
||||
title->getLastText().trimmed(),
|
||||
state->defaultIcon.current().colorId,
|
||||
};
|
||||
}, box->lifetime());
|
||||
|
||||
Settings::AddDividerText(
|
||||
top,
|
||||
tr::lng_forum_choose_title_and_icon());
|
||||
|
||||
box->setScrollStyle(st::reactPanelScroll);
|
||||
|
||||
auto selector = AddIconSelector(
|
||||
box,
|
||||
icon,
|
||||
controller,
|
||||
state->defaultIcon.value(),
|
||||
top->heightValue(),
|
||||
state->iconId.current(),
|
||||
[&](object_ptr<Ui::RpWidget> footer) {
|
||||
top->add(std::move(footer)); });
|
||||
state->paintIconFrame = std::move(selector.paintIconFrame);
|
||||
std::move(
|
||||
selector.iconIdValue
|
||||
) | rpl::start_with_next([=](DocumentId iconId) {
|
||||
state->iconId = (iconId != kDefaultIconId) ? iconId : 0;
|
||||
}, box->lifetime());
|
||||
|
||||
const auto create = [=] {
|
||||
const auto channel = forum->peer->asChannel();
|
||||
if (!channel || !channel->isForum()) {
|
||||
box->closeBox();
|
||||
return;
|
||||
} else if (title->getLastText().trimmed().isEmpty()) {
|
||||
title->showError();
|
||||
return;
|
||||
}
|
||||
controller->showSection(
|
||||
std::make_shared<HistoryView::RepliesMemento>(
|
||||
forum,
|
||||
channel->forum()->reserveCreatingId(
|
||||
title->getLastText().trimmed(),
|
||||
state->defaultIcon.current().colorId,
|
||||
state->iconId.current())),
|
||||
Window::SectionShow::Way::ClearStack);
|
||||
};
|
||||
|
||||
const auto save = [=] {
|
||||
const auto parent = forum->peer->forum();
|
||||
const auto topic = parent
|
||||
? parent->topicFor(rootId)
|
||||
: nullptr;
|
||||
if (!topic) {
|
||||
box->closeBox();
|
||||
return;
|
||||
} else if (state->requestId > 0) {
|
||||
return;
|
||||
} else if (title->getLastText().trimmed().isEmpty()) {
|
||||
title->showError();
|
||||
return;
|
||||
} else if (parent->creating(rootId)) {
|
||||
topic->applyTitle(title->getLastText().trimmed());
|
||||
topic->applyColorId(state->defaultIcon.current().colorId);
|
||||
topic->applyIconId(state->iconId.current());
|
||||
box->closeBox();
|
||||
} else {
|
||||
using Flag = MTPchannels_EditForumTopic::Flag;
|
||||
const auto api = &forum->session().api();
|
||||
const auto weak = Ui::MakeWeak(box.get());
|
||||
state->requestId = api->request(MTPchannels_EditForumTopic(
|
||||
MTP_flags(Flag::f_title | Flag::f_icon_emoji_id),
|
||||
topic->channel()->inputChannel,
|
||||
MTP_int(rootId),
|
||||
MTP_string(title->getLastText().trimmed()),
|
||||
MTP_long(state->iconId.current()),
|
||||
MTPBool() // closed
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.data()) {
|
||||
if (error.type() == u"TOPIC_NOT_MODIFIED") {
|
||||
strong->closeBox();
|
||||
} else {
|
||||
state->requestId = -1;
|
||||
}
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
};
|
||||
|
||||
if (creating) {
|
||||
box->addButton(tr::lng_create_group_create(), create);
|
||||
} else {
|
||||
box->addButton(tr::lng_settings_save(), save);
|
||||
}
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
27
Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
class History;
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
void NewForumTopicBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> forum);
|
||||
|
||||
void EditForumTopicBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> forum,
|
||||
MsgId rootId);
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -32,6 +33,7 @@ constexpr auto kEnableSearchRowsCount = 10;
|
||||
class Controller : public PeerListController, public base::has_weak_ptr {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
ChannelData *chat,
|
||||
const std::vector<not_null<PeerData*>> &chats,
|
||||
@@ -47,6 +49,7 @@ private:
|
||||
void choose(not_null<ChannelData*> chat);
|
||||
void choose(not_null<ChatData*> chat);
|
||||
|
||||
not_null<Window::SessionNavigation*> _navigation;
|
||||
not_null<ChannelData*> _channel;
|
||||
ChannelData *_chat = nullptr;
|
||||
std::vector<not_null<PeerData*>> _chats;
|
||||
@@ -59,12 +62,14 @@ private:
|
||||
};
|
||||
|
||||
Controller::Controller(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
ChannelData *chat,
|
||||
const std::vector<not_null<PeerData*>> &chats,
|
||||
Fn<void(ChannelData*)> callback,
|
||||
Fn<void(not_null<PeerData*>)> showHistoryCallback)
|
||||
: _channel(channel)
|
||||
: _navigation(navigation)
|
||||
, _channel(channel)
|
||||
, _chat(chat)
|
||||
, _chats(std::move(chats))
|
||||
, _callback(std::move(callback))
|
||||
@@ -131,6 +136,10 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
}
|
||||
|
||||
void Controller::choose(not_null<ChannelData*> chat) {
|
||||
if (chat->isForum()) {
|
||||
ShowForumForDiscussionError(_navigation);
|
||||
return;
|
||||
}
|
||||
auto text = tr::lng_manage_discussion_group_sure(
|
||||
tr::now,
|
||||
lt_group,
|
||||
@@ -324,6 +333,7 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
|
||||
ShowAtUnreadMsgId);
|
||||
};
|
||||
auto controller = std::make_unique<Controller>(
|
||||
navigation,
|
||||
channel,
|
||||
chat,
|
||||
std::move(chats),
|
||||
@@ -362,3 +372,13 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
|
||||
canEdit,
|
||||
callback);
|
||||
}
|
||||
|
||||
void ShowForumForDiscussionError(
|
||||
not_null<Window::SessionNavigation*> navigation) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Window::Show(navigation).toastParent(),
|
||||
.text = tr::lng_forum_topics_no_discussion(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,3 +29,6 @@ object_ptr<Ui::BoxContent> EditLinkedChatBox(
|
||||
not_null<ChannelData*> channel,
|
||||
std::vector<not_null<PeerData*>> &&chats,
|
||||
Fn<void(ChannelData*)> callback);
|
||||
|
||||
void ShowForumForDiscussionError(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/painter.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
@@ -221,14 +222,15 @@ ChatAdminRightsInfo EditAdminBox::defaultRights() const {
|
||||
? ChatAdminRightsInfo{ (Flag::ChangeInfo
|
||||
| Flag::DeleteMessages
|
||||
| Flag::BanUsers
|
||||
| Flag::InviteUsers
|
||||
| Flag::InviteByLinkOrAdd
|
||||
| Flag::ManageTopics
|
||||
| Flag::PinMessages
|
||||
| Flag::ManageCall) }
|
||||
: ChatAdminRightsInfo{ (Flag::ChangeInfo
|
||||
| Flag::PostMessages
|
||||
| Flag::EditMessages
|
||||
| Flag::DeleteMessages
|
||||
| Flag::InviteUsers
|
||||
| Flag::InviteByLinkOrAdd
|
||||
| Flag::ManageCall) };
|
||||
}
|
||||
|
||||
@@ -327,13 +329,17 @@ void EditAdminBox::prepare() {
|
||||
const auto anyoneCanAddMembers = chat
|
||||
? chat->anyoneCanAddMembers()
|
||||
: channel->anyoneCanAddMembers();
|
||||
const auto options = Data::AdminRightsSetOptions{
|
||||
.isGroup = isGroup,
|
||||
.isForum = peer()->isForum(),
|
||||
.anyoneCanAddMembers = anyoneCanAddMembers,
|
||||
};
|
||||
auto [checkboxes, getChecked, changes] = CreateEditAdminRights(
|
||||
inner,
|
||||
tr::lng_rights_edit_admin_header(),
|
||||
prepareFlags,
|
||||
disabledMessages,
|
||||
isGroup,
|
||||
anyoneCanAddMembers);
|
||||
options);
|
||||
inner->add(std::move(checkboxes), QMargins());
|
||||
|
||||
auto selectedFlags = rpl::single(
|
||||
@@ -354,7 +360,7 @@ void EditAdminBox::prepare() {
|
||||
}, lifetime());
|
||||
|
||||
if (canTransferOwnership()) {
|
||||
const auto allFlags = AdminRightsForOwnershipTransfer(isGroup);
|
||||
const auto allFlags = AdminRightsForOwnershipTransfer(options);
|
||||
setupTransferButton(
|
||||
inner,
|
||||
isGroup
|
||||
@@ -745,7 +751,8 @@ void EditRestrictedBox::prepare() {
|
||||
this,
|
||||
tr::lng_rights_user_restrictions_header(),
|
||||
prepareFlags,
|
||||
disabledMessages);
|
||||
disabledMessages,
|
||||
{ .isForum = peer()->isForum() });
|
||||
addControl(std::move(checkboxes), QMargins());
|
||||
|
||||
_until = prepareRights.until;
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "api/api_user_names.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -38,10 +39,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -66,12 +72,10 @@ namespace {
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto ToPositiveNumberStringRestrictions() {
|
||||
return rpl::map([](int count) {
|
||||
return QString::number(count)
|
||||
+ QString("/")
|
||||
+ QString::number(int(Data::ListOfRestrictions().size()));
|
||||
});
|
||||
[[nodiscard]] int EnableForumMinMembers(not_null<PeerData*> peer) {
|
||||
return peer->session().account().appConfig().get<int>(
|
||||
u"forum_upgrade_participants_min"_q,
|
||||
200);
|
||||
}
|
||||
|
||||
void AddSkip(
|
||||
@@ -248,6 +252,7 @@ public:
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Ui::BoxContent*> box,
|
||||
not_null<PeerData*> peer);
|
||||
~Controller();
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::VerticalLayout> createContent();
|
||||
void setFocus();
|
||||
@@ -259,13 +264,17 @@ private:
|
||||
Ui::UserpicButton *photo = nullptr;
|
||||
rpl::lifetime initialPhotoImageWaiting;
|
||||
Ui::VerticalLayout *buttonsLayout = nullptr;
|
||||
Ui::SettingsButton *forumToggle = nullptr;
|
||||
bool forumToggleLocked = false;
|
||||
Ui::SlideWrap<> *historyVisibilityWrap = nullptr;
|
||||
};
|
||||
struct Saving {
|
||||
std::optional<QString> username;
|
||||
std::optional<std::vector<QString>> usernamesOrder;
|
||||
std::optional<QString> title;
|
||||
std::optional<QString> description;
|
||||
std::optional<bool> hiddenPreHistory;
|
||||
std::optional<bool> forum;
|
||||
std::optional<bool> signatures;
|
||||
std::optional<bool> noForwards;
|
||||
std::optional<bool> joinToWrite;
|
||||
@@ -283,12 +292,14 @@ private:
|
||||
[[nodiscard]] bool canEditInformation() const;
|
||||
[[nodiscard]] bool canEditReactions() const;
|
||||
void refreshHistoryVisibility();
|
||||
void refreshForumToggleLocked();
|
||||
void showEditPeerTypeBox(
|
||||
std::optional<rpl::producer<QString>> error = {});
|
||||
void showEditLinkedChatBox();
|
||||
void fillPrivacyTypeButton();
|
||||
void fillLinkedChatButton();
|
||||
//void fillInviteLinkButton();
|
||||
void fillForumButton();
|
||||
void fillSignaturesButton();
|
||||
void fillHistoryVisibilityButton();
|
||||
void fillManageSection();
|
||||
@@ -300,22 +311,26 @@ private:
|
||||
void deleteChannel();
|
||||
|
||||
[[nodiscard]] std::optional<Saving> validate() const;
|
||||
[[nodiscard]] bool validateUsernamesOrder(Saving &to) const;
|
||||
[[nodiscard]] bool validateUsername(Saving &to) const;
|
||||
[[nodiscard]] bool validateLinkedChat(Saving &to) const;
|
||||
[[nodiscard]] bool validateTitle(Saving &to) const;
|
||||
[[nodiscard]] bool validateDescription(Saving &to) const;
|
||||
[[nodiscard]] bool validateHistoryVisibility(Saving &to) const;
|
||||
[[nodiscard]] bool validateForum(Saving &to) const;
|
||||
[[nodiscard]] bool validateSignatures(Saving &to) const;
|
||||
[[nodiscard]] bool validateForwards(Saving &to) const;
|
||||
[[nodiscard]] bool validateJoinToWrite(Saving &to) const;
|
||||
[[nodiscard]] bool validateRequestToJoin(Saving &to) const;
|
||||
|
||||
void save();
|
||||
void saveUsernamesOrder();
|
||||
void saveUsername();
|
||||
void saveLinkedChat();
|
||||
void saveTitle();
|
||||
void saveDescription();
|
||||
void saveHistoryVisibility();
|
||||
void saveForum();
|
||||
void saveSignatures();
|
||||
void saveForwards();
|
||||
void saveJoinToWrite();
|
||||
@@ -339,6 +354,7 @@ private:
|
||||
bool _channelHasLocationOriginalValue = false;
|
||||
std::optional<HistoryVisibility> _historyVisibilitySavedValue;
|
||||
std::optional<EditPeerTypeData> _typeDataSavedValue;
|
||||
std::optional<bool> _forumSavedValue;
|
||||
std::optional<bool> _signaturesSavedValue;
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
@@ -383,6 +399,8 @@ Controller::Controller(
|
||||
_peer->updateFull();
|
||||
}
|
||||
|
||||
Controller::~Controller() = default;
|
||||
|
||||
void Controller::subscribeToMigration() {
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
@@ -610,7 +628,8 @@ void Controller::refreshHistoryVisibility() {
|
||||
_controls.historyVisibilityWrap->toggle(
|
||||
(!withUsername
|
||||
&& !_channelHasLocationOriginalValue
|
||||
&& (!_linkedChatSavedValue || !*_linkedChatSavedValue)),
|
||||
&& (!_linkedChatSavedValue || !*_linkedChatSavedValue)
|
||||
&& (!_forumSavedValue || !*_forumSavedValue)),
|
||||
anim::type::instant);
|
||||
}
|
||||
|
||||
@@ -623,7 +642,7 @@ void Controller::showEditPeerTypeBox(
|
||||
});
|
||||
_typeDataSavedValue->hasLinkedChat
|
||||
= (_linkedChatSavedValue.value_or(nullptr) != nullptr);
|
||||
_navigation->parentController()->show(
|
||||
const auto box = _navigation->parentController()->show(
|
||||
Box<EditPeerTypeBox>(
|
||||
_navigation,
|
||||
_peer,
|
||||
@@ -632,11 +651,20 @@ void Controller::showEditPeerTypeBox(
|
||||
_typeDataSavedValue,
|
||||
error),
|
||||
Ui::LayerOption::KeepOther);
|
||||
box->boxClosing(
|
||||
) | rpl::start_with_next([peer = _peer] {
|
||||
peer->session().api().usernames().requestToCache(peer);
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
void Controller::showEditLinkedChatBox() {
|
||||
Expects(_peer->isChannel());
|
||||
|
||||
if (_forumSavedValue && *_forumSavedValue) {
|
||||
ShowForumForDiscussionError(_navigation);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto callback = [=](ChannelData *result) {
|
||||
@@ -646,6 +674,7 @@ void Controller::showEditLinkedChatBox() {
|
||||
*_linkedChatSavedValue = result;
|
||||
_linkedChatUpdates.fire_copy(result);
|
||||
refreshHistoryVisibility();
|
||||
refreshForumToggleLocked();
|
||||
};
|
||||
const auto canEdit = channel->isBroadcast()
|
||||
? channel->canEditInformation()
|
||||
@@ -667,8 +696,14 @@ void Controller::showEditLinkedChatBox() {
|
||||
} else if (!canEdit || _linkedChatsRequestId) {
|
||||
return;
|
||||
} else if (channel->isMegagroup()) {
|
||||
// Restore original linked channel.
|
||||
callback(_linkedChatOriginalValue);
|
||||
if (_forumSavedValue
|
||||
&& *_forumSavedValue
|
||||
&& _linkedChatOriginalValue) {
|
||||
ShowForumForDiscussionError(_navigation);
|
||||
} else {
|
||||
// Restore original linked channel.
|
||||
callback(_linkedChatOriginalValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_linkedChatsRequestId = _api.request(
|
||||
@@ -707,8 +742,11 @@ void Controller::fillPrivacyTypeButton() {
|
||||
? Privacy::HasUsername
|
||||
: Privacy::NoUsername),
|
||||
.username = (_peer->isChannel()
|
||||
? _peer->asChannel()->username
|
||||
? _peer->asChannel()->editableUsername()
|
||||
: QString()),
|
||||
.usernamesOrder = (_peer->isChannel()
|
||||
? _peer->asChannel()->usernames()
|
||||
: std::vector<QString>()),
|
||||
.noForwards = !_peer->allowsForwarding(),
|
||||
.joinToWrite = (_peer->isMegagroup()
|
||||
&& _peer->asChannel()->joinToWrite()),
|
||||
@@ -728,6 +766,9 @@ void Controller::fillPrivacyTypeButton() {
|
||||
: tr::lng_manage_peer_channel_type)(),
|
||||
_privacyTypeUpdates.events(
|
||||
) | rpl::map([=](Privacy flag) {
|
||||
if (flag == Privacy::HasUsername) {
|
||||
_peer->session().api().usernames().requestToCache(_peer);
|
||||
}
|
||||
return (flag == Privacy::HasUsername)
|
||||
? (hasLocation
|
||||
? tr::lng_manage_peer_link_permanent
|
||||
@@ -800,6 +841,63 @@ void Controller::fillLinkedChatButton() {
|
||||
// buttonCallback);
|
||||
//}
|
||||
|
||||
void Controller::fillForumButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
const auto button = _controls.forumToggle = _controls.buttonsLayout->add(
|
||||
EditPeerInfoBox::CreateButton(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_forum_topics_switch(),
|
||||
rpl::single(QString()),
|
||||
[] {},
|
||||
st::manageGroupTopicsButton,
|
||||
{ &st::settingsIconTopics, Settings::kIconPurple }));
|
||||
const auto unlocks = std::make_shared<rpl::event_stream<bool>>();
|
||||
button->toggleOn(
|
||||
rpl::single(_peer->isForum()) | rpl::then(unlocks->events())
|
||||
)->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
if (_controls.forumToggleLocked && toggled) {
|
||||
unlocks->fire(false);
|
||||
if (_linkedChatSavedValue && *_linkedChatSavedValue) {
|
||||
ShowForumForDiscussionError(_navigation);
|
||||
} else {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = Window::Show(
|
||||
_navigation).toastParent(),
|
||||
.text = tr::lng_forum_topics_not_enough(
|
||||
tr::now,
|
||||
lt_count,
|
||||
EnableForumMinMembers(_peer),
|
||||
Ui::Text::RichLangValue),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
_forumSavedValue = toggled;
|
||||
if (toggled) {
|
||||
_savingData.hiddenPreHistory = false;
|
||||
}
|
||||
refreshHistoryVisibility();
|
||||
}
|
||||
}, _controls.buttonsLayout->lifetime());
|
||||
refreshForumToggleLocked();
|
||||
}
|
||||
|
||||
void Controller::refreshForumToggleLocked() {
|
||||
if (!_controls.forumToggle) {
|
||||
return;
|
||||
}
|
||||
const auto limit = EnableForumMinMembers(_peer);
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto notenough = !_peer->isForum()
|
||||
&& ((chat ? chat->count : channel->membersCount()) < limit);
|
||||
const auto linked = _linkedChatSavedValue
|
||||
&& *_linkedChatSavedValue;
|
||||
const auto locked = _controls.forumToggleLocked = notenough || linked;
|
||||
_controls.forumToggle->setToggleLocked(locked);
|
||||
}
|
||||
|
||||
void Controller::fillSignaturesButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
@@ -907,6 +1005,9 @@ void Controller::fillManageSection() {
|
||||
? channel->canEditPreHistoryHidden()
|
||||
: chat->canEditPreHistoryHidden();
|
||||
}();
|
||||
const auto canEditForum = isChannel
|
||||
? (channel->isMegagroup() && channel->amCreator())
|
||||
: chat->amCreator();
|
||||
|
||||
const auto canEditPermissions = [&] {
|
||||
return isChannel
|
||||
@@ -972,10 +1073,14 @@ void Controller::fillManageSection() {
|
||||
if (canEditPreHistoryHidden) {
|
||||
fillHistoryVisibilityButton();
|
||||
}
|
||||
if (canEditForum) {
|
||||
fillForumButton();
|
||||
}
|
||||
if (canEditSignatures) {
|
||||
fillSignaturesButton();
|
||||
}
|
||||
if (canEditPreHistoryHidden
|
||||
|| canEditForum
|
||||
|| canEditSignatures
|
||||
//|| canEditInviteLinks
|
||||
|| canViewOrEditLinkedChat
|
||||
@@ -1036,10 +1141,16 @@ void Controller::fillManageSection() {
|
||||
tr::lng_manage_peer_permissions(),
|
||||
Info::Profile::MigratedOrMeValue(
|
||||
_peer
|
||||
) | rpl::map(
|
||||
Info::Profile::RestrictionsCountValue
|
||||
) | rpl::flatten_latest(
|
||||
) | ToPositiveNumberStringRestrictions(),
|
||||
) | rpl::map([=](not_null<PeerData*> peer) {
|
||||
return Info::Profile::RestrictionsCountValue(
|
||||
peer
|
||||
) | rpl::map([=](int count) {
|
||||
return QString::number(count)
|
||||
+ QString("/")
|
||||
+ QString::number(int(Data::ListOfRestrictions(
|
||||
{ .isForum = peer->isForum() }).size()));
|
||||
});
|
||||
}) | rpl::flatten_latest(),
|
||||
[=] { ShowEditPermissions(_navigation, _peer); },
|
||||
{ &st::settingsIconKey, Settings::kIconGreen });
|
||||
}
|
||||
@@ -1078,16 +1189,7 @@ void Controller::fillManageSection() {
|
||||
Ui::LayerOption::KeepOther);
|
||||
},
|
||||
{ &st::infoRoundedIconInviteLinks, Settings::kIconLightOrange });
|
||||
|
||||
if (_typeDataSavedValue) {
|
||||
_privacyTypeUpdates.events_starting_with_copy(
|
||||
_typeDataSavedValue->privacy
|
||||
) | rpl::start_with_next([=](Privacy flag) {
|
||||
wrap->toggle(
|
||||
flag != Privacy::HasUsername,
|
||||
anim::type::instant);
|
||||
}, wrap->lifetime());
|
||||
}
|
||||
wrap->toggle(true, anim::type::instant);
|
||||
}
|
||||
if (canViewAdmins) {
|
||||
AddButtonWithCount(
|
||||
@@ -1230,11 +1332,13 @@ void Controller::submitDescription() {
|
||||
|
||||
std::optional<Controller::Saving> Controller::validate() const {
|
||||
auto result = Saving();
|
||||
if (validateUsername(result)
|
||||
if (validateUsernamesOrder(result)
|
||||
&& validateUsername(result)
|
||||
&& validateLinkedChat(result)
|
||||
&& validateTitle(result)
|
||||
&& validateDescription(result)
|
||||
&& validateHistoryVisibility(result)
|
||||
&& validateForum(result)
|
||||
&& validateSignatures(result)
|
||||
&& validateForwards(result)
|
||||
&& validateJoinToWrite(result)
|
||||
@@ -1244,6 +1348,17 @@ std::optional<Controller::Saving> Controller::validate() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Controller::validateUsernamesOrder(Saving &to) const {
|
||||
if (!_typeDataSavedValue) {
|
||||
return true;
|
||||
} else if (_typeDataSavedValue->privacy != Privacy::HasUsername) {
|
||||
to.usernamesOrder = std::vector<QString>();
|
||||
return true;
|
||||
}
|
||||
to.usernamesOrder = _typeDataSavedValue->usernamesOrder;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::validateUsername(Saving &to) const {
|
||||
if (!_typeDataSavedValue) {
|
||||
return true;
|
||||
@@ -1253,7 +1368,8 @@ bool Controller::validateUsername(Saving &to) const {
|
||||
}
|
||||
const auto username = _typeDataSavedValue->username;
|
||||
if (username.isEmpty()) {
|
||||
return false;
|
||||
to.username = QString();
|
||||
return true;
|
||||
}
|
||||
to.username = username;
|
||||
return true;
|
||||
@@ -1302,6 +1418,14 @@ bool Controller::validateHistoryVisibility(Saving &to) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::validateForum(Saving &to) const {
|
||||
if (!_forumSavedValue.has_value()) {
|
||||
return true;
|
||||
}
|
||||
to.forum = _forumSavedValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::validateSignatures(Saving &to) const {
|
||||
if (!_signaturesSavedValue.has_value()) {
|
||||
return true;
|
||||
@@ -1342,11 +1466,13 @@ void Controller::save() {
|
||||
}
|
||||
if (const auto saving = validate()) {
|
||||
_savingData = *saving;
|
||||
pushSaveStage([=] { saveUsernamesOrder(); });
|
||||
pushSaveStage([=] { saveUsername(); });
|
||||
pushSaveStage([=] { saveLinkedChat(); });
|
||||
pushSaveStage([=] { saveTitle(); });
|
||||
pushSaveStage([=] { saveDescription(); });
|
||||
pushSaveStage([=] { saveHistoryVisibility(); });
|
||||
pushSaveStage([=] { saveForum(); });
|
||||
pushSaveStage([=] { saveSignatures(); });
|
||||
pushSaveStage([=] { saveForwards(); });
|
||||
pushSaveStage([=] { saveJoinToWrite(); });
|
||||
@@ -1372,9 +1498,49 @@ void Controller::cancelSave() {
|
||||
_saveStagesQueue.clear();
|
||||
}
|
||||
|
||||
void Controller::saveUsernamesOrder() {
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!_savingData.usernamesOrder || !channel) {
|
||||
return continueSave();
|
||||
}
|
||||
if (_savingData.usernamesOrder->empty()) {
|
||||
_api.request(MTPchannels_DeactivateAllUsernames(
|
||||
channel->inputChannel
|
||||
)).done([=] {
|
||||
channel->setUsernames(channel->editableUsername().isEmpty()
|
||||
? Data::Usernames()
|
||||
: Data::Usernames{
|
||||
{ channel->editableUsername(), true, true }
|
||||
});
|
||||
continueSave();
|
||||
}).send();
|
||||
} else {
|
||||
const auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
const auto newUsernames = (*_savingData.usernamesOrder);
|
||||
_peer->session().api().usernames().reorder(
|
||||
_peer,
|
||||
newUsernames
|
||||
) | rpl::start_with_done([=] {
|
||||
channel->setUsernames(ranges::views::all(
|
||||
newUsernames
|
||||
) | ranges::views::transform([&](QString username) {
|
||||
const auto editable =
|
||||
(channel->editableUsername() == username);
|
||||
return Data::Username{
|
||||
.username = std::move(username),
|
||||
.active = true,
|
||||
.editable = editable,
|
||||
};
|
||||
}) | ranges::to_vector);
|
||||
continueSave();
|
||||
lifetime->destroy();
|
||||
}, *lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::saveUsername() {
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto username = (channel ? channel->username : QString());
|
||||
const auto username = (channel ? channel->editableUsername() : QString());
|
||||
if (!_savingData.username || *_savingData.username == username) {
|
||||
return continueSave();
|
||||
} else if (!channel) {
|
||||
@@ -1391,13 +1557,14 @@ void Controller::saveUsername() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto newUsername = (*_savingData.username);
|
||||
_api.request(MTPchannels_UpdateUsername(
|
||||
channel->inputChannel,
|
||||
MTP_string(*_savingData.username)
|
||||
MTP_string(newUsername)
|
||||
)).done([=] {
|
||||
channel->setName(
|
||||
TextUtilities::SingleLine(channel->name()),
|
||||
*_savingData.username);
|
||||
newUsername);
|
||||
continueSave();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
const auto &type = error.type();
|
||||
@@ -1471,7 +1638,9 @@ void Controller::saveTitle() {
|
||||
if (type == qstr("CHAT_NOT_MODIFIED")
|
||||
|| type == qstr("CHAT_TITLE_NOT_MODIFIED")) {
|
||||
if (const auto channel = _peer->asChannel()) {
|
||||
channel->setName(*_savingData.title, channel->username);
|
||||
channel->setName(
|
||||
*_savingData.title,
|
||||
channel->editableUsername());
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
chat->setName(*_savingData.title);
|
||||
}
|
||||
@@ -1585,6 +1754,39 @@ void Controller::togglePreHistoryHidden(
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Controller::saveForum() {
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!_savingData.forum
|
||||
|| *_savingData.forum == _peer->isForum()) {
|
||||
return continueSave();
|
||||
} else if (!channel) {
|
||||
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||
if (_peer->asChannel() == channel) {
|
||||
saveForum();
|
||||
} else {
|
||||
cancelSave();
|
||||
}
|
||||
};
|
||||
_peer->session().api().migrateChat(
|
||||
_peer->asChat(),
|
||||
crl::guard(this, saveForChannel));
|
||||
return;
|
||||
}
|
||||
_api.request(MTPchannels_ToggleForum(
|
||||
channel->inputChannel,
|
||||
MTP_bool(*_savingData.forum)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
channel->session().api().applyUpdates(result);
|
||||
continueSave();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
|
||||
continueSave();
|
||||
} else {
|
||||
cancelSave();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Controller::saveSignatures() {
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!_savingData.signatures
|
||||
|
||||
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/boxes/edit_invite_link.h"
|
||||
#include "ui/painter.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
|
||||
#include "history/history_message.h" // GetErrorTextForSending.
|
||||
@@ -1140,7 +1141,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
showToast(tr::lng_group_invite_copied(tr::now));
|
||||
};
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<PeerData*>> &&result,
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
@@ -1149,13 +1150,12 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
}
|
||||
|
||||
const auto error = [&] {
|
||||
for (const auto peer : result) {
|
||||
for (const auto thread : result) {
|
||||
const auto error = GetErrorTextForSending(
|
||||
peer,
|
||||
{},
|
||||
comment);
|
||||
thread,
|
||||
{ .text = &comment });
|
||||
if (!error.isEmpty()) {
|
||||
return std::make_pair(error, peer);
|
||||
return std::make_pair(error, thread);
|
||||
}
|
||||
}
|
||||
return std::make_pair(QString(), result.front());
|
||||
@@ -1164,7 +1164,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
auto text = TextWithEntities();
|
||||
if (result.size() > 1) {
|
||||
text.append(
|
||||
Ui::Text::Bold(error.second->name())
|
||||
Ui::Text::Bold(error.second->chatListName())
|
||||
).append("\n\n");
|
||||
}
|
||||
text.append(error.first);
|
||||
@@ -1186,12 +1186,10 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
} else {
|
||||
comment.text = link;
|
||||
}
|
||||
const auto owner = &peer->owner();
|
||||
auto &api = peer->session().api();
|
||||
for (const auto peer : result) {
|
||||
const auto history = owner->history(peer);
|
||||
for (const auto thread : result) {
|
||||
auto message = Api::MessageToSend(
|
||||
Api::SendAction(history, options));
|
||||
Api::SendAction(thread, options));
|
||||
message.textWithTags = comment;
|
||||
message.action.clearDraft = false;
|
||||
api.sendMessage(std::move(message));
|
||||
@@ -1205,7 +1203,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||
.session = &peer->session(),
|
||||
.copyCallback = std::move(copyCallback),
|
||||
.submitCallback = std::move(submitCallback),
|
||||
.filterCallback = [](auto peer) { return peer->canWrite(); },
|
||||
.filterCallback = [](auto thread) { return thread->canWrite(); },
|
||||
});
|
||||
*box = Ui::MakeWeak(object.data());
|
||||
return object;
|
||||
@@ -1375,7 +1373,7 @@ QString PrepareRequestedRowStatus(TimeId date) {
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
const auto parsed = base::unixtime::parse(date);
|
||||
const auto parsedDate = parsed.date();
|
||||
const auto time = parsed.time().toString(cTimeFormat());
|
||||
const auto time = QLocale().toString(parsed.time(), cTimeFormat());
|
||||
const auto generic = [&] {
|
||||
return tr::lng_group_requests_status_date_time(
|
||||
tr::now,
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/painter.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
@@ -199,7 +200,7 @@ private:
|
||||
left / 86400));
|
||||
} else {
|
||||
const auto time = base::unixtime::parse(link.expireDate).time();
|
||||
add(QLocale::system().toString(time, QLocale::LongFormat));
|
||||
add(QLocale().toString(time, QLocale::LongFormat));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -328,7 +329,7 @@ QString Row::generateShortName() {
|
||||
|
||||
PaintRoundImageCallback Row::generatePaintUserpicCallback() {
|
||||
return [=](
|
||||
Painter &p,
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
|
||||
@@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/box_content_divider.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
@@ -106,58 +106,6 @@ void ApplyDependencies(
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::pair<ChatRestrictions, QString>> RestrictionLabels() {
|
||||
const auto langKeys = {
|
||||
tr::lng_rights_chat_send_text,
|
||||
tr::lng_rights_chat_send_media,
|
||||
tr::lng_rights_chat_send_stickers,
|
||||
tr::lng_rights_chat_send_links,
|
||||
tr::lng_rights_chat_send_polls,
|
||||
tr::lng_rights_chat_add_members,
|
||||
tr::lng_rights_group_pin,
|
||||
tr::lng_rights_group_info,
|
||||
};
|
||||
|
||||
std::vector<std::pair<ChatRestrictions, QString>> vector;
|
||||
const auto restrictions = Data::ListOfRestrictions();
|
||||
auto i = 0;
|
||||
for (const auto &key : langKeys) {
|
||||
vector.emplace_back(restrictions[i++], key(tr::now));
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
std::vector<std::pair<ChatAdminRights, QString>> AdminRightLabels(
|
||||
bool isGroup,
|
||||
bool anyoneCanAddMembers) {
|
||||
using Flag = ChatAdminRight;
|
||||
|
||||
if (isGroup) {
|
||||
return {
|
||||
{ Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
|
||||
{ Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
|
||||
{ Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
|
||||
{ Flag::InviteUsers, anyoneCanAddMembers
|
||||
? tr::lng_rights_group_invite_link(tr::now)
|
||||
: tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
|
||||
{ Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
|
||||
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
{ Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
|
||||
{ Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
|
||||
{ Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
|
||||
{ Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
|
||||
{ Flag::InviteUsers, tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
|
||||
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
auto Dependencies(ChatRestrictions)
|
||||
-> std::vector<std::pair<ChatRestriction, ChatRestriction>> {
|
||||
using Flag = ChatRestriction;
|
||||
@@ -200,7 +148,8 @@ ChatRestrictions NegateRestrictions(ChatRestrictions value) {
|
||||
//| Flag::ViewMessages
|
||||
| Flag::ChangeInfo
|
||||
| Flag::EmbedLinks
|
||||
| Flag::InviteUsers
|
||||
| Flag::AddParticipants
|
||||
| Flag::CreateTopics
|
||||
| Flag::PinMessages
|
||||
| Flag::SendGames
|
||||
| Flag::SendGifs
|
||||
@@ -237,17 +186,117 @@ ChatRestrictions DisabledByAdminRights(not_null<PeerData*> peer) {
|
||||
Unexpected("User in DisabledByAdminRights.");
|
||||
}();
|
||||
return Flag(0)
|
||||
| ((adminRights & Admin::ManageTopics)
|
||||
? Flag(0)
|
||||
: Flag::CreateTopics)
|
||||
| ((adminRights & Admin::PinMessages)
|
||||
? Flag(0)
|
||||
: Flag::PinMessages)
|
||||
| ((adminRights & Admin::InviteUsers)
|
||||
| ((adminRights & Admin::InviteByLinkOrAdd)
|
||||
? Flag(0)
|
||||
: Flag::InviteUsers)
|
||||
: Flag::AddParticipants)
|
||||
| ((adminRights & Admin::ChangeInfo)
|
||||
? Flag(0)
|
||||
: Flag::ChangeInfo);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Flags,
|
||||
typename DisabledMessagePairs,
|
||||
typename FlagLabelPairs>
|
||||
[[nodiscard]] EditFlagsControl<Flags> CreateEditFlags(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
Flags checked,
|
||||
const DisabledMessagePairs &disabledMessagePairs,
|
||||
const FlagLabelPairs &flagLabelPairs) {
|
||||
auto widget = object_ptr<Ui::VerticalLayout>(parent);
|
||||
const auto container = widget.data();
|
||||
|
||||
const auto checkboxes = container->lifetime(
|
||||
).make_state<std::map<Flags, QPointer<Ui::Checkbox>>>();
|
||||
|
||||
const auto value = [=] {
|
||||
auto result = Flags(0);
|
||||
for (const auto &[flags, checkbox] : *checkboxes) {
|
||||
if (checkbox->checked()) {
|
||||
result |= flags;
|
||||
} else {
|
||||
result &= ~flags;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const auto changes = container->lifetime(
|
||||
).make_state<rpl::event_stream<>>();
|
||||
|
||||
const auto applyDependencies = [=](Ui::Checkbox *control) {
|
||||
static const auto dependencies = Dependencies(Flags());
|
||||
ApplyDependencies(*checkboxes, dependencies, control);
|
||||
};
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
std::move(header),
|
||||
st::rightsHeaderLabel),
|
||||
st::rightsHeaderMargin);
|
||||
|
||||
auto addCheckbox = [&](Flags flags, const QString &text) {
|
||||
const auto lockedIt = ranges::find_if(
|
||||
disabledMessagePairs,
|
||||
[&](const auto &pair) { return (pair.first & flags) != 0; });
|
||||
const auto locked = (lockedIt != end(disabledMessagePairs))
|
||||
? std::make_optional(lockedIt->second)
|
||||
: std::nullopt;
|
||||
const auto toggled = ((checked & flags) != 0);
|
||||
auto toggle = std::make_unique<Ui::ToggleView>(
|
||||
st::rightsToggle,
|
||||
toggled);
|
||||
toggle->setLocked(locked.has_value());
|
||||
const auto control = container->add(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
container,
|
||||
text,
|
||||
st::rightsCheckbox,
|
||||
std::move(toggle)),
|
||||
st::rightsToggleMargin);
|
||||
control->checkedChanges(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
if (locked.has_value()) {
|
||||
if (checked != toggled) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = parent,
|
||||
.text = { *locked },
|
||||
});
|
||||
control->setChecked(toggled);
|
||||
}
|
||||
} else {
|
||||
InvokeQueued(control, [=] {
|
||||
applyDependencies(control);
|
||||
changes->fire({});
|
||||
});
|
||||
}
|
||||
}, control->lifetime());
|
||||
checkboxes->emplace(flags, control);
|
||||
};
|
||||
for (const auto &[flags, label] : flagLabelPairs) {
|
||||
addCheckbox(flags, label);
|
||||
}
|
||||
|
||||
applyDependencies(nullptr);
|
||||
for (const auto &[flags, checkbox] : *checkboxes) {
|
||||
checkbox->finishAnimating();
|
||||
}
|
||||
|
||||
return {
|
||||
std::move(widget),
|
||||
value,
|
||||
changes->events() | rpl::map(value)
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer) {
|
||||
@@ -271,9 +320,9 @@ ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer) {
|
||||
// is chosen in default permissions for 'invite_users', because
|
||||
// if everyone can 'invite_users' it handles invite link for admins.
|
||||
//
|
||||
//| ((restrictions & Restriction::InviteUsers)
|
||||
//| ((restrictions & Restriction::AddParticipants)
|
||||
// ? Flag(0)
|
||||
// : Flag::InviteUsers)
|
||||
// : Flag::InviteByLinkOrAdd)
|
||||
//
|
||||
| ((restrictions & Restriction::ChangeInfo)
|
||||
? Flag(0)
|
||||
@@ -306,9 +355,10 @@ ChatRestrictions FixDependentRestrictions(ChatRestrictions restrictions) {
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup) {
|
||||
ChatAdminRights AdminRightsForOwnershipTransfer(
|
||||
Data::AdminRightsSetOptions options) {
|
||||
auto result = ChatAdminRights();
|
||||
for (const auto &[flag, label] : AdminRightLabels(isGroup, true)) {
|
||||
for (const auto &[flag, label] : AdminRightLabels(options)) {
|
||||
if (!(flag & ChatAdminRight::Anonymous)) {
|
||||
result |= flag;
|
||||
}
|
||||
@@ -319,7 +369,7 @@ ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup) {
|
||||
Fn<void()> AboutGigagroupCallback(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
const auto weak = base::make_weak(controller);
|
||||
|
||||
const auto converting = std::make_shared<bool>();
|
||||
const auto convertSure = [=] {
|
||||
@@ -333,9 +383,10 @@ Fn<void()> AboutGigagroupCallback(
|
||||
channel->session().api().applyUpdates(result);
|
||||
if (const auto strongController = weak.get()) {
|
||||
strongController->window().hideSettingsAndLayer();
|
||||
Ui::Toast::Show(
|
||||
strongController->widget(),
|
||||
tr::lng_gigagroup_done(tr::now));
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = strongController->widget(),
|
||||
.text = { tr::lng_gigagroup_done(tr::now) },
|
||||
});
|
||||
}
|
||||
}).fail([=] {
|
||||
*converting = false;
|
||||
@@ -430,7 +481,8 @@ void EditPeerPermissionsBox::prepare() {
|
||||
disabledByAdminRights,
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
if (const auto channel = _peer->asChannel()) {
|
||||
if (channel->isPublic()) {
|
||||
if (channel->isPublic()
|
||||
|| (channel->isMegagroup() && channel->linkedChat())) {
|
||||
result.emplace(
|
||||
Flag::ChangeInfo | Flag::PinMessages,
|
||||
tr::lng_rights_permission_unavailable(tr::now));
|
||||
@@ -443,7 +495,8 @@ void EditPeerPermissionsBox::prepare() {
|
||||
this,
|
||||
tr::lng_rights_default_restrictions_header(),
|
||||
restrictions,
|
||||
disabledMessages);
|
||||
disabledMessages,
|
||||
{ .isForum = _peer->isForum() });
|
||||
|
||||
inner->add(std::move(checkboxes));
|
||||
|
||||
@@ -666,111 +719,86 @@ void EditPeerPermissionsBox::addBannedButtons(
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Flags,
|
||||
typename DisabledMessagePairs,
|
||||
typename FlagLabelPairs>
|
||||
EditFlagsControl<Flags> CreateEditFlags(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
Flags checked,
|
||||
const DisabledMessagePairs &disabledMessagePairs,
|
||||
const FlagLabelPairs &flagLabelPairs) {
|
||||
auto widget = object_ptr<Ui::VerticalLayout>(parent);
|
||||
const auto container = widget.data();
|
||||
std::vector<RestrictionLabel> RestrictionLabels(
|
||||
Data::RestrictionsSetOptions options) {
|
||||
using Flag = ChatRestriction;
|
||||
auto result = std::vector<RestrictionLabel>{
|
||||
{ Flag::SendMessages, tr::lng_rights_chat_send_text(tr::now) },
|
||||
{ Flag::SendMedia, tr::lng_rights_chat_send_media(tr::now) },
|
||||
{ Flag::SendStickers
|
||||
| Flag::SendGifs
|
||||
| Flag::SendGames
|
||||
| Flag::SendInline, tr::lng_rights_chat_send_stickers(tr::now) },
|
||||
{ Flag::EmbedLinks, tr::lng_rights_chat_send_links(tr::now) },
|
||||
{ Flag::SendPolls, tr::lng_rights_chat_send_polls(tr::now) },
|
||||
{ Flag::AddParticipants, tr::lng_rights_chat_add_members(tr::now) },
|
||||
{ Flag::CreateTopics, tr::lng_rights_group_add_topics(tr::now) },
|
||||
{ Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
|
||||
{ Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
|
||||
};
|
||||
if (!options.isForum) {
|
||||
result.erase(
|
||||
ranges::remove(
|
||||
result,
|
||||
Flag::CreateTopics,
|
||||
&RestrictionLabel::flags),
|
||||
end(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto checkboxes = container->lifetime(
|
||||
).make_state<std::map<Flags, QPointer<Ui::Checkbox>>>();
|
||||
std::vector<AdminRightLabel> AdminRightLabels(
|
||||
Data::AdminRightsSetOptions options) {
|
||||
using Flag = ChatAdminRight;
|
||||
|
||||
const auto value = [=] {
|
||||
auto result = Flags(0);
|
||||
for (const auto &[flags, checkbox] : *checkboxes) {
|
||||
if (checkbox->checked()) {
|
||||
result |= flags;
|
||||
} else {
|
||||
result &= ~flags;
|
||||
}
|
||||
if (options.isGroup) {
|
||||
auto result = std::vector<AdminRightLabel>{
|
||||
{ Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
|
||||
{ Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
|
||||
{ Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
|
||||
{ Flag::InviteByLinkOrAdd, options.anyoneCanAddMembers
|
||||
? tr::lng_rights_group_invite_link(tr::now)
|
||||
: tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::ManageTopics, tr::lng_rights_group_topics(tr::now) },
|
||||
{ Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
|
||||
{ Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
|
||||
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
|
||||
};
|
||||
if (!options.isForum) {
|
||||
result.erase(
|
||||
ranges::remove(
|
||||
result,
|
||||
Flag::ManageTopics,
|
||||
&AdminRightLabel::flags),
|
||||
end(result));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const auto changes = container->lifetime(
|
||||
).make_state<rpl::event_stream<>>();
|
||||
|
||||
const auto applyDependencies = [=](Ui::Checkbox *control) {
|
||||
static const auto dependencies = Dependencies(Flags());
|
||||
ApplyDependencies(*checkboxes, dependencies, control);
|
||||
};
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
std::move(header),
|
||||
st::rightsHeaderLabel),
|
||||
st::rightsHeaderMargin);
|
||||
|
||||
auto addCheckbox = [&](Flags flags, const QString &text) {
|
||||
const auto lockedIt = ranges::find_if(
|
||||
disabledMessagePairs,
|
||||
[&](const auto &pair) { return (pair.first & flags) != 0; });
|
||||
const auto locked = (lockedIt != end(disabledMessagePairs))
|
||||
? std::make_optional(lockedIt->second)
|
||||
: std::nullopt;
|
||||
const auto toggled = ((checked & flags) != 0);
|
||||
auto toggle = std::make_unique<Ui::ToggleView>(
|
||||
st::rightsToggle,
|
||||
toggled);
|
||||
toggle->setLocked(locked.has_value());
|
||||
const auto control = container->add(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
container,
|
||||
text,
|
||||
st::rightsCheckbox,
|
||||
std::move(toggle)),
|
||||
st::rightsToggleMargin);
|
||||
control->checkedChanges(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
if (locked.has_value()) {
|
||||
if (checked != toggled) {
|
||||
Ui::Toast::Show(parent, *locked);
|
||||
control->setChecked(toggled);
|
||||
}
|
||||
} else {
|
||||
InvokeQueued(control, [=] {
|
||||
applyDependencies(control);
|
||||
changes->fire({});
|
||||
});
|
||||
}
|
||||
}, control->lifetime());
|
||||
checkboxes->emplace(flags, control);
|
||||
};
|
||||
for (const auto &[flags, label] : flagLabelPairs) {
|
||||
addCheckbox(flags, label);
|
||||
} else {
|
||||
return {
|
||||
{ Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
|
||||
{ Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
|
||||
{ Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
|
||||
{ Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
|
||||
{ Flag::InviteByLinkOrAdd, tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
|
||||
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) }
|
||||
};
|
||||
}
|
||||
|
||||
applyDependencies(nullptr);
|
||||
for (const auto &[flags, checkbox] : *checkboxes) {
|
||||
checkbox->finishAnimating();
|
||||
}
|
||||
|
||||
return {
|
||||
std::move(widget),
|
||||
value,
|
||||
changes->events() | rpl::map(value)
|
||||
};
|
||||
}
|
||||
|
||||
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatRestrictions restrictions,
|
||||
std::map<ChatRestrictions, QString> disabledMessages) {
|
||||
std::map<ChatRestrictions, QString> disabledMessages,
|
||||
Data::RestrictionsSetOptions options) {
|
||||
auto result = CreateEditFlags(
|
||||
parent,
|
||||
header,
|
||||
NegateRestrictions(restrictions),
|
||||
disabledMessages,
|
||||
RestrictionLabels());
|
||||
RestrictionLabels(options));
|
||||
result.value = [original = std::move(result.value)]{
|
||||
return NegateRestrictions(original());
|
||||
};
|
||||
@@ -786,12 +814,11 @@ EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||
rpl::producer<QString> header,
|
||||
ChatAdminRights rights,
|
||||
std::map<ChatAdminRights, QString> disabledMessages,
|
||||
bool isGroup,
|
||||
bool anyoneCanAddMembers) {
|
||||
Data::AdminRightsSetOptions options) {
|
||||
return CreateEditFlags(
|
||||
parent,
|
||||
header,
|
||||
rights,
|
||||
disabledMessages,
|
||||
AdminRightLabels(isGroup, anyoneCanAddMembers));
|
||||
AdminRightLabels(options));
|
||||
}
|
||||
|
||||
@@ -54,6 +54,20 @@ private:
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
struct RestrictionLabel {
|
||||
ChatRestrictions flags;
|
||||
QString label;
|
||||
};
|
||||
[[nodiscard]] std::vector<RestrictionLabel> RestrictionLabels(
|
||||
Data::RestrictionsSetOptions options);
|
||||
|
||||
struct AdminRightLabel {
|
||||
ChatAdminRights flags;
|
||||
QString label;
|
||||
};
|
||||
[[nodiscard]] std::vector<AdminRightLabel> AdminRightLabels(
|
||||
Data::AdminRightsSetOptions options);
|
||||
|
||||
template <typename Flags>
|
||||
struct EditFlagsControl {
|
||||
object_ptr<Ui::RpWidget> widget;
|
||||
@@ -61,20 +75,23 @@ struct EditFlagsControl {
|
||||
rpl::producer<Flags> changes;
|
||||
};
|
||||
|
||||
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
[[nodiscard]] EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatRestrictions restrictions,
|
||||
std::map<ChatRestrictions, QString> disabledMessages);
|
||||
std::map<ChatRestrictions, QString> disabledMessages,
|
||||
Data::RestrictionsSetOptions options);
|
||||
|
||||
EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||
[[nodiscard]] EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> header,
|
||||
ChatAdminRights rights,
|
||||
std::map<ChatAdminRights, QString> disabledMessages,
|
||||
bool isGroup,
|
||||
bool anyoneCanAddMembers);
|
||||
Data::AdminRightsSetOptions options);
|
||||
|
||||
ChatAdminRights DisabledByDefaultRestrictions(not_null<PeerData*> peer);
|
||||
ChatRestrictions FixDependentRestrictions(ChatRestrictions restrictions);
|
||||
ChatAdminRights AdminRightsForOwnershipTransfer(bool isGroup);
|
||||
[[nodiscard]] ChatAdminRights DisabledByDefaultRestrictions(
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] ChatRestrictions FixDependentRestrictions(
|
||||
ChatRestrictions restrictions);
|
||||
[[nodiscard]] ChatAdminRights AdminRightsForOwnershipTransfer(
|
||||
Data::AdminRightsSetOptions options);
|
||||
|
||||
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/painter.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -137,7 +138,7 @@ void Row::elementAddRipple(
|
||||
}
|
||||
auto &ripple = *pointer;
|
||||
if (!ripple) {
|
||||
auto mask = Ui::RippleAnimation::roundRectMask(
|
||||
auto mask = Ui::RippleAnimation::RoundRectMask(
|
||||
(element == kAcceptButton
|
||||
? _delegate->rowAcceptButtonSize()
|
||||
: _delegate->rowRejectButtonSize()),
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_peer_info_box.h" // CreateButton.
|
||||
#include "boxes/peers/edit_peer_invite_link.h"
|
||||
#include "boxes/peers/edit_peer_invite_links.h"
|
||||
#include "boxes/peers/edit_peer_usernames_list.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
|
||||
void createContent();
|
||||
[[nodiscard]] QString getUsernameInput() const;
|
||||
[[nodiscard]] std::vector<QString> usernamesOrder() const;
|
||||
void setFocusUsername();
|
||||
|
||||
[[nodiscard]] rpl::producer<QString> getTitle() const {
|
||||
@@ -86,6 +88,10 @@ public:
|
||||
return _controls.requestToJoin && _controls.requestToJoin->toggled();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<int> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
void showError(rpl::producer<QString> text) {
|
||||
_controls.usernameInput->showError();
|
||||
showUsernameError(std::move(text));
|
||||
@@ -96,6 +102,7 @@ private:
|
||||
std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacy;
|
||||
Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr;
|
||||
Ui::UsernameInput *usernameInput = nullptr;
|
||||
UsernamesList *usernamesList = nullptr;
|
||||
base::unique_qptr<Ui::FlatLabel> usernameResult;
|
||||
const style::FlatLabel *usernameResultStyle = nullptr;
|
||||
|
||||
@@ -152,6 +159,8 @@ private:
|
||||
UsernameState _usernameState = UsernameState::Normal;
|
||||
rpl::event_stream<rpl::producer<QString>> _usernameResultTexts;
|
||||
|
||||
rpl::event_stream<int> _scrollToRequests;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
@@ -173,7 +182,7 @@ Controller::Controller(
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup())
|
||||
, _goodUsername(_dataSavedValue
|
||||
? !_dataSavedValue->username.isEmpty()
|
||||
: (_peer->isChannel() && !_peer->asChannel()->username.isEmpty()))
|
||||
: (_peer->isChannel() && !_peer->asChannel()->editableUsername().isEmpty()))
|
||||
, _wrap(container)
|
||||
, _checkUsernameTimer([=] { checkUsernameAvailability(); }) {
|
||||
_peer->updateFull();
|
||||
@@ -199,21 +208,6 @@ void Controller::createContent() {
|
||||
}
|
||||
|
||||
using namespace Settings;
|
||||
AddSkip(_wrap.get());
|
||||
_wrap->add(EditPeerInfoBox::CreateButton(
|
||||
_wrap.get(),
|
||||
tr::lng_group_invite_manage(),
|
||||
rpl::single(QString()),
|
||||
[=] {
|
||||
const auto admin = _peer->session().user();
|
||||
_show->showBox(
|
||||
Box(ManageInviteLinksBox, _peer, admin, 0, 0),
|
||||
Ui::LayerOption::KeepOther);
|
||||
},
|
||||
st::manageGroupButton,
|
||||
{ &st::infoRoundedIconInviteLinks, Settings::kIconLightOrange }));
|
||||
AddSkip(_wrap.get());
|
||||
AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about());
|
||||
|
||||
if (!_linkOnly) {
|
||||
if (_peer->isMegagroup()) {
|
||||
@@ -402,13 +396,19 @@ QString Controller::getUsernameInput() const {
|
||||
return _controls.usernameInput->getLastText().trimmed();
|
||||
}
|
||||
|
||||
std::vector<QString> Controller::usernamesOrder() const {
|
||||
return _controls.usernamesList
|
||||
? _controls.usernamesList->order()
|
||||
: std::vector<QString>();
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
Expects(_wrap != nullptr);
|
||||
|
||||
const auto channel = _peer->asChannel();
|
||||
const auto username = (!_dataSavedValue || !channel)
|
||||
? QString()
|
||||
: channel->username;
|
||||
: channel->editableUsername();
|
||||
|
||||
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_wrap,
|
||||
@@ -454,6 +454,18 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
container,
|
||||
tr::lng_create_channel_link_about());
|
||||
|
||||
if (channel) {
|
||||
const auto focusCallback = [=] {
|
||||
_scrollToRequests.fire(container->y());
|
||||
_controls.usernameInput->setFocusFast();
|
||||
};
|
||||
_controls.usernamesList = container->add(object_ptr<UsernamesList>(
|
||||
container,
|
||||
channel,
|
||||
_show,
|
||||
focusCallback));
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
_controls.usernameInput,
|
||||
&Ui::UsernameInput::changed,
|
||||
@@ -535,7 +547,7 @@ void Controller::checkUsernameAvailability() {
|
||||
_api.request(_checkUsernameRequestId).cancel();
|
||||
}
|
||||
const auto channel = _peer->migrateToOrMe()->asChannel();
|
||||
const auto username = channel ? channel->username : QString();
|
||||
const auto username = channel ? channel->editableUsername() : QString();
|
||||
_checkUsernameRequestId = _api.request(MTPchannels_CheckUsername(
|
||||
channel ? channel->inputChannel : MTP_inputChannelEmpty(),
|
||||
MTP_string(checking)
|
||||
@@ -720,6 +732,10 @@ void EditPeerTypeBox::prepare() {
|
||||
_peer,
|
||||
_useLocationPhrases,
|
||||
_dataSavedValue);
|
||||
controller->scrollToRequests(
|
||||
) | rpl::start_with_next([=, raw = content.data()](int y) {
|
||||
scrollToY(raw->y() + y);
|
||||
}, lifetime());
|
||||
_focusRequests.events(
|
||||
) | rpl::start_with_next(
|
||||
[=] {
|
||||
@@ -738,8 +754,11 @@ void EditPeerTypeBox::prepare() {
|
||||
addButton(tr::lng_settings_save(), [=] {
|
||||
const auto v = controller->getPrivacy();
|
||||
if ((v == Privacy::HasUsername) && !controller->goodUsername()) {
|
||||
controller->setFocusUsername();
|
||||
return;
|
||||
if (!controller->getUsernameInput().isEmpty()
|
||||
|| controller->usernamesOrder().empty()) {
|
||||
controller->setFocusUsername();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto local = std::move(*_savedCallback);
|
||||
@@ -748,6 +767,9 @@ void EditPeerTypeBox::prepare() {
|
||||
.username = (v == Privacy::HasUsername
|
||||
? controller->getUsernameInput()
|
||||
: QString()),
|
||||
.usernamesOrder = (v == Privacy::HasUsername
|
||||
? controller->usernamesOrder()
|
||||
: std::vector<QString>()),
|
||||
.noForwards = controller->noForwards(),
|
||||
.joinToWrite = controller->joinToWrite(),
|
||||
.requestToJoin = controller->requestToJoin(),
|
||||
|
||||
@@ -36,6 +36,7 @@ enum class UsernameState {
|
||||
struct EditPeerTypeData {
|
||||
Privacy privacy = Privacy::NoUsername;
|
||||
QString username;
|
||||
std::vector<QString> usernamesOrder;
|
||||
bool hasLinkedChat = false;
|
||||
bool noForwards = false;
|
||||
bool joinToWrite = false;
|
||||
|
||||
382
Telegram/SourceFiles/boxes/peers/edit_peer_usernames_list.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peers/edit_peer_usernames_list.h"
|
||||
|
||||
#include "api/api_user_names.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/layers/show.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::RichLangValue.
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/vertical_layout_reorder.h"
|
||||
#include "styles/style_boxes.h" // contactsStatusFont.
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
class RightAction final : public Ui::RpWidget {
|
||||
public:
|
||||
RightAction(not_null<Ui::RpWidget*> parent);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
|
||||
};
|
||||
|
||||
RightAction::RightAction(not_null<Ui::RpWidget*> parent)
|
||||
: RpWidget(parent) {
|
||||
setCursor(style::cur_sizeall);
|
||||
const auto &st = st::inviteLinkThreeDots;
|
||||
resize(st.width, st.height);
|
||||
}
|
||||
|
||||
void RightAction::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
st::usernamesReorderIcon.paintInCenter(p, rect());
|
||||
}
|
||||
|
||||
void RightAction::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class UsernamesList::Row final : public Ui::SettingsButton {
|
||||
public:
|
||||
Row(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::Username &data,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
QString link);
|
||||
|
||||
[[nodiscard]] const Data::Username &username() const;
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> rightAction() const;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
const style::PeerListItem &_st;
|
||||
const Data::Username _data;
|
||||
const QString _status;
|
||||
const not_null<Ui::RpWidget*> _rightAction;
|
||||
const QRect _iconRect;
|
||||
std::shared_ptr<Ui::Show> _show;
|
||||
Ui::Text::String _title;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
};
|
||||
|
||||
UsernamesList::Row::Row(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const Data::Username &data,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
QString link)
|
||||
: Ui::SettingsButton(parent, rpl::never<QString>())
|
||||
, _st(st::inviteLinkListItem)
|
||||
, _data(data)
|
||||
, _status(data.editable
|
||||
? tr::lng_usernames_edit(tr::now)
|
||||
: data.active
|
||||
? tr::lng_usernames_active(tr::now)
|
||||
: tr::lng_usernames_non_active(tr::now))
|
||||
, _rightAction(Ui::CreateChild<RightAction>(this))
|
||||
, _iconRect(
|
||||
_st.photoPosition.x() + st::inviteLinkIconSkip,
|
||||
_st.photoPosition.y() + st::inviteLinkIconSkip,
|
||||
_st.photoSize - st::inviteLinkIconSkip * 2,
|
||||
_st.photoSize - st::inviteLinkIconSkip * 2)
|
||||
, _show(show)
|
||||
, _title(_st.nameStyle, '@' + data.username) {
|
||||
base::install_event_filter(this, [=](not_null<QEvent*> e) {
|
||||
if (e->type() != QEvent::ContextMenu) {
|
||||
return base::EventFilterResult::Continue;
|
||||
}
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
this,
|
||||
st::popupMenuWithIcons);
|
||||
_menu->addAction(
|
||||
tr::lng_group_invite_context_copy(tr::now),
|
||||
[=] {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(
|
||||
show->toastParent(),
|
||||
tr::lng_create_channel_link_copied(tr::now));
|
||||
},
|
||||
&st::menuIconCopy);
|
||||
_menu->popup(QCursor::pos());
|
||||
return base::EventFilterResult::Cancel;
|
||||
});
|
||||
|
||||
_rightAction->setVisible(data.active);
|
||||
sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
_rightAction->moveToLeft(
|
||||
s.width() - _rightAction->width() - st::inviteLinkThreeDotsSkip,
|
||||
(s.height() - _rightAction->height()) / 2);
|
||||
}, _rightAction->lifetime());
|
||||
}
|
||||
|
||||
const Data::Username &UsernamesList::Row::username() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
not_null<Ui::RpWidget*> UsernamesList::Row::rightAction() const {
|
||||
return _rightAction;
|
||||
}
|
||||
|
||||
int UsernamesList::Row::resizeGetHeight(int newWidth) {
|
||||
return _st.height;
|
||||
}
|
||||
|
||||
void UsernamesList::Row::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
|
||||
const auto paintOver = (isOver() || isDown()) && !isDisabled();
|
||||
Ui::SettingsButton::paintBg(p, e->rect(), paintOver);
|
||||
Ui::SettingsButton::paintRipple(p, 0, 0);
|
||||
|
||||
const auto active = _data.active;
|
||||
|
||||
const auto &color = active ? st::msgFile1Bg : st::windowSubTextFg;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(color);
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawEllipse(_iconRect);
|
||||
}
|
||||
(!active
|
||||
? st::inviteLinkRevokedIcon
|
||||
: st::inviteLinkIcon).paintInCenter(p, _iconRect);
|
||||
|
||||
p.setPen(_st.nameFg);
|
||||
_title.drawLeft(
|
||||
p,
|
||||
_st.namePosition.x(),
|
||||
_st.namePosition.y(),
|
||||
width(),
|
||||
width() - _st.namePosition.x());
|
||||
|
||||
p.setPen(active
|
||||
? _st.statusFgActive
|
||||
: paintOver
|
||||
? _st.statusFgOver
|
||||
: _st.statusFg);
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.drawTextLeft(
|
||||
_st.statusPosition.x(),
|
||||
_st.statusPosition.y(),
|
||||
width() - _st.statusPosition.x(),
|
||||
_status);
|
||||
}
|
||||
|
||||
UsernamesList::UsernamesList(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
Fn<void()> focusCallback)
|
||||
: RpWidget(parent)
|
||||
, _show(show)
|
||||
, _peer(peer)
|
||||
, _focusCallback(std::move(focusCallback)) {
|
||||
{
|
||||
auto &api = _peer->session().api();
|
||||
const auto usernames = api.usernames().cacheFor(_peer->id);
|
||||
if (!usernames.empty()) {
|
||||
rebuild(usernames);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}
|
||||
|
||||
void UsernamesList::load() {
|
||||
_loadLifetime = _peer->session().api().usernames().loadUsernames(
|
||||
_peer
|
||||
) | rpl::start_with_next([=](const Data::Usernames &usernames) {
|
||||
if (usernames.empty()) {
|
||||
_container = nullptr;
|
||||
resize(0, 0);
|
||||
} else {
|
||||
rebuild(usernames);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
if (_reorder) {
|
||||
_reorder->cancel();
|
||||
}
|
||||
_rows.clear();
|
||||
_rows.reserve(usernames.size());
|
||||
_container = base::make_unique_q<Ui::VerticalLayout>(this);
|
||||
|
||||
{
|
||||
Settings::AddSkip(_container);
|
||||
_container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_container,
|
||||
_peer->isSelf()
|
||||
? tr::lng_usernames_subtitle()
|
||||
: tr::lng_channel_usernames_subtitle(),
|
||||
st::settingsSubsectionTitle),
|
||||
st::settingsSubsectionTitlePadding);
|
||||
}
|
||||
|
||||
const auto content = _container->add(
|
||||
object_ptr<Ui::VerticalLayout>(_container));
|
||||
for (const auto &username : usernames) {
|
||||
const auto link = _peer->session().createInternalLinkFull(
|
||||
username.username);
|
||||
const auto row = content->add(
|
||||
object_ptr<Row>(content, username, _show, link));
|
||||
_rows.push_back(row);
|
||||
row->addClickHandler([=] {
|
||||
if (_reordering || (!_peer->isSelf() && !_peer->isChannel())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.editable) {
|
||||
_focusCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
auto text = _peer->isSelf()
|
||||
? (username.active
|
||||
? tr::lng_usernames_deactivate_description()
|
||||
: tr::lng_usernames_activate_description())
|
||||
: (username.active
|
||||
? tr::lng_channel_usernames_deactivate_description()
|
||||
: tr::lng_channel_usernames_activate_description());
|
||||
|
||||
auto confirmText = username.active
|
||||
? tr::lng_usernames_deactivate_confirm()
|
||||
: tr::lng_usernames_activate_confirm();
|
||||
|
||||
auto args = Ui::ConfirmBoxArgs{
|
||||
.text = std::move(text),
|
||||
.confirmed = crl::guard(this, [=](Fn<void()> close) {
|
||||
auto &api = _peer->session().api();
|
||||
_toggleLifetime = api.usernames().reorder(
|
||||
_peer,
|
||||
order()
|
||||
) | rpl::start_with_done([=] {
|
||||
auto &api = _peer->session().api();
|
||||
_toggleLifetime = api.usernames().toggle(
|
||||
_peer,
|
||||
username.username,
|
||||
!username.active
|
||||
) | rpl::start_with_error_done([=](
|
||||
Api::Usernames::Error error) {
|
||||
if (error == Api::Usernames::Error::TooMuch) {
|
||||
constexpr auto kMaxUsernames = 10.;
|
||||
_show->showBox(
|
||||
Ui::MakeInformBox(
|
||||
tr::lng_usernames_activate_error(
|
||||
lt_count,
|
||||
rpl::single(kMaxUsernames),
|
||||
Ui::Text::RichLangValue)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
load();
|
||||
_toggleLifetime.destroy();
|
||||
}, [=] {
|
||||
load();
|
||||
_toggleLifetime.destroy();
|
||||
});
|
||||
});
|
||||
close();
|
||||
}),
|
||||
.confirmText = std::move(confirmText),
|
||||
};
|
||||
_show->showBox(
|
||||
Ui::MakeConfirmBox(std::move(args)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
});
|
||||
}
|
||||
|
||||
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(content);
|
||||
_reorder->setMouseEventProxy([=](int i) {
|
||||
return _rows[i]->rightAction();
|
||||
});
|
||||
|
||||
{
|
||||
const auto it = ranges::find_if(usernames, [&](
|
||||
const Data::Username username) {
|
||||
return !username.active;
|
||||
});
|
||||
if (it != end(usernames)) {
|
||||
const auto from = std::distance(begin(usernames), it);
|
||||
const auto length = std::distance(it, end(usernames));
|
||||
_reorder->addPinnedInterval(from, length);
|
||||
if (from == 1) {
|
||||
// Can't be reordered.
|
||||
_rows[0]->rightAction()->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
_reorder->start();
|
||||
|
||||
_reorder->updates(
|
||||
) | rpl::start_with_next([=](Ui::VerticalLayoutReorder::Single data) {
|
||||
using State = Ui::VerticalLayoutReorder::State;
|
||||
if (data.state == State::Started) {
|
||||
++_reordering;
|
||||
} else {
|
||||
Ui::PostponeCall(content, [=] {
|
||||
--_reordering;
|
||||
});
|
||||
if (data.state == State::Applied) {
|
||||
base::reorder(
|
||||
_rows,
|
||||
data.oldPosition,
|
||||
data.newPosition);
|
||||
}
|
||||
}
|
||||
}, content->lifetime());
|
||||
|
||||
{
|
||||
Settings::AddSkip(_container);
|
||||
Settings::AddDividerText(
|
||||
_container,
|
||||
_peer->isSelf()
|
||||
? tr::lng_usernames_description()
|
||||
: tr::lng_channel_usernames_description());
|
||||
}
|
||||
|
||||
Ui::ResizeFitChild(this, _container.get());
|
||||
content->show();
|
||||
_container->show();
|
||||
}
|
||||
|
||||
std::vector<QString> UsernamesList::order() const {
|
||||
return ranges::views::all(
|
||||
_rows
|
||||
) | ranges::views::filter([](not_null<Row*> row) {
|
||||
return row->username().active;
|
||||
}) | ranges::views::transform([](not_null<Row*> row) {
|
||||
return row->username().username;
|
||||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
rpl::producer<> UsernamesList::save() {
|
||||
return _peer->session().api().usernames().reorder(_peer, order());
|
||||
}
|
||||
55
Telegram/SourceFiles/boxes/peers/edit_peer_usernames_list.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/unique_qptr.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
class VerticalLayoutReorder;
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
struct Username;
|
||||
} // namespace Data
|
||||
|
||||
class UsernamesList final : public Ui::RpWidget {
|
||||
public:
|
||||
UsernamesList(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
Fn<void()> focusCallback);
|
||||
|
||||
[[nodiscard]] rpl::producer<> save();
|
||||
[[nodiscard]] std::vector<QString> order() const;
|
||||
|
||||
private:
|
||||
void rebuild(const std::vector<Data::Username> &usernames);
|
||||
void load();
|
||||
|
||||
class Row;
|
||||
|
||||
const std::shared_ptr<Ui::Show> _show;
|
||||
const not_null<PeerData*> _peer;
|
||||
Fn<void()> _focusCallback;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _container;
|
||||
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
|
||||
std::vector<Row*> _rows;
|
||||
|
||||
int _reordering = 0;
|
||||
|
||||
rpl::lifetime _loadLifetime;
|
||||
rpl::lifetime _toggleLifetime;
|
||||
|
||||
};
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/wrap.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/painter.h"
|
||||
#include "info/profile/info_profile_text.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
|
||||
@@ -19,10 +19,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool IsOldForPin(MsgId id, not_null<PeerData*> peer) {
|
||||
[[nodiscard]] bool IsOldForPin(
|
||||
MsgId id,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId topicRootId) {
|
||||
const auto normal = peer->migrateToOrMe();
|
||||
const auto migrated = normal->migrateFrom();
|
||||
const auto top = Data::ResolveTopPinnedId(normal, migrated);
|
||||
const auto top = Data::ResolveTopPinnedId(normal, topicRootId, migrated);
|
||||
if (!top) {
|
||||
return false;
|
||||
} else if (peer == migrated) {
|
||||
@@ -46,7 +49,7 @@ void PinMessageBox(
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
const auto pinningOld = IsOldForPin(msgId, peer);
|
||||
const auto pinningOld = IsOldForPin(msgId, peer, MsgId(0));
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
const auto api = box->lifetime().make_state<MTP::Sender>(
|
||||
&peer->session().mtp());
|
||||
|
||||
@@ -361,7 +361,7 @@ void PublicsController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
||||
tr::now);
|
||||
const auto closeBox = _closeBox;
|
||||
const auto once = std::make_shared<bool>(false);
|
||||
auto callback = crl::guard(_navigation, [=](Fn<void()> &&close) {
|
||||
auto callback = crl::guard(_navigation, [=](Fn<void()> close) {
|
||||
if (*once) {
|
||||
return;
|
||||
}
|
||||
@@ -369,9 +369,13 @@ void PublicsController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
||||
peer->session().api().request(MTPchannels_UpdateUsername(
|
||||
peer->asChannel()->inputChannel,
|
||||
MTP_string()
|
||||
)).done([=, close = std::move(close)] {
|
||||
closeBox();
|
||||
close();
|
||||
)).done([=] {
|
||||
peer->session().api().request(MTPchannels_DeactivateAllUsernames(
|
||||
peer->asChannel()->inputChannel
|
||||
)).done([=] {
|
||||
closeBox();
|
||||
close();
|
||||
}).send();
|
||||
}).send();
|
||||
});
|
||||
_navigation->parentController()->show(
|
||||
|
||||