Merge branch 'dev' into Add-keyword-based-message-deletion
42
.github/workflows/docker.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
name: Docker.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/docker.yml'
|
||||||
|
- 'Telegram/build/docker/centos_env/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
name: Ubuntu
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref_name == github.event.repository.default_branch
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_TAG: ghcr.io/${{ github.repository }}/centos_env:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone.
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: First set up.
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
curl -sSL https://install.python-poetry.org | python3 -
|
||||||
|
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
|
||||||
|
|
||||||
|
- name: Free up some disk space.
|
||||||
|
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||||
|
with:
|
||||||
|
tool-cache: true
|
||||||
|
|
||||||
|
- name: Docker image build.
|
||||||
|
run: |
|
||||||
|
cd Telegram/build/docker/centos_env
|
||||||
|
poetry install
|
||||||
|
DEBUG= LTO= poetry run gen_dockerfile | DOCKER_BUILDKIT=1 docker build -t $IMAGE_TAG -
|
||||||
|
|
||||||
|
- name: Push the Docker image.
|
||||||
|
run: docker push $IMAGE_TAG
|
6
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
||||||
[submodule "Telegram/ThirdParty/libtgvoip"]
|
|
||||||
path = Telegram/ThirdParty/libtgvoip
|
|
||||||
url = https://github.com/telegramdesktop/libtgvoip
|
|
||||||
[submodule "Telegram/ThirdParty/GSL"]
|
[submodule "Telegram/ThirdParty/GSL"]
|
||||||
path = Telegram/ThirdParty/GSL
|
path = Telegram/ThirdParty/GSL
|
||||||
url = https://github.com/Microsoft/GSL.git
|
url = https://github.com/Microsoft/GSL.git
|
||||||
|
@ -76,9 +73,6 @@
|
||||||
[submodule "Telegram/lib_webview"]
|
[submodule "Telegram/lib_webview"]
|
||||||
path = Telegram/lib_webview
|
path = Telegram/lib_webview
|
||||||
url = https://github.com/desktop-app/lib_webview.git
|
url = https://github.com/desktop-app/lib_webview.git
|
||||||
[submodule "Telegram/ThirdParty/jemalloc"]
|
|
||||||
path = Telegram/ThirdParty/jemalloc
|
|
||||||
url = https://github.com/jemalloc/jemalloc
|
|
||||||
[submodule "Telegram/ThirdParty/dispatch"]
|
[submodule "Telegram/ThirdParty/dispatch"]
|
||||||
path = Telegram/ThirdParty/dispatch
|
path = Telegram/ThirdParty/dispatch
|
||||||
url = https://github.com/apple/swift-corelibs-libdispatch
|
url = https://github.com/apple/swift-corelibs-libdispatch
|
||||||
|
|
|
@ -12,19 +12,17 @@ include(cmake/validate_special_target.cmake)
|
||||||
include(cmake/version.cmake)
|
include(cmake/version.cmake)
|
||||||
desktop_app_parse_version(Telegram/build/version)
|
desktop_app_parse_version(Telegram/build/version)
|
||||||
|
|
||||||
set(project_langs C CXX)
|
|
||||||
if (APPLE)
|
|
||||||
list(APPEND project_langs OBJC OBJCXX)
|
|
||||||
elseif (LINUX)
|
|
||||||
list(APPEND project_langs ASM)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(Telegram
|
project(Telegram
|
||||||
LANGUAGES ${project_langs}
|
LANGUAGES C CXX
|
||||||
VERSION ${desktop_app_version_cmake}
|
VERSION ${desktop_app_version_cmake}
|
||||||
DESCRIPTION "AyuGram Desktop"
|
DESCRIPTION "AyuGram Desktop"
|
||||||
HOMEPAGE_URL "https://ayugram.one"
|
HOMEPAGE_URL "https://ayugram.one"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
enable_language(OBJC OBJCXX)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Telegram)
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Telegram)
|
||||||
|
|
||||||
get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
|
get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
|
||||||
|
@ -39,9 +37,7 @@ include(cmake/variables.cmake)
|
||||||
include(cmake/nice_target_sources.cmake)
|
include(cmake/nice_target_sources.cmake)
|
||||||
include(cmake/target_compile_options_if_exists.cmake)
|
include(cmake/target_compile_options_if_exists.cmake)
|
||||||
include(cmake/target_link_frameworks.cmake)
|
include(cmake/target_link_frameworks.cmake)
|
||||||
include(cmake/target_link_optional_libraries.cmake)
|
|
||||||
include(cmake/target_link_options_if_exists.cmake)
|
include(cmake/target_link_options_if_exists.cmake)
|
||||||
include(cmake/target_link_static_libraries.cmake)
|
|
||||||
include(cmake/init_target.cmake)
|
include(cmake/init_target.cmake)
|
||||||
include(cmake/generate_target.cmake)
|
include(cmake/generate_target.cmake)
|
||||||
include(cmake/nuget.cmake)
|
include(cmake/nuget.cmake)
|
||||||
|
|
21
README-RU.md
|
@ -67,12 +67,28 @@ brew install --cask ayugram
|
||||||
|
|
||||||
### Arch Linux
|
### Arch Linux
|
||||||
|
|
||||||
Вы можете установить `ayugram-desktop` из [AUR](https://aur.archlinux.org/packages?O=0&K=ayugram).
|
#### Из исходников (рекомендованный способ)
|
||||||
|
|
||||||
|
Установите `ayugram-desktop` из [AUR](https://aur.archlinux.org/packages/ayugram-desktop).
|
||||||
|
|
||||||
|
#### Готовые бинарники
|
||||||
|
|
||||||
|
Установите `ayugram-desktop-bin` из [AUR](https://aur.archlinux.org/packages/ayugram-desktop-bin).
|
||||||
|
|
||||||
|
Примечание: данный пакет собирается не нами.
|
||||||
|
|
||||||
### NixOS
|
### NixOS
|
||||||
|
|
||||||
Попробуйте [этот репозиторий](https://github.com/ayugram-port/ayugram-desktop).
|
Попробуйте [этот репозиторий](https://github.com/ayugram-port/ayugram-desktop).
|
||||||
|
|
||||||
|
### ALT Linux
|
||||||
|
|
||||||
|
[Sisyphus](https://packages.altlinux.org/en/sisyphus/srpms/ayugram-desktop/)
|
||||||
|
|
||||||
|
### EPM
|
||||||
|
|
||||||
|
`epm play ayugram`
|
||||||
|
|
||||||
### Любой другой Линукс дистрибутив
|
### Любой другой Линукс дистрибутив
|
||||||
|
|
||||||
Следуйте [официальному руководству](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
Следуйте [официальному руководству](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||||
|
@ -112,5 +128,4 @@ brew install --cask ayugram
|
||||||
|
|
||||||
### Боты
|
### Боты
|
||||||
|
|
||||||
- [TelegramDB](https://t.me/tgdatabase) для получения юзернейма по ID (до закрытия бесплатной версии 25 мая 2025)
|
- [TelegramDB](https://t.me/tgdatabase) для получения юзернейма по ID
|
||||||
- [usinfobot](https://t.me/usinfobot) для получения юзернейма по ID
|
|
||||||
|
|
21
README.md
|
@ -68,12 +68,28 @@ brew install --cask ayugram
|
||||||
|
|
||||||
### Arch Linux
|
### Arch Linux
|
||||||
|
|
||||||
You can install `ayugram-desktop` from [AUR](https://aur.archlinux.org/packages?O=0&K=ayugram).
|
#### From source (recommended)
|
||||||
|
|
||||||
|
Install `ayugram-desktop` from [AUR](https://aur.archlinux.org/packages/ayugram-desktop).
|
||||||
|
|
||||||
|
#### Prebuilt binaries
|
||||||
|
|
||||||
|
Install `ayugram-desktop-bin` from [AUR](https://aur.archlinux.org/packages/ayugram-desktop-bin).
|
||||||
|
|
||||||
|
Note: these binaries aren't officially maintained by us.
|
||||||
|
|
||||||
### NixOS
|
### NixOS
|
||||||
|
|
||||||
See [this repository](https://github.com/ayugram-port/ayugram-desktop) for installation manual.
|
See [this repository](https://github.com/ayugram-port/ayugram-desktop) for installation manual.
|
||||||
|
|
||||||
|
### ALT Linux
|
||||||
|
|
||||||
|
[Sisyphus](https://packages.altlinux.org/en/sisyphus/srpms/ayugram-desktop/)
|
||||||
|
|
||||||
|
### EPM
|
||||||
|
|
||||||
|
`epm play ayugram`
|
||||||
|
|
||||||
### Any other Linux distro
|
### Any other Linux distro
|
||||||
|
|
||||||
Follow the [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
Follow the [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||||
|
@ -113,5 +129,4 @@ Enjoy using **AyuGram**? Consider sending us a tip!
|
||||||
|
|
||||||
### Bots
|
### Bots
|
||||||
|
|
||||||
- [TelegramDB](https://t.me/tgdatabase) for username lookup by ID (until closing free inline mode at 25 May 2025)
|
- [TelegramDB](https://t.me/tgdatabase) for username lookup by ID
|
||||||
- [usinfobot](https://t.me/usinfobot) for username lookup by ID
|
|
||||||
|
|
|
@ -53,9 +53,7 @@ set_target_properties(Telegram PROPERTIES AUTOMOC ON)
|
||||||
|
|
||||||
target_link_libraries(Telegram
|
target_link_libraries(Telegram
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# tdesktop::lib_tgcalls_legacy
|
|
||||||
tdesktop::lib_tgcalls
|
tdesktop::lib_tgcalls
|
||||||
# tdesktop::lib_tgvoip
|
|
||||||
|
|
||||||
# Order in this list defines the order of include paths in command line.
|
# Order in this list defines the order of include paths in command line.
|
||||||
# We need to place desktop-app::external_minizip this early to have its
|
# We need to place desktop-app::external_minizip this early to have its
|
||||||
|
@ -129,8 +127,6 @@ set(ayugram_files
|
||||||
ayu/ui/ayu_logo.h
|
ayu/ui/ayu_logo.h
|
||||||
ayu/ui/utils/ayu_profile_values.cpp
|
ayu/ui/utils/ayu_profile_values.cpp
|
||||||
ayu/ui/utils/ayu_profile_values.h
|
ayu/ui/utils/ayu_profile_values.h
|
||||||
ayu/ui/settings/icon_picker.cpp
|
|
||||||
ayu/ui/settings/icon_picker.h
|
|
||||||
ayu/ui/settings/settings_ayu.cpp
|
ayu/ui/settings/settings_ayu.cpp
|
||||||
ayu/ui/settings/settings_ayu.h
|
ayu/ui/settings/settings_ayu.h
|
||||||
ayu/ui/context_menu/context_menu.cpp
|
ayu/ui/context_menu/context_menu.cpp
|
||||||
|
@ -143,10 +139,8 @@ set(ayugram_files
|
||||||
ayu/ui/message_history/history_item.h
|
ayu/ui/message_history/history_item.h
|
||||||
ayu/ui/message_history/history_section.cpp
|
ayu/ui/message_history/history_section.cpp
|
||||||
ayu/ui/message_history/history_section.h
|
ayu/ui/message_history/history_section.h
|
||||||
ayu/ui/boxes/edit_deleted_mark.cpp
|
ayu/ui/boxes/edit_mark_box.cpp
|
||||||
ayu/ui/boxes/edit_deleted_mark.h
|
ayu/ui/boxes/edit_mark_box.h
|
||||||
ayu/ui/boxes/edit_edited_mark.cpp
|
|
||||||
ayu/ui/boxes/edit_edited_mark.h
|
|
||||||
ayu/ui/boxes/font_selector.cpp
|
ayu/ui/boxes/font_selector.cpp
|
||||||
ayu/ui/boxes/font_selector.h
|
ayu/ui/boxes/font_selector.h
|
||||||
ayu/ui/boxes/theme_selector_box.cpp
|
ayu/ui/boxes/theme_selector_box.cpp
|
||||||
|
@ -155,6 +149,8 @@ set(ayugram_files
|
||||||
ayu/ui/boxes/message_shot_box.h
|
ayu/ui/boxes/message_shot_box.h
|
||||||
ayu/ui/components/image_view.cpp
|
ayu/ui/components/image_view.cpp
|
||||||
ayu/ui/components/image_view.h
|
ayu/ui/components/image_view.h
|
||||||
|
ayu/ui/components/icon_picker.cpp
|
||||||
|
ayu/ui/components/icon_picker.h
|
||||||
ayu/libs/json.hpp
|
ayu/libs/json.hpp
|
||||||
ayu/libs/json_ext.hpp
|
ayu/libs/json_ext.hpp
|
||||||
ayu/libs/sqlite/sqlite3.c
|
ayu/libs/sqlite/sqlite3.c
|
||||||
|
@ -170,6 +166,10 @@ set(ayugram_files
|
||||||
ayu/features/streamer_mode/streamer_mode.h
|
ayu/features/streamer_mode/streamer_mode.h
|
||||||
ayu/features/messageshot/message_shot.cpp
|
ayu/features/messageshot/message_shot.cpp
|
||||||
ayu/features/messageshot/message_shot.h
|
ayu/features/messageshot/message_shot.h
|
||||||
|
ayu/features/forward/ayu_forward.cpp
|
||||||
|
ayu/features/forward/ayu_forward.h
|
||||||
|
ayu/features/forward/ayu_sync.cpp
|
||||||
|
ayu/features/forward/ayu_sync.h
|
||||||
ayu/data/messages_storage.cpp
|
ayu/data/messages_storage.cpp
|
||||||
ayu/data/messages_storage.h
|
ayu/data/messages_storage.h
|
||||||
ayu/data/entities.h
|
ayu/data/entities.h
|
||||||
|
@ -209,6 +209,8 @@ PRIVATE
|
||||||
api/api_confirm_phone.h
|
api/api_confirm_phone.h
|
||||||
api/api_credits.cpp
|
api/api_credits.cpp
|
||||||
api/api_credits.h
|
api/api_credits.h
|
||||||
|
api/api_credits_history_entry.cpp
|
||||||
|
api/api_credits_history_entry.h
|
||||||
api/api_earn.cpp
|
api/api_earn.cpp
|
||||||
api/api_earn.h
|
api/api_earn.h
|
||||||
api/api_editing.cpp
|
api/api_editing.cpp
|
||||||
|
@ -258,8 +260,12 @@ PRIVATE
|
||||||
api/api_statistics_data_deserialize.h
|
api/api_statistics_data_deserialize.h
|
||||||
api/api_statistics_sender.cpp
|
api/api_statistics_sender.cpp
|
||||||
api/api_statistics_sender.h
|
api/api_statistics_sender.h
|
||||||
|
api/api_suggest_post.cpp
|
||||||
|
api/api_suggest_post.h
|
||||||
api/api_text_entities.cpp
|
api/api_text_entities.cpp
|
||||||
api/api_text_entities.h
|
api/api_text_entities.h
|
||||||
|
api/api_todo_lists.cpp
|
||||||
|
api/api_todo_lists.h
|
||||||
api/api_toggling_media.cpp
|
api/api_toggling_media.cpp
|
||||||
api/api_toggling_media.h
|
api/api_toggling_media.h
|
||||||
api/api_transcribes.cpp
|
api/api_transcribes.cpp
|
||||||
|
@ -363,6 +369,8 @@ PRIVATE
|
||||||
boxes/edit_caption_box.h
|
boxes/edit_caption_box.h
|
||||||
boxes/edit_privacy_box.cpp
|
boxes/edit_privacy_box.cpp
|
||||||
boxes/edit_privacy_box.h
|
boxes/edit_privacy_box.h
|
||||||
|
boxes/edit_todo_list_box.cpp
|
||||||
|
boxes/edit_todo_list_box.h
|
||||||
boxes/gift_credits_box.cpp
|
boxes/gift_credits_box.cpp
|
||||||
boxes/gift_credits_box.h
|
boxes/gift_credits_box.h
|
||||||
boxes/gift_premium_box.cpp
|
boxes/gift_premium_box.cpp
|
||||||
|
@ -546,6 +554,7 @@ PRIVATE
|
||||||
core/crash_report_window.h
|
core/crash_report_window.h
|
||||||
core/crash_reports.cpp
|
core/crash_reports.cpp
|
||||||
core/crash_reports.h
|
core/crash_reports.h
|
||||||
|
core/credits_amount.h
|
||||||
core/deadlock_detector.h
|
core/deadlock_detector.h
|
||||||
core/file_utilities.cpp
|
core/file_utilities.cpp
|
||||||
core/file_utilities.h
|
core/file_utilities.h
|
||||||
|
@ -559,7 +568,6 @@ PRIVATE
|
||||||
core/sandbox.h
|
core/sandbox.h
|
||||||
core/shortcuts.cpp
|
core/shortcuts.cpp
|
||||||
core/shortcuts.h
|
core/shortcuts.h
|
||||||
core/stars_amount.h
|
|
||||||
core/ui_integration.cpp
|
core/ui_integration.cpp
|
||||||
core/ui_integration.h
|
core/ui_integration.h
|
||||||
core/update_checker.cpp
|
core/update_checker.cpp
|
||||||
|
@ -587,6 +595,8 @@ PRIVATE
|
||||||
data/components/promo_suggestions.h
|
data/components/promo_suggestions.h
|
||||||
data/components/recent_peers.cpp
|
data/components/recent_peers.cpp
|
||||||
data/components/recent_peers.h
|
data/components/recent_peers.h
|
||||||
|
data/components/recent_shared_media_gifts.cpp
|
||||||
|
data/components/recent_shared_media_gifts.h
|
||||||
data/components/scheduled_messages.cpp
|
data/components/scheduled_messages.cpp
|
||||||
data/components/scheduled_messages.h
|
data/components/scheduled_messages.h
|
||||||
data/components/sponsored_messages.cpp
|
data/components/sponsored_messages.cpp
|
||||||
|
@ -731,6 +741,8 @@ PRIVATE
|
||||||
data/data_streaming.h
|
data/data_streaming.h
|
||||||
data/data_thread.cpp
|
data/data_thread.cpp
|
||||||
data/data_thread.h
|
data/data_thread.h
|
||||||
|
data/data_todo_list.cpp
|
||||||
|
data/data_todo_list.h
|
||||||
data/data_types.cpp
|
data/data_types.cpp
|
||||||
data/data_types.h
|
data/data_types.h
|
||||||
data/data_unread_value.cpp
|
data/data_unread_value.cpp
|
||||||
|
@ -831,6 +843,8 @@ PRIVATE
|
||||||
history/view/controls/history_view_draft_options.h
|
history/view/controls/history_view_draft_options.h
|
||||||
history/view/controls/history_view_forward_panel.cpp
|
history/view/controls/history_view_forward_panel.cpp
|
||||||
history/view/controls/history_view_forward_panel.h
|
history/view/controls/history_view_forward_panel.h
|
||||||
|
history/view/controls/history_view_suggest_options.cpp
|
||||||
|
history/view/controls/history_view_suggest_options.h
|
||||||
history/view/controls/history_view_ttl_button.cpp
|
history/view/controls/history_view_ttl_button.cpp
|
||||||
history/view/controls/history_view_ttl_button.h
|
history/view/controls/history_view_ttl_button.h
|
||||||
history/view/controls/history_view_voice_record_bar.cpp
|
history/view/controls/history_view_voice_record_bar.cpp
|
||||||
|
@ -892,8 +906,12 @@ PRIVATE
|
||||||
history/view/media/history_view_sticker_player_abstract.h
|
history/view/media/history_view_sticker_player_abstract.h
|
||||||
history/view/media/history_view_story_mention.cpp
|
history/view/media/history_view_story_mention.cpp
|
||||||
history/view/media/history_view_story_mention.h
|
history/view/media/history_view_story_mention.h
|
||||||
|
history/view/media/history_view_suggest_decision.cpp
|
||||||
|
history/view/media/history_view_suggest_decision.h
|
||||||
history/view/media/history_view_theme_document.cpp
|
history/view/media/history_view_theme_document.cpp
|
||||||
history/view/media/history_view_theme_document.h
|
history/view/media/history_view_theme_document.h
|
||||||
|
history/view/media/history_view_todo_list.cpp
|
||||||
|
history/view/media/history_view_todo_list.h
|
||||||
history/view/media/history_view_unique_gift.cpp
|
history/view/media/history_view_unique_gift.cpp
|
||||||
history/view/media/history_view_unique_gift.h
|
history/view/media/history_view_unique_gift.h
|
||||||
history/view/media/history_view_userpic_suggestion.cpp
|
history/view/media/history_view_userpic_suggestion.cpp
|
||||||
|
@ -918,6 +936,8 @@ PRIVATE
|
||||||
history/view/history_view_bottom_info.h
|
history/view/history_view_bottom_info.h
|
||||||
history/view/history_view_chat_preview.cpp
|
history/view/history_view_chat_preview.cpp
|
||||||
history/view/history_view_chat_preview.h
|
history/view/history_view_chat_preview.h
|
||||||
|
history/view/history_view_chat_section.cpp
|
||||||
|
history/view/history_view_chat_section.h
|
||||||
history/view/history_view_contact_status.cpp
|
history/view/history_view_contact_status.cpp
|
||||||
history/view/history_view_contact_status.h
|
history/view/history_view_contact_status.h
|
||||||
history/view/history_view_context_menu.cpp
|
history/view/history_view_context_menu.cpp
|
||||||
|
@ -952,8 +972,6 @@ PRIVATE
|
||||||
history/view/history_view_pinned_tracker.h
|
history/view/history_view_pinned_tracker.h
|
||||||
history/view/history_view_quick_action.cpp
|
history/view/history_view_quick_action.cpp
|
||||||
history/view/history_view_quick_action.h
|
history/view/history_view_quick_action.h
|
||||||
history/view/history_view_replies_section.cpp
|
|
||||||
history/view/history_view_replies_section.h
|
|
||||||
history/view/history_view_reply.cpp
|
history/view/history_view_reply.cpp
|
||||||
history/view/history_view_reply.h
|
history/view/history_view_reply.h
|
||||||
history/view/history_view_requests_bar.cpp
|
history/view/history_view_requests_bar.cpp
|
||||||
|
@ -970,8 +988,8 @@ PRIVATE
|
||||||
history/view/history_view_sponsored_click_handler.h
|
history/view/history_view_sponsored_click_handler.h
|
||||||
history/view/history_view_sticker_toast.cpp
|
history/view/history_view_sticker_toast.cpp
|
||||||
history/view/history_view_sticker_toast.h
|
history/view/history_view_sticker_toast.h
|
||||||
history/view/history_view_sublist_section.cpp
|
history/view/history_view_subsection_tabs.cpp
|
||||||
history/view/history_view_sublist_section.h
|
history/view/history_view_subsection_tabs.h
|
||||||
history/view/history_view_text_helper.cpp
|
history/view/history_view_text_helper.cpp
|
||||||
history/view/history_view_text_helper.h
|
history/view/history_view_text_helper.h
|
||||||
history/view/history_view_transcribe_button.cpp
|
history/view/history_view_transcribe_button.cpp
|
||||||
|
@ -1526,6 +1544,8 @@ PRIVATE
|
||||||
settings/cloud_password/settings_cloud_password_start.h
|
settings/cloud_password/settings_cloud_password_start.h
|
||||||
settings/cloud_password/settings_cloud_password_step.cpp
|
settings/cloud_password/settings_cloud_password_step.cpp
|
||||||
settings/cloud_password/settings_cloud_password_step.h
|
settings/cloud_password/settings_cloud_password_step.h
|
||||||
|
settings/cloud_password/settings_cloud_password_validate_icon.cpp
|
||||||
|
settings/cloud_password/settings_cloud_password_validate_icon.h
|
||||||
settings/settings_active_sessions.cpp
|
settings/settings_active_sessions.cpp
|
||||||
settings/settings_active_sessions.h
|
settings/settings_active_sessions.h
|
||||||
settings/settings_advanced.cpp
|
settings/settings_advanced.cpp
|
||||||
|
@ -1946,11 +1966,7 @@ else()
|
||||||
set(bundle_identifier "one.ayugram.AyuGramDesktop")
|
set(bundle_identifier "one.ayugram.AyuGramDesktop")
|
||||||
endif()
|
endif()
|
||||||
set(bundle_entitlements "Telegram.entitlements")
|
set(bundle_entitlements "Telegram.entitlements")
|
||||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
set(output_name "AyuGram")
|
||||||
set(output_name "ayugram-desktop")
|
|
||||||
else()
|
|
||||||
set(output_name "AyuGram")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_GENERATOR STREQUAL Xcode)
|
if (CMAKE_GENERATOR STREQUAL Xcode)
|
||||||
|
@ -1992,8 +2008,9 @@ PRIVATE
|
||||||
G_LOG_DOMAIN="Telegram"
|
G_LOG_DOMAIN="Telegram"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
if (APPLE
|
if (APPLE
|
||||||
OR "${CMAKE_GENERATOR}" STREQUAL "Ninja Multi-Config"
|
OR is_multi_config
|
||||||
OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL ""
|
OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL ""
|
||||||
OR NOT "${output_name}" STREQUAL "AyuGram")
|
OR NOT "${output_name}" STREQUAL "AyuGram")
|
||||||
set(output_folder ${CMAKE_BINARY_DIR})
|
set(output_folder ${CMAKE_BINARY_DIR})
|
||||||
|
@ -2010,8 +2027,67 @@ if (MSVC)
|
||||||
)
|
)
|
||||||
target_link_options(Telegram
|
target_link_options(Telegram
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
/DELAYLOAD:secur32.dll
|
||||||
|
/DELAYLOAD:winmm.dll
|
||||||
|
/DELAYLOAD:ws2_32.dll
|
||||||
|
/DELAYLOAD:user32.dll
|
||||||
|
/DELAYLOAD:gdi32.dll
|
||||||
/DELAYLOAD:advapi32.dll
|
/DELAYLOAD:advapi32.dll
|
||||||
|
/DELAYLOAD:avrt.dll
|
||||||
|
/DELAYLOAD:shell32.dll
|
||||||
|
/DELAYLOAD:ole32.dll
|
||||||
|
/DELAYLOAD:oleaut32.dll
|
||||||
|
/DELAYLOAD:shlwapi.dll
|
||||||
|
/DELAYLOAD:iphlpapi.dll
|
||||||
|
/DELAYLOAD:gdiplus.dll
|
||||||
|
/DELAYLOAD:version.dll
|
||||||
|
/DELAYLOAD:dwmapi.dll
|
||||||
|
/DELAYLOAD:uxtheme.dll
|
||||||
|
/DELAYLOAD:crypt32.dll
|
||||||
|
/DELAYLOAD:bcrypt.dll
|
||||||
|
/DELAYLOAD:netapi32.dll
|
||||||
|
/DELAYLOAD:imm32.dll
|
||||||
|
/DELAYLOAD:userenv.dll
|
||||||
|
/DELAYLOAD:wtsapi32.dll
|
||||||
|
/DELAYLOAD:propsys.dll
|
||||||
)
|
)
|
||||||
|
if (QT_VERSION GREATER 6)
|
||||||
|
if (NOT build_winarm)
|
||||||
|
target_link_options(Telegram PRIVATE
|
||||||
|
/DELAYLOAD:API-MS-Win-EventLog-Legacy-l1-1-0.dll
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_options(Telegram
|
||||||
|
PRIVATE
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Console-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Fibers-l2-1-1.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-File-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-LibraryLoader-l1-2-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Localization-l1-2-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Memory-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Memory-l1-1-1.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-ProcessThreads-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Synch-l1-2-0.dll # Synchronization.lib
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-SysInfo-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-Timezone-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-WinRT-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-WinRT-Error-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Core-WinRT-String-l1-1-0.dll
|
||||||
|
/DELAYLOAD:API-MS-Win-Security-CryptoAPI-l1-1-0.dll
|
||||||
|
# /DELAYLOAD:API-MS-Win-Shcore-Scaling-l1-1-1.dll # We shadowed GetDpiForMonitor
|
||||||
|
/DELAYLOAD:authz.dll # Authz.lib
|
||||||
|
/DELAYLOAD:comdlg32.dll
|
||||||
|
/DELAYLOAD:dwrite.dll # DWrite.lib
|
||||||
|
/DELAYLOAD:dxgi.dll # DXGI.lib
|
||||||
|
/DELAYLOAD:d3d9.dll # D3D9.lib
|
||||||
|
/DELAYLOAD:d3d11.dll # D3D11.lib
|
||||||
|
/DELAYLOAD:d3d12.dll # D3D12.lib
|
||||||
|
/DELAYLOAD:setupapi.dll # SetupAPI.lib
|
||||||
|
/DELAYLOAD:winhttp.dll
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_prepare_qrc(Telegram)
|
target_prepare_qrc(Telegram)
|
||||||
|
@ -2042,6 +2118,22 @@ if (NOT DESKTOP_APP_DISABLE_AUTOUPDATE AND NOT build_macstore AND NOT build_wins
|
||||||
base/platform/win/base_windows_safe_library.h
|
base/platform/win/base_windows_safe_library.h
|
||||||
)
|
)
|
||||||
target_include_directories(Updater PRIVATE ${lib_base_loc})
|
target_include_directories(Updater PRIVATE ${lib_base_loc})
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(Updater
|
||||||
|
PRIVATE
|
||||||
|
delayimp
|
||||||
|
)
|
||||||
|
target_link_options(Updater
|
||||||
|
PRIVATE
|
||||||
|
/DELAYLOAD:user32.dll
|
||||||
|
/DELAYLOAD:advapi32.dll
|
||||||
|
/DELAYLOAD:shell32.dll
|
||||||
|
/DELAYLOAD:ole32.dll
|
||||||
|
/DELAYLOAD:shlwapi.dll
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_link_options(Updater PRIVATE -municode)
|
||||||
|
endif()
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
add_custom_command(TARGET Updater
|
add_custom_command(TARGET Updater
|
||||||
PRE_LINK
|
PRE_LINK
|
||||||
|
@ -2070,6 +2162,10 @@ if (NOT DESKTOP_APP_DISABLE_AUTOUPDATE AND NOT build_macstore AND NOT build_wins
|
||||||
desktop-app::external_openssl
|
desktop-app::external_openssl
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (DESKTOP_APP_USE_PACKAGED)
|
||||||
|
target_compile_definitions(Packer PRIVATE PACKER_USE_PACKAGED)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(Packer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
|
set_target_properties(Packer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
|
||||||
endif()
|
endif()
|
||||||
elseif (build_winstore)
|
elseif (build_winstore)
|
||||||
|
|
BIN
Telegram/Resources/animations/cloud_password/validate.tgs
Normal file
BIN
Telegram/Resources/animations/diamond.tgs
Normal file
BIN
Telegram/Resources/animations/edit_peers/direct_messages.tgs
Normal file
BIN
Telegram/Resources/animations/edit_peers/topics.tgs
Normal file
BIN
Telegram/Resources/animations/edit_peers/topics_list.tgs
Normal file
BIN
Telegram/Resources/animations/edit_peers/topics_tabs.tgs
Normal file
8
Telegram/Resources/icons/chat/input_paid.svg
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Icon / Input / input_paid</title>
|
||||||
|
<g id="Icon-/-Input-/-input_paid" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M20,9.8 C25.6299826,9.8 30.2,14.2730045 30.2,19.7983329 C30.2,25.3236612 25.6299826,29.7966657 20,29.7966657 C18.7457032,29.7966657 17.522531,29.57429 16.3765194,29.1472418 L16.049,29.018 L15.8675895,29.1274403 L15.6273764,29.2612632 C14.545782,29.8404011 13.0955737,30.1473058 11.2731839,30.1996583 C10.8153842,30.2127839 10.4336239,29.8523042 10.4204985,29.3945045 C10.4177264,29.2978179 10.4318913,29.2013918 10.4663408,29.0979123 C10.9468917,27.7307176 11.2958938,26.5818971 11.5130707,25.6565167 L11.566,25.42 L11.5361505,25.3785138 C10.4824637,23.8473989 9.87852612,22.0565089 9.80714989,20.1756532 L9.8,19.7983329 C9.8,14.2730045 14.3700174,9.8 20,9.8 Z M20,11.2 C15.1365724,11.2 11.2,15.0530063 11.2,19.7983329 C11.2,21.6384229 11.7922255,23.3893508 12.8759186,24.8453165 L13.0610907,25.0940992 L13.001223,25.3983974 C12.8243697,26.2973155 12.5137714,27.4099145 12.0719423,28.73207 L12.064,28.754 L12.2244984,28.7405395 C13.2682683,28.6413859 14.1190062,28.4334572 14.7754263,28.1231964 L14.9665215,28.0270547 C15.164827,27.9208723 15.3780604,27.7932923 15.605736,27.6441968 L15.9287098,27.4326945 L16.2799121,27.5930136 C17.4341359,28.1199012 18.6962936,28.3966657 20,28.3966657 C24.8634276,28.3966657 28.8,24.5436594 28.8,19.7983329 C28.8,15.0530063 24.8634276,11.2 20,11.2 Z" id="Shape---" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M20.2258661,25.6815247 C20.5071894,25.6815247 20.874026,25.4574745 20.874026,25.0631159 L20.874026,24.4093038 C22.5557139,24.2186972 23.4454545,23.1802196 23.4454545,21.6685117 C23.4454545,20.3671283 22.7015108,19.5784112 21.1573587,19.2234884 L19.8882782,18.9211469 C19.0943214,18.7371128 18.7067205,18.3558996 18.7067205,17.7972249 C18.7067205,17.1268153 19.2568638,16.6404397 20.1195884,16.6404397 C20.8197708,16.6404397 21.3073978,16.8902001 21.8512894,17.5277465 C22.126361,17.8300881 22.3389164,17.941823 22.6264913,17.941823 C22.9765824,17.941823 23.2454024,17.68549 23.2454024,17.3042767 C23.2454024,16.9362087 23.0390987,16.5352774 22.6890076,16.1737821 C22.2263871,15.713697 21.7022327,15.4113555 20.9207792,15.3061932 L20.9207792,14.6509738 C20.9207792,14.2631879 20.5009377,13.9363049 20.2133629,13.9363049 C19.9320396,13.9363049 19.5493506,14.2566152 19.5493506,14.6509738 L19.5493506,15.2864752 C17.930179,15.4442187 17.0312842,16.4629784 17.0312842,17.9221051 C17.0312842,19.1971979 17.7752279,20.0450688 19.2005991,20.3802736 L20.4696796,20.6891878 C21.3949206,20.9192303 21.7762699,21.2610078 21.7762699,21.8394004 C21.7762699,22.5886817 21.219875,23.061912 20.2258661,23.061912 C19.4819224,23.061912 18.8630112,22.766143 18.3003647,22.1285967 C17.9815316,21.7933919 17.8064861,21.7210928 17.5689242,21.7210928 C17.1875749,21.7210928 16.9,21.9774259 16.9,22.417793 C16.9,22.8055789 17.1125554,23.2065101 17.4939047,23.5548602 C17.9940349,24.0346632 18.7115432,24.3238594 19.5805195,24.4093038 L19.5805195,25.0565432 C19.5805195,25.4509018 19.9382912,25.6815247 20.2258661,25.6815247 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
BIN
Telegram/Resources/icons/chat/large_messages.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/chat/large_messages@2x.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/chat/large_messages@3x.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
7
Telegram/Resources/icons/chat/paid_approve.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Icon / Filled / paid_approve</title>
|
||||||
|
<g id="Icon-/-Filled-/-paid_approve" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M12,4.5 C16.14,4.5 19.5,7.86 19.5,12 C19.5,16.14 16.14,19.5 12,19.5 C7.86,19.5 4.5,16.14 4.5,12 C4.5,7.86 7.86,4.5 12,4.5 Z M15.7577636,9.89127556 C15.4992394,9.62810439 15.0763217,9.62433727 14.8131506,9.88286145 L10.7479688,13.8761719 L9.18684944,12.3424898 C8.92367827,12.0839656 8.50076063,12.0877327 8.24223645,12.3509039 C7.98371227,12.6140751 7.98747939,13.0369927 8.25065056,13.2955169 L10.204967,15.2153247 C10.5064723,15.5115061 10.9896874,15.5115061 11.2911927,15.2153247 L15.7493494,10.8358886 C16.0125206,10.5773644 16.0162877,10.1544467 15.7577636,9.89127556 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 999 B |
7
Telegram/Resources/icons/chat/paid_decline.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Icon / Filled / paid_decline</title>
|
||||||
|
<g id="Icon-/-Filled-/-paid_decline" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M12,4.5 C16.1421356,4.5 19.5,7.85786438 19.5,12 C19.5,16.1421356 16.1421356,19.5 12,19.5 C7.85786438,19.5 4.5,16.1421356 4.5,12 C4.5,7.85786438 7.85786438,4.5 12,4.5 Z M9.73375908,8.63964245 C9.43162712,8.33751049 8.94177442,8.33751049 8.63964245,8.63964245 L8.59853606,8.68404063 C8.33819559,8.98800138 8.35189772,9.44601435 8.63964245,9.73375908 L10.9059783,12 L8.63964245,14.2662409 C8.33751049,14.5683729 8.33751049,15.0582256 8.63964245,15.3603575 L8.68404063,15.4014639 C8.98800138,15.6618044 9.44601435,15.6481023 9.73375908,15.3603575 L12,13.0936701 L14.2662409,15.3603575 C14.5683729,15.6624895 15.0582256,15.6624895 15.3603575,15.3603575 L15.4014639,15.3159594 C15.6618044,15.0119986 15.6481023,14.5539856 15.3603575,14.2662409 L13.0936701,12 L15.3603575,9.73375908 C15.6624895,9.43162712 15.6624895,8.94177442 15.3603575,8.63964245 L15.3159594,8.59853606 C15.0119986,8.33819559 14.5539856,8.35189772 14.2662409,8.63964245 L12,10.9059783 L9.73375908,8.63964245 Z" id="Shape" fill="#FFFFFF"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
7
Telegram/Resources/icons/chat/paid_edit.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Icon / Filled / paid_edit</title>
|
||||||
|
<g id="Icon-/-Filled-/-paid_edit" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M13.5029106,7.64699152 C13.6056531,7.5472623 13.7697888,7.549705 13.869518,7.65244745 L16.2610253,10.1162122 C16.3593492,10.2175069 16.3585592,10.3788499 16.259248,10.4791768 L7.97687653,18.8462593 C7.87948817,18.9446437 7.74680196,19 7.6083679,19 L5.51851849,19 C5.23214864,19 5,18.7678513 5,18.4814815 L5,16.3683223 C5,16.2308422 5.05459809,16.0989896 5.15178971,16.0017551 L13.5029106,7.64699152 Z M16.0299869,5.19856593 C16.3408365,4.91998643 16.8161619,4.93645109 17.1069903,5.23587194 L18.7801411,6.95845528 C19.073802,7.26079221 19.0732123,7.74202152 18.7788114,8.04363789 L17.6449122,9.20518682 C17.5421105,9.3048549 17.3779763,9.30231456 17.2783082,9.19951281 L14.8031149,6.64621027 C14.707554,6.53957983 14.7165277,6.3756714 14.8231582,6.28011055 L16.0299869,5.19856593 Z" id="Shape" fill="#FFFFFF"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/menu/cancel_fee.png
Normal file
After Width: | Height: | Size: 865 B |
BIN
Telegram/Resources/icons/menu/cancel_fee@2x.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/menu/cancel_fee@3x.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/settings/earn.png
Normal file
After Width: | Height: | Size: 889 B |
BIN
Telegram/Resources/icons/settings/earn@2x.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/settings/earn@3x.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/settings/gift.png
Normal file
After Width: | Height: | Size: 574 B |
BIN
Telegram/Resources/icons/settings/gift@2x.png
Normal file
After Width: | Height: | Size: 1,016 B |
BIN
Telegram/Resources/icons/settings/gift@3x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
7
Telegram/Resources/icons/settings/premium/checklist.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Icon / Filled / checklist</title>
|
||||||
|
<g id="Icon-/-Filled-/-checklist" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M12,4 C16.416,4 20,7.584 20,12 C20,16.416 16.416,20 12,20 C7.584,20 4,16.416 4,12 C4,7.584 7.584,4 12,4 Z M16.4818432,9.54348643 C16.1872637,9.22288416 15.7053635,9.21829494 15.4054889,9.53323613 L10.7733532,14.3979954 L8.99451107,12.5296222 C8.69463652,12.2146811 8.21273632,12.2192703 7.91815685,12.5398725 C7.62357737,12.8604748 7.62786989,13.3756846 7.92774444,13.6906258 L10.1546212,16.0293878 C10.4981762,16.3902041 11.0487833,16.3902041 11.3923383,16.0293878 L16.4722556,10.6942397 C16.7721301,10.3792985 16.7764226,9.8640887 16.4818432,9.54348643 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 975 B |
|
@ -164,11 +164,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_chat_status_members_online" = "{members_count}, {online_count}";
|
"lng_chat_status_members_online" = "{members_count}, {online_count}";
|
||||||
"lng_chat_status_subscribers#one" = "{count} subscriber";
|
"lng_chat_status_subscribers#one" = "{count} subscriber";
|
||||||
"lng_chat_status_subscribers#other" = "{count} subscribers";
|
"lng_chat_status_subscribers#other" = "{count} subscribers";
|
||||||
|
"lng_chat_status_direct" = "Direct messages";
|
||||||
|
|
||||||
"lng_channel_status" = "channel";
|
"lng_channel_status" = "channel";
|
||||||
"lng_group_status" = "group";
|
"lng_group_status" = "group";
|
||||||
"lng_scam_badge" = "SCAM";
|
"lng_scam_badge" = "SCAM";
|
||||||
"lng_fake_badge" = "FAKE";
|
"lng_fake_badge" = "FAKE";
|
||||||
|
"lng_direct_badge" = "DIRECT";
|
||||||
|
|
||||||
"lng_remember" = "Remember this choice";
|
"lng_remember" = "Remember this choice";
|
||||||
|
|
||||||
|
@ -840,6 +842,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_suggestion_phone_number_about" = "Keep your number up to date to ensure you can always log into Telegram. {link}";
|
"lng_settings_suggestion_phone_number_about" = "Keep your number up to date to ensure you can always log into Telegram. {link}";
|
||||||
"lng_settings_suggestion_phone_number_about_link" = "https://telegram.org/faq#q-i-have-a-new-phone-number-what-do-i-do";
|
"lng_settings_suggestion_phone_number_about_link" = "https://telegram.org/faq#q-i-have-a-new-phone-number-what-do-i-do";
|
||||||
"lng_settings_suggestion_phone_number_change" = "Please change your phone number in the official Telegram app on your phone as soon as possible. {emoji}";
|
"lng_settings_suggestion_phone_number_change" = "Please change your phone number in the official Telegram app on your phone as soon as possible. {emoji}";
|
||||||
|
"lng_settings_suggestion_password_title" = "Your password";
|
||||||
|
"lng_settings_suggestion_password_about" = "Your account is protected by 2-Step Veritifaction. Do you still remember your password?";
|
||||||
|
"lng_settings_suggestion_password_yes" = "Yes, definitely";
|
||||||
|
"lng_settings_suggestion_password_no" = "Not sure";
|
||||||
|
"lng_settings_suggestion_password_step_input_title" = "Enter your password";
|
||||||
|
"lng_settings_suggestion_password_step_input_about" = "Do you still remember your password?";
|
||||||
|
"lng_settings_suggestion_password_step_finish_title" = "Perfect!";
|
||||||
|
"lng_settings_suggestion_password_step_finish_about" = "You still remember your password.";
|
||||||
|
|
||||||
"lng_settings_power_menu" = "Battery and Animations";
|
"lng_settings_power_menu" = "Battery and Animations";
|
||||||
"lng_settings_power_title" = "Power Usage";
|
"lng_settings_power_title" = "Power Usage";
|
||||||
|
@ -1152,6 +1162,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_proxy_menu_delete" = "Delete";
|
"lng_proxy_menu_delete" = "Delete";
|
||||||
"lng_proxy_menu_restore" = "Restore";
|
"lng_proxy_menu_restore" = "Restore";
|
||||||
"lng_proxy_edit_share" = "Share";
|
"lng_proxy_edit_share" = "Share";
|
||||||
|
"lng_proxy_edit_share_qr_box_title" = "Share proxy with QR code";
|
||||||
|
"lng_proxy_edit_share_list_button" = "Share Proxy List";
|
||||||
|
"lng_proxy_edit_share_list_toast" = "Proxy List copied to clipboard.";
|
||||||
"lng_proxy_address_label" = "Socket address";
|
"lng_proxy_address_label" = "Socket address";
|
||||||
"lng_proxy_credentials_optional" = "Credentials (optional)";
|
"lng_proxy_credentials_optional" = "Credentials (optional)";
|
||||||
"lng_proxy_credentials" = "Credentials";
|
"lng_proxy_credentials" = "Credentials";
|
||||||
|
@ -1190,6 +1203,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
|
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
|
||||||
"lng_settings_features" = "Telegram Features";
|
"lng_settings_features" = "Telegram Features";
|
||||||
"lng_settings_credits" = "My Stars";
|
"lng_settings_credits" = "My Stars";
|
||||||
|
"lng_settings_currency" = "My TON";
|
||||||
"lng_settings_logout" = "Log out";
|
"lng_settings_logout" = "Log out";
|
||||||
"lng_sure_logout" = "Are you sure you want to log out?";
|
"lng_sure_logout" = "Are you sure you want to log out?";
|
||||||
|
|
||||||
|
@ -1481,6 +1495,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_profile_hide_participants_about" = "Switch this on to hide the list of members in this group. Admins will remain visible.";
|
"lng_profile_hide_participants_about" = "Switch this on to hide the list of members in this group. Admins will remain visible.";
|
||||||
"lng_profile_view_channel" = "View Channel";
|
"lng_profile_view_channel" = "View Channel";
|
||||||
"lng_profile_view_discussion" = "View discussion";
|
"lng_profile_view_discussion" = "View discussion";
|
||||||
|
"lng_profile_direct_messages" = "Direct messages";
|
||||||
"lng_profile_join_channel" = "Join Channel";
|
"lng_profile_join_channel" = "Join Channel";
|
||||||
"lng_profile_join_group" = "Join Group";
|
"lng_profile_join_group" = "Join Group";
|
||||||
"lng_profile_apply_to_join_group" = "Apply to Join Group";
|
"lng_profile_apply_to_join_group" = "Apply to Join Group";
|
||||||
|
@ -1878,6 +1893,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_manage_linked_channel_posted" = "All new posts from this channel are forwarded to the group.";
|
"lng_manage_linked_channel_posted" = "All new posts from this channel are forwarded to the group.";
|
||||||
"lng_manage_discussion_group_warning" = "\"Chat history for new members\" will be switched to **Visible**.";
|
"lng_manage_discussion_group_warning" = "\"Chat history for new members\" will be switched to **Visible**.";
|
||||||
|
|
||||||
|
"lng_manage_monoforum" = "Direct Messages";
|
||||||
|
"lng_manage_monoforum_off" = "Off";
|
||||||
|
"lng_manage_monoforum_free" = "Free";
|
||||||
|
"lng_manage_monoforum_allow" = "Allow Channel Messages";
|
||||||
|
"lng_manage_monoforum_price" = "Price for each message";
|
||||||
|
"lng_manage_monoforum_about" = "Allow users to send messages to your channel, with the option to charge a fee for each message.";
|
||||||
|
"lng_manage_monoforum_price_about" = "Your channel will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||||
|
|
||||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||||
"lng_manage_history_visibility_shown" = "Visible";
|
"lng_manage_history_visibility_shown" = "Visible";
|
||||||
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
||||||
|
@ -2061,6 +2084,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_changed_title" = "{from} changed group name to «{title}»";
|
"lng_action_changed_title" = "{from} changed group name to «{title}»";
|
||||||
"lng_action_changed_title_channel" = "Channel name was changed to «{title}»";
|
"lng_action_changed_title_channel" = "Channel name was changed to «{title}»";
|
||||||
"lng_action_created_chat" = "{from} created the group «{title}»";
|
"lng_action_created_chat" = "{from} created the group «{title}»";
|
||||||
|
"lng_action_created_monoforum" = "Direct messages were enabled in this channel.";
|
||||||
"lng_action_ttl_changed" = "{from} set messages to auto-delete in {duration}";
|
"lng_action_ttl_changed" = "{from} set messages to auto-delete in {duration}";
|
||||||
"lng_action_ttl_changed_you" = "You set messages to auto-delete in {duration}";
|
"lng_action_ttl_changed_you" = "You set messages to auto-delete in {duration}";
|
||||||
"lng_action_ttl_changed_channel" = "Messages in this channel will be automatically deleted after {duration}";
|
"lng_action_ttl_changed_channel" = "Messages in this channel will be automatically deleted after {duration}";
|
||||||
|
@ -2168,6 +2192,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_gift_premium_months#other" = "{count} Months Premium";
|
"lng_action_gift_premium_months#other" = "{count} Months Premium";
|
||||||
"lng_action_gift_premium_about" = "Subscription for exclusive Telegram features.";
|
"lng_action_gift_premium_about" = "Subscription for exclusive Telegram features.";
|
||||||
"lng_action_gift_refunded" = "This gift was downgraded because a request to refund the payment related to this gift was made, and the money was returned.";
|
"lng_action_gift_refunded" = "This gift was downgraded because a request to refund the payment related to this gift was made, and the money was returned.";
|
||||||
|
"lng_action_gift_got_ton" = "Use TON to suggest posts to channels.";
|
||||||
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
|
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
|
||||||
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
|
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
|
||||||
"lng_action_suggested_photo_button" = "View Photo";
|
"lng_action_suggested_photo_button" = "View Photo";
|
||||||
|
@ -2235,6 +2260,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_message_price_free" = "Messages are now free in this group.";
|
"lng_action_message_price_free" = "Messages are now free in this group.";
|
||||||
"lng_action_message_price_paid#one" = "Messages now cost {count} Star each in this group.";
|
"lng_action_message_price_paid#one" = "Messages now cost {count} Star each in this group.";
|
||||||
"lng_action_message_price_paid#other" = "Messages now cost {count} Stars each in this group.";
|
"lng_action_message_price_paid#other" = "Messages now cost {count} Stars each in this group.";
|
||||||
|
"lng_action_direct_messages_enabled" = "Channel enabled Direct Messages.";
|
||||||
|
"lng_action_direct_messages_paid#one" = "Channel allows Direct Messages for {count} Star each.";
|
||||||
|
"lng_action_direct_messages_paid#other" = "Channel allows Direct Messages for {count} Stars each.";
|
||||||
|
"lng_action_direct_messages_disabled" = "Channel disabled Direct Messages.";
|
||||||
|
"lng_action_todo_marked_done" = "{from} marked {tasks} as done.";
|
||||||
|
"lng_action_todo_marked_done_self" = "You marked {tasks} as done.";
|
||||||
|
"lng_action_todo_marked_not_done" = "{from} marked {tasks} as not done.";
|
||||||
|
"lng_action_todo_marked_not_done_self" = "You marked {tasks} as not done.";
|
||||||
|
"lng_action_todo_added" = "{from} added {tasks} to the list.";
|
||||||
|
"lng_action_todo_added_self" = "You added {tasks} to the list.";
|
||||||
|
"lng_action_todo_tasks_fallback#one" = "task";
|
||||||
|
"lng_action_todo_tasks_fallback#other" = "{count} tasks";
|
||||||
|
"lng_action_todo_tasks_and_one" = "{tasks}, {task}";
|
||||||
|
"lng_action_todo_tasks_and_last" = "{tasks} and {task}";
|
||||||
|
"lng_action_suggest_success_stars#one" = "{from} has received {count} Star for publishing post.";
|
||||||
|
"lng_action_suggest_success_stars#other" = "{from} has received {count} Stars for publishing post.";
|
||||||
|
"lng_action_suggest_success_ton#one" = "{from} has received {count} TON for publishing post.";
|
||||||
|
"lng_action_suggest_success_ton#other" = "{from} has received {count} TON for publishing post.";
|
||||||
|
"lng_action_suggest_refund_user" = "User refunded the Stars so that post was deleted.";
|
||||||
|
"lng_action_suggest_refund_admin" = "Admin deleted the post early so that the price was refunded to the user.";
|
||||||
|
"lng_action_post_rejected" = "The post was rejected.";
|
||||||
|
"lng_action_not_enough_funds" = "Transaction failed.";
|
||||||
"lng_you_paid_stars#one" = "You paid {count} Star.";
|
"lng_you_paid_stars#one" = "You paid {count} Star.";
|
||||||
"lng_you_paid_stars#other" = "You paid {count} Stars.";
|
"lng_you_paid_stars#other" = "You paid {count} Stars.";
|
||||||
|
|
||||||
|
@ -2602,6 +2649,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_premium_summary_about_effects" = "Add over 500 animated effects to private messages.";
|
"lng_premium_summary_about_effects" = "Add over 500 animated effects to private messages.";
|
||||||
"lng_premium_summary_subtitle_filter_tags" = "Tag Your Chats";
|
"lng_premium_summary_subtitle_filter_tags" = "Tag Your Chats";
|
||||||
"lng_premium_summary_about_filter_tags" = "Display folder names for each chat in the chat list.";
|
"lng_premium_summary_about_filter_tags" = "Display folder names for each chat in the chat list.";
|
||||||
|
"lng_premium_summary_subtitle_todo_lists" = "Checklists";
|
||||||
|
"lng_premium_summary_about_todo_lists" = "Plan, assign, and complete tasks - seamlessly and efficiently.";
|
||||||
"lng_premium_summary_bottom_subtitle" = "About Telegram Premium";
|
"lng_premium_summary_bottom_subtitle" = "About Telegram Premium";
|
||||||
"lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
|
"lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
|
||||||
"lng_premium_summary_button" = "Subscribe for {cost} per month";
|
"lng_premium_summary_button" = "Subscribe for {cost} per month";
|
||||||
|
@ -2732,6 +2781,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"lng_credits_summary_title" = "Telegram Stars";
|
"lng_credits_summary_title" = "Telegram Stars";
|
||||||
"lng_credits_summary_about" = "Buy Stars to unlock content and services in miniapps on Telegram.";
|
"lng_credits_summary_about" = "Buy Stars to unlock content and services in miniapps on Telegram.";
|
||||||
|
"lng_credits_currency_summary_title" = "TON Balance";
|
||||||
|
"lng_credits_currency_summary_about" = "Offer TON to submit post suggestions to channels on Telegram.";
|
||||||
|
"lng_credits_currency_summary_subtitle" = "You can withdraw your TON using Fragment.";
|
||||||
|
"lng_credits_currency_summary_in_button" = "Top-up via Fragment";
|
||||||
|
"lng_credits_currency_summary_in_subtitle" = "You can top-up your TON balance via Fragment.";
|
||||||
"lng_credits_summary_options_subtitle" = "Choose package";
|
"lng_credits_summary_options_subtitle" = "Choose package";
|
||||||
"lng_credits_summary_options_credits#one" = "{count} Star";
|
"lng_credits_summary_options_credits#one" = "{count} Star";
|
||||||
"lng_credits_summary_options_credits#other" = "{count} Stars";
|
"lng_credits_summary_options_credits#other" = "{count} Stars";
|
||||||
|
@ -2755,8 +2809,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_premium_gift_duration" = "Duration";
|
"lng_credits_premium_gift_duration" = "Duration";
|
||||||
"lng_credits_more_options" = "More Options";
|
"lng_credits_more_options" = "More Options";
|
||||||
"lng_credits_balance_me" = "your balance";
|
"lng_credits_balance_me" = "your balance";
|
||||||
"lng_credits_buy_button" = "Buy More Stars";
|
"lng_credits_balance_me_count" = "Your balance: {emoji} {amount}";
|
||||||
|
"lng_credits_buy_button" = "Top Up Balance";
|
||||||
|
"lng_credits_topup_button" = "{emoji} Top Up Balance";
|
||||||
|
"lng_credits_buy_button_short" = "Top Up";
|
||||||
|
"lng_credits_stats_button_short" = "Stats";
|
||||||
|
"lng_credits_stats_button" = "View Statistics";
|
||||||
"lng_credits_gift_button" = "Gift Stars to Friends";
|
"lng_credits_gift_button" = "Gift Stars to Friends";
|
||||||
|
"lng_credits_earn_button" = "Earn Stars from Mini Apps";
|
||||||
"lng_credits_box_out_title" = "Confirm Your Purchase";
|
"lng_credits_box_out_title" = "Confirm Your Purchase";
|
||||||
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
|
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
|
||||||
"lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?";
|
"lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?";
|
||||||
|
@ -2803,6 +2863,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_box_history_entry_gift_converted" = "Converted Gift";
|
"lng_credits_box_history_entry_gift_converted" = "Converted Gift";
|
||||||
"lng_credits_box_history_entry_gift_transfer" = "Gift Transfer";
|
"lng_credits_box_history_entry_gift_transfer" = "Gift Transfer";
|
||||||
"lng_credits_box_history_entry_gift_unavailable" = "Unavailable";
|
"lng_credits_box_history_entry_gift_unavailable" = "Unavailable";
|
||||||
|
"lng_credits_box_history_entry_gift_released" = "released by {name}";
|
||||||
"lng_credits_box_history_entry_gift_sold_out" = "This gift has sold out";
|
"lng_credits_box_history_entry_gift_sold_out" = "This gift has sold out";
|
||||||
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
|
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
|
||||||
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
|
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
|
||||||
|
@ -2810,6 +2871,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_box_history_entry_gift_examples" = "Examples";
|
"lng_credits_box_history_entry_gift_examples" = "Examples";
|
||||||
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
||||||
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
|
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
|
||||||
|
"lng_credits_box_history_entry_currency_in" = "TON Top-Up";
|
||||||
"lng_credits_box_history_entry_api" = "Paid Broadcast";
|
"lng_credits_box_history_entry_api" = "Paid Broadcast";
|
||||||
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
|
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
|
||||||
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
|
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
|
||||||
|
@ -2873,6 +2935,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
|
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
|
||||||
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
|
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
|
||||||
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
|
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
|
||||||
|
"lng_credits_small_balance_for_suggest" = "Buy **Stars** to suggest post to {channel}.";
|
||||||
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
|
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
|
||||||
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
||||||
"lng_credits_enough" = "You have enough stars at the moment. {link}";
|
"lng_credits_enough" = "You have enough stars at the moment. {link}";
|
||||||
|
@ -3196,6 +3259,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_feature_transcribe" = "Voice-to-Text Conversion";
|
"lng_feature_transcribe" = "Voice-to-Text Conversion";
|
||||||
"lng_feature_autotranslate" = "Autotranslation of Messages";
|
"lng_feature_autotranslate" = "Autotranslation of Messages";
|
||||||
|
|
||||||
|
"lng_edit_topics_enable" = "Enable Topics";
|
||||||
|
"lng_edit_topics_about" = "The group chat will be divided into topics created by admins or users.";
|
||||||
|
"lng_edit_topics_layout" = "Topics layout";
|
||||||
|
"lng_edit_topics_layout_about" = "Choose how topics appear for all members.";
|
||||||
|
"lng_edit_topics_tabs" = "Tabs";
|
||||||
|
"lng_edit_topics_list" = "List";
|
||||||
|
|
||||||
"lng_giveaway_new_title" = "Boosts via Gifts";
|
"lng_giveaway_new_title" = "Boosts via Gifts";
|
||||||
"lng_giveaway_new_about" = "Get more boosts for your channel by gifting Premium to your subscribers.";
|
"lng_giveaway_new_about" = "Get more boosts for your channel by gifting Premium to your subscribers.";
|
||||||
"lng_giveaway_new_about_group" = "Get more boosts for your group by gifting Premium to your members.";
|
"lng_giveaway_new_about_group" = "Get more boosts for your group by gifting Premium to your members.";
|
||||||
|
@ -3432,6 +3502,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_gift_stars_incoming" = "Use Stars to unlock content and services on Telegram.";
|
"lng_gift_stars_incoming" = "Use Stars to unlock content and services on Telegram.";
|
||||||
"lng_gift_until" = "Until";
|
"lng_gift_until" = "Until";
|
||||||
|
|
||||||
|
"lng_gift_ton_amount#one" = "{count} TON";
|
||||||
|
"lng_gift_ton_amount#other" = "{count} TON";
|
||||||
|
|
||||||
"lng_gift_premium_or_stars" = "Gift Premium or Stars";
|
"lng_gift_premium_or_stars" = "Gift Premium or Stars";
|
||||||
"lng_gift_premium_subtitle" = "Gift Premium";
|
"lng_gift_premium_subtitle" = "Gift Premium";
|
||||||
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
|
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
|
||||||
|
@ -3509,12 +3582,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
|
"lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
|
||||||
"lng_gift_channel_title" = "Send a Gift";
|
"lng_gift_channel_title" = "Send a Gift";
|
||||||
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
|
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
|
||||||
|
"lng_gift_released_by" = "Released by {name}";
|
||||||
"lng_gift_unique_owner" = "Owner";
|
"lng_gift_unique_owner" = "Owner";
|
||||||
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
||||||
"lng_gift_unique_status" = "Status";
|
"lng_gift_unique_status" = "Status";
|
||||||
"lng_gift_unique_status_non" = "Non-Unique";
|
"lng_gift_unique_status_non" = "Non-Unique";
|
||||||
"lng_gift_unique_status_upgrade" = "upgrade";
|
"lng_gift_unique_status_upgrade" = "upgrade";
|
||||||
"lng_gift_unique_number" = "Collectible #{index}";
|
"lng_gift_unique_number" = "Collectible #{index}";
|
||||||
|
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
|
||||||
"lng_gift_unique_model" = "Model";
|
"lng_gift_unique_model" = "Model";
|
||||||
"lng_gift_unique_backdrop" = "Backdrop";
|
"lng_gift_unique_backdrop" = "Backdrop";
|
||||||
"lng_gift_unique_symbol" = "Symbol";
|
"lng_gift_unique_symbol" = "Symbol";
|
||||||
|
@ -3758,6 +3833,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
|
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
|
||||||
"lng_in_dlg_poll" = "Poll";
|
"lng_in_dlg_poll" = "Poll";
|
||||||
"lng_in_dlg_story" = "Story";
|
"lng_in_dlg_story" = "Story";
|
||||||
|
"lng_in_dlg_todo_list" = "Checklist";
|
||||||
"lng_in_dlg_story_expired" = "Expired story";
|
"lng_in_dlg_story_expired" = "Expired story";
|
||||||
"lng_in_dlg_media_count#one" = "{count} media";
|
"lng_in_dlg_media_count#one" = "{count} media";
|
||||||
"lng_in_dlg_media_count#other" = "{count} media";
|
"lng_in_dlg_media_count#other" = "{count} media";
|
||||||
|
@ -3847,7 +3923,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_send_anonymous_ph" = "Send anonymously...";
|
"lng_send_anonymous_ph" = "Send anonymously...";
|
||||||
"lng_story_reply_ph" = "Reply privately...";
|
"lng_story_reply_ph" = "Reply privately...";
|
||||||
"lng_story_comment_ph" = "Comment story...";
|
"lng_story_comment_ph" = "Comment story...";
|
||||||
"lng_message_paid_ph" = "Message for {amount}";
|
"lng_message_stars_ph#one" = "Message for {count} Star";
|
||||||
|
"lng_message_stars_ph#other" = "Message for {count} Stars";
|
||||||
"lng_send_text_no" = "Text not allowed.";
|
"lng_send_text_no" = "Text not allowed.";
|
||||||
"lng_send_text_no_about" = "The admins of this group only allow sending {types}.";
|
"lng_send_text_no_about" = "The admins of this group only allow sending {types}.";
|
||||||
"lng_send_text_type_and_last" = "{types} and {last}";
|
"lng_send_text_type_and_last" = "{types} and {last}";
|
||||||
|
@ -4186,6 +4263,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_edit_msg" = "Edit";
|
"lng_context_edit_msg" = "Edit";
|
||||||
"lng_context_add_factcheck" = "Add Fact Check";
|
"lng_context_add_factcheck" = "Add Fact Check";
|
||||||
"lng_context_edit_factcheck" = "Edit Fact Check";
|
"lng_context_edit_factcheck" = "Edit Fact Check";
|
||||||
|
"lng_context_add_offer" = "Add Offer";
|
||||||
"lng_context_forward_msg" = "Forward";
|
"lng_context_forward_msg" = "Forward";
|
||||||
"lng_context_send_now_msg" = "Send Now";
|
"lng_context_send_now_msg" = "Send Now";
|
||||||
"lng_context_reschedule" = "Reschedule";
|
"lng_context_reschedule" = "Reschedule";
|
||||||
|
@ -4197,6 +4275,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_pin_msg" = "Pin";
|
"lng_context_pin_msg" = "Pin";
|
||||||
"lng_context_unpin_msg" = "Unpin";
|
"lng_context_unpin_msg" = "Unpin";
|
||||||
"lng_context_cancel_upload" = "Cancel Upload";
|
"lng_context_cancel_upload" = "Cancel Upload";
|
||||||
|
"lng_context_upload_edit_caption" = "Edit Caption";
|
||||||
"lng_context_copy_selected" = "Copy Selected Text";
|
"lng_context_copy_selected" = "Copy Selected Text";
|
||||||
"lng_context_copy_selected_items" = "Copy Selected as Text";
|
"lng_context_copy_selected_items" = "Copy Selected as Text";
|
||||||
"lng_context_forward_selected" = "Forward Selected";
|
"lng_context_forward_selected" = "Forward Selected";
|
||||||
|
@ -4220,6 +4299,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_seen_reacted#other" = "{count} Reacted";
|
"lng_context_seen_reacted#other" = "{count} Reacted";
|
||||||
"lng_context_seen_reacted_none" = "Nobody Reacted";
|
"lng_context_seen_reacted_none" = "Nobody Reacted";
|
||||||
"lng_context_seen_reacted_all" = "Show All Reactions";
|
"lng_context_seen_reacted_all" = "Show All Reactions";
|
||||||
|
"lng_context_sent_by" = "Sent by {user}";
|
||||||
"lng_context_set_as_quick" = "Set As Quick";
|
"lng_context_set_as_quick" = "Set As Quick";
|
||||||
"lng_context_filter_by_tag" = "Filter by Tag";
|
"lng_context_filter_by_tag" = "Filter by Tag";
|
||||||
"lng_context_tag_add_name" = "Add Name";
|
"lng_context_tag_add_name" = "Add Name";
|
||||||
|
@ -4235,6 +4315,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_edit_shortcut" = "Edit Shortcut";
|
"lng_context_edit_shortcut" = "Edit Shortcut";
|
||||||
"lng_context_delete_shortcut" = "Delete Quick Reply";
|
"lng_context_delete_shortcut" = "Delete Quick Reply";
|
||||||
"lng_context_gift_send" = "Send Another Gift";
|
"lng_context_gift_send" = "Send Another Gift";
|
||||||
|
"lng_context_charge_fee" = "Charge Fee";
|
||||||
|
"lng_context_remove_fee" = "Remove Fee";
|
||||||
|
"lng_context_fee_now" = "{name} pays {amount} per message.";
|
||||||
|
"lng_context_fee_free" = "{name} can send messages for free.";
|
||||||
|
|
||||||
"lng_add_tag_about" = "Tag this message with an emoji for quick search.";
|
"lng_add_tag_about" = "Tag this message with an emoji for quick search.";
|
||||||
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
|
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
|
||||||
|
@ -4367,6 +4451,113 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_preview_reply_to" = "Reply to {name}";
|
"lng_preview_reply_to" = "Reply to {name}";
|
||||||
"lng_preview_reply_to_quote" = "Reply to quote from {name}";
|
"lng_preview_reply_to_quote" = "Reply to quote from {name}";
|
||||||
|
|
||||||
|
"lng_suggest_bar_title" = "Suggest a Post Below";
|
||||||
|
"lng_suggest_bar_text" = "Click to offer a price for publishing.";
|
||||||
|
"lng_suggest_bar_priced" = "{amount} for publishing anytime.";
|
||||||
|
"lng_suggest_bar_dated" = "Publish on {date}";
|
||||||
|
"lng_suggest_options_title" = "Suggest a Message";
|
||||||
|
"lng_suggest_options_change" = "Suggest Changes";
|
||||||
|
"lng_suggest_options_stars_offer" = "Offer Stars";
|
||||||
|
"lng_suggest_options_stars_request" = "Request Stars";
|
||||||
|
"lng_suggest_options_stars_price" = "Enter Price in Stars";
|
||||||
|
"lng_suggest_options_stars_price_about" = "Choose how many Stars to pay to publish this message.";
|
||||||
|
"lng_suggest_options_ton_offer" = "Offer TON";
|
||||||
|
"lng_suggest_options_ton_request" = "Request TON";
|
||||||
|
"lng_suggest_options_ton_price" = "Enter Price in TON";
|
||||||
|
"lng_suggest_options_ton_price_about" = "Choose how many TON to pay to publish this message.";
|
||||||
|
"lng_suggest_options_date" = "Time";
|
||||||
|
"lng_suggest_options_date_any" = "Anytime";
|
||||||
|
"lng_suggest_options_date_publish" = "Publish";
|
||||||
|
"lng_suggest_options_date_now" = "Publish Now";
|
||||||
|
"lng_suggest_options_date_about" = "Select the date and time you want the message to be published. The post will remain available for at least 24 hours from this date.";
|
||||||
|
"lng_suggest_options_you_get_stars#one" = "You will receive {count} Star ({percent}) for publishing this post.";
|
||||||
|
"lng_suggest_options_you_get_stars#other" = "You will receive {count} Stars ({percent}) for publishing this post.";
|
||||||
|
"lng_suggest_options_you_get_ton#one" = "You will receive {count} TON ({percent}) for publishing this post.";
|
||||||
|
"lng_suggest_options_you_get_ton#other" = "You will receive {count} TON ({percent}) for publishing this post.";
|
||||||
|
"lng_suggest_options_stars_warning" = "Transactions in **Stars** may be reversed by the payment provider within **21** days. Only accept Stars from people you trust.";
|
||||||
|
"lng_suggest_options_offer" = "Offer {amount}";
|
||||||
|
"lng_suggest_options_offer_free" = "Offer for Free";
|
||||||
|
"lng_suggest_options_update" = "Update Terms";
|
||||||
|
"lng_suggest_options_update_date" = "Update Time";
|
||||||
|
|
||||||
|
"lng_suggest_action_decline" = "Decline";
|
||||||
|
"lng_suggest_action_accept" = "Accept";
|
||||||
|
"lng_suggest_action_change" = "Suggest Changes";
|
||||||
|
"lng_suggest_action_your" = "You suggest to post this message.";
|
||||||
|
"lng_suggest_action_his" = "{from} suggests to post this message.";
|
||||||
|
"lng_suggest_action_price_label" = "Price";
|
||||||
|
"lng_suggest_action_price_free" = "Free";
|
||||||
|
"lng_suggest_action_time_label" = "Time";
|
||||||
|
"lng_suggest_action_time_any" = "Anytime";
|
||||||
|
"lng_suggest_action_agreement" = "Agreement reached!";
|
||||||
|
"lng_suggest_action_agree_date" = "The post will be automatically published on {channel} {date}.";
|
||||||
|
"lng_suggest_action_your_charged_stars#one" = "You have been charged **{count} Star**.";
|
||||||
|
"lng_suggest_action_your_charged_stars#other" = "You have been charged **{count} Stars**.";
|
||||||
|
"lng_suggest_action_your_charged_ton#one" = "You have been charged **{count} TON**.";
|
||||||
|
"lng_suggest_action_your_charged_ton#other" = "You have been charged **{count} TON**.";
|
||||||
|
"lng_suggest_action_his_charged_stars#one" = "{from} has been charged **{count} Star**.";
|
||||||
|
"lng_suggest_action_his_charged_stars#other" = "{from} has been charged **{count} Stars**.";
|
||||||
|
"lng_suggest_action_his_charged_ton#one" = "{from} has been charged **{count} TON**.";
|
||||||
|
"lng_suggest_action_his_charged_ton#other" = "{from} has been charged **{count} TON**.";
|
||||||
|
"lng_suggest_action_agree_receive_stars" = "{channel} will receive the Stars once the post has been live for 24 hours.";
|
||||||
|
"lng_suggest_action_agree_receive_ton" = "{channel} will receive TON once the post has been live for 24 hours.";
|
||||||
|
"lng_suggest_action_agree_removed_stars" = "If {channel} removes the post before it has been live for 24 hours, the Stars will be refunded.";
|
||||||
|
"lng_suggest_action_agree_removed_ton" = "If {channel} removes the post before it has been live for 24 hours, TON will be refunded.";
|
||||||
|
"lng_suggest_action_your_not_enough_stars" = "**Transaction failed** because you didn't have enough Stars.";
|
||||||
|
"lng_suggest_action_your_not_enough_ton" = "**Transaction failed** because you didn't have enough TON.";
|
||||||
|
"lng_suggest_action_his_not_enough_stars" = "**Transaction failed** because the user didn't have enough Stars.";
|
||||||
|
"lng_suggest_action_his_not_enough_ton" = "**Transaction failed** because the user didn't have enough TON.";
|
||||||
|
"lng_suggest_action_declined" = "{from} rejected the message.";
|
||||||
|
"lng_suggest_action_declined_reason" = "{from} rejected the message with the comment.";
|
||||||
|
"lng_suggest_change_price" = "{from} suggests a new price for the message.";
|
||||||
|
"lng_suggest_change_time" = "{from} suggests a new time for the message.";
|
||||||
|
"lng_suggest_change_price_time" = "{from} suggests a new price and time for the message.";
|
||||||
|
"lng_suggest_change_content" = "{from} suggests changes for the message.";
|
||||||
|
"lng_suggest_change_price_label" = "New Price";
|
||||||
|
"lng_suggest_change_time_label" = "New Time";
|
||||||
|
"lng_suggest_change_text_label" = "Check the suggested message below";
|
||||||
|
"lng_suggest_menu_edit_message" = "Edit Message";
|
||||||
|
"lng_suggest_menu_edit_price" = "Edit Price";
|
||||||
|
"lng_suggest_menu_edit_time" = "Edit Time";
|
||||||
|
"lng_suggest_decline_title" = "Decline";
|
||||||
|
"lng_suggest_decline_text" = "Do you want to decline publishing this post from {from}?";
|
||||||
|
"lng_suggest_decline_text_to" = "Do you want to decline publishing this post to {channel}?";
|
||||||
|
"lng_suggest_decline_reason" = "Add a reason (optional)";
|
||||||
|
"lng_suggest_accept_title" = "Accept Terms";
|
||||||
|
"lng_suggest_accept_text" = "Do you want to publish this post from {from}?";
|
||||||
|
"lng_suggest_accept_text_to" = "Do you want to publish this post to {channel}?";
|
||||||
|
"lng_suggest_accept_receive_stars#one" = "{channel} will receive **{count} Star** ({percent}) for publishing {date}.";
|
||||||
|
"lng_suggest_accept_receive_stars#other" = "{channel} will receive **{count} Stars** ({percent}) for publishing {date}.";
|
||||||
|
"lng_suggest_accept_receive_ton#one" = "{channel} will receive **{count} TON** ({percent}) for publishing {date}.";
|
||||||
|
"lng_suggest_accept_receive_ton#other" = "{channel} will receive **{count} TON** ({percent}) for publishing {date}.";
|
||||||
|
"lng_suggest_accept_receive_now_stars#one" = "{channel} will receive **{count} Star** ({percent}) for publishing right now.";
|
||||||
|
"lng_suggest_accept_receive_now_stars#other" = "{channel} will receive **{count} Stars** ({percent}) for publishing right now.";
|
||||||
|
"lng_suggest_accept_receive_now_ton#one" = "{channel} will receive **{count} TON** ({percent}) for publishing right now.";
|
||||||
|
"lng_suggest_accept_receive_now_ton#other" = "{channel} will receive **{count} TON** ({percent}) for publishing right now.";
|
||||||
|
"lng_suggest_accept_receive_if" = "It must remain visible for at least **24** hours after publication.";
|
||||||
|
"lng_suggest_accept_pay_stars#one" = "You will pay **{count} Star** for publishing {date}.";
|
||||||
|
"lng_suggest_accept_pay_stars#other" = "You will pay **{count} Stars** for publishing {date}.";
|
||||||
|
"lng_suggest_accept_pay_ton#one" = "You will pay **{count} TON** for publishing {date}.";
|
||||||
|
"lng_suggest_accept_pay_ton#other" = "You will pay **{count} TON** for publishing {date}.";
|
||||||
|
"lng_suggest_accept_pay_now_stars#one" = "You will pay **{count} Star** for publishing right now.";
|
||||||
|
"lng_suggest_accept_pay_now_stars#other" = "You will pay **{count} Stars** for publishing right now.";
|
||||||
|
"lng_suggest_accept_pay_now_ton#one" = "You will pay **{count} TON** for publishing right now.";
|
||||||
|
"lng_suggest_accept_pay_now_ton#other" = "You will pay **{count} TON** for publishing right now.";
|
||||||
|
"lng_suggest_accept_send" = "Publish";
|
||||||
|
"lng_suggest_stars_amount#one" = "{count} Star";
|
||||||
|
"lng_suggest_stars_amount#other" = "{count} Stars";
|
||||||
|
"lng_suggest_ton_amount#one" = "{count} TON";
|
||||||
|
"lng_suggest_ton_amount#other" = "{count} TON";
|
||||||
|
"lng_suggest_warn_title_stars" = "Stars will be lost";
|
||||||
|
"lng_suggest_warn_title_ton" = "TON will be lost";
|
||||||
|
"lng_suggest_warn_text_stars" = "You won't receive **Stars** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published.";
|
||||||
|
"lng_suggest_warn_text_ton" = "You won't receive **TON** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published.";
|
||||||
|
"lng_suggest_warn_delete_anyway" = "Delete Anyway";
|
||||||
|
"lng_suggest_low_ton_title" = "{amount} TON Needed";
|
||||||
|
"lng_suggest_low_ton_text" = "You can add funds to your balance via the third-party platform Fragment.";
|
||||||
|
"lng_suggest_low_ton_fragment" = "Add Funds via Fragment";
|
||||||
|
"lng_suggest_low_ton_fragment_url" = "https://fragment.com/ads/topup";
|
||||||
|
|
||||||
"lng_reply_in_another_title" = "Reply in...";
|
"lng_reply_in_another_title" = "Reply in...";
|
||||||
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
||||||
"lng_reply_in_author" = "Message author";
|
"lng_reply_in_author" = "Message author";
|
||||||
|
@ -5130,6 +5321,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_payment_bar_button" = "Remove Fee";
|
"lng_payment_bar_button" = "Remove Fee";
|
||||||
"lng_payment_refund_title" = "Remove Fee";
|
"lng_payment_refund_title" = "Remove Fee";
|
||||||
"lng_payment_refund_text" = "Are you sure you want to allow {name} to message you for free?";
|
"lng_payment_refund_text" = "Are you sure you want to allow {name} to message you for free?";
|
||||||
|
"lng_payment_refund_channel" = "Do you want to allow {name} to message the channel for free?";
|
||||||
"lng_payment_refund_also#one" = "Refund already paid {count} Star";
|
"lng_payment_refund_also#one" = "Refund already paid {count} Star";
|
||||||
"lng_payment_refund_also#other" = "Refund already paid {count} Stars";
|
"lng_payment_refund_also#other" = "Refund already paid {count} Stars";
|
||||||
"lng_payment_refund_confirm" = "Confirm";
|
"lng_payment_refund_confirm" = "Confirm";
|
||||||
|
@ -5159,6 +5351,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_rights_channel_edit_stories" = "Edit stories of others";
|
"lng_rights_channel_edit_stories" = "Edit stories of others";
|
||||||
"lng_rights_channel_delete_stories" = "Delete stories of others";
|
"lng_rights_channel_delete_stories" = "Delete stories of others";
|
||||||
"lng_rights_channel_manage_calls" = "Manage live streams";
|
"lng_rights_channel_manage_calls" = "Manage live streams";
|
||||||
|
"lng_rights_channel_manage_direct" = "Manage direct messages";
|
||||||
"lng_rights_group_info" = "Change group info";
|
"lng_rights_group_info" = "Change group info";
|
||||||
"lng_rights_group_ban" = "Ban users";
|
"lng_rights_group_ban" = "Ban users";
|
||||||
"lng_rights_group_invite_link" = "Invite users via link";
|
"lng_rights_group_invite_link" = "Invite users via link";
|
||||||
|
@ -5251,6 +5444,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_restricted_send_polls_all" = "Sorry, sending polls is not allowed in this group.";
|
"lng_restricted_send_polls_all" = "Sorry, sending polls is not allowed in this group.";
|
||||||
|
|
||||||
"lng_restricted_send_public_polls" = "Sorry, polls with visible votes can't be forwarded to channels.";
|
"lng_restricted_send_public_polls" = "Sorry, polls with visible votes can't be forwarded to channels.";
|
||||||
|
"lng_restricted_send_todo_lists" = "Sorry, Checklists can't be forwarded to channels.";
|
||||||
"lng_restricted_send_paid_media" = "Sorry, paid media can't be sent to this channel.";
|
"lng_restricted_send_paid_media" = "Sorry, paid media can't be sent to this channel.";
|
||||||
|
|
||||||
"lng_restricted_send_voice_messages" = "{user} doesn't accept voice messages.";
|
"lng_restricted_send_voice_messages" = "{user} doesn't accept voice messages.";
|
||||||
|
@ -5265,6 +5459,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers.";
|
"lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers.";
|
||||||
"lng_send_non_premium_message_toast_link" = "Telegram Premium";
|
"lng_send_non_premium_message_toast_link" = "Telegram Premium";
|
||||||
|
|
||||||
|
"lng_send_charges_stars_channel" = "{channel} charges {amount} per message to its admin.";
|
||||||
|
"lng_send_free_channel" = "Send a direct message to the administrator of {channel}.";
|
||||||
"lng_send_charges_stars_text" = "{user} charges {amount} for each message.";
|
"lng_send_charges_stars_text" = "{user} charges {amount} for each message.";
|
||||||
"lng_send_charges_stars_go" = "Buy Stars";
|
"lng_send_charges_stars_go" = "Buy Stars";
|
||||||
|
|
||||||
|
@ -5480,6 +5676,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_admin_log_admin_create_topics" = "Create topics";
|
"lng_admin_log_admin_create_topics" = "Create topics";
|
||||||
"lng_admin_log_admin_manage_calls" = "Manage video chats";
|
"lng_admin_log_admin_manage_calls" = "Manage video chats";
|
||||||
"lng_admin_log_admin_manage_calls_channel" = "Manage live streams";
|
"lng_admin_log_admin_manage_calls_channel" = "Manage live streams";
|
||||||
|
"lng_admin_log_admin_manage_direct" = "Manage direct messages";
|
||||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||||
"lng_admin_log_subscription_extend" = "{name} renewed subscription until {date}";
|
"lng_admin_log_subscription_extend" = "{name} renewed subscription until {date}";
|
||||||
|
|
||||||
|
@ -5779,6 +5976,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_polls_stop" = "Stop poll";
|
"lng_polls_stop" = "Stop poll";
|
||||||
"lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.";
|
"lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.";
|
||||||
"lng_polls_stop_sure" = "Stop";
|
"lng_polls_stop_sure" = "Stop";
|
||||||
|
"lng_polls_menu_item" = "Poll";
|
||||||
"lng_polls_create" = "Create poll";
|
"lng_polls_create" = "Create poll";
|
||||||
"lng_polls_create_title" = "New poll";
|
"lng_polls_create_title" = "New poll";
|
||||||
"lng_polls_create_question" = "Question";
|
"lng_polls_create_question" = "Question";
|
||||||
|
@ -5807,6 +6005,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_polls_show_more#other" = "Show more ({count})";
|
"lng_polls_show_more#other" = "Show more ({count})";
|
||||||
"lng_polls_votes_collapse" = "Collapse";
|
"lng_polls_votes_collapse" = "Collapse";
|
||||||
|
|
||||||
|
"lng_todo_title" = "Checklist";
|
||||||
|
"lng_todo_title_group" = "Group Checklist";
|
||||||
|
"lng_todo_completed#one" = "{count} of {total} completed";
|
||||||
|
"lng_todo_completed#other" = "{count} of {total} completed";
|
||||||
|
"lng_todo_completed_none" = "None of {total} completed";
|
||||||
|
"lng_todo_menu_item" = "Checklist";
|
||||||
|
"lng_todo_create" = "Create Checklist";
|
||||||
|
"lng_todo_create_title" = "New Checklist";
|
||||||
|
"lng_todo_create_title_placeholder" = "Title";
|
||||||
|
"lng_todo_create_list" = "Tasks List";
|
||||||
|
"lng_todo_create_list_add" = "Add a task...";
|
||||||
|
"lng_todo_create_limit#one" = "You can add {count} more task.";
|
||||||
|
"lng_todo_create_limit#other" = "You can add {count} more tasks.";
|
||||||
|
"lng_todo_create_maximum" = "You have added the maximum number of tasks.";
|
||||||
|
"lng_todo_create_settings" = "Settings";
|
||||||
|
"lng_todo_create_allow_add" = "Allow Others to Add Tasks";
|
||||||
|
"lng_todo_create_allow_mark" = "Allow Others to Mark As Done";
|
||||||
|
"lng_todo_create_button" = "Create";
|
||||||
|
"lng_todo_choose_title" = "Please enter a title.";
|
||||||
|
"lng_todo_choose_tasks" = "Please enter at least one task.";
|
||||||
|
|
||||||
|
"lng_todo_add_title" = "Add Tasks";
|
||||||
|
"lng_todo_create_premium" = "Only subscribers of {link} can create Checklists.";
|
||||||
|
"lng_todo_add_premium" = "Only subscribers of {link} can add tasks.";
|
||||||
|
"lng_todo_mark_premium" = "Only subscribers of {link} can mark tasks as done.";
|
||||||
|
"lng_todo_premium_link" = "Telegram Premium";
|
||||||
|
"lng_todo_mark_restricted" = "{user} has restricted others from marking tasks as done.";
|
||||||
|
"lng_todo_mark_forwarded" = "You can't change forwarded checklists.";
|
||||||
|
|
||||||
"lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM.";
|
"lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM.";
|
||||||
"lng_outdated_title_bits" = "PLEASE SWITCH TO A 64-BIT OPERATING SYSTEM.";
|
"lng_outdated_title_bits" = "PLEASE SWITCH TO A 64-BIT OPERATING SYSTEM.";
|
||||||
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";
|
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";
|
||||||
|
@ -6071,6 +6298,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_forum_messages#other" = "{count} messages";
|
"lng_forum_messages#other" = "{count} messages";
|
||||||
"lng_forum_show_topics_list" = "Show Topics List";
|
"lng_forum_show_topics_list" = "Show Topics List";
|
||||||
|
|
||||||
|
"lng_monoforum_choose_to_reply" = "Choose a message to reply.";
|
||||||
|
|
||||||
"lng_request_peer_requirements" = "Requirements";
|
"lng_request_peer_requirements" = "Requirements";
|
||||||
"lng_request_peer_rights" = "You must have these admin rights: {rights}.";
|
"lng_request_peer_rights" = "You must have these admin rights: {rights}.";
|
||||||
"lng_request_peer_rights_and" = "{rights} and {last}";
|
"lng_request_peer_rights_and" = "{rights} and {last}";
|
||||||
|
@ -6117,6 +6346,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_request_channel_delete_messages" = "delete messages";
|
"lng_request_channel_delete_messages" = "delete messages";
|
||||||
"lng_request_channel_add_subscribers" = "add subscribers";
|
"lng_request_channel_add_subscribers" = "add subscribers";
|
||||||
"lng_request_channel_manage_livestreams" = "manage live streams";
|
"lng_request_channel_manage_livestreams" = "manage live streams";
|
||||||
|
"lng_request_channel_manage_direct" = "manage direct messages";
|
||||||
"lng_request_channel_add_admins" = "add new admins";
|
"lng_request_channel_add_admins" = "add new admins";
|
||||||
"lng_request_channel_create" = "Create a New Channel for This";
|
"lng_request_channel_create" = "Create a New Channel for This";
|
||||||
|
|
||||||
|
@ -6394,7 +6624,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_bot_earn_balance_button_locked" = "Withdraw";
|
"lng_bot_earn_balance_button_locked" = "Withdraw";
|
||||||
"lng_bot_earn_balance_button_buy_ads" = "Buy Ads";
|
"lng_bot_earn_balance_button_buy_ads" = "Buy Ads";
|
||||||
"lng_bot_earn_learn_credits_out_about" = "You can withdraw Stars using Fragment, or use Stars to advertise your bot. {link}";
|
"lng_bot_earn_learn_credits_out_about" = "You can withdraw Stars using Fragment, or use Stars to advertise your bot. {link}";
|
||||||
|
"lng_self_earn_learn_credits_out_about" = "You can withdraw from 10 Stars using Fragment. {link}";
|
||||||
"lng_bot_earn_out_ph" = "Enter amount to withdraw";
|
"lng_bot_earn_out_ph" = "Enter amount to withdraw";
|
||||||
|
"lng_bot_earn_out_ph_max" = "Enter amount to withdraw (max. {amount})";
|
||||||
"lng_bot_earn_balance_password_title" = "Two-step verification";
|
"lng_bot_earn_balance_password_title" = "Two-step verification";
|
||||||
"lng_bot_earn_balance_password_description" = "Please enter your password to collect.";
|
"lng_bot_earn_balance_password_description" = "Please enter your password to collect.";
|
||||||
"lng_bot_earn_credits_out_minimal" = "You cannot withdraw less than {link}.";
|
"lng_bot_earn_credits_out_minimal" = "You cannot withdraw less than {link}.";
|
||||||
|
@ -6576,6 +6808,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_mac_menu_new_channel" = "New Channel";
|
"lng_mac_menu_new_channel" = "New Channel";
|
||||||
"lng_mac_menu_show" = "Show Telegram";
|
"lng_mac_menu_show" = "Show Telegram";
|
||||||
"lng_mac_menu_emoji_and_symbols" = "Emoji & Symbols";
|
"lng_mac_menu_emoji_and_symbols" = "Emoji & Symbols";
|
||||||
|
"lng_mac_menu_fullscreen" = "Toggle Full Screen";
|
||||||
|
|
||||||
"lng_mac_menu_player_pause" = "Pause";
|
"lng_mac_menu_player_pause" = "Pause";
|
||||||
"lng_mac_menu_player_resume" = "Resume";
|
"lng_mac_menu_player_resume" = "Resume";
|
||||||
|
@ -6612,6 +6845,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_UseScheduledMessages" = "Schedule Messages";
|
"ayu_UseScheduledMessages" = "Schedule Messages";
|
||||||
"ayu_UseScheduledMessagesDescription" = "Automatically schedules outgoing messages to send after ~12 seconds (longer for media). Using this feature, you won't appear online.\nAvoid using on unreliable networks.";
|
"ayu_UseScheduledMessagesDescription" = "Automatically schedules outgoing messages to send after ~12 seconds (longer for media). Using this feature, you won't appear online.\nAvoid using on unreliable networks.";
|
||||||
"ayu_SendWithoutSoundByDefault" = "Send without Sound";
|
"ayu_SendWithoutSoundByDefault" = "Send without Sound";
|
||||||
|
"ayu_SendWithoutSoundByDefaultNever" = "Never";
|
||||||
|
"ayu_SendWithoutSoundByDefaultInGhostMode" = "When using Ghost Mode";
|
||||||
|
"ayu_SendWithoutSoundByDefaultAlways" = "Always";
|
||||||
"ayu_SendWithoutSoundByDefaultDescription" = "Sends outgoing messages without sound by default.";
|
"ayu_SendWithoutSoundByDefaultDescription" = "Sends outgoing messages without sound by default.";
|
||||||
"ayu_SuggestGhostModeBeforeViewingStory" = "Story Ghost Mode Alert";
|
"ayu_SuggestGhostModeBeforeViewingStory" = "Story Ghost Mode Alert";
|
||||||
"ayu_SuggestGhostModeBeforeViewingStoryDescription" = "Displays an alert before opening a story, suggesting you enable Ghost Mode.";
|
"ayu_SuggestGhostModeBeforeViewingStoryDescription" = "Displays an alert before opening a story, suggesting you enable Ghost Mode.";
|
||||||
|
@ -6626,6 +6862,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_MessageSavingSaveMediaInPublicGroups" = "Public Groups";
|
"ayu_MessageSavingSaveMediaInPublicGroups" = "Public Groups";
|
||||||
"ayu_MessageSavingSaveMediaInPrivateGroups" = "Private Groups";
|
"ayu_MessageSavingSaveMediaInPrivateGroups" = "Private Groups";
|
||||||
"ayu_MessageSavingSaveMediaHintPopup" = "Make sure it's configured to your needs.";
|
"ayu_MessageSavingSaveMediaHintPopup" = "Make sure it's configured to your needs.";
|
||||||
|
"ayu_AttachmentsFolderMaxSizeHeader" = "Max folder size";
|
||||||
|
"ayu_AttachmentsFolderMaxSizeDescription" = "If folder size exceeds this limit, the oldest attachments will be removed from the device.";
|
||||||
"ayu_MaximumMediaSizeCellular" = "Media size limit (cellular data)";
|
"ayu_MaximumMediaSizeCellular" = "Media size limit (cellular data)";
|
||||||
"ayu_MaximumMediaSizeWiFi" = "Media size limit (WiFi)";
|
"ayu_MaximumMediaSizeWiFi" = "Media size limit (WiFi)";
|
||||||
"ayu_MessageSavingOtherHeader" = "Other";
|
"ayu_MessageSavingOtherHeader" = "Other";
|
||||||
|
@ -6647,6 +6885,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_LocalPremium" = "Local Telegram Premium";
|
"ayu_LocalPremium" = "Local Telegram Premium";
|
||||||
"ayu_DisplayGhostStatus" = "Display Ghost Mode Status";
|
"ayu_DisplayGhostStatus" = "Display Ghost Mode Status";
|
||||||
"ayu_CopyUsernameAsLink" = "Copy Username as Link";
|
"ayu_CopyUsernameAsLink" = "Copy Username as Link";
|
||||||
|
"ayu_HideChannelReactions" = "Hide Reactions in Channels";
|
||||||
|
"ayu_HideGroupReactions" = "Hide Reactions in Groups";
|
||||||
"ayu_CustomizationHeader" = "Customization";
|
"ayu_CustomizationHeader" = "Customization";
|
||||||
"ayu_DeletedMarkText" = "Deleted Mark";
|
"ayu_DeletedMarkText" = "Deleted Mark";
|
||||||
"ayu_DeletedMarkNothing" = "Nothing";
|
"ayu_DeletedMarkNothing" = "Nothing";
|
||||||
|
@ -6710,6 +6950,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_RegexFiltersExcludedAmount#other" = "{count} excluded";
|
"ayu_RegexFiltersExcludedAmount#other" = "{count} excluded";
|
||||||
"ayu_RegexFiltersHeader" = "Filters";
|
"ayu_RegexFiltersHeader" = "Filters";
|
||||||
"ayu_RegexFiltersShared" = "Shared Filters";
|
"ayu_RegexFiltersShared" = "Shared Filters";
|
||||||
|
"ayu_FiltersShadowBan" = "Shadow Ban";
|
||||||
"ayu_RegexFiltersExcluded" = "Excluded Filters";
|
"ayu_RegexFiltersExcluded" = "Excluded Filters";
|
||||||
"ayu_RegexFiltersEnable" = "Enable Filters";
|
"ayu_RegexFiltersEnable" = "Enable Filters";
|
||||||
"ayu_FiltersHideFromBlocked" = "Hide from Blocked Users";
|
"ayu_FiltersHideFromBlocked" = "Hide from Blocked Users";
|
||||||
|
@ -6726,6 +6967,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_RegexFilterBulletinText" = "Filter added to the **Shared filters**.";
|
"ayu_RegexFilterBulletinText" = "Filter added to the **Shared filters**.";
|
||||||
"ayu_RegexFilterBulletinAction" = "To Current Chat";
|
"ayu_RegexFilterBulletinAction" = "To Current Chat";
|
||||||
"ayu_RegexFiltersListEmpty" = "No filters here yet.";
|
"ayu_RegexFiltersListEmpty" = "No filters here yet.";
|
||||||
|
"ayu_FiltersQuickShadowBan" = "Shadow Ban";
|
||||||
|
"ayu_FiltersQuickUnshadowBan" = "Unshadow Ban";
|
||||||
"ayu_FiltersMenuSelectChat" = "Select Chat";
|
"ayu_FiltersMenuSelectChat" = "Select Chat";
|
||||||
"ayu_FiltersMenuImport" = "Import";
|
"ayu_FiltersMenuImport" = "Import";
|
||||||
"ayu_FiltersMenuExport" = "Export";
|
"ayu_FiltersMenuExport" = "Export";
|
||||||
|
@ -6767,6 +7010,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_IconNothing" = "Nothing";
|
"ayu_IconNothing" = "Nothing";
|
||||||
"ayu_IconBard" = "Google Bard";
|
"ayu_IconBard" = "Google Bard";
|
||||||
"ayu_IconYaPlus" = "Yandex Plus";
|
"ayu_IconYaPlus" = "Yandex Plus";
|
||||||
|
"ayu_IconAyuTyan" = "AyuTyan";
|
||||||
"ayu_WALMode" = "Enable WAL mode";
|
"ayu_WALMode" = "Enable WAL mode";
|
||||||
"ayu_PushNotificationCount" = "FCM notifications received";
|
"ayu_PushNotificationCount" = "FCM notifications received";
|
||||||
"ayu_AyuAttachments" = "AyuGram Attachments Folder";
|
"ayu_AyuAttachments" = "AyuGram Attachments Folder";
|
||||||
|
@ -6835,6 +7079,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"ayu_ClearDeletedMessagesTitle" = "Clear deleted messages";
|
"ayu_ClearDeletedMessagesTitle" = "Clear deleted messages";
|
||||||
"ayu_ClearDeletedMessagesText" = "Are you sure you want to clear all deleted messages in this chat?";
|
"ayu_ClearDeletedMessagesText" = "Are you sure you want to clear all deleted messages in this chat?";
|
||||||
"ayu_ClearDeletedMessagesActionText" = "Clear";
|
"ayu_ClearDeletedMessagesActionText" = "Clear";
|
||||||
|
"ayu_DeleteOwnMessages" = "Delete own messages";
|
||||||
|
"ayu_DeleteOwnMessagesConfirmation" = "Are you sure you want to delete all your messages from this group?";
|
||||||
"ayu_SuggestGhostModeTitle" = "Ghost Mode";
|
"ayu_SuggestGhostModeTitle" = "Ghost Mode";
|
||||||
"ayu_SuggestGhostModeStoryText" = "Do you want to enable **Ghost Mode** before viewing the story?";
|
"ayu_SuggestGhostModeStoryText" = "Do you want to enable **Ghost Mode** before viewing the story?";
|
||||||
"ayu_SuggestGhostModeStoryActionTextYes" = "Yes";
|
"ayu_SuggestGhostModeStoryActionTextYes" = "Yes";
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<file alias="cloud_password/password_input.tgs">../../animations/cloud_password/password_input.tgs</file>
|
<file alias="cloud_password/password_input.tgs">../../animations/cloud_password/password_input.tgs</file>
|
||||||
<file alias="cloud_password/hint.tgs">../../animations/cloud_password/hint.tgs</file>
|
<file alias="cloud_password/hint.tgs">../../animations/cloud_password/hint.tgs</file>
|
||||||
<file alias="cloud_password/email.tgs">../../animations/cloud_password/email.tgs</file>
|
<file alias="cloud_password/email.tgs">../../animations/cloud_password/email.tgs</file>
|
||||||
|
<file alias="cloud_password/validate.tgs">../../animations/cloud_password/validate.tgs</file>
|
||||||
<file alias="ttl.tgs">../../animations/ttl.tgs</file>
|
<file alias="ttl.tgs">../../animations/ttl.tgs</file>
|
||||||
<file alias="discussion.tgs">../../animations/discussion.tgs</file>
|
<file alias="discussion.tgs">../../animations/discussion.tgs</file>
|
||||||
<file alias="stats.tgs">../../animations/stats.tgs</file>
|
<file alias="stats.tgs">../../animations/stats.tgs</file>
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
<file alias="hours.tgs">../../animations/hours.tgs</file>
|
<file alias="hours.tgs">../../animations/hours.tgs</file>
|
||||||
<file alias="phone.tgs">../../animations/phone.tgs</file>
|
<file alias="phone.tgs">../../animations/phone.tgs</file>
|
||||||
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
|
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
|
||||||
|
<file alias="diamond.tgs">../../animations/diamond.tgs</file>
|
||||||
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
|
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
|
||||||
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
|
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
|
||||||
<file alias="search.tgs">../../animations/search.tgs</file>
|
<file alias="search.tgs">../../animations/search.tgs</file>
|
||||||
|
@ -32,6 +34,10 @@
|
||||||
<file alias="hello_status.tgs">../../animations/hello_status.tgs</file>
|
<file alias="hello_status.tgs">../../animations/hello_status.tgs</file>
|
||||||
<file alias="starref_link.tgs">../../animations/starref_link.tgs</file>
|
<file alias="starref_link.tgs">../../animations/starref_link.tgs</file>
|
||||||
<file alias="media_forbidden.tgs">../../animations/media_forbidden.tgs</file>
|
<file alias="media_forbidden.tgs">../../animations/media_forbidden.tgs</file>
|
||||||
|
<file alias="topics.tgs">../../animations/edit_peers/topics.tgs</file>
|
||||||
|
<file alias="topics_tabs.tgs">../../animations/edit_peers/topics_tabs.tgs</file>
|
||||||
|
<file alias="topics_list.tgs">../../animations/edit_peers/topics_list.tgs</file>
|
||||||
|
<file alias="direct_messages.tgs">../../animations/edit_peers/direct_messages.tgs</file>
|
||||||
|
|
||||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="5.14.3.0" />
|
Version="5.16.3.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,14,3,0
|
FILEVERSION 5,16,3,0
|
||||||
PRODUCTVERSION 5,14,3,0
|
PRODUCTVERSION 5,16,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "5.14.3.0"
|
VALUE "FileVersion", "5.16.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.14.3.0"
|
VALUE "ProductVersion", "5.16.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,14,3,0
|
FILEVERSION 5,16,3,0
|
||||||
PRODUCTVERSION 5,14,3,0
|
PRODUCTVERSION 5,16,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "5.14.3.0"
|
VALUE "FileVersion", "5.16.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.14.3.0"
|
VALUE "ProductVersion", "5.16.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -282,7 +282,7 @@ int main(int argc, char *argv[])
|
||||||
cout << "Compression start, size: " << resultSize << "\n";
|
cout << "Compression start, size: " << resultSize << "\n";
|
||||||
|
|
||||||
QByteArray compressed, resultCheck;
|
QByteArray compressed, resultCheck;
|
||||||
#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
|
#if defined Q_OS_WIN && !defined PACKER_USE_PACKAGED // use Lzma SDK for win
|
||||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
||||||
|
|
||||||
compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
|
compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
|
||||||
|
|
|
@ -27,7 +27,7 @@ extern "C" {
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
|
#if defined Q_OS_WIN && !defined PACKER_USE_PACKAGED // use Lzma SDK for win
|
||||||
#include <LzmaLib.h>
|
#include <LzmaLib.h>
|
||||||
#else
|
#else
|
||||||
#include <lzma.h>
|
#include <lzma.h>
|
||||||
|
|
|
@ -5,6 +5,7 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
|
#define _GLIBCXX_USE_CXX11_ABI 0
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "api/api_cloud_password.h"
|
#include "api/api_cloud_password.h"
|
||||||
#include "api/api_send_progress.h"
|
#include "api/api_send_progress.h"
|
||||||
|
#include "api/api_suggest_post.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
#include "boxes/passcode_box.h"
|
#include "boxes/passcode_box.h"
|
||||||
#include "boxes/url_auth_box.h"
|
#include "boxes/url_auth_box.h"
|
||||||
|
@ -399,10 +400,12 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto replyTo = FullReplyTo();
|
const auto replyTo = FullReplyTo();
|
||||||
|
const auto suggest = SuggestPostOptions();
|
||||||
Window::PeerMenuCreatePoll(
|
Window::PeerMenuCreatePoll(
|
||||||
controller,
|
controller,
|
||||||
item->history()->peer,
|
item->history()->peer,
|
||||||
replyTo,
|
replyTo,
|
||||||
|
suggest,
|
||||||
chosen,
|
chosen,
|
||||||
disabled);
|
disabled);
|
||||||
} break;
|
} break;
|
||||||
|
@ -519,6 +522,27 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||||
controller->showToast(tr::lng_text_copied(tr::now));
|
controller->showToast(tr::lng_text_copied(tr::now));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case ButtonType::SuggestAccept: {
|
||||||
|
Api::AcceptClickHandler(item)->onClick(ClickContext{
|
||||||
|
Qt::LeftButton,
|
||||||
|
QVariant::fromValue(context),
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ButtonType::SuggestDecline: {
|
||||||
|
Api::DeclineClickHandler(item)->onClick(ClickContext{
|
||||||
|
Qt::LeftButton,
|
||||||
|
QVariant::fromValue(context),
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ButtonType::SuggestChange: {
|
||||||
|
Api::SuggestChangesClickHandler(item)->onClick(ClickContext{
|
||||||
|
Qt::LeftButton,
|
||||||
|
QVariant::fromValue(context),
|
||||||
|
});
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -256,6 +256,7 @@ void ConfirmSubscriptionBox(
|
||||||
{
|
{
|
||||||
const auto balance = Settings::AddBalanceWidget(
|
const auto balance = Settings::AddBalanceWidget(
|
||||||
content,
|
content,
|
||||||
|
session,
|
||||||
session->credits().balanceValue(),
|
session->credits().balanceValue(),
|
||||||
true);
|
true);
|
||||||
session->credits().load(true);
|
session->credits().load(true);
|
||||||
|
@ -436,6 +437,12 @@ void CheckChatInvite(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [=](const MTP::Error &error) {
|
}, [=](const MTP::Error &error) {
|
||||||
|
if (MTP::IsFloodError(error)) {
|
||||||
|
if (const auto strong = weak.get()) {
|
||||||
|
strong->show(Ui::MakeInformBox(tr::lng_flood_error()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (error.code() != 400) {
|
if (error.code() != 400) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ class ChannelData;
|
||||||
|
|
||||||
namespace Info::Profile {
|
namespace Info::Profile {
|
||||||
class Badge;
|
class Badge;
|
||||||
enum class BadgeType;
|
enum class BadgeType : uchar;
|
||||||
} // namespace Info::Profile
|
} // namespace Info::Profile
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
|
|
|
@ -494,8 +494,15 @@ void ChatParticipants::requestBots(not_null<ChannelData*> channel) {
|
||||||
LOG(("API Error: "
|
LOG(("API Error: "
|
||||||
"channels.channelParticipantsNotModified received!"));
|
"channels.channelParticipantsNotModified received!"));
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_botsRequests.remove(channel);
|
_botsRequests.remove(channel);
|
||||||
|
if (error.type() == u"CHANNEL_MONOFORUM_UNSUPPORTED"_q) {
|
||||||
|
channel->mgInfo->bots.clear();
|
||||||
|
channel->mgInfo->botStatus = -1;
|
||||||
|
channel->session().changes().peerUpdated(
|
||||||
|
channel,
|
||||||
|
Data::PeerUpdate::Flag::FullInfo);
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
|
|
||||||
_botsRequests[channel] = requestId;
|
_botsRequests[channel] = requestId;
|
||||||
|
@ -648,10 +655,7 @@ void ChatParticipants::Restrict(
|
||||||
channel->session().api().request(MTPchannels_EditBanned(
|
channel->session().api().request(MTPchannels_EditBanned(
|
||||||
channel->inputChannel,
|
channel->inputChannel,
|
||||||
participant->input,
|
participant->input,
|
||||||
MTP_chatBannedRights(
|
RestrictionsToMTP(newRights)
|
||||||
MTP_flags(MTPDchatBannedRights::Flags::from_raw(
|
|
||||||
uint32(newRights.flags))),
|
|
||||||
MTP_int(newRights.until))
|
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
channel->session().api().applyUpdates(result);
|
channel->session().api().applyUpdates(result);
|
||||||
channel->applyEditBanned(participant, oldRights, newRights);
|
channel->applyEditBanned(participant, oldRights, newRights);
|
||||||
|
@ -756,10 +760,7 @@ void ChatParticipants::kick(
|
||||||
const auto requestId = _api.request(MTPchannels_EditBanned(
|
const auto requestId = _api.request(MTPchannels_EditBanned(
|
||||||
channel->inputChannel,
|
channel->inputChannel,
|
||||||
participant->input,
|
participant->input,
|
||||||
MTP_chatBannedRights(
|
RestrictionsToMTP(rights)
|
||||||
MTP_flags(
|
|
||||||
MTPDchatBannedRights::Flags::from_raw(uint32(rights.flags))),
|
|
||||||
MTP_int(rights.until))
|
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
channel->session().api().applyUpdates(result);
|
channel->session().api().applyUpdates(result);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
|
MTPSuggestedPost SuggestToMTP(SuggestPostOptions suggest) {
|
||||||
|
using Flag = MTPDsuggestedPost::Flag;
|
||||||
|
return suggest.exists
|
||||||
|
? MTP_suggestedPost(
|
||||||
|
MTP_flags((suggest.date ? Flag::f_schedule_date : Flag())
|
||||||
|
| (suggest.price().empty() ? Flag() : Flag::f_price)),
|
||||||
|
StarsAmountToTL(suggest.price()),
|
||||||
|
MTP_int(suggest.date))
|
||||||
|
: MTPSuggestedPost();
|
||||||
|
}
|
||||||
|
|
||||||
SendAction::SendAction(
|
SendAction::SendAction(
|
||||||
not_null<Data::Thread*> thread,
|
not_null<Data::Thread*> thread,
|
||||||
SendOptions options)
|
SendOptions options)
|
||||||
|
|
|
@ -19,6 +19,8 @@ namespace Api {
|
||||||
|
|
||||||
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
||||||
|
|
||||||
|
[[nodiscard]] MTPSuggestedPost SuggestToMTP(SuggestPostOptions suggest);
|
||||||
|
|
||||||
struct SendOptions {
|
struct SendOptions {
|
||||||
uint64 price = 0;
|
uint64 price = 0;
|
||||||
PeerData *sendAs = nullptr;
|
PeerData *sendAs = nullptr;
|
||||||
|
@ -31,6 +33,7 @@ struct SendOptions {
|
||||||
bool invertCaption = false;
|
bool invertCaption = false;
|
||||||
bool hideViaBot = false;
|
bool hideViaBot = false;
|
||||||
crl::time ttlSeconds = 0;
|
crl::time ttlSeconds = 0;
|
||||||
|
SuggestPostOptions suggest;
|
||||||
|
|
||||||
friend inline bool operator==(
|
friend inline bool operator==(
|
||||||
const SendOptions &,
|
const SendOptions &,
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "api/api_credits.h"
|
#include "api/api_credits.h"
|
||||||
|
|
||||||
|
#include "api/api_credits_history_entry.h"
|
||||||
#include "api/api_premium.h"
|
#include "api/api_premium.h"
|
||||||
#include "api/api_statistics_data_deserialize.h"
|
#include "api/api_statistics_data_deserialize.h"
|
||||||
#include "api/api_updates.h"
|
#include "api/api_updates.h"
|
||||||
|
@ -27,146 +28,6 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kTransactionsLimit = 100;
|
constexpr auto kTransactionsLimit = 100;
|
||||||
|
|
||||||
[[nodiscard]] Data::CreditsHistoryEntry HistoryFromTL(
|
|
||||||
const MTPStarsTransaction &tl,
|
|
||||||
not_null<PeerData*> peer) {
|
|
||||||
using HistoryPeerTL = MTPDstarsTransactionPeer;
|
|
||||||
using namespace Data;
|
|
||||||
const auto owner = &peer->owner();
|
|
||||||
const auto photo = tl.data().vphoto()
|
|
||||||
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
|
|
||||||
: nullptr;
|
|
||||||
auto extended = std::vector<CreditsHistoryMedia>();
|
|
||||||
if (const auto list = tl.data().vextended_media()) {
|
|
||||||
extended.reserve(list->v.size());
|
|
||||||
for (const auto &media : list->v) {
|
|
||||||
media.match([&](const MTPDmessageMediaPhoto &data) {
|
|
||||||
if (const auto inner = data.vphoto()) {
|
|
||||||
const auto photo = owner->processPhoto(*inner);
|
|
||||||
if (!photo->isNull()) {
|
|
||||||
extended.push_back(CreditsHistoryMedia{
|
|
||||||
.type = CreditsHistoryMediaType::Photo,
|
|
||||||
.id = photo->id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [&](const MTPDmessageMediaDocument &data) {
|
|
||||||
if (const auto inner = data.vdocument()) {
|
|
||||||
const auto document = owner->processDocument(
|
|
||||||
*inner,
|
|
||||||
data.valt_documents());
|
|
||||||
if (document->isAnimation()
|
|
||||||
|| document->isVideoFile()
|
|
||||||
|| document->isGifv()) {
|
|
||||||
extended.push_back(CreditsHistoryMedia{
|
|
||||||
.type = CreditsHistoryMediaType::Video,
|
|
||||||
.id = document->id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [&](const auto &) {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto barePeerId = tl.data().vpeer().match([](
|
|
||||||
const HistoryPeerTL &p) {
|
|
||||||
return peerFromMTP(p.vpeer());
|
|
||||||
}, [](const auto &) {
|
|
||||||
return PeerId(0);
|
|
||||||
}).value;
|
|
||||||
const auto stargift = tl.data().vstargift();
|
|
||||||
const auto nonUniqueGift = stargift
|
|
||||||
? stargift->match([&](const MTPDstarGift &data) {
|
|
||||||
return &data;
|
|
||||||
}, [](const auto &) { return (const MTPDstarGift*)nullptr; })
|
|
||||||
: nullptr;
|
|
||||||
const auto reaction = tl.data().is_reaction();
|
|
||||||
const auto amount = Data::FromTL(tl.data().vstars());
|
|
||||||
const auto starrefAmount = tl.data().vstarref_amount()
|
|
||||||
? Data::FromTL(*tl.data().vstarref_amount())
|
|
||||||
: StarsAmount();
|
|
||||||
const auto starrefCommission
|
|
||||||
= tl.data().vstarref_commission_permille().value_or_empty();
|
|
||||||
const auto starrefBarePeerId = tl.data().vstarref_peer()
|
|
||||||
? peerFromMTP(*tl.data().vstarref_peer()).value
|
|
||||||
: 0;
|
|
||||||
const auto incoming = (amount >= StarsAmount());
|
|
||||||
const auto paidMessagesCount
|
|
||||||
= tl.data().vpaid_messages().value_or_empty();
|
|
||||||
const auto premiumMonthsForStars
|
|
||||||
= tl.data().vpremium_gift_months().value_or_empty();
|
|
||||||
const auto saveActorId = (reaction
|
|
||||||
|| !extended.empty()
|
|
||||||
|| paidMessagesCount) && incoming;
|
|
||||||
const auto parsedGift = stargift
|
|
||||||
? FromTL(&peer->session(), *stargift)
|
|
||||||
: std::optional<Data::StarGift>();
|
|
||||||
const auto giftStickerId = parsedGift ? parsedGift->document->id : 0;
|
|
||||||
return Data::CreditsHistoryEntry{
|
|
||||||
.id = qs(tl.data().vid()),
|
|
||||||
.title = qs(tl.data().vtitle().value_or_empty()),
|
|
||||||
.description = { qs(tl.data().vdescription().value_or_empty()) },
|
|
||||||
.date = base::unixtime::parse(tl.data().vdate().v),
|
|
||||||
.photoId = photo ? photo->id : 0,
|
|
||||||
.extended = std::move(extended),
|
|
||||||
.credits = Data::FromTL(tl.data().vstars()),
|
|
||||||
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
|
|
||||||
.barePeerId = saveActorId ? peer->id.value : barePeerId,
|
|
||||||
.bareGiveawayMsgId = uint64(
|
|
||||||
tl.data().vgiveaway_post_id().value_or_empty()),
|
|
||||||
.bareGiftStickerId = giftStickerId,
|
|
||||||
.bareActorId = saveActorId ? barePeerId : uint64(0),
|
|
||||||
.uniqueGift = parsedGift ? parsedGift->unique : nullptr,
|
|
||||||
.starrefAmount = paidMessagesCount ? StarsAmount() : starrefAmount,
|
|
||||||
.starrefCommission = paidMessagesCount ? 0 : starrefCommission,
|
|
||||||
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
|
|
||||||
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::Peer;
|
|
||||||
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::PlayMarket;
|
|
||||||
}, [](const MTPDstarsTransactionPeerFragment &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::Fragment;
|
|
||||||
}, [](const MTPDstarsTransactionPeerAppStore &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::AppStore;
|
|
||||||
}, [](const MTPDstarsTransactionPeerUnsupported &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::Unsupported;
|
|
||||||
}, [](const MTPDstarsTransactionPeerPremiumBot &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
|
|
||||||
}, [](const MTPDstarsTransactionPeerAds &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::Ads;
|
|
||||||
}, [](const MTPDstarsTransactionPeerAPI &) {
|
|
||||||
return Data::CreditsHistoryEntry::PeerType::API;
|
|
||||||
}),
|
|
||||||
.subscriptionUntil = tl.data().vsubscription_period()
|
|
||||||
? base::unixtime::parse(base::unixtime::now()
|
|
||||||
+ tl.data().vsubscription_period()->v)
|
|
||||||
: QDateTime(),
|
|
||||||
.successDate = tl.data().vtransaction_date()
|
|
||||||
? base::unixtime::parse(tl.data().vtransaction_date()->v)
|
|
||||||
: QDateTime(),
|
|
||||||
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
|
|
||||||
.paidMessagesCount = paidMessagesCount,
|
|
||||||
.paidMessagesAmount = (paidMessagesCount
|
|
||||||
? starrefAmount
|
|
||||||
: StarsAmount()),
|
|
||||||
.paidMessagesCommission = paidMessagesCount ? starrefCommission : 0,
|
|
||||||
.starsConverted = int(nonUniqueGift
|
|
||||||
? nonUniqueGift->vconvert_stars().v
|
|
||||||
: 0),
|
|
||||||
.premiumMonthsForStars = premiumMonthsForStars,
|
|
||||||
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
|
|
||||||
.converted = stargift && incoming,
|
|
||||||
.stargift = stargift.has_value(),
|
|
||||||
.giftUpgraded = tl.data().is_stargift_upgrade(),
|
|
||||||
.giftResale = tl.data().is_stargift_resale(),
|
|
||||||
.reaction = tl.data().is_reaction(),
|
|
||||||
.refunded = tl.data().is_refund(),
|
|
||||||
.pending = tl.data().is_pending(),
|
|
||||||
.failed = tl.data().is_failed(),
|
|
||||||
.in = incoming,
|
|
||||||
.gift = tl.data().is_gift() || stargift.has_value(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
|
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
|
||||||
const MTPStarsSubscription &tl,
|
const MTPStarsSubscription &tl,
|
||||||
not_null<PeerData*> peer) {
|
not_null<PeerData*> peer) {
|
||||||
|
@ -203,7 +64,7 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
if (const auto history = data.vhistory()) {
|
if (const auto history = data.vhistory()) {
|
||||||
entries.reserve(history->v.size());
|
entries.reserve(history->v.size());
|
||||||
for (const auto &tl : history->v) {
|
for (const auto &tl : history->v) {
|
||||||
entries.push_back(HistoryFromTL(tl, peer));
|
entries.push_back(CreditsHistoryEntryFromTL(tl, peer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto subscriptions = std::vector<Data::SubscriptionEntry>();
|
auto subscriptions = std::vector<Data::SubscriptionEntry>();
|
||||||
|
@ -216,7 +77,7 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
return Data::CreditsStatusSlice{
|
return Data::CreditsStatusSlice{
|
||||||
.list = std::move(entries),
|
.list = std::move(entries),
|
||||||
.subscriptions = std::move(subscriptions),
|
.subscriptions = std::move(subscriptions),
|
||||||
.balance = Data::FromTL(status.data().vbalance()),
|
.balance = CreditsAmountFromTL(status.data().vbalance()),
|
||||||
.subscriptionsMissingBalance
|
.subscriptionsMissingBalance
|
||||||
= status.data().vsubscriptions_missing_balance().value_or_empty(),
|
= status.data().vsubscriptions_missing_balance().value_or_empty(),
|
||||||
.allLoaded = !status.data().vnext_offset().has_value()
|
.allLoaded = !status.data().vnext_offset().has_value()
|
||||||
|
@ -300,11 +161,14 @@ void CreditsStatus::request(
|
||||||
using TLResult = MTPpayments_StarsStatus;
|
using TLResult = MTPpayments_StarsStatus;
|
||||||
|
|
||||||
_requestId = _api.request(MTPpayments_GetStarsStatus(
|
_requestId = _api.request(MTPpayments_GetStarsStatus(
|
||||||
|
MTP_flags(0),
|
||||||
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input
|
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input
|
||||||
)).done([=](const TLResult &result) {
|
)).done([=](const TLResult &result) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
const auto &balance = result.data().vbalance();
|
const auto &balance = result.data().vbalance();
|
||||||
_peer->session().credits().apply(_peer->id, Data::FromTL(balance));
|
_peer->session().credits().apply(
|
||||||
|
_peer->id,
|
||||||
|
CreditsAmountFromTL(balance));
|
||||||
if (const auto onstack = done) {
|
if (const auto onstack = done) {
|
||||||
onstack(StatusFromTL(result, _peer));
|
onstack(StatusFromTL(result, _peer));
|
||||||
}
|
}
|
||||||
|
@ -316,13 +180,18 @@ void CreditsStatus::request(
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
CreditsHistory::CreditsHistory(not_null<PeerData*> peer, bool in, bool out)
|
CreditsHistory::CreditsHistory(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
bool in,
|
||||||
|
bool out,
|
||||||
|
bool currency)
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _flags((in == out)
|
, _flags(((in == out)
|
||||||
? HistoryTL::Flags(0)
|
? HistoryTL::Flags(0)
|
||||||
: HistoryTL::Flags(0)
|
: HistoryTL::Flags(0)
|
||||||
| (in ? HistoryTL::Flag::f_inbound : HistoryTL::Flags(0))
|
| (in ? HistoryTL::Flag::f_inbound : HistoryTL::Flags(0))
|
||||||
| (out ? HistoryTL::Flag::f_outbound : HistoryTL::Flags(0)))
|
| (out ? HistoryTL::Flag::f_outbound : HistoryTL::Flags(0)))
|
||||||
|
| (currency ? HistoryTL::Flag::f_ton : HistoryTL::Flags(0)))
|
||||||
, _api(&peer->session().api().instance()) {
|
, _api(&peer->session().api().instance()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,19 +283,21 @@ rpl::producer<rpl::no_value, QString> CreditsEarnStatistics::request() {
|
||||||
auto lifetime = rpl::lifetime();
|
auto lifetime = rpl::lifetime();
|
||||||
|
|
||||||
const auto finish = [=](const QString &url) {
|
const auto finish = [=](const QString &url) {
|
||||||
makeRequest(MTPpayments_GetStarsRevenueStats(
|
api().request(MTPpayments_GetStarsRevenueStats(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
(_isUser ? user()->input : channel()->input)
|
(_isUser ? user()->input : channel()->input)
|
||||||
)).done([=](const MTPpayments_StarsRevenueStats &result) {
|
)).done([=](const MTPpayments_StarsRevenueStats &result) {
|
||||||
const auto &data = result.data();
|
const auto &data = result.data();
|
||||||
const auto &status = data.vstatus().data();
|
const auto &status = data.vstatus().data();
|
||||||
using Data::FromTL;
|
|
||||||
_data = Data::CreditsEarnStatistics{
|
_data = Data::CreditsEarnStatistics{
|
||||||
.revenueGraph = StatisticalGraphFromTL(
|
.revenueGraph = StatisticalGraphFromTL(
|
||||||
data.vrevenue_graph()),
|
data.vrevenue_graph()),
|
||||||
.currentBalance = FromTL(status.vcurrent_balance()),
|
.currentBalance = CreditsAmountFromTL(
|
||||||
.availableBalance = FromTL(status.vavailable_balance()),
|
status.vcurrent_balance()),
|
||||||
.overallRevenue = FromTL(status.voverall_revenue()),
|
.availableBalance = CreditsAmountFromTL(
|
||||||
|
status.vavailable_balance()),
|
||||||
|
.overallRevenue = CreditsAmountFromTL(
|
||||||
|
status.voverall_revenue()),
|
||||||
.usdRate = data.vusd_rate().v,
|
.usdRate = data.vusd_rate().v,
|
||||||
.isWithdrawalEnabled = status.is_withdrawal_enabled(),
|
.isWithdrawalEnabled = status.is_withdrawal_enabled(),
|
||||||
.nextWithdrawalAt = status.vnext_withdrawal_at()
|
.nextWithdrawalAt = status.vnext_withdrawal_at()
|
||||||
|
@ -442,7 +313,7 @@ rpl::producer<rpl::no_value, QString> CreditsEarnStatistics::request() {
|
||||||
}).send();
|
}).send();
|
||||||
};
|
};
|
||||||
|
|
||||||
makeRequest(
|
api().request(
|
||||||
MTPpayments_GetStarsRevenueAdsAccountUrl(
|
MTPpayments_GetStarsRevenueAdsAccountUrl(
|
||||||
(_isUser ? user()->input : channel()->input))
|
(_isUser ? user()->input : channel()->input))
|
||||||
).done([=](const MTPpayments_StarsRevenueAdsAccountUrl &result) {
|
).done([=](const MTPpayments_StarsRevenueAdsAccountUrl &result) {
|
||||||
|
|
|
@ -75,7 +75,11 @@ private:
|
||||||
|
|
||||||
class CreditsHistory final {
|
class CreditsHistory final {
|
||||||
public:
|
public:
|
||||||
CreditsHistory(not_null<PeerData*> peer, bool in, bool out);
|
CreditsHistory(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
bool in,
|
||||||
|
bool out,
|
||||||
|
bool currency = false);
|
||||||
|
|
||||||
void request(
|
void request(
|
||||||
const Data::CreditsStatusSlice::OffsetToken &token,
|
const Data::CreditsStatusSlice::OffsetToken &token,
|
||||||
|
|
167
Telegram/SourceFiles/api/api_credits_history_entry.cpp
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
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_credits_history_entry.h"
|
||||||
|
|
||||||
|
#include "api/api_premium.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_credits.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
|
||||||
|
Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
|
||||||
|
const MTPStarsTransaction &tl,
|
||||||
|
not_null<PeerData*> peer) {
|
||||||
|
using HistoryPeerTL = MTPDstarsTransactionPeer;
|
||||||
|
using namespace Data;
|
||||||
|
const auto owner = &peer->owner();
|
||||||
|
const auto photo = tl.data().vphoto()
|
||||||
|
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
|
||||||
|
: nullptr;
|
||||||
|
auto extended = std::vector<CreditsHistoryMedia>();
|
||||||
|
if (const auto list = tl.data().vextended_media()) {
|
||||||
|
extended.reserve(list->v.size());
|
||||||
|
for (const auto &media : list->v) {
|
||||||
|
media.match([&](const MTPDmessageMediaPhoto &data) {
|
||||||
|
if (const auto inner = data.vphoto()) {
|
||||||
|
const auto photo = owner->processPhoto(*inner);
|
||||||
|
if (!photo->isNull()) {
|
||||||
|
extended.push_back(CreditsHistoryMedia{
|
||||||
|
.type = CreditsHistoryMediaType::Photo,
|
||||||
|
.id = photo->id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [&](const MTPDmessageMediaDocument &data) {
|
||||||
|
if (const auto inner = data.vdocument()) {
|
||||||
|
const auto document = owner->processDocument(
|
||||||
|
*inner,
|
||||||
|
data.valt_documents());
|
||||||
|
if (document->isAnimation()
|
||||||
|
|| document->isVideoFile()
|
||||||
|
|| document->isGifv()) {
|
||||||
|
extended.push_back(CreditsHistoryMedia{
|
||||||
|
.type = CreditsHistoryMediaType::Video,
|
||||||
|
.id = document->id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [&](const auto &) {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto barePeerId = tl.data().vpeer().match([](
|
||||||
|
const HistoryPeerTL &p) {
|
||||||
|
return peerFromMTP(p.vpeer());
|
||||||
|
}, [](const auto &) {
|
||||||
|
return PeerId(0);
|
||||||
|
}).value;
|
||||||
|
const auto stargift = tl.data().vstargift();
|
||||||
|
const auto nonUniqueGift = stargift
|
||||||
|
? stargift->match([&](const MTPDstarGift &data) {
|
||||||
|
return &data;
|
||||||
|
}, [](const auto &) { return (const MTPDstarGift*)nullptr; })
|
||||||
|
: nullptr;
|
||||||
|
const auto reaction = tl.data().is_reaction();
|
||||||
|
const auto amount = CreditsAmountFromTL(tl.data().vamount());
|
||||||
|
const auto starrefAmount = CreditsAmountFromTL(
|
||||||
|
tl.data().vstarref_amount());
|
||||||
|
const auto starrefCommission
|
||||||
|
= tl.data().vstarref_commission_permille().value_or_empty();
|
||||||
|
const auto starrefBarePeerId = tl.data().vstarref_peer()
|
||||||
|
? peerFromMTP(*tl.data().vstarref_peer()).value
|
||||||
|
: 0;
|
||||||
|
const auto incoming = (amount >= CreditsAmount());
|
||||||
|
const auto paidMessagesCount
|
||||||
|
= tl.data().vpaid_messages().value_or_empty();
|
||||||
|
const auto premiumMonthsForStars
|
||||||
|
= tl.data().vpremium_gift_months().value_or_empty();
|
||||||
|
const auto saveActorId = (reaction
|
||||||
|
|| !extended.empty()
|
||||||
|
|| paidMessagesCount) && incoming;
|
||||||
|
const auto parsedGift = stargift
|
||||||
|
? FromTL(&peer->session(), *stargift)
|
||||||
|
: std::optional<Data::StarGift>();
|
||||||
|
const auto giftStickerId = parsedGift ? parsedGift->document->id : 0;
|
||||||
|
return Data::CreditsHistoryEntry{
|
||||||
|
.id = qs(tl.data().vid()),
|
||||||
|
.title = qs(tl.data().vtitle().value_or_empty()),
|
||||||
|
.description = { qs(tl.data().vdescription().value_or_empty()) },
|
||||||
|
.date = base::unixtime::parse(
|
||||||
|
tl.data().vads_proceeds_from_date().value_or(
|
||||||
|
tl.data().vdate().v)),
|
||||||
|
.photoId = photo ? photo->id : 0,
|
||||||
|
.extended = std::move(extended),
|
||||||
|
.credits = CreditsAmountFromTL(tl.data().vamount()),
|
||||||
|
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
|
||||||
|
.barePeerId = saveActorId ? peer->id.value : barePeerId,
|
||||||
|
.bareGiveawayMsgId = uint64(
|
||||||
|
tl.data().vgiveaway_post_id().value_or_empty()),
|
||||||
|
.bareGiftStickerId = giftStickerId,
|
||||||
|
.bareActorId = saveActorId ? barePeerId : uint64(0),
|
||||||
|
.uniqueGift = parsedGift ? parsedGift->unique : nullptr,
|
||||||
|
.starrefAmount = paidMessagesCount ? CreditsAmount() : starrefAmount,
|
||||||
|
.starrefCommission = paidMessagesCount ? 0 : starrefCommission,
|
||||||
|
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
|
||||||
|
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::Peer;
|
||||||
|
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::PlayMarket;
|
||||||
|
}, [](const MTPDstarsTransactionPeerFragment &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::Fragment;
|
||||||
|
}, [](const MTPDstarsTransactionPeerAppStore &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::AppStore;
|
||||||
|
}, [](const MTPDstarsTransactionPeerUnsupported &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::Unsupported;
|
||||||
|
}, [](const MTPDstarsTransactionPeerPremiumBot &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
|
||||||
|
}, [](const MTPDstarsTransactionPeerAds &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::Ads;
|
||||||
|
}, [](const MTPDstarsTransactionPeerAPI &) {
|
||||||
|
return Data::CreditsHistoryEntry::PeerType::API;
|
||||||
|
}),
|
||||||
|
.subscriptionUntil = tl.data().vsubscription_period()
|
||||||
|
? base::unixtime::parse(base::unixtime::now()
|
||||||
|
+ tl.data().vsubscription_period()->v)
|
||||||
|
: QDateTime(),
|
||||||
|
.adsProceedsToDate = tl.data().vads_proceeds_to_date()
|
||||||
|
? base::unixtime::parse(tl.data().vads_proceeds_to_date()->v)
|
||||||
|
: QDateTime(),
|
||||||
|
.successDate = tl.data().vtransaction_date()
|
||||||
|
? base::unixtime::parse(tl.data().vtransaction_date()->v)
|
||||||
|
: QDateTime(),
|
||||||
|
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
|
||||||
|
.paidMessagesCount = paidMessagesCount,
|
||||||
|
.paidMessagesAmount = (paidMessagesCount
|
||||||
|
? starrefAmount
|
||||||
|
: CreditsAmount()),
|
||||||
|
.paidMessagesCommission = paidMessagesCount ? starrefCommission : 0,
|
||||||
|
.starsConverted = int(nonUniqueGift
|
||||||
|
? nonUniqueGift->vconvert_stars().v
|
||||||
|
: 0),
|
||||||
|
.premiumMonthsForStars = premiumMonthsForStars,
|
||||||
|
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
|
||||||
|
.converted = stargift && incoming,
|
||||||
|
.stargift = stargift.has_value(),
|
||||||
|
.giftUpgraded = tl.data().is_stargift_upgrade(),
|
||||||
|
.giftResale = tl.data().is_stargift_resale(),
|
||||||
|
.reaction = tl.data().is_reaction(),
|
||||||
|
.refunded = tl.data().is_refund(),
|
||||||
|
.pending = tl.data().is_pending(),
|
||||||
|
.failed = tl.data().is_failed(),
|
||||||
|
.in = incoming,
|
||||||
|
.gift = tl.data().is_gift() || stargift.has_value(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Api
|
22
Telegram/SourceFiles/api/api_credits_history_entry.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class PeerData;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct CreditsHistoryEntry;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
|
||||||
|
[[nodiscard]] Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
|
||||||
|
const MTPStarsTransaction &tl,
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
} // namespace Api
|
|
@ -56,7 +56,6 @@ void HandleWithdrawalButton(
|
||||||
? ¤cyReceiver->session()
|
? ¤cyReceiver->session()
|
||||||
: &creditsReceiver->session());
|
: &creditsReceiver->session());
|
||||||
|
|
||||||
using ChannelOutUrl = MTPstats_BroadcastRevenueWithdrawalUrl;
|
|
||||||
using CreditsOutUrl = MTPpayments_StarsRevenueWithdrawalUrl;
|
using CreditsOutUrl = MTPpayments_StarsRevenueWithdrawalUrl;
|
||||||
|
|
||||||
session->api().cloudPassword().reload();
|
session->api().cloudPassword().reload();
|
||||||
|
@ -98,19 +97,19 @@ void HandleWithdrawalButton(
|
||||||
show->showToast(message);
|
show->showToast(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (currencyReceiver) {
|
if (currencyReceiver || creditsReceiver) {
|
||||||
session->api().request(
|
using F = MTPpayments_getStarsRevenueWithdrawalUrl::Flag;
|
||||||
MTPstats_GetBroadcastRevenueWithdrawalUrl(
|
|
||||||
currencyReceiver->input,
|
|
||||||
result.result
|
|
||||||
)).done([=](const ChannelOutUrl &r) {
|
|
||||||
done(qs(r.data().vurl()));
|
|
||||||
}).fail(fail).send();
|
|
||||||
} else if (creditsReceiver) {
|
|
||||||
session->api().request(
|
session->api().request(
|
||||||
MTPpayments_GetStarsRevenueWithdrawalUrl(
|
MTPpayments_GetStarsRevenueWithdrawalUrl(
|
||||||
creditsReceiver->input,
|
MTP_flags(currencyReceiver
|
||||||
MTP_long(receiver.creditsAmount()),
|
? F::f_ton
|
||||||
|
: F::f_amount),
|
||||||
|
currencyReceiver
|
||||||
|
? currencyReceiver->input
|
||||||
|
: creditsReceiver->input,
|
||||||
|
MTP_long(creditsReceiver
|
||||||
|
? receiver.creditsAmount()
|
||||||
|
: 0),
|
||||||
result.result
|
result.result
|
||||||
)).done([=](const CreditsOutUrl &r) {
|
)).done([=](const CreditsOutUrl &r) {
|
||||||
done(qs(r.data().vurl()));
|
done(qs(r.data().vurl()));
|
||||||
|
@ -138,17 +137,19 @@ void HandleWithdrawalButton(
|
||||||
processOut();
|
processOut();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (currencyReceiver) {
|
if (currencyReceiver || creditsReceiver) {
|
||||||
session->api().request(
|
using F = MTPpayments_getStarsRevenueWithdrawalUrl::Flag;
|
||||||
MTPstats_GetBroadcastRevenueWithdrawalUrl(
|
|
||||||
currencyReceiver->input,
|
|
||||||
MTP_inputCheckPasswordEmpty()
|
|
||||||
)).fail(fail).send();
|
|
||||||
} else if (creditsReceiver) {
|
|
||||||
session->api().request(
|
session->api().request(
|
||||||
MTPpayments_GetStarsRevenueWithdrawalUrl(
|
MTPpayments_GetStarsRevenueWithdrawalUrl(
|
||||||
creditsReceiver->input,
|
MTP_flags(currencyReceiver
|
||||||
MTP_long(receiver.creditsAmount()),
|
? F::f_ton
|
||||||
|
: F::f_amount),
|
||||||
|
currencyReceiver
|
||||||
|
? currencyReceiver->input
|
||||||
|
: creditsReceiver->input,
|
||||||
|
MTP_long(creditsReceiver
|
||||||
|
? receiver.creditsAmount()
|
||||||
|
: 0),
|
||||||
MTP_inputCheckPasswordEmpty()
|
MTP_inputCheckPasswordEmpty()
|
||||||
)).fail(fail).send();
|
)).fail(fail).send();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "api/api_media.h"
|
#include "api/api_media.h"
|
||||||
#include "api/api_text_entities.h"
|
#include "api/api_text_entities.h"
|
||||||
|
#include "base/random.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "data/business/data_shortcut_messages.h"
|
#include "data/business/data_shortcut_messages.h"
|
||||||
#include "data/components/scheduled_messages.h"
|
#include "data/components/scheduled_messages.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_todo_list.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "history/view/controls/history_view_compose_media_edit_manager.h"
|
#include "history/view/controls/history_view_compose_media_edit_manager.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
#include "history/history_item_components.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "mtproto/mtproto_response.h"
|
#include "mtproto/mtproto_response.h"
|
||||||
|
@ -45,6 +49,193 @@ template <typename T>
|
||||||
constexpr auto ErrorWithoutId
|
constexpr auto ErrorWithoutId
|
||||||
= is_callable_plain_v<T, QString>;
|
= is_callable_plain_v<T, QString>;
|
||||||
|
|
||||||
|
template <typename DoneCallback, typename FailCallback>
|
||||||
|
mtpRequestId SuggestMessage(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TextWithEntities &textWithEntities,
|
||||||
|
Data::WebPageDraft webpage,
|
||||||
|
SendOptions options,
|
||||||
|
DoneCallback &&done,
|
||||||
|
FailCallback &&fail) {
|
||||||
|
Expects(options.suggest.exists);
|
||||||
|
Expects(!options.scheduled);
|
||||||
|
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
const auto api = &session->api();
|
||||||
|
|
||||||
|
const auto thread = item->history()->amMonoforumAdmin()
|
||||||
|
? item->savedSublist()
|
||||||
|
: (Data::Thread*)item->history();
|
||||||
|
auto action = SendAction(thread, options);
|
||||||
|
action.replyTo = FullReplyTo{
|
||||||
|
.messageId = item->fullId(),
|
||||||
|
.monoforumPeerId = (item->history()->amMonoforumAdmin()
|
||||||
|
? item->sublistPeerId()
|
||||||
|
: PeerId()),
|
||||||
|
};
|
||||||
|
|
||||||
|
auto message = MessageToSend(std::move(action));
|
||||||
|
message.textWithTags = TextWithTags{
|
||||||
|
textWithEntities.text,
|
||||||
|
TextUtilities::ConvertEntitiesToTextTags(textWithEntities.entities)
|
||||||
|
};
|
||||||
|
message.webPage = webpage;
|
||||||
|
api->sendMessage(std::move(message));
|
||||||
|
|
||||||
|
const auto requestId = -1;
|
||||||
|
crl::on_main(session, [=] {
|
||||||
|
const auto type = u"MESSAGE_NOT_MODIFIED"_q;
|
||||||
|
if constexpr (ErrorWithId<FailCallback>) {
|
||||||
|
fail(type, requestId);
|
||||||
|
} else if constexpr (ErrorWithoutId<FailCallback>) {
|
||||||
|
fail(type);
|
||||||
|
} else if constexpr (WithoutCallback<FailCallback>) {
|
||||||
|
fail();
|
||||||
|
} else {
|
||||||
|
t_bad_callback(fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return requestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DoneCallback, typename FailCallback>
|
||||||
|
mtpRequestId SuggestMedia(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TextWithEntities &textWithEntities,
|
||||||
|
Data::WebPageDraft webpage,
|
||||||
|
SendOptions options,
|
||||||
|
DoneCallback &&done,
|
||||||
|
FailCallback &&fail,
|
||||||
|
std::optional<MTPInputMedia> inputMedia) {
|
||||||
|
Expects(options.suggest.exists);
|
||||||
|
Expects(!options.scheduled);
|
||||||
|
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
const auto api = &session->api();
|
||||||
|
|
||||||
|
const auto text = textWithEntities.text;
|
||||||
|
const auto sentEntities = EntitiesToMTP(
|
||||||
|
session,
|
||||||
|
textWithEntities.entities,
|
||||||
|
ConvertOption::SkipLocal);
|
||||||
|
|
||||||
|
const auto updateRecentStickers = inputMedia
|
||||||
|
? Api::HasAttachedStickers(*inputMedia)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const auto emptyFlag = MTPmessages_SendMedia::Flag(0);
|
||||||
|
auto replyTo = FullReplyTo{
|
||||||
|
.messageId = item->fullId(),
|
||||||
|
.monoforumPeerId = (item->history()->amMonoforumAdmin()
|
||||||
|
? item->sublistPeerId()
|
||||||
|
: PeerId()),
|
||||||
|
};
|
||||||
|
const auto flags = emptyFlag
|
||||||
|
| MTPmessages_SendMedia::Flag::f_reply_to
|
||||||
|
| MTPmessages_SendMedia::Flag::f_suggested_post
|
||||||
|
| (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|
||||||
|
|| options.invertCaption)
|
||||||
|
? MTPmessages_SendMedia::Flag::f_invert_media
|
||||||
|
: emptyFlag)
|
||||||
|
| (!sentEntities.v.isEmpty()
|
||||||
|
? MTPmessages_SendMedia::Flag::f_entities
|
||||||
|
: emptyFlag)
|
||||||
|
| (options.starsApproved
|
||||||
|
? MTPmessages_SendMedia::Flag::f_allow_paid_stars
|
||||||
|
: emptyFlag);
|
||||||
|
const auto randomId = base::RandomValue<uint64>();
|
||||||
|
return api->request(MTPmessages_SendMedia(
|
||||||
|
MTP_flags(flags),
|
||||||
|
item->history()->peer->input,
|
||||||
|
ReplyToForMTP(item->history(), replyTo),
|
||||||
|
inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())),
|
||||||
|
MTP_string(text),
|
||||||
|
MTP_long(randomId),
|
||||||
|
MTPReplyMarkup(),
|
||||||
|
sentEntities,
|
||||||
|
MTPint(), // schedule_date
|
||||||
|
MTPInputPeer(), // send_as
|
||||||
|
MTPInputQuickReplyShortcut(), // quick_reply_shortcut
|
||||||
|
MTPlong(), // effect
|
||||||
|
MTP_long(options.starsApproved),
|
||||||
|
Api::SuggestToMTP(options.suggest)
|
||||||
|
)).done([=](
|
||||||
|
const MTPUpdates &result,
|
||||||
|
[[maybe_unused]] mtpRequestId requestId) {
|
||||||
|
const auto apply = [=] { api->applyUpdates(result); };
|
||||||
|
|
||||||
|
if constexpr (WithId<DoneCallback>) {
|
||||||
|
done(apply, requestId);
|
||||||
|
} else if constexpr (WithoutId<DoneCallback>) {
|
||||||
|
done(apply);
|
||||||
|
} else if constexpr (WithoutCallback<DoneCallback>) {
|
||||||
|
done();
|
||||||
|
apply();
|
||||||
|
} else {
|
||||||
|
t_bad_callback(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateRecentStickers) {
|
||||||
|
api->requestSpecialStickersForce(false, false, true);
|
||||||
|
}
|
||||||
|
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
|
||||||
|
if constexpr (ErrorWithId<FailCallback>) {
|
||||||
|
fail(error.type(), requestId);
|
||||||
|
} else if constexpr (ErrorWithoutId<FailCallback>) {
|
||||||
|
fail(error.type());
|
||||||
|
} else if constexpr (WithoutCallback<FailCallback>) {
|
||||||
|
fail();
|
||||||
|
} else {
|
||||||
|
t_bad_callback(fail);
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DoneCallback, typename FailCallback>
|
||||||
|
mtpRequestId SuggestMessageOrMedia(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TextWithEntities &textWithEntities,
|
||||||
|
Data::WebPageDraft webpage,
|
||||||
|
SendOptions options,
|
||||||
|
DoneCallback &&done,
|
||||||
|
FailCallback &&fail,
|
||||||
|
std::optional<MTPInputMedia> inputMedia) {
|
||||||
|
const auto wasMedia = item->media();
|
||||||
|
if (!inputMedia && wasMedia && wasMedia->allowsEditCaption()) {
|
||||||
|
if (const auto photo = wasMedia->photo()) {
|
||||||
|
inputMedia = MTP_inputMediaPhoto(
|
||||||
|
MTP_flags(0),
|
||||||
|
photo->mtpInput(),
|
||||||
|
MTPint()); // ttl_seconds
|
||||||
|
} else if (const auto document = wasMedia->document()) {
|
||||||
|
inputMedia = MTP_inputMediaDocument(
|
||||||
|
MTP_flags(0),
|
||||||
|
document->mtpInput(),
|
||||||
|
MTPInputPhoto(), // video_cover
|
||||||
|
MTPint(), // video_timestamp
|
||||||
|
MTPint(), // ttl_seconds
|
||||||
|
MTPstring()); // query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inputMedia) {
|
||||||
|
return SuggestMedia(
|
||||||
|
item,
|
||||||
|
textWithEntities,
|
||||||
|
webpage,
|
||||||
|
options,
|
||||||
|
std::move(done),
|
||||||
|
std::move(fail),
|
||||||
|
inputMedia);
|
||||||
|
}
|
||||||
|
return SuggestMessage(
|
||||||
|
item,
|
||||||
|
textWithEntities,
|
||||||
|
webpage,
|
||||||
|
options,
|
||||||
|
std::move(done),
|
||||||
|
std::move(fail));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename DoneCallback, typename FailCallback>
|
template <typename DoneCallback, typename FailCallback>
|
||||||
mtpRequestId EditMessage(
|
mtpRequestId EditMessage(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
|
@ -54,6 +245,18 @@ mtpRequestId EditMessage(
|
||||||
DoneCallback &&done,
|
DoneCallback &&done,
|
||||||
FailCallback &&fail,
|
FailCallback &&fail,
|
||||||
std::optional<MTPInputMedia> inputMedia = std::nullopt) {
|
std::optional<MTPInputMedia> inputMedia = std::nullopt) {
|
||||||
|
if (item->computeSuggestionActions()
|
||||||
|
== SuggestionActions::AcceptAndDecline) {
|
||||||
|
return SuggestMessageOrMedia(
|
||||||
|
item,
|
||||||
|
textWithEntities,
|
||||||
|
webpage,
|
||||||
|
options,
|
||||||
|
std::move(done),
|
||||||
|
std::move(fail),
|
||||||
|
inputMedia);
|
||||||
|
}
|
||||||
|
|
||||||
const auto session = &item->history()->session();
|
const auto session = &item->history()->session();
|
||||||
const auto api = &session->api();
|
const auto api = &session->api();
|
||||||
|
|
||||||
|
@ -70,31 +273,31 @@ mtpRequestId EditMessage(
|
||||||
|
|
||||||
const auto emptyFlag = MTPmessages_EditMessage::Flag(0);
|
const auto emptyFlag = MTPmessages_EditMessage::Flag(0);
|
||||||
const auto flags = emptyFlag
|
const auto flags = emptyFlag
|
||||||
| ((!text.isEmpty() || media)
|
| ((!text.isEmpty() || media)
|
||||||
? MTPmessages_EditMessage::Flag::f_message
|
? MTPmessages_EditMessage::Flag::f_message
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| ((media && inputMedia.has_value())
|
| ((media && inputMedia.has_value())
|
||||||
? MTPmessages_EditMessage::Flag::f_media
|
? MTPmessages_EditMessage::Flag::f_media
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| (webpage.removed
|
| (webpage.removed
|
||||||
? MTPmessages_EditMessage::Flag::f_no_webpage
|
? MTPmessages_EditMessage::Flag::f_no_webpage
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| ((!webpage.removed && !webpage.url.isEmpty())
|
| ((!webpage.removed && !webpage.url.isEmpty())
|
||||||
? MTPmessages_EditMessage::Flag::f_media
|
? MTPmessages_EditMessage::Flag::f_media
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|
| (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
|
||||||
|| options.invertCaption)
|
|| options.invertCaption)
|
||||||
? MTPmessages_EditMessage::Flag::f_invert_media
|
? MTPmessages_EditMessage::Flag::f_invert_media
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| (!sentEntities.v.isEmpty()
|
| (!sentEntities.v.isEmpty()
|
||||||
? MTPmessages_EditMessage::Flag::f_entities
|
? MTPmessages_EditMessage::Flag::f_entities
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| (options.scheduled
|
| (options.scheduled
|
||||||
? MTPmessages_EditMessage::Flag::f_schedule_date
|
? MTPmessages_EditMessage::Flag::f_schedule_date
|
||||||
: emptyFlag)
|
: emptyFlag)
|
||||||
| (item->isBusinessShortcut()
|
| (item->isBusinessShortcut()
|
||||||
? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
|
? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
|
||||||
: emptyFlag);
|
: emptyFlag);
|
||||||
|
|
||||||
const auto id = item->isScheduled()
|
const auto id = item->isScheduled()
|
||||||
? session->scheduledMessages().lookupId(item)
|
? session->scheduledMessages().lookupId(item)
|
||||||
|
@ -358,4 +561,22 @@ mtpRequestId EditTextMessage(
|
||||||
std::nullopt);
|
std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditTodoList(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TodoListData &data,
|
||||||
|
SendOptions options,
|
||||||
|
Fn<void(mtpRequestId requestId)> done,
|
||||||
|
Fn<void(const QString &error, mtpRequestId requestId)> fail) {
|
||||||
|
const auto callback = [=](Fn<void()> applyUpdates, mtpRequestId id) {
|
||||||
|
applyUpdates();
|
||||||
|
done(id);
|
||||||
|
};
|
||||||
|
EditMessage(
|
||||||
|
item,
|
||||||
|
options,
|
||||||
|
callback,
|
||||||
|
fail,
|
||||||
|
MTP_inputMediaTodo(TodoListDataToMTP(&data)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -58,4 +58,11 @@ mtpRequestId EditTextMessage(
|
||||||
Fn<void(const QString &error, mtpRequestId requestId)> fail,
|
Fn<void(const QString &error, mtpRequestId requestId)> fail,
|
||||||
bool spoilered);
|
bool spoilered);
|
||||||
|
|
||||||
|
void EditTodoList(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TodoListData &data,
|
||||||
|
SendOptions options,
|
||||||
|
Fn<void(mtpRequestId requestId)> done,
|
||||||
|
Fn<void(const QString &error, mtpRequestId requestId)> fail);
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -52,6 +52,7 @@ void Polls::create(
|
||||||
const auto topicRootId = action.replyTo.messageId
|
const auto topicRootId = action.replyTo.messageId
|
||||||
? action.replyTo.topicRootId
|
? action.replyTo.topicRootId
|
||||||
: 0;
|
: 0;
|
||||||
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||||
if (action.replyTo) {
|
if (action.replyTo) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||||
|
@ -59,9 +60,9 @@ void Polls::create(
|
||||||
const auto clearCloudDraft = action.clearDraft;
|
const auto clearCloudDraft = action.clearDraft;
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||||
history->clearLocalDraft(topicRootId);
|
history->clearLocalDraft(topicRootId, monoforumPeerId);
|
||||||
history->clearCloudDraft(topicRootId);
|
history->clearCloudDraft(topicRootId, monoforumPeerId);
|
||||||
history->startSavingCloudDraft(topicRootId);
|
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
|
||||||
}
|
}
|
||||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
const auto starsPaid = std::min(
|
const auto starsPaid = std::min(
|
||||||
|
@ -79,6 +80,9 @@ void Polls::create(
|
||||||
if (action.options.effectId) {
|
if (action.options.effectId) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
if (starsPaid) {
|
if (starsPaid) {
|
||||||
action.options.starsApproved -= starsPaid;
|
action.options.starsApproved -= starsPaid;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||||
|
@ -106,11 +110,13 @@ void Polls::create(
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
SuggestToMTP(action.options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
_session->changes().historyUpdated(
|
_session->changes().historyUpdated(
|
||||||
|
@ -123,6 +129,7 @@ void Polls::create(
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
fail();
|
fail();
|
||||||
|
@ -171,7 +178,7 @@ void Polls::sendVotes(
|
||||||
hideSending();
|
hideSending();
|
||||||
_session->updates().applyUpdates(result);
|
_session->updates().applyUpdates(result);
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.sendReadMessages && settings.markReadAfterAction && item)
|
if (!settings.sendReadMessages && settings.markReadAfterAction && item)
|
||||||
{
|
{
|
||||||
readHistory(item);
|
readHistory(item);
|
||||||
|
|
|
@ -424,7 +424,7 @@ void Premium::requestPremiumRequiredSlice() {
|
||||||
constexpr auto hasPrem = Flag::HasRequirePremiumToWrite;
|
constexpr auto hasPrem = Flag::HasRequirePremiumToWrite;
|
||||||
constexpr auto hasStars = Flag::HasStarsPerMessage;
|
constexpr auto hasStars = Flag::HasStarsPerMessage;
|
||||||
user->setStarsPerMessage(stars);
|
user->setStarsPerMessage(stars);
|
||||||
user->setFlags((user->flags() & ~(me | hasPrem | hasStars))
|
user->setFlags((user->flags() & ~me)
|
||||||
| known
|
| known
|
||||||
| (requirePremium ? (me | hasPrem) : Flag())
|
| (requirePremium ? (me | hasPrem) : Flag())
|
||||||
| (stars ? hasStars : Flag()));
|
| (stars ? hasStars : Flag()));
|
||||||
|
@ -619,6 +619,8 @@ auto PremiumGiftCodeOptions::requestStarGifts()
|
||||||
MTP_int(0)
|
MTP_int(0)
|
||||||
)).done([=](const MTPpayments_StarGifts &result) {
|
)).done([=](const MTPpayments_StarGifts &result) {
|
||||||
result.match([&](const MTPDpayments_starGifts &data) {
|
result.match([&](const MTPDpayments_starGifts &data) {
|
||||||
|
_peer->owner().processUsers(data.vusers());
|
||||||
|
_peer->owner().processChats(data.vchats());
|
||||||
_giftsHash = data.vhash().v;
|
_giftsHash = data.vhash().v;
|
||||||
const auto &list = data.vgifts().v;
|
const auto &list = data.vgifts().v;
|
||||||
const auto session = &_peer->session();
|
const auto session = &_peer->session();
|
||||||
|
@ -805,6 +807,12 @@ std::optional<Data::StarGift> FromTL(
|
||||||
if (!document->sticker()) {
|
if (!document->sticker()) {
|
||||||
return std::optional<Data::StarGift>();
|
return std::optional<Data::StarGift>();
|
||||||
}
|
}
|
||||||
|
const auto releasedById = data.vreleased_by()
|
||||||
|
? peerFromMTP(*data.vreleased_by())
|
||||||
|
: PeerId();
|
||||||
|
const auto releasedBy = releasedById
|
||||||
|
? session->data().peer(releasedById).get()
|
||||||
|
: nullptr;
|
||||||
return std::optional<Data::StarGift>(Data::StarGift{
|
return std::optional<Data::StarGift>(Data::StarGift{
|
||||||
.id = uint64(data.vid().v),
|
.id = uint64(data.vid().v),
|
||||||
.stars = int64(data.vstars().v),
|
.stars = int64(data.vstars().v),
|
||||||
|
@ -812,6 +820,7 @@ std::optional<Data::StarGift> FromTL(
|
||||||
.starsToUpgrade = int64(data.vupgrade_stars().value_or_empty()),
|
.starsToUpgrade = int64(data.vupgrade_stars().value_or_empty()),
|
||||||
.starsResellMin = int64(resellPrice),
|
.starsResellMin = int64(resellPrice),
|
||||||
.document = document,
|
.document = document,
|
||||||
|
.releasedBy = releasedBy,
|
||||||
.resellTitle = qs(data.vtitle().value_or_empty()),
|
.resellTitle = qs(data.vtitle().value_or_empty()),
|
||||||
.resellCount = int(data.vavailability_resale().value_or_empty()),
|
.resellCount = int(data.vavailability_resale().value_or_empty()),
|
||||||
.limitedLeft = remaining.value_or_empty(),
|
.limitedLeft = remaining.value_or_empty(),
|
||||||
|
@ -841,6 +850,12 @@ std::optional<Data::StarGift> FromTL(
|
||||||
|| !pattern->document->sticker()) {
|
|| !pattern->document->sticker()) {
|
||||||
return std::optional<Data::StarGift>();
|
return std::optional<Data::StarGift>();
|
||||||
}
|
}
|
||||||
|
const auto releasedById = data.vreleased_by()
|
||||||
|
? peerFromMTP(*data.vreleased_by())
|
||||||
|
: PeerId();
|
||||||
|
const auto releasedBy = releasedById
|
||||||
|
? session->data().peer(releasedById).get()
|
||||||
|
: nullptr;
|
||||||
auto result = Data::StarGift{
|
auto result = Data::StarGift{
|
||||||
.id = uint64(data.vid().v),
|
.id = uint64(data.vid().v),
|
||||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||||
|
@ -852,12 +867,14 @@ std::optional<Data::StarGift> FromTL(
|
||||||
.ownerId = (data.vowner_id()
|
.ownerId = (data.vowner_id()
|
||||||
? peerFromMTP(*data.vowner_id())
|
? peerFromMTP(*data.vowner_id())
|
||||||
: PeerId()),
|
: PeerId()),
|
||||||
|
.releasedBy = releasedBy,
|
||||||
.number = data.vnum().v,
|
.number = data.vnum().v,
|
||||||
.starsForResale = int(data.vresell_stars().value_or_empty()),
|
.starsForResale = int(data.vresell_stars().value_or_empty()),
|
||||||
.model = *model,
|
.model = *model,
|
||||||
.pattern = *pattern,
|
.pattern = *pattern,
|
||||||
}),
|
}),
|
||||||
.document = model->document,
|
.document = model->document,
|
||||||
|
.releasedBy = releasedBy,
|
||||||
.limitedLeft = (total - data.vavailability_issued().v),
|
.limitedLeft = (total - data.vavailability_issued().v),
|
||||||
.limitedCount = total,
|
.limitedCount = total,
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,7 +118,7 @@ void SendProgressManager::send(const Key &key, int progress) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AyuGram sendUploadProgress
|
// AyuGram sendUploadProgress
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.sendUploadProgress)
|
if (!settings.sendUploadProgress)
|
||||||
{
|
{
|
||||||
DEBUG_LOG(("[AyuGram] Don't send upload progress"));
|
DEBUG_LOG(("[AyuGram] Don't send upload progress"));
|
||||||
|
|
|
@ -109,6 +109,9 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||||
if (action.options.effectId) {
|
if (action.options.effectId) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
if (action.options.invertCaption) {
|
if (action.options.invertCaption) {
|
||||||
flags |= MessageFlag::InvertMedia;
|
flags |= MessageFlag::InvertMedia;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||||
|
@ -136,7 +139,8 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
SuggestToMTP(action.options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
api->sendMessageFail(error, peer, randomId);
|
api->sendMessageFail(error, peer, randomId);
|
||||||
|
@ -211,6 +215,9 @@ void SendExistingMedia(
|
||||||
if (action.options.effectId) {
|
if (action.options.effectId) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
if (action.options.invertCaption) {
|
if (action.options.invertCaption) {
|
||||||
flags |= MessageFlag::InvertMedia;
|
flags |= MessageFlag::InvertMedia;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||||
|
@ -232,6 +239,7 @@ void SendExistingMedia(
|
||||||
.starsPaid = starsPaid,
|
.starsPaid = starsPaid,
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
.effectId = action.options.effectId,
|
.effectId = action.options.effectId,
|
||||||
|
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||||
}, media, caption);
|
}, media, caption);
|
||||||
|
|
||||||
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
const auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||||
|
@ -255,7 +263,8 @@ void SendExistingMedia(
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
SuggestToMTP(action.options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
if (error.code() == 400
|
if (error.code() == 400
|
||||||
|
@ -391,6 +400,9 @@ bool SendDice(MessageToSend &message) {
|
||||||
if (action.options.effectId) {
|
if (action.options.effectId) {
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
if (action.options.invertCaption) {
|
if (action.options.invertCaption) {
|
||||||
flags |= MessageFlag::InvertMedia;
|
flags |= MessageFlag::InvertMedia;
|
||||||
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
|
||||||
|
@ -415,6 +427,7 @@ bool SendDice(MessageToSend &message) {
|
||||||
.starsPaid = starsPaid,
|
.starsPaid = starsPaid,
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
.effectId = action.options.effectId,
|
.effectId = action.options.effectId,
|
||||||
|
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||||
}, TextWithEntities(), MTP_messageMediaDice(
|
}, TextWithEntities(), MTP_messageMediaDice(
|
||||||
MTP_int(0),
|
MTP_int(0),
|
||||||
MTP_string(emoji)));
|
MTP_string(emoji)));
|
||||||
|
@ -435,7 +448,8 @@ bool SendDice(MessageToSend &message) {
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
Data::ShortcutIdToMTP(session, action.options.shortcutId),
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
SuggestToMTP(action.options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
api->sendMessageFail(error, peer, randomId, newId);
|
api->sendMessageFail(error, peer, randomId, newId);
|
||||||
|
@ -624,6 +638,7 @@ void SendConfirmedFile(
|
||||||
edition.useSameMarkup = true;
|
edition.useSameMarkup = true;
|
||||||
edition.useSameReplies = true;
|
edition.useSameReplies = true;
|
||||||
edition.useSameReactions = true;
|
edition.useSameReactions = true;
|
||||||
|
edition.useSameSuggest = true;
|
||||||
edition.savePreviousMedia = true;
|
edition.savePreviousMedia = true;
|
||||||
itemToEdit->applyEdition(std::move(edition));
|
itemToEdit->applyEdition(std::move(edition));
|
||||||
} else {
|
} else {
|
||||||
|
@ -640,6 +655,7 @@ void SendConfirmedFile(
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
.groupedId = groupId,
|
.groupedId = groupId,
|
||||||
.effectId = file->to.options.effectId,
|
.effectId = file->to.options.effectId,
|
||||||
|
.suggest = HistoryMessageSuggestInfo(file->to.options),
|
||||||
}, caption, media);
|
}, caption, media);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "api/api_statistics.h"
|
#include "api/api_statistics.h"
|
||||||
|
|
||||||
|
#include "api/api_credits_history_entry.h"
|
||||||
#include "api/api_statistics_data_deserialize.h"
|
#include "api/api_statistics_data_deserialize.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
@ -695,19 +696,23 @@ rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
auto lifetime = rpl::lifetime();
|
auto lifetime = rpl::lifetime();
|
||||||
|
|
||||||
makeRequest(MTPstats_GetBroadcastRevenueStats(
|
api().request(MTPpayments_GetStarsRevenueStats(
|
||||||
MTP_flags(0),
|
MTP_flags(MTPpayments_getStarsRevenueStats::Flag::f_ton),
|
||||||
(_isUser ? user()->input : channel()->input)
|
(_isUser ? user()->input : channel()->input)
|
||||||
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
|
)).done([=](const MTPpayments_StarsRevenueStats &result) {
|
||||||
const auto &data = result.data();
|
const auto &data = result.data();
|
||||||
const auto &balances = data.vbalances().data();
|
const auto &balances = data.vstatus().data();
|
||||||
|
const auto amount = [](const auto &a) {
|
||||||
|
return CreditsAmountFromTL(a);
|
||||||
|
};
|
||||||
_data = Data::EarnStatistics{
|
_data = Data::EarnStatistics{
|
||||||
.topHoursGraph = StatisticalGraphFromTL(
|
.topHoursGraph = data.vtop_hours_graph()
|
||||||
data.vtop_hours_graph()),
|
? StatisticalGraphFromTL(*data.vtop_hours_graph())
|
||||||
|
: Data::StatisticalGraph(),
|
||||||
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
|
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
|
||||||
.currentBalance = balances.vcurrent_balance().v,
|
.currentBalance = amount(balances.vcurrent_balance()),
|
||||||
.availableBalance = balances.vavailable_balance().v,
|
.availableBalance = amount(balances.vavailable_balance()),
|
||||||
.overallRevenue = balances.voverall_revenue().v,
|
.overallRevenue = amount(balances.voverall_revenue()),
|
||||||
.usdRate = data.vusd_rate().v,
|
.usdRate = data.vusd_rate().v,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -745,62 +750,35 @@ void EarnStatistics::requestHistory(
|
||||||
if (_requestId) {
|
if (_requestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
|
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
|
||||||
constexpr auto kTlLimit = tl::make_int(kLimit);
|
constexpr auto kTlLimit = tl::make_int(kLimit);
|
||||||
_requestId = api().request(MTPstats_GetBroadcastRevenueTransactions(
|
|
||||||
|
_requestId = api().request(MTPpayments_GetStarsTransactions(
|
||||||
|
MTP_flags(MTPpayments_getStarsTransactions::Flag::f_ton),
|
||||||
|
MTP_string(), // Subscription ID.
|
||||||
(_isUser ? user()->input : channel()->input),
|
(_isUser ? user()->input : channel()->input),
|
||||||
MTP_int(token),
|
MTP_string(token),
|
||||||
(!token) ? kTlFirstSlice : kTlLimit
|
token.isEmpty() ? kTlFirstSlice : kTlLimit
|
||||||
)).done([=](const MTPstats_BroadcastRevenueTransactions &result) {
|
)).done([=](const MTPpayments_StarsStatus &result) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
|
|
||||||
const auto &tlTransactions = result.data().vtransactions().v;
|
const auto nextToken = result.data().vnext_offset().value_or_empty();
|
||||||
|
|
||||||
auto list = std::vector<Data::EarnHistoryEntry>();
|
const auto tlTransactions
|
||||||
list.reserve(tlTransactions.size());
|
= result.data().vhistory().value_or_empty();
|
||||||
for (const auto &tlTransaction : tlTransactions) {
|
|
||||||
list.push_back(tlTransaction.match([&](
|
const auto peer = _isUser ? (PeerData*)user() : (PeerData*)channel();
|
||||||
const MTPDbroadcastRevenueTransactionProceeds &d) {
|
auto list = ranges::views::all(
|
||||||
return Data::EarnHistoryEntry{
|
tlTransactions
|
||||||
.type = Data::EarnHistoryEntry::Type::In,
|
) | ranges::views::transform([=](const auto &d) {
|
||||||
.amount = d.vamount().v,
|
return CreditsHistoryEntryFromTL(d, peer);
|
||||||
.date = base::unixtime::parse(d.vfrom_date().v),
|
}) | ranges::to_vector;
|
||||||
.dateTo = base::unixtime::parse(d.vto_date().v),
|
|
||||||
};
|
|
||||||
}, [&](const MTPDbroadcastRevenueTransactionWithdrawal &d) {
|
|
||||||
return Data::EarnHistoryEntry{
|
|
||||||
.type = Data::EarnHistoryEntry::Type::Out,
|
|
||||||
.status = d.is_pending()
|
|
||||||
? Data::EarnHistoryEntry::Status::Pending
|
|
||||||
: d.is_failed()
|
|
||||||
? Data::EarnHistoryEntry::Status::Failed
|
|
||||||
: Data::EarnHistoryEntry::Status::Success,
|
|
||||||
.amount = (std::numeric_limits<Data::EarnInt>::max()
|
|
||||||
- d.vamount().v
|
|
||||||
+ 1),
|
|
||||||
.date = base::unixtime::parse(d.vdate().v),
|
|
||||||
// .provider = qs(d.vprovider()),
|
|
||||||
.successDate = d.vtransaction_date()
|
|
||||||
? base::unixtime::parse(d.vtransaction_date()->v)
|
|
||||||
: QDateTime(),
|
|
||||||
.successLink = d.vtransaction_url()
|
|
||||||
? qs(*d.vtransaction_url())
|
|
||||||
: QString(),
|
|
||||||
};
|
|
||||||
}, [&](const MTPDbroadcastRevenueTransactionRefund &d) {
|
|
||||||
return Data::EarnHistoryEntry{
|
|
||||||
.type = Data::EarnHistoryEntry::Type::Return,
|
|
||||||
.amount = d.vamount().v,
|
|
||||||
.date = base::unixtime::parse(d.vdate().v),
|
|
||||||
// .provider = qs(d.vprovider()),
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
const auto nextToken = token + tlTransactions.size();
|
|
||||||
done(Data::EarnHistorySlice{
|
done(Data::EarnHistorySlice{
|
||||||
.list = std::move(list),
|
.list = std::move(list),
|
||||||
.total = result.data().vcount().v,
|
.total = int(tlTransactions.size()),
|
||||||
.allLoaded = (result.data().vcount().v == nextToken),
|
// .total = result.data().vcount().v,
|
||||||
|
.allLoaded = nextToken.isEmpty(),
|
||||||
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
|
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
|
|
638
Telegram/SourceFiles/api/api_suggest_post.cpp
Normal file
|
@ -0,0 +1,638 @@
|
||||||
|
/*
|
||||||
|
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_suggest_post.h"
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "chat_helpers/message_field.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "data/components/credits.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
|
#include "history/view/controls/history_view_suggest_options.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item_helpers.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "settings/settings_credits_graphics.h"
|
||||||
|
#include "ui/boxes/choose_date_time.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/widgets/fields/input_field.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void SendApproval(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
TimeId scheduleDate = 0) {
|
||||||
|
using Flag = MTPmessages_ToggleSuggestedPostApproval::Flag;
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion
|
||||||
|
|| suggestion->accepted
|
||||||
|
|| suggestion->rejected
|
||||||
|
|| suggestion->requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto session = &show->session();
|
||||||
|
const auto finish = [=] {
|
||||||
|
if (const auto item = session->data().message(id)) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (suggestion) {
|
||||||
|
suggestion->requestId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
suggestion->requestId = session->api().request(
|
||||||
|
MTPmessages_ToggleSuggestedPostApproval(
|
||||||
|
MTP_flags(scheduleDate ? Flag::f_schedule_date : Flag()),
|
||||||
|
item->history()->peer->input,
|
||||||
|
MTP_int(item->id.bare),
|
||||||
|
MTP_int(scheduleDate),
|
||||||
|
MTPstring()) // reject_comment
|
||||||
|
).done([=](const MTPUpdates &result) {
|
||||||
|
session->api().applyUpdates(result);
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
show->showToast(error.type());
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfirmApproval(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
TimeId scheduleDate = 0,
|
||||||
|
Fn<void()> accepted = nullptr) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion
|
||||||
|
|| suggestion->accepted
|
||||||
|
|| suggestion->rejected
|
||||||
|
|| suggestion->requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto price = suggestion->price;
|
||||||
|
const auto admin = item->history()->amMonoforumAdmin();
|
||||||
|
if (!admin && !price.empty()) {
|
||||||
|
const auto credits = &item->history()->session().credits();
|
||||||
|
if (price.ton()) {
|
||||||
|
if (!credits->tonLoaded()) {
|
||||||
|
credits->tonLoad();
|
||||||
|
return;
|
||||||
|
} else if (price > credits->tonBalance()) {
|
||||||
|
const auto peer = item->history()->peer;
|
||||||
|
show->show(
|
||||||
|
Box(HistoryView::InsufficientTonBox, peer, price));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!credits->loaded()) {
|
||||||
|
credits->load();
|
||||||
|
return;
|
||||||
|
} else if (price > credits->balance()) {
|
||||||
|
using namespace Settings;
|
||||||
|
const auto peer = item->history()->peer;
|
||||||
|
const auto broadcast = peer->monoforumBroadcast();
|
||||||
|
const auto broadcastId = (broadcast ? broadcast : peer)->id;
|
||||||
|
const auto done = [=](SmallBalanceResult result) {
|
||||||
|
if (result == SmallBalanceResult::Success
|
||||||
|
|| result == SmallBalanceResult::Already) {
|
||||||
|
const auto item = peer->owner().message(id);
|
||||||
|
if (item) {
|
||||||
|
ConfirmApproval(
|
||||||
|
show,
|
||||||
|
item,
|
||||||
|
scheduleDate,
|
||||||
|
accepted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MaybeRequestBalanceIncrease(
|
||||||
|
show,
|
||||||
|
int(base::SafeRound(price.value())),
|
||||||
|
SmallBalanceForSuggest{ broadcastId },
|
||||||
|
done);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto peer = item->history()->peer;
|
||||||
|
const auto session = &peer->session();
|
||||||
|
const auto broadcast = peer->monoforumBroadcast();
|
||||||
|
const auto channelName = (broadcast ? broadcast : peer)->name();
|
||||||
|
const auto amount = admin
|
||||||
|
? HistoryView::PriceAfterCommission(session, price)
|
||||||
|
: price;
|
||||||
|
const auto commission = HistoryView::FormatAfterCommissionPercent(
|
||||||
|
session,
|
||||||
|
price);
|
||||||
|
const auto date = langDateTime(base::unixtime::parse(scheduleDate));
|
||||||
|
show->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto callback = std::make_shared<Fn<void()>>();
|
||||||
|
auto text = admin
|
||||||
|
? tr::lng_suggest_accept_text(
|
||||||
|
tr::now,
|
||||||
|
lt_from,
|
||||||
|
Ui::Text::Bold(item->from()->shortName()),
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: tr::lng_suggest_accept_text_to(
|
||||||
|
tr::now,
|
||||||
|
lt_channel,
|
||||||
|
Ui::Text::Bold(channelName),
|
||||||
|
Ui::Text::WithEntities);
|
||||||
|
if (price) {
|
||||||
|
text.append("\n\n").append(admin
|
||||||
|
? (scheduleDate
|
||||||
|
? (amount.stars()
|
||||||
|
? tr::lng_suggest_accept_receive_stars
|
||||||
|
: tr::lng_suggest_accept_receive_ton)(
|
||||||
|
tr::now,
|
||||||
|
lt_count_decimal,
|
||||||
|
amount.value(),
|
||||||
|
lt_channel,
|
||||||
|
Ui::Text::Bold(channelName),
|
||||||
|
lt_percent,
|
||||||
|
TextWithEntities{ commission },
|
||||||
|
lt_date,
|
||||||
|
Ui::Text::Bold(date),
|
||||||
|
Ui::Text::RichLangValue)
|
||||||
|
: (amount.stars()
|
||||||
|
? tr::lng_suggest_accept_receive_now_stars
|
||||||
|
: tr::lng_suggest_accept_receive_now_ton)(
|
||||||
|
tr::now,
|
||||||
|
lt_count_decimal,
|
||||||
|
amount.value(),
|
||||||
|
lt_channel,
|
||||||
|
Ui::Text::Bold(channelName),
|
||||||
|
lt_percent,
|
||||||
|
TextWithEntities{ commission },
|
||||||
|
Ui::Text::RichLangValue))
|
||||||
|
: (scheduleDate
|
||||||
|
? (amount.stars()
|
||||||
|
? tr::lng_suggest_accept_pay_stars
|
||||||
|
: tr::lng_suggest_accept_pay_ton)(
|
||||||
|
tr::now,
|
||||||
|
lt_count_decimal,
|
||||||
|
amount.value(),
|
||||||
|
lt_date,
|
||||||
|
Ui::Text::Bold(date),
|
||||||
|
Ui::Text::RichLangValue)
|
||||||
|
: (amount.stars()
|
||||||
|
? tr::lng_suggest_accept_pay_now_stars
|
||||||
|
: tr::lng_suggest_accept_pay_now_ton)(
|
||||||
|
tr::now,
|
||||||
|
lt_count_decimal,
|
||||||
|
amount.value(),
|
||||||
|
Ui::Text::RichLangValue)));
|
||||||
|
if (admin) {
|
||||||
|
text.append(' ').append(
|
||||||
|
tr::lng_suggest_accept_receive_if(
|
||||||
|
tr::now,
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
|
if (price.stars()) {
|
||||||
|
text.append("\n\n").append(
|
||||||
|
tr::lng_suggest_options_stars_warning(
|
||||||
|
tr::now,
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ui::ConfirmBox(box, {
|
||||||
|
.text = text,
|
||||||
|
.confirmed = [=](Fn<void()> close) { (*callback)(); close(); },
|
||||||
|
.confirmText = tr::lng_suggest_accept_send(),
|
||||||
|
.title = tr::lng_suggest_accept_title(),
|
||||||
|
});
|
||||||
|
*callback = [=, weak = Ui::MakeWeak(box)] {
|
||||||
|
if (const auto onstack = accepted) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
const auto item = show->session().data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SendApproval(show, item, scheduleDate);
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendDecline(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const QString &comment) {
|
||||||
|
using Flag = MTPmessages_ToggleSuggestedPostApproval::Flag;
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion
|
||||||
|
|| suggestion->accepted
|
||||||
|
|| suggestion->rejected
|
||||||
|
|| suggestion->requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto session = &show->session();
|
||||||
|
const auto finish = [=] {
|
||||||
|
if (const auto item = session->data().message(id)) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (suggestion) {
|
||||||
|
suggestion->requestId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
suggestion->requestId = session->api().request(
|
||||||
|
MTPmessages_ToggleSuggestedPostApproval(
|
||||||
|
MTP_flags(Flag::f_reject
|
||||||
|
| (comment.isEmpty() ? Flag() : Flag::f_reject_comment)),
|
||||||
|
item->history()->peer->input,
|
||||||
|
MTP_int(item->id.bare),
|
||||||
|
MTPint(), // schedule_date
|
||||||
|
MTP_string(comment))
|
||||||
|
).done([=](const MTPUpdates &result) {
|
||||||
|
session->api().applyUpdates(result);
|
||||||
|
finish();
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
show->showToast(error.type());
|
||||||
|
finish();
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestApprovalDate(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||||
|
const auto close = [=] {
|
||||||
|
if (const auto strong = weak->data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto done = [=](TimeId result) {
|
||||||
|
if (const auto item = show->session().data().message(id)) {
|
||||||
|
ConfirmApproval(show, item, result, close);
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using namespace HistoryView;
|
||||||
|
auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{
|
||||||
|
.session = &show->session(),
|
||||||
|
.done = done,
|
||||||
|
.mode = SuggestMode::Publish,
|
||||||
|
});
|
||||||
|
*weak = dateBox.data();
|
||||||
|
show->show(std::move(dateBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDeclineComment(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto admin = item->history()->amMonoforumAdmin();
|
||||||
|
const auto peer = item->history()->peer;
|
||||||
|
const auto broadcast = peer->monoforumBroadcast();
|
||||||
|
const auto channelName = (broadcast ? broadcast : peer)->name();
|
||||||
|
show->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto callback = std::make_shared<Fn<void()>>();
|
||||||
|
Ui::ConfirmBox(box, {
|
||||||
|
.text = (admin
|
||||||
|
? tr::lng_suggest_decline_text(
|
||||||
|
lt_from,
|
||||||
|
rpl::single(Ui::Text::Bold(item->from()->shortName())),
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: tr::lng_suggest_decline_text_to(
|
||||||
|
lt_channel,
|
||||||
|
rpl::single(Ui::Text::Bold(channelName)),
|
||||||
|
Ui::Text::WithEntities)),
|
||||||
|
.confirmed = [=](Fn<void()> close) { (*callback)(); close(); },
|
||||||
|
.confirmText = tr::lng_suggest_action_decline(),
|
||||||
|
.confirmStyle = &st::attentionBoxButton,
|
||||||
|
.title = tr::lng_suggest_decline_title(),
|
||||||
|
});
|
||||||
|
const auto reason = box->addRow(object_ptr<Ui::InputField>(
|
||||||
|
box,
|
||||||
|
st::factcheckField,
|
||||||
|
Ui::InputField::Mode::NoNewlines,
|
||||||
|
tr::lng_suggest_decline_reason()));
|
||||||
|
box->setFocusCallback([=] {
|
||||||
|
reason->setFocusFast();
|
||||||
|
});
|
||||||
|
*callback = [=, weak = Ui::MakeWeak(box)] {
|
||||||
|
const auto item = show->session().data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SendDecline(show, item, reason->getLastText().trimmed());
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reason->submits(
|
||||||
|
) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
|
||||||
|
if (!(modifiers & Qt::ShiftModifier)) {
|
||||||
|
(*callback)();
|
||||||
|
}
|
||||||
|
}, box->lifetime());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SendSuggestState {
|
||||||
|
SendPaymentHelper sendPayment;
|
||||||
|
};
|
||||||
|
void SendSuggest(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
std::shared_ptr<SendSuggestState> state,
|
||||||
|
Fn<void(SuggestPostOptions&)> modify,
|
||||||
|
Fn<void()> done = nullptr,
|
||||||
|
int starsApproved = 0) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto withPaymentApproved = [=](int stars) {
|
||||||
|
if (const auto item = show->session().data().message(id)) {
|
||||||
|
SendSuggest(show, item, state, modify, done, stars);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto isForward = item->Get<HistoryMessageForwarded>();
|
||||||
|
auto action = SendAction(item->history());
|
||||||
|
action.options.suggest.exists = 1;
|
||||||
|
if (suggestion) {
|
||||||
|
action.options.suggest.date = suggestion->date;
|
||||||
|
action.options.suggest.priceWhole = suggestion->price.whole();
|
||||||
|
action.options.suggest.priceNano = suggestion->price.nano();
|
||||||
|
action.options.suggest.ton = suggestion->price.ton() ? 1 : 0;
|
||||||
|
}
|
||||||
|
modify(action.options.suggest);
|
||||||
|
action.options.starsApproved = starsApproved;
|
||||||
|
action.replyTo.monoforumPeerId = item->history()->amMonoforumAdmin()
|
||||||
|
? item->sublistPeerId()
|
||||||
|
: PeerId();
|
||||||
|
action.replyTo.messageId = item->fullId();
|
||||||
|
|
||||||
|
const auto checked = state->sendPayment.check(
|
||||||
|
show,
|
||||||
|
item->history()->peer,
|
||||||
|
action.options,
|
||||||
|
1,
|
||||||
|
withPaymentApproved);
|
||||||
|
if (!checked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
show->session().api().sendAction(action);
|
||||||
|
show->session().api().forwardMessages({
|
||||||
|
.items = { item },
|
||||||
|
.options = (isForward
|
||||||
|
? Data::ForwardOptions::PreserveInfo
|
||||||
|
: Data::ForwardOptions::NoSenderNames),
|
||||||
|
}, action);
|
||||||
|
if (const auto onstack = done) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuggestApprovalDate(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto state = std::make_shared<SendSuggestState>();
|
||||||
|
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||||
|
const auto done = [=](TimeId result) {
|
||||||
|
const auto item = show->session().data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto close = [=] {
|
||||||
|
if (const auto strong = weak->data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
SendSuggest(
|
||||||
|
show,
|
||||||
|
item,
|
||||||
|
state,
|
||||||
|
[=](SuggestPostOptions &options) { options.date = result; },
|
||||||
|
close);
|
||||||
|
};
|
||||||
|
using namespace HistoryView;
|
||||||
|
auto dateBox = Box(ChooseSuggestTimeBox, SuggestTimeBoxArgs{
|
||||||
|
.session = &show->session(),
|
||||||
|
.done = done,
|
||||||
|
.value = suggestion->date,
|
||||||
|
.mode = SuggestMode::Change,
|
||||||
|
});
|
||||||
|
*weak = dateBox.data();
|
||||||
|
show->show(std::move(dateBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuggestOfferForMessage(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
SuggestPostOptions values,
|
||||||
|
HistoryView::SuggestMode mode) {
|
||||||
|
const auto id = item->fullId();
|
||||||
|
const auto state = std::make_shared<SendSuggestState>();
|
||||||
|
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||||
|
const auto done = [=](SuggestPostOptions result) {
|
||||||
|
const auto item = show->session().data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto close = [=] {
|
||||||
|
if (const auto strong = weak->data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
SendSuggest(
|
||||||
|
show,
|
||||||
|
item,
|
||||||
|
state,
|
||||||
|
[=](SuggestPostOptions &options) { options = result; },
|
||||||
|
close);
|
||||||
|
};
|
||||||
|
using namespace HistoryView;
|
||||||
|
auto priceBox = Box(ChooseSuggestPriceBox, SuggestPriceBoxArgs{
|
||||||
|
.peer = item->history()->peer,
|
||||||
|
.done = done,
|
||||||
|
.value = values,
|
||||||
|
.mode = mode,
|
||||||
|
});
|
||||||
|
*weak = priceBox.data();
|
||||||
|
show->show(std::move(priceBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuggestApprovalPrice(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using namespace HistoryView;
|
||||||
|
SuggestOfferForMessage(show, item, {
|
||||||
|
.exists = uint32(1),
|
||||||
|
.priceWhole = uint32(suggestion->price.whole()),
|
||||||
|
.priceNano = uint32(suggestion->price.nano()),
|
||||||
|
.ton = uint32(suggestion->price.ton() ? 1 : 0),
|
||||||
|
.date = suggestion->date,
|
||||||
|
}, SuggestMode::Change);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::shared_ptr<ClickHandler> AcceptClickHandler(
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
const auto id = item->fullId();
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
const auto controller = my.sessionWindow.get();
|
||||||
|
if (!controller || &controller->session() != session) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto item = session->data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto show = controller->uiShow();
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion) {
|
||||||
|
return;
|
||||||
|
} else if (!suggestion->date) {
|
||||||
|
RequestApprovalDate(show, item);
|
||||||
|
} else {
|
||||||
|
ConfirmApproval(show, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ClickHandler> DeclineClickHandler(
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
const auto id = item->fullId();
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
const auto controller = my.sessionWindow.get();
|
||||||
|
if (!controller || &controller->session() != session) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto item = session->data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RequestDeclineComment(controller->uiShow(), item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ClickHandler> SuggestChangesClickHandler(
|
||||||
|
not_null<HistoryItem*> item) {
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
const auto id = item->fullId();
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
const auto window = my.sessionWindow.get();
|
||||||
|
if (!window || &window->session() != session) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto item = session->data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto menu = Ui::CreateChild<Ui::PopupMenu>(
|
||||||
|
window->widget(),
|
||||||
|
st::popupMenuWithIcons);
|
||||||
|
if (HistoryView::CanEditSuggestedMessage(item)) {
|
||||||
|
menu->addAction(tr::lng_suggest_menu_edit_message(tr::now), [=] {
|
||||||
|
const auto item = session->data().message(id);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||||
|
if (!suggestion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto history = item->history();
|
||||||
|
const auto editData = PrepareEditText(item);
|
||||||
|
const auto cursor = MessageCursor{
|
||||||
|
int(editData.text.size()),
|
||||||
|
int(editData.text.size()),
|
||||||
|
Ui::kQFixedMax
|
||||||
|
};
|
||||||
|
const auto monoforumPeerId = history->amMonoforumAdmin()
|
||||||
|
? item->sublistPeerId()
|
||||||
|
: PeerId();
|
||||||
|
const auto previewDraft = Data::WebPageDraft::FromItem(item);
|
||||||
|
history->setLocalEditDraft(std::make_unique<Data::Draft>(
|
||||||
|
editData,
|
||||||
|
FullReplyTo{
|
||||||
|
.messageId = FullMsgId(history->peer->id, item->id),
|
||||||
|
.monoforumPeerId = monoforumPeerId,
|
||||||
|
},
|
||||||
|
SuggestPostOptions{
|
||||||
|
.exists = uint32(1),
|
||||||
|
.priceWhole = uint32(suggestion->price.whole()),
|
||||||
|
.priceNano = uint32(suggestion->price.nano()),
|
||||||
|
.ton = uint32(suggestion->price.ton() ? 1 : 0),
|
||||||
|
.date = suggestion->date,
|
||||||
|
},
|
||||||
|
cursor,
|
||||||
|
previewDraft));
|
||||||
|
history->session().changes().entryUpdated(
|
||||||
|
(monoforumPeerId
|
||||||
|
? item->savedSublist()
|
||||||
|
: (Data::Thread*)history.get()),
|
||||||
|
Data::EntryUpdate::Flag::LocalDraftSet);
|
||||||
|
}, &st::menuIconEdit);
|
||||||
|
}
|
||||||
|
menu->addAction(tr::lng_suggest_menu_edit_price(tr::now), [=] {
|
||||||
|
if (const auto item = session->data().message(id)) {
|
||||||
|
SuggestApprovalPrice(window->uiShow(), item);
|
||||||
|
}
|
||||||
|
}, &st::menuIconTagSell);
|
||||||
|
menu->addAction(tr::lng_suggest_menu_edit_time(tr::now), [=] {
|
||||||
|
if (const auto item = session->data().message(id)) {
|
||||||
|
SuggestApprovalDate(window->uiShow(), item);
|
||||||
|
}
|
||||||
|
}, &st::menuIconSchedule);
|
||||||
|
menu->popup(QCursor::pos());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddOfferToMessage(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
FullMsgId itemId) {
|
||||||
|
const auto session = &show->session();
|
||||||
|
const auto item = session->data().message(itemId);
|
||||||
|
if (!item || !HistoryView::CanAddOfferToMessage(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SuggestOfferForMessage(show, item, {}, HistoryView::SuggestMode::New);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Api
|
29
Telegram/SourceFiles/api/api_suggest_post.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class ClickHandler;
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class SessionShow;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<ClickHandler> AcceptClickHandler(
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
[[nodiscard]] std::shared_ptr<ClickHandler> DeclineClickHandler(
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
[[nodiscard]] std::shared_ptr<ClickHandler> SuggestChangesClickHandler(
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
void AddOfferToMessage(
|
||||||
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
|
FullMsgId itemId);
|
||||||
|
|
||||||
|
} // namespace Api
|
257
Telegram/SourceFiles/api/api_todo_lists.cpp
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
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_todo_lists.h"
|
||||||
|
|
||||||
|
#include "api/api_editing.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/random.h"
|
||||||
|
#include "data/business/data_shortcut_messages.h" // ShortcutIdToMTP
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_histories.h"
|
||||||
|
#include "data/data_todo_list.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/history_item_helpers.h" // ShouldSendSilent
|
||||||
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kSendTogglesDelay = 3 * crl::time(1000);
|
||||||
|
|
||||||
|
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||||
|
return TimeId(msgId >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TodoLists::TodoLists(not_null<ApiWrap*> api)
|
||||||
|
: _session(&api->session())
|
||||||
|
, _api(&api->instance())
|
||||||
|
, _sendTimer([=] { sendAccumulatedToggles(false); }) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::create(
|
||||||
|
const TodoListData &data,
|
||||||
|
SendAction action,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail) {
|
||||||
|
_session->api().sendAction(action);
|
||||||
|
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
const auto topicRootId = action.replyTo.messageId
|
||||||
|
? action.replyTo.topicRootId
|
||||||
|
: 0;
|
||||||
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
|
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||||
|
if (action.replyTo) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||||
|
}
|
||||||
|
const auto clearCloudDraft = action.clearDraft;
|
||||||
|
if (clearCloudDraft) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||||
|
history->clearLocalDraft(topicRootId, monoforumPeerId);
|
||||||
|
history->clearCloudDraft(topicRootId, monoforumPeerId);
|
||||||
|
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
|
||||||
|
}
|
||||||
|
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||||
|
const auto starsPaid = std::min(
|
||||||
|
peer->starsPerMessageChecked(),
|
||||||
|
action.options.starsApproved);
|
||||||
|
if (silentPost) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||||
|
}
|
||||||
|
if (action.options.scheduled) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||||
|
}
|
||||||
|
if (action.options.shortcutId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
|
||||||
|
}
|
||||||
|
if (action.options.effectId) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
|
if (starsPaid) {
|
||||||
|
action.options.starsApproved -= starsPaid;
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
|
||||||
|
}
|
||||||
|
const auto sendAs = action.options.sendAs;
|
||||||
|
if (sendAs) {
|
||||||
|
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
|
||||||
|
}
|
||||||
|
auto &histories = history->owner().histories();
|
||||||
|
const auto randomId = base::RandomValue<uint64>();
|
||||||
|
histories.sendPreparedMessage(
|
||||||
|
history,
|
||||||
|
action.replyTo,
|
||||||
|
randomId,
|
||||||
|
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||||
|
MTP_flags(sendFlags),
|
||||||
|
peer->input,
|
||||||
|
Data::Histories::ReplyToPlaceholder(),
|
||||||
|
TodoListDataToInputMedia(&data),
|
||||||
|
MTP_string(),
|
||||||
|
MTP_long(randomId),
|
||||||
|
MTPReplyMarkup(),
|
||||||
|
MTPVector<MTPMessageEntity>(),
|
||||||
|
MTP_int(action.options.scheduled),
|
||||||
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||||
|
MTP_long(action.options.effectId),
|
||||||
|
MTP_long(starsPaid),
|
||||||
|
SuggestToMTP(action.options.suggest)
|
||||||
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
|
if (clearCloudDraft) {
|
||||||
|
history->finishSavingCloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
|
}
|
||||||
|
_session->changes().historyUpdated(
|
||||||
|
history,
|
||||||
|
(action.options.scheduled
|
||||||
|
? Data::HistoryUpdate::Flag::ScheduledSent
|
||||||
|
: Data::HistoryUpdate::Flag::MessageSent));
|
||||||
|
if (const auto onstack = done) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||||
|
if (clearCloudDraft) {
|
||||||
|
history->finishSavingCloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
|
}
|
||||||
|
if (const auto onstack = fail) {
|
||||||
|
onstack(error.type());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::edit(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TodoListData &data,
|
||||||
|
SendOptions options,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail) {
|
||||||
|
EditTodoList(item, data, options, [=](mtpRequestId) {
|
||||||
|
if (const auto onstack = done) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
}, [=](const QString &error, mtpRequestId) {
|
||||||
|
if (const auto onstack = fail) {
|
||||||
|
onstack(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::add(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const std::vector<TodoListItem> &items,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail) {
|
||||||
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto session = _session;
|
||||||
|
_session->api().request(MTPmessages_AppendTodoList(
|
||||||
|
item->history()->peer->input,
|
||||||
|
MTP_int(item->id.bare),
|
||||||
|
TodoListItemsToMTP(&item->history()->session(), items)
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
session->api().applyUpdates(result);
|
||||||
|
if (const auto onstack = done) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
if (const auto onstack = fail) {
|
||||||
|
onstack(error.type());
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::toggleCompletion(FullMsgId itemId, int id, bool completed) {
|
||||||
|
auto &entry = _toggles[itemId];
|
||||||
|
if (completed) {
|
||||||
|
const auto changed1 = entry.completed.emplace(id).second;
|
||||||
|
const auto changed2 = entry.incompleted.remove(id);
|
||||||
|
if (!changed1 && !changed2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto changed1 = entry.incompleted.emplace(id).second;
|
||||||
|
const auto changed2 = entry.completed.remove(id);
|
||||||
|
if (!changed1 && !changed2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.scheduled = crl::now();
|
||||||
|
if (!entry.requestId && !_sendTimer.isActive()) {
|
||||||
|
_sendTimer.callOnce(kSendTogglesDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::sendAccumulatedToggles(bool force) {
|
||||||
|
const auto now = crl::now();
|
||||||
|
auto nearest = crl::time(0);
|
||||||
|
for (auto &[itemId, entry] : _toggles) {
|
||||||
|
if (entry.requestId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto wait = entry.scheduled + kSendTogglesDelay - now;
|
||||||
|
if (wait <= 0) {
|
||||||
|
entry.scheduled = 0;
|
||||||
|
send(itemId, entry);
|
||||||
|
} else if (!nearest || nearest > wait) {
|
||||||
|
nearest = wait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearest > 0) {
|
||||||
|
_sendTimer.callOnce(nearest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::send(FullMsgId itemId, Accumulated &entry) {
|
||||||
|
const auto item = _session->data().message(itemId);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto completed = entry.completed
|
||||||
|
| ranges::views::transform([](int id) { return MTP_int(id); });
|
||||||
|
auto incompleted = entry.incompleted
|
||||||
|
| ranges::views::transform([](int id) { return MTP_int(id); });
|
||||||
|
entry.requestId = _api.request(MTPmessages_ToggleTodoCompleted(
|
||||||
|
item->history()->peer->input,
|
||||||
|
MTP_int(item->id),
|
||||||
|
MTP_vector_from_range(completed),
|
||||||
|
MTP_vector_from_range(incompleted)
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
_session->api().applyUpdates(result);
|
||||||
|
finishRequest(itemId);
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
finishRequest(itemId);
|
||||||
|
}).send();
|
||||||
|
entry.completed.clear();
|
||||||
|
entry.incompleted.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TodoLists::finishRequest(FullMsgId itemId) {
|
||||||
|
auto &entry = _toggles[itemId];
|
||||||
|
entry.requestId = 0;
|
||||||
|
if (entry.completed.empty() && entry.incompleted.empty()) {
|
||||||
|
_toggles.remove(itemId);
|
||||||
|
} else {
|
||||||
|
sendAccumulatedToggles(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Api
|
69
Telegram/SourceFiles/api/api_todo_lists.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
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/timer.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
|
class ApiWrap;
|
||||||
|
class HistoryItem;
|
||||||
|
struct TodoListItem;
|
||||||
|
struct TodoListData;
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
|
||||||
|
struct SendAction;
|
||||||
|
struct SendOptions;
|
||||||
|
|
||||||
|
class TodoLists final {
|
||||||
|
public:
|
||||||
|
explicit TodoLists(not_null<ApiWrap*> api);
|
||||||
|
|
||||||
|
void create(
|
||||||
|
const TodoListData &data,
|
||||||
|
SendAction action,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail);
|
||||||
|
void edit(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const TodoListData &data,
|
||||||
|
SendOptions options,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail);
|
||||||
|
void add(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const std::vector<TodoListItem> &items,
|
||||||
|
Fn<void()> done,
|
||||||
|
Fn<void(QString)> fail);
|
||||||
|
void toggleCompletion(FullMsgId itemId, int id, bool completed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Accumulated {
|
||||||
|
base::flat_set<int> completed;
|
||||||
|
base::flat_set<int> incompleted;
|
||||||
|
crl::time scheduled = 0;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void sendAccumulatedToggles(bool force);
|
||||||
|
void send(FullMsgId itemId, Accumulated &entry);
|
||||||
|
void finishRequest(FullMsgId itemId);
|
||||||
|
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
MTP::Sender _api;
|
||||||
|
|
||||||
|
base::flat_map<FullMsgId, Accumulated> _toggles;
|
||||||
|
base::Timer _sendTimer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Api
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
@ -17,6 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_unread_things.h"
|
#include "history/history_unread_things.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
// AyuGram includes
|
||||||
|
#include "ayu/ayu_settings.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -31,12 +36,24 @@ UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
|
||||||
|
|
||||||
bool UnreadThings::trackMentions(Data::Thread *thread) const {
|
bool UnreadThings::trackMentions(Data::Thread *thread) const {
|
||||||
const auto peer = thread ? thread->peer().get() : nullptr;
|
const auto peer = thread ? thread->peer().get() : nullptr;
|
||||||
return peer && (peer->isChat() || peer->isMegagroup());
|
return peer
|
||||||
|
&& (peer->isChat() || peer->isMegagroup())
|
||||||
|
&& !peer->isMonoforum();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnreadThings::trackReactions(Data::Thread *thread) const {
|
bool UnreadThings::trackReactions(Data::Thread *thread) const {
|
||||||
const auto peer = thread ? thread->peer().get() : nullptr;
|
const auto peer = thread ? thread->peer().get() : nullptr;
|
||||||
return peer && (peer->isUser() || peer->isChat() || peer->isMegagroup());
|
if (!peer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto &settings = AyuSettings::getInstance();
|
||||||
|
if (peer->isChannel() && !peer->isMegagroup() && !settings.hideChannelReactions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (peer->isMegagroup() && !settings.hideGroupReactions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return peer->isUser() || peer->isChat() || peer->isMegagroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnreadThings::preloadEnough(Data::Thread *thread) {
|
void UnreadThings::preloadEnough(Data::Thread *thread) {
|
||||||
|
@ -93,7 +110,7 @@ void UnreadThings::cancelRequests(not_null<Data::Thread*> thread) {
|
||||||
void UnreadThings::requestMentions(
|
void UnreadThings::requestMentions(
|
||||||
not_null<Data::Thread*> thread,
|
not_null<Data::Thread*> thread,
|
||||||
int loaded) {
|
int loaded) {
|
||||||
if (_mentionsRequests.contains(thread)) {
|
if (_mentionsRequests.contains(thread) || thread->asSublist()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto offsetId = std::max(
|
const auto offsetId = std::max(
|
||||||
|
@ -138,12 +155,15 @@ void UnreadThings::requestReactions(
|
||||||
const auto maxId = 0;
|
const auto maxId = 0;
|
||||||
const auto minId = 0;
|
const auto minId = 0;
|
||||||
const auto history = thread->owningHistory();
|
const auto history = thread->owningHistory();
|
||||||
|
const auto sublist = thread->asSublist();
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
using Flag = MTPmessages_GetUnreadReactions::Flag;
|
using Flag = MTPmessages_GetUnreadReactions::Flag;
|
||||||
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
||||||
MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
|
MTP_flags((topic ? Flag::f_top_msg_id : Flag())
|
||||||
|
| (sublist ? Flag::f_saved_peer_id : Flag())),
|
||||||
history->peer->input,
|
history->peer->input,
|
||||||
MTP_int(topic ? topic->rootId() : 0),
|
MTP_int(topic ? topic->rootId() : 0),
|
||||||
|
(sublist ? sublist->sublistPeer()->input : MTPInputPeer()),
|
||||||
MTP_int(offsetId),
|
MTP_int(offsetId),
|
||||||
MTP_int(addOffset),
|
MTP_int(addOffset),
|
||||||
MTP_int(limit),
|
MTP_int(limit),
|
||||||
|
|
|
@ -998,7 +998,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// AyuGram sendOnlinePackets
|
// AyuGram sendOnlinePackets
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
const auto& config = _session->serverConfig();
|
const auto& config = _session->serverConfig();
|
||||||
bool isOnlineOrig = Core::App().hasActiveWindow(&session());
|
bool isOnlineOrig = Core::App().hasActiveWindow(&session());
|
||||||
bool isOnline = settings.sendOnlinePackets && isOnlineOrig;
|
bool isOnline = settings.sendOnlinePackets && isOnlineOrig;
|
||||||
|
@ -1236,7 +1236,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||||
MTPlong(), // effect
|
MTPlong(), // effect
|
||||||
MTPFactCheck(),
|
MTPFactCheck(),
|
||||||
MTPint(), // report_delivery_until_date
|
MTPint(), // report_delivery_until_date
|
||||||
MTPlong()), // paid_message_stars
|
MTPlong(), // paid_message_stars
|
||||||
|
MTPSuggestedPost()),
|
||||||
MessageFlags(),
|
MessageFlags(),
|
||||||
NewMessageType::Unread);
|
NewMessageType::Unread);
|
||||||
} break;
|
} break;
|
||||||
|
@ -1275,7 +1276,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||||
MTPlong(), // effect
|
MTPlong(), // effect
|
||||||
MTPFactCheck(),
|
MTPFactCheck(),
|
||||||
MTPint(), // report_delivery_until_date
|
MTPint(), // report_delivery_until_date
|
||||||
MTPlong()), // paid_message_stars
|
MTPlong(), // paid_message_stars
|
||||||
|
MTPSuggestedPost()),
|
||||||
MessageFlags(),
|
MessageFlags(),
|
||||||
NewMessageType::Unread);
|
NewMessageType::Unread);
|
||||||
} break;
|
} break;
|
||||||
|
@ -1924,7 +1926,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
|
|
||||||
// Update web page anyway.
|
// Update web page anyway.
|
||||||
session().data().processWebpage(d.vwebpage());
|
session().data().processWebpage(d.vwebpage());
|
||||||
session().data().sendWebPageGamePollNotifications();
|
session().data().sendWebPageGamePollTodoListNotifications();
|
||||||
|
|
||||||
updateAndApply(d.vpts().v, d.vpts_count().v, update);
|
updateAndApply(d.vpts().v, d.vpts_count().v, update);
|
||||||
} break;
|
} break;
|
||||||
|
@ -1934,7 +1936,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
|
|
||||||
// Update web page anyway.
|
// Update web page anyway.
|
||||||
session().data().processWebpage(d.vwebpage());
|
session().data().processWebpage(d.vwebpage());
|
||||||
session().data().sendWebPageGamePollNotifications();
|
session().data().sendWebPageGamePollTodoListNotifications();
|
||||||
|
|
||||||
auto channel = session().data().channelLoaded(d.vchannel_id());
|
auto channel = session().data().channelLoaded(d.vchannel_id());
|
||||||
if (channel && !_handlingChannelDifference) {
|
if (channel && !_handlingChannelDifference) {
|
||||||
|
@ -2450,6 +2452,32 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
session().data().updateRepliesReadTill({ id, readTillId, true });
|
session().data().updateRepliesReadTill({ id, readTillId, true });
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updateReadMonoForumInbox: {
|
||||||
|
const auto &d = update.c_updateReadMonoForumInbox();
|
||||||
|
const auto parentChatId = ChannelId(d.vchannel_id());
|
||||||
|
const auto sublistPeerId = peerFromMTP(d.vsaved_peer_id());
|
||||||
|
const auto readTillId = d.vread_max_id().v;
|
||||||
|
session().data().updateSublistReadTill({
|
||||||
|
parentChatId,
|
||||||
|
sublistPeerId,
|
||||||
|
readTillId,
|
||||||
|
false,
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updateReadMonoForumOutbox: {
|
||||||
|
const auto &d = update.c_updateReadMonoForumOutbox();
|
||||||
|
const auto parentChatId = ChannelId(d.vchannel_id());
|
||||||
|
const auto sublistPeerId = peerFromMTP(d.vsaved_peer_id());
|
||||||
|
const auto readTillId = d.vread_max_id().v;
|
||||||
|
session().data().updateSublistReadTill({
|
||||||
|
parentChatId,
|
||||||
|
sublistPeerId,
|
||||||
|
readTillId,
|
||||||
|
true,
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
case mtpc_updateChannelAvailableMessages: {
|
case mtpc_updateChannelAvailableMessages: {
|
||||||
auto &d = update.c_updateChannelAvailableMessages();
|
auto &d = update.c_updateChannelAvailableMessages();
|
||||||
if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
|
if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
|
||||||
|
@ -2669,13 +2697,22 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
const auto &data = update.c_updateDraftMessage();
|
const auto &data = update.c_updateDraftMessage();
|
||||||
const auto peerId = peerFromMTP(data.vpeer());
|
const auto peerId = peerFromMTP(data.vpeer());
|
||||||
const auto topicRootId = data.vtop_msg_id().value_or_empty();
|
const auto topicRootId = data.vtop_msg_id().value_or_empty();
|
||||||
|
const auto monoforumPeerId = data.vsaved_peer_id()
|
||||||
|
? peerFromMTP(*data.vsaved_peer_id())
|
||||||
|
: PeerId();
|
||||||
data.vdraft().match([&](const MTPDdraftMessage &data) {
|
data.vdraft().match([&](const MTPDdraftMessage &data) {
|
||||||
Data::ApplyPeerCloudDraft(&session(), peerId, topicRootId, data);
|
Data::ApplyPeerCloudDraft(
|
||||||
|
&session(),
|
||||||
|
peerId,
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
data);
|
||||||
}, [&](const MTPDdraftMessageEmpty &data) {
|
}, [&](const MTPDdraftMessageEmpty &data) {
|
||||||
Data::ClearPeerCloudDraft(
|
Data::ClearPeerCloudDraft(
|
||||||
&session(),
|
&session(),
|
||||||
peerId,
|
peerId,
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
data.vdate().value_or_empty());
|
data.vdate().value_or_empty());
|
||||||
});
|
});
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -712,7 +712,8 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||||
const auto megagroup = peer->asMegagroup();
|
const auto megagroup = peer->asMegagroup();
|
||||||
if ((!chat && !megagroup)
|
if ((!chat && !megagroup)
|
||||||
|| (megagroup
|
|| (megagroup
|
||||||
&& (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) {
|
&& (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))
|
||||||
|
|| (megagroup && megagroup->isMonoforum())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto &appConfig = peer->session().appConfig();
|
const auto &appConfig = peer->session().appConfig();
|
||||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_polls.h"
|
#include "api/api_polls.h"
|
||||||
#include "api/api_sending.h"
|
#include "api/api_sending.h"
|
||||||
#include "api/api_text_entities.h"
|
#include "api/api_text_entities.h"
|
||||||
|
#include "api/api_todo_lists.h"
|
||||||
#include "api/api_self_destruct.h"
|
#include "api/api_self_destruct.h"
|
||||||
#include "api/api_sensitive_content.h"
|
#include "api/api_sensitive_content.h"
|
||||||
#include "api/api_global_privacy.h"
|
#include "api/api_global_privacy.h"
|
||||||
|
@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
|
#include "data/data_saved_messages.h"
|
||||||
#include "data/data_saved_sublist.h"
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_search_controller.h"
|
#include "data/data_search_controller.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -90,6 +92,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ayu/ayu_settings.h"
|
#include "ayu/ayu_settings.h"
|
||||||
#include "ayu/ayu_worker.h"
|
#include "ayu/ayu_worker.h"
|
||||||
#include "ayu/utils/telegram_helpers.h"
|
#include "ayu/utils/telegram_helpers.h"
|
||||||
|
#include "ayu/features/forward/ayu_forward.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -183,6 +186,7 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||||
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
|
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
|
||||||
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
|
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
|
||||||
, _polls(std::make_unique<Api::Polls>(this))
|
, _polls(std::make_unique<Api::Polls>(this))
|
||||||
|
, _todoLists(std::make_unique<Api::TodoLists>(this))
|
||||||
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this))
|
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this))
|
||||||
, _unreadThings(std::make_unique<Api::UnreadThings>(this))
|
, _unreadThings(std::make_unique<Api::UnreadThings>(this))
|
||||||
, _ringtones(std::make_unique<Api::Ringtones>(this))
|
, _ringtones(std::make_unique<Api::Ringtones>(this))
|
||||||
|
@ -327,7 +331,7 @@ void ApiWrap::checkChatInvite(
|
||||||
request(base::take(_checkInviteRequestId)).cancel();
|
request(base::take(_checkInviteRequestId)).cancel();
|
||||||
_checkInviteRequestId = request(MTPmessages_CheckChatInvite(
|
_checkInviteRequestId = request(MTPmessages_CheckChatInvite(
|
||||||
MTP_string(hash)
|
MTP_string(hash)
|
||||||
)).done(std::move(done)).fail(std::move(fail)).send();
|
)).done(std::move(done)).fail(std::move(fail)).handleFloodErrors().send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::checkFilterInvite(
|
void ApiWrap::checkFilterInvite(
|
||||||
|
@ -387,10 +391,13 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
|
void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
|
||||||
|
if (saved->parentChat()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto &order = _session->data().pinnedChatsOrder(saved);
|
const auto &order = _session->data().pinnedChatsOrder(saved);
|
||||||
const auto input = [](Dialogs::Key key) {
|
const auto input = [](Dialogs::Key key) {
|
||||||
if (const auto sublist = key.sublist()) {
|
if (const auto sublist = key.sublist()) {
|
||||||
return MTP_inputDialogPeer(sublist->peer()->input);
|
return MTP_inputDialogPeer(sublist->sublistPeer()->input);
|
||||||
}
|
}
|
||||||
Unexpected("Key type in pinnedDialogsOrder().");
|
Unexpected("Key type in pinnedDialogsOrder().");
|
||||||
};
|
};
|
||||||
|
@ -426,7 +433,7 @@ void ApiWrap::toggleHistoryArchived(
|
||||||
if (archived) {
|
if (archived) {
|
||||||
history->setFolder(_session->data().folder(archiveId));
|
history->setFolder(_session->data().folder(archiveId));
|
||||||
} else {
|
} else {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (settings.hideAllChatsFolder) {
|
if (settings.hideAllChatsFolder) {
|
||||||
if (const auto window = Core::App().activeWindow()) {
|
if (const auto window = Core::App().activeWindow()) {
|
||||||
if (const auto controller = window->sessionController()) {
|
if (const auto controller = window->sessionController()) {
|
||||||
|
@ -1303,7 +1310,7 @@ void ApiWrap::migrateFail(not_null<PeerData*> peer, const QString &error) {
|
||||||
|
|
||||||
void ApiWrap::markContentsRead(
|
void ApiWrap::markContentsRead(
|
||||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
|
|
||||||
auto markedIds = QVector<MTPint>();
|
auto markedIds = QVector<MTPint>();
|
||||||
auto channelMarkedIds = base::flat_map<
|
auto channelMarkedIds = base::flat_map<
|
||||||
|
@ -1349,7 +1356,7 @@ void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.sendReadMessages && !passthrough) {
|
if (!settings.sendReadMessages && !passthrough) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1403,6 +1410,32 @@ void ApiWrap::deleteAllFromParticipantSend(
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::deleteSublistHistory(
|
||||||
|
not_null<ChannelData*> channel,
|
||||||
|
not_null<PeerData*> sublistPeer) {
|
||||||
|
deleteSublistHistorySend(channel, sublistPeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::deleteSublistHistorySend(
|
||||||
|
not_null<ChannelData*> parentChat,
|
||||||
|
not_null<PeerData*> sublistPeer) {
|
||||||
|
request(MTPmessages_DeleteSavedHistory(
|
||||||
|
MTP_flags(MTPmessages_DeleteSavedHistory::Flag::f_parent_peer),
|
||||||
|
parentChat->input,
|
||||||
|
sublistPeer->input,
|
||||||
|
MTP_int(0), // max_id
|
||||||
|
MTP_int(0), // min_date
|
||||||
|
MTP_int(0) // max_date
|
||||||
|
)).done([=](const MTPmessages_AffectedHistory &result) {
|
||||||
|
const auto offset = applyAffectedHistory(parentChat, result);
|
||||||
|
if (offset > 0) {
|
||||||
|
deleteSublistHistorySend(parentChat, sublistPeer);
|
||||||
|
} else if (const auto monoforum = parentChat->monoforum()) {
|
||||||
|
monoforum->applySublistDeleted(sublistPeer);
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
|
void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
|
||||||
if (!_stickerSetRequests.contains(setId)) {
|
if (!_stickerSetRequests.contains(setId)) {
|
||||||
_stickerSetRequests.emplace(setId, StickerSetRequest{ access });
|
_stickerSetRequests.emplace(setId, StickerSetRequest{ access });
|
||||||
|
@ -1752,7 +1785,7 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
|
||||||
using Flag = ChannelDataFlag;
|
using Flag = ChannelDataFlag;
|
||||||
chatParticipants().loadSimilarPeers(channel);
|
chatParticipants().loadSimilarPeers(channel);
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.collapseSimilarChannels) {
|
if (!settings.collapseSimilarChannels) {
|
||||||
channel->setFlags(channel->flags() | Flag::SimilarExpanded);
|
channel->setFlags(channel->flags() | Flag::SimilarExpanded);
|
||||||
}
|
}
|
||||||
|
@ -2098,8 +2131,13 @@ void ApiWrap::saveCurrentDraftToCloud() {
|
||||||
_session->local().writeDrafts(history);
|
_session->local().writeDrafts(history);
|
||||||
|
|
||||||
const auto topicRootId = thread->topicRootId();
|
const auto topicRootId = thread->topicRootId();
|
||||||
const auto localDraft = history->localDraft(topicRootId);
|
const auto monoforumPeerId = thread->monoforumPeerId();
|
||||||
const auto cloudDraft = history->cloudDraft(topicRootId);
|
const auto localDraft = history->localDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId);
|
||||||
|
const auto cloudDraft = history->cloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId);
|
||||||
if (!Data::DraftsAreEqual(localDraft, cloudDraft)
|
if (!Data::DraftsAreEqual(localDraft, cloudDraft)
|
||||||
&& !_session->supportMode()) {
|
&& !_session->supportMode()) {
|
||||||
saveDraftToCloudDelayed(thread);
|
saveDraftToCloudDelayed(thread);
|
||||||
|
@ -2122,15 +2160,22 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
|
|
||||||
const auto history = thread->owningHistory();
|
const auto history = thread->owningHistory();
|
||||||
const auto topicRootId = thread->topicRootId();
|
const auto topicRootId = thread->topicRootId();
|
||||||
auto cloudDraft = history->cloudDraft(topicRootId);
|
const auto monoforumPeerId = thread->monoforumPeerId();
|
||||||
auto localDraft = history->localDraft(topicRootId);
|
auto cloudDraft = history->cloudDraft(topicRootId, monoforumPeerId);
|
||||||
|
auto localDraft = history->localDraft(topicRootId, monoforumPeerId);
|
||||||
if (cloudDraft && cloudDraft->saveRequestId) {
|
if (cloudDraft && cloudDraft->saveRequestId) {
|
||||||
request(base::take(cloudDraft->saveRequestId)).cancel();
|
request(base::take(cloudDraft->saveRequestId)).cancel();
|
||||||
}
|
}
|
||||||
if (!_session->supportMode()) {
|
if (!_session->supportMode()) {
|
||||||
cloudDraft = history->createCloudDraft(topicRootId, localDraft);
|
cloudDraft = history->createCloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
localDraft);
|
||||||
} else if (!cloudDraft) {
|
} else if (!cloudDraft) {
|
||||||
cloudDraft = history->createCloudDraft(topicRootId, nullptr);
|
cloudDraft = history->createCloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto flags = MTPmessages_SaveDraft::Flags(0);
|
auto flags = MTPmessages_SaveDraft::Flags(0);
|
||||||
|
@ -2140,18 +2185,23 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
} else if (!cloudDraft->webpage.url.isEmpty()) {
|
} else if (!cloudDraft->webpage.url.isEmpty()) {
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_media;
|
flags |= MTPmessages_SaveDraft::Flag::f_media;
|
||||||
}
|
}
|
||||||
if (cloudDraft->reply.messageId || cloudDraft->reply.topicRootId) {
|
if (cloudDraft->reply.messageId
|
||||||
|
|| cloudDraft->reply.topicRootId
|
||||||
|
|| cloudDraft->reply.monoforumPeerId) {
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_reply_to;
|
flags |= MTPmessages_SaveDraft::Flag::f_reply_to;
|
||||||
}
|
}
|
||||||
if (!textWithTags.tags.isEmpty()) {
|
if (!textWithTags.tags.isEmpty()) {
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
||||||
}
|
}
|
||||||
|
if (cloudDraft->suggest) {
|
||||||
|
flags |= MTPmessages_SaveDraft::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
auto entities = Api::EntitiesToMTP(
|
auto entities = Api::EntitiesToMTP(
|
||||||
_session,
|
_session,
|
||||||
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
|
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
|
||||||
Api::ConvertOption::SkipLocal);
|
Api::ConvertOption::SkipLocal);
|
||||||
|
|
||||||
history->startSavingCloudDraft(topicRootId);
|
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
|
||||||
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
|
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
ReplyToForMTP(history, cloudDraft->reply),
|
ReplyToForMTP(history, cloudDraft->reply),
|
||||||
|
@ -2161,16 +2211,21 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
Data::WebPageForMTP(
|
Data::WebPageForMTP(
|
||||||
cloudDraft->webpage,
|
cloudDraft->webpage,
|
||||||
textWithTags.text.isEmpty()),
|
textWithTags.text.isEmpty()),
|
||||||
MTP_long(0) // effect
|
MTP_long(0), // effect
|
||||||
|
Api::SuggestToMTP(cloudDraft->suggest)
|
||||||
)).done([=](const MTPBool &result, const MTP::Response &response) {
|
)).done([=](const MTPBool &result, const MTP::Response &response) {
|
||||||
const auto requestId = response.requestId;
|
const auto requestId = response.requestId;
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
|
const auto cloudDraft = history->cloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId);
|
||||||
|
if (cloudDraft) {
|
||||||
if (cloudDraft->saveRequestId == requestId) {
|
if (cloudDraft->saveRequestId == requestId) {
|
||||||
cloudDraft->saveRequestId = 0;
|
cloudDraft->saveRequestId = 0;
|
||||||
history->draftSavedToCloud(topicRootId);
|
history->draftSavedToCloud(topicRootId, monoforumPeerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto i = _draftsSaveRequestIds.find(weak);
|
const auto i = _draftsSaveRequestIds.find(weak);
|
||||||
|
@ -2183,10 +2238,14 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
const auto requestId = response.requestId;
|
const auto requestId = response.requestId;
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
if (const auto cloudDraft = history->cloudDraft(topicRootId)) {
|
const auto cloudDraft = history->cloudDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId);
|
||||||
|
if (cloudDraft) {
|
||||||
if (cloudDraft->saveRequestId == requestId) {
|
if (cloudDraft->saveRequestId == requestId) {
|
||||||
history->clearCloudDraft(topicRootId);
|
history->clearCloudDraft(topicRootId, monoforumPeerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto i = _draftsSaveRequestIds.find(weak);
|
const auto i = _draftsSaveRequestIds.find(weak);
|
||||||
|
@ -2560,7 +2619,10 @@ void ApiWrap::refreshFileReference(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req) {
|
void ApiWrap::gotWebPages(
|
||||||
|
ChannelData *channel,
|
||||||
|
const MTPmessages_Messages &result,
|
||||||
|
mtpRequestId req) {
|
||||||
WebPageData::ApplyChanges(_session, channel, result);
|
WebPageData::ApplyChanges(_session, channel, result);
|
||||||
for (auto i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
|
for (auto i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
|
||||||
if (i->second == req) {
|
if (i->second == req) {
|
||||||
|
@ -2574,7 +2636,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &resu
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_session->data().sendWebPageGamePollNotifications();
|
_session->data().sendWebPageGamePollTodoListNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::updateStickers() {
|
void ApiWrap::updateStickers() {
|
||||||
|
@ -2964,17 +3026,27 @@ void ApiWrap::resolveJumpToDate(
|
||||||
Fn<void(not_null<PeerData*>, MsgId)> callback) {
|
Fn<void(not_null<PeerData*>, MsgId)> callback) {
|
||||||
if (const auto peer = chat.peer()) {
|
if (const auto peer = chat.peer()) {
|
||||||
const auto topic = chat.topic();
|
const auto topic = chat.topic();
|
||||||
const auto rootId = topic ? topic->rootId() : 0;
|
const auto sublist = chat.sublist();
|
||||||
resolveJumpToHistoryDate(peer, rootId, date, std::move(callback));
|
const auto rootId = topic ? topic->rootId() : MsgId();
|
||||||
|
const auto monoforumPeerId = sublist
|
||||||
|
? sublist->sublistPeer()->id
|
||||||
|
: PeerId();
|
||||||
|
resolveJumpToHistoryDate(
|
||||||
|
peer,
|
||||||
|
rootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
date,
|
||||||
|
std::move(callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
void ApiWrap::requestMessageAfterDate(
|
void ApiWrap::requestMessageAfterDate(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
const QDate &date,
|
PeerId monoforumPeerId,
|
||||||
Callback &&callback) {
|
const QDate &date,
|
||||||
|
Callback &&callback) {
|
||||||
// API returns a message with date <= offset_date.
|
// API returns a message with date <= offset_date.
|
||||||
// So we request a message with offset_date = desired_date - 1 and add_offset = -1.
|
// So we request a message with offset_date = desired_date - 1 and add_offset = -1.
|
||||||
// This should give us the first message with date >= desired_date.
|
// This should give us the first message with date >= desired_date.
|
||||||
|
@ -2997,7 +3069,7 @@ void ApiWrap::requestMessageAfterDate(
|
||||||
return &messages.vmessages().v;
|
return &messages.vmessages().v;
|
||||||
};
|
};
|
||||||
const auto list = result.match([&](
|
const auto list = result.match([&](
|
||||||
const MTPDmessages_messages &data) {
|
const MTPDmessages_messages &data) {
|
||||||
return handleMessages(data);
|
return handleMessages(data);
|
||||||
}, [&](const MTPDmessages_messagesSlice &data) {
|
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||||
return handleMessages(data);
|
return handleMessages(data);
|
||||||
|
@ -3040,6 +3112,18 @@ void ApiWrap::requestMessageAfterDate(
|
||||||
MTP_int(maxId),
|
MTP_int(maxId),
|
||||||
MTP_int(minId),
|
MTP_int(minId),
|
||||||
MTP_long(historyHash)));
|
MTP_long(historyHash)));
|
||||||
|
} else if (monoforumPeerId) {
|
||||||
|
send(MTPmessages_GetSavedHistory(
|
||||||
|
MTP_flags(MTPmessages_GetSavedHistory::Flag::f_parent_peer),
|
||||||
|
peer->input,
|
||||||
|
session().data().peer(monoforumPeerId)->input,
|
||||||
|
MTP_int(offsetId),
|
||||||
|
MTP_int(offsetDate),
|
||||||
|
MTP_int(addOffset),
|
||||||
|
MTP_int(limit),
|
||||||
|
MTP_int(maxId),
|
||||||
|
MTP_int(minId),
|
||||||
|
MTP_long(historyHash)));
|
||||||
} else {
|
} else {
|
||||||
send(MTPmessages_GetHistory(
|
send(MTPmessages_GetHistory(
|
||||||
peer->input,
|
peer->input,
|
||||||
|
@ -3056,28 +3140,41 @@ void ApiWrap::requestMessageAfterDate(
|
||||||
void ApiWrap::resolveJumpToHistoryDate(
|
void ApiWrap::resolveJumpToHistoryDate(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
const QDate &date,
|
const QDate &date,
|
||||||
Fn<void(not_null<PeerData*>, MsgId)> callback) {
|
Fn<void(not_null<PeerData*>, MsgId)> callback) {
|
||||||
if (const auto channel = peer->migrateTo()) {
|
if (const auto channel = peer->migrateTo()) {
|
||||||
return resolveJumpToHistoryDate(
|
return resolveJumpToHistoryDate(
|
||||||
channel,
|
channel,
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
date,
|
date,
|
||||||
std::move(callback));
|
std::move(callback));
|
||||||
}
|
}
|
||||||
const auto jumpToDateInPeer = [=] {
|
const auto jumpToDateInPeer = [=] {
|
||||||
requestMessageAfterDate(peer, topicRootId, date, [=](MsgId itemId) {
|
requestMessageAfterDate(
|
||||||
callback(peer, itemId);
|
peer,
|
||||||
});
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
date,
|
||||||
|
[=](MsgId itemId) { callback(peer, itemId); });
|
||||||
};
|
};
|
||||||
if (const auto chat = topicRootId ? nullptr : peer->migrateFrom()) {
|
const auto migrated = (topicRootId || monoforumPeerId)
|
||||||
requestMessageAfterDate(chat, 0, date, [=](MsgId itemId) {
|
? nullptr
|
||||||
if (itemId) {
|
: peer->migrateFrom();
|
||||||
callback(chat, itemId);
|
if (migrated) {
|
||||||
} else {
|
requestMessageAfterDate(
|
||||||
jumpToDateInPeer();
|
migrated,
|
||||||
}
|
MsgId(),
|
||||||
});
|
PeerId(),
|
||||||
|
date,
|
||||||
|
[=](MsgId itemId) {
|
||||||
|
if (itemId) {
|
||||||
|
callback(migrated, itemId);
|
||||||
|
} else {
|
||||||
|
jumpToDateInPeer();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
jumpToDateInPeer();
|
jumpToDateInPeer();
|
||||||
}
|
}
|
||||||
|
@ -3126,12 +3223,14 @@ void ApiWrap::requestHistory(
|
||||||
void ApiWrap::requestSharedMedia(
|
void ApiWrap::requestSharedMedia(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
SharedMediaType type,
|
SharedMediaType type,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SliceType slice) {
|
SliceType slice) {
|
||||||
const auto key = SharedMediaRequest{
|
const auto key = SharedMediaRequest{
|
||||||
peer,
|
peer,
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
type,
|
type,
|
||||||
messageId,
|
messageId,
|
||||||
slice,
|
slice,
|
||||||
|
@ -3143,6 +3242,7 @@ void ApiWrap::requestSharedMedia(
|
||||||
const auto prepared = Api::PrepareSearchRequest(
|
const auto prepared = Api::PrepareSearchRequest(
|
||||||
peer,
|
peer,
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
type,
|
type,
|
||||||
QString(),
|
QString(),
|
||||||
messageId,
|
messageId,
|
||||||
|
@ -3165,7 +3265,12 @@ void ApiWrap::requestSharedMedia(
|
||||||
messageId,
|
messageId,
|
||||||
slice,
|
slice,
|
||||||
result);
|
result);
|
||||||
sharedMediaDone(peer, topicRootId, type, std::move(parsed));
|
sharedMediaDone(
|
||||||
|
peer,
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
|
type,
|
||||||
|
std::move(parsed));
|
||||||
finish();
|
finish();
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_sharedMediaRequests.remove(key);
|
_sharedMediaRequests.remove(key);
|
||||||
|
@ -3178,16 +3283,19 @@ void ApiWrap::requestSharedMedia(
|
||||||
void ApiWrap::sharedMediaDone(
|
void ApiWrap::sharedMediaDone(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
SharedMediaType type,
|
SharedMediaType type,
|
||||||
Api::SearchResult &&parsed) {
|
Api::SearchResult &&parsed) {
|
||||||
const auto topic = peer->forumTopicFor(topicRootId);
|
const auto topic = peer->forumTopicFor(topicRootId);
|
||||||
if (topicRootId && !topic) {
|
const auto sublist = peer->monoforumSublistFor(monoforumPeerId);
|
||||||
|
if ((topicRootId && !topic) || (monoforumPeerId && !sublist)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto hasMessages = !parsed.messageIds.empty();
|
const auto hasMessages = !parsed.messageIds.empty();
|
||||||
_session->storage().add(Storage::SharedMediaAddSlice(
|
_session->storage().add(Storage::SharedMediaAddSlice(
|
||||||
peer->id,
|
peer->id,
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
type,
|
type,
|
||||||
std::move(parsed.messageIds),
|
std::move(parsed.messageIds),
|
||||||
parsed.noSkipRange,
|
parsed.noSkipRange,
|
||||||
|
@ -3198,6 +3306,9 @@ void ApiWrap::sharedMediaDone(
|
||||||
if (topic) {
|
if (topic) {
|
||||||
topic->setHasPinnedMessages(true);
|
topic->setHasPinnedMessages(true);
|
||||||
}
|
}
|
||||||
|
if (sublist) {
|
||||||
|
sublist->setHasPinnedMessages(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3234,8 +3345,14 @@ void ApiWrap::sendAction(const SendAction &action) {
|
||||||
const auto topic = topicRootId
|
const auto topic = topicRootId
|
||||||
? action.history->peer->forumTopicFor(topicRootId)
|
? action.history->peer->forumTopicFor(topicRootId)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
|
const auto sublist = monoforumPeerId
|
||||||
|
? action.history->peer->monoforumSublistFor(monoforumPeerId)
|
||||||
|
: nullptr;
|
||||||
if (topic) {
|
if (topic) {
|
||||||
topic->readTillEnd();
|
topic->readTillEnd();
|
||||||
|
} else if (sublist) {
|
||||||
|
sublist->readTillEnd();
|
||||||
} else {
|
} else {
|
||||||
_session->data().histories().readInbox(action.history);
|
_session->data().histories().readInbox(action.history);
|
||||||
}
|
}
|
||||||
|
@ -3247,7 +3364,10 @@ void ApiWrap::sendAction(const SendAction &action) {
|
||||||
void ApiWrap::finishForwarding(const SendAction &action) {
|
void ApiWrap::finishForwarding(const SendAction &action) {
|
||||||
const auto history = action.history;
|
const auto history = action.history;
|
||||||
const auto topicRootId = action.replyTo.topicRootId;
|
const auto topicRootId = action.replyTo.topicRootId;
|
||||||
auto toForward = history->resolveForwardDraft(topicRootId);
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
|
auto toForward = history->resolveForwardDraft(
|
||||||
|
topicRootId,
|
||||||
|
monoforumPeerId);
|
||||||
if (!toForward.items.empty()) {
|
if (!toForward.items.empty()) {
|
||||||
const auto error = GetErrorForSending(
|
const auto error = GetErrorForSending(
|
||||||
history->peer,
|
history->peer,
|
||||||
|
@ -3260,7 +3380,7 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
forwardMessages(std::move(toForward), action);
|
forwardMessages(std::move(toForward), action);
|
||||||
history->setForwardDraft(topicRootId, {});
|
history->setForwardDraft(topicRootId, monoforumPeerId, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
_session->data().sendHistoryChangeNotifications();
|
_session->data().sendHistoryChangeNotifications();
|
||||||
|
@ -3279,6 +3399,22 @@ void ApiWrap::forwardMessages(
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
Expects(!draft.items.empty());
|
Expects(!draft.items.empty());
|
||||||
|
|
||||||
|
const auto fullAyuForward = AyuForward::isFullAyuForwardNeeded(draft.items.front());
|
||||||
|
if (fullAyuForward) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::forwardMessages(_session, action, false, draft);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ayuIntelligentForwardNeeded = AyuForward::isAyuForwardNeeded(draft.items);
|
||||||
|
if (ayuIntelligentForwardNeeded) {
|
||||||
|
crl::async([=] {
|
||||||
|
AyuForward::intelligentForward(_session, action, draft);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto &histories = _session->data().histories();
|
auto &histories = _session->data().histories();
|
||||||
|
|
||||||
struct SharedCallback {
|
struct SharedCallback {
|
||||||
|
@ -3329,6 +3465,9 @@ void ApiWrap::forwardMessages(
|
||||||
if (sendAs) {
|
if (sendAs) {
|
||||||
sendFlags |= SendFlag::f_send_as;
|
sendFlags |= SendFlag::f_send_as;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= SendFlag::f_suggested_post;
|
||||||
|
}
|
||||||
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
||||||
const auto topicRootId = action.replyTo.topicRootId;
|
const auto topicRootId = action.replyTo.topicRootId;
|
||||||
const auto topMsgId = (topicRootId == kGeneralId)
|
const auto topMsgId = (topicRootId == kGeneralId)
|
||||||
|
@ -3337,6 +3476,13 @@ void ApiWrap::forwardMessages(
|
||||||
if (topMsgId) {
|
if (topMsgId) {
|
||||||
sendFlags |= SendFlag::f_top_msg_id;
|
sendFlags |= SendFlag::f_top_msg_id;
|
||||||
}
|
}
|
||||||
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
|
const auto monoforumPeer = monoforumPeerId
|
||||||
|
? session().data().peer(monoforumPeerId).get()
|
||||||
|
: nullptr;
|
||||||
|
if (monoforumPeer || (action.options.suggest && action.replyTo)) {
|
||||||
|
sendFlags |= SendFlag::f_reply_to;
|
||||||
|
}
|
||||||
|
|
||||||
auto forwardFrom = draft.items.front()->history()->peer;
|
auto forwardFrom = draft.items.front()->history()->peer;
|
||||||
auto ids = QVector<MTPint>();
|
auto ids = QVector<MTPint>();
|
||||||
|
@ -3366,11 +3512,17 @@ void ApiWrap::forwardMessages(
|
||||||
MTP_vector<MTPlong>(randomIds),
|
MTP_vector<MTPlong>(randomIds),
|
||||||
peer->input,
|
peer->input,
|
||||||
MTP_int(topMsgId),
|
MTP_int(topMsgId),
|
||||||
|
(action.options.suggest
|
||||||
|
? ReplyToForMTP(history, action.replyTo)
|
||||||
|
: monoforumPeer
|
||||||
|
? MTP_inputReplyToMonoForum(monoforumPeer->input)
|
||||||
|
: MTPInputReplyTo()),
|
||||||
MTP_int(action.options.scheduled),
|
MTP_int(action.options.scheduled),
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
|
||||||
MTPint(), // video_timestamp
|
MTPint(), // video_timestamp
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
Api::SuggestToMTP(action.options.suggest)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
if (!scheduled) {
|
if (!scheduled) {
|
||||||
this->updates().checkForSentToScheduled(result);
|
this->updates().checkForSentToScheduled(result);
|
||||||
|
@ -3380,7 +3532,7 @@ void ApiWrap::forwardMessages(
|
||||||
shared->callback();
|
shared->callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.sendReadMessages && settings.markReadAfterAction && history->lastMessage())
|
if (!settings.sendReadMessages && settings.markReadAfterAction && history->lastMessage())
|
||||||
{
|
{
|
||||||
readHistory(history->lastMessage());
|
readHistory(history->lastMessage());
|
||||||
|
@ -3419,12 +3571,15 @@ void ApiWrap::forwardMessages(
|
||||||
.id = newId.msg,
|
.id = newId.msg,
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.from = NewMessageFromId(action),
|
.from = NewMessageFromId(action),
|
||||||
.replyTo = { .topicRootId = topMsgId },
|
.replyTo = {
|
||||||
|
.topicRootId = topMsgId,
|
||||||
|
.monoforumPeerId = monoforumPeerId,
|
||||||
|
},
|
||||||
.date = NewMessageDate(action.options),
|
.date = NewMessageDate(action.options),
|
||||||
.shortcutId = action.options.shortcutId,
|
.shortcutId = action.options.shortcutId,
|
||||||
.starsPaid = action.options.starsApproved,
|
.starsPaid = action.options.starsApproved,
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
|
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||||
// forwarded messages don't have effects
|
// forwarded messages don't have effects
|
||||||
//.effectId = action.options.effectId,
|
//.effectId = action.options.effectId,
|
||||||
}, item);
|
}, item);
|
||||||
|
@ -3519,6 +3674,7 @@ void ApiWrap::sendSharedContact(
|
||||||
.starsPaid = action.options.starsApproved,
|
.starsPaid = action.options.starsApproved,
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
.effectId = action.options.effectId,
|
.effectId = action.options.effectId,
|
||||||
|
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||||
}, TextWithEntities(), MTP_messageMediaContact(
|
}, TextWithEntities(), MTP_messageMediaContact(
|
||||||
MTP_string(phone),
|
MTP_string(phone),
|
||||||
MTP_string(firstName),
|
MTP_string(firstName),
|
||||||
|
@ -3567,7 +3723,19 @@ void ApiWrap::editMedia(
|
||||||
if (list.files.empty()) return;
|
if (list.files.empty()) return;
|
||||||
|
|
||||||
auto &file = list.files.front();
|
auto &file = list.files.front();
|
||||||
const auto to = FileLoadTaskOptions(action);
|
auto to = FileLoadTaskOptions(action);
|
||||||
|
const auto existing = to.replaceMediaOf
|
||||||
|
? session().data().message(action.history->peer, to.replaceMediaOf)
|
||||||
|
: nullptr;
|
||||||
|
if (existing && existing->computeSuggestionActions()
|
||||||
|
== SuggestionActions::AcceptAndDecline) {
|
||||||
|
to.replyTo.messageId = {
|
||||||
|
action.history->peer->id,
|
||||||
|
to.replaceMediaOf
|
||||||
|
};
|
||||||
|
to.replyTo.monoforumPeerId = existing->sublistPeerId();
|
||||||
|
to.replaceMediaOf = MsgId();
|
||||||
|
}
|
||||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||||
&session(),
|
&session(),
|
||||||
file.path,
|
file.path,
|
||||||
|
@ -3757,6 +3925,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
|
|
||||||
const auto clearCloudDraft = action.clearDraft;
|
const auto clearCloudDraft = action.clearDraft;
|
||||||
const auto draftTopicRootId = action.replyTo.topicRootId;
|
const auto draftTopicRootId = action.replyTo.topicRootId;
|
||||||
|
const auto draftMonoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
const auto replyTo = action.replyTo.messageId
|
const auto replyTo = action.replyTo.messageId
|
||||||
? peer->owner().message(action.replyTo.messageId)
|
? peer->owner().message(action.replyTo.messageId)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
@ -3766,8 +3935,12 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
? replyTo->topicRootId()
|
? replyTo->topicRootId()
|
||||||
: Data::ForumTopic::kGeneralId;
|
: Data::ForumTopic::kGeneralId;
|
||||||
const auto topic = peer->forumTopicFor(topicRootId);
|
const auto topic = peer->forumTopicFor(topicRootId);
|
||||||
if (!(topic ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer))
|
|
||||||
|| Api::SendDice(message)) {
|
const bool canSendTexts = topic
|
||||||
|
? Data::CanSendTexts(topic)
|
||||||
|
: Data::CanSendTexts(peer);
|
||||||
|
|
||||||
|
if (!canSendTexts && !AyuForward::isForwarding(peer->id) || Api::SendDice(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
local().saveRecentSentHashtags(textWithTags.text);
|
local().saveRecentSentHashtags(textWithTags.text);
|
||||||
|
@ -3866,8 +4039,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
|
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
|
||||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
mediaFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||||
history->clearCloudDraft(draftTopicRootId);
|
history->clearCloudDraft(draftTopicRootId, draftMonoforumPeerId);
|
||||||
history->startSavingCloudDraft(draftTopicRootId);
|
history->startSavingCloudDraft(
|
||||||
|
draftTopicRootId,
|
||||||
|
draftMonoforumPeerId);
|
||||||
}
|
}
|
||||||
const auto sendAs = action.options.sendAs;
|
const auto sendAs = action.options.sendAs;
|
||||||
if (sendAs) {
|
if (sendAs) {
|
||||||
|
@ -3888,6 +4063,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
|
sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
|
||||||
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
|
||||||
}
|
}
|
||||||
|
if (action.options.suggest) {
|
||||||
|
sendFlags |= MTPmessages_SendMessage::Flag::f_suggested_post;
|
||||||
|
mediaFlags |= MTPmessages_SendMedia::Flag::f_suggested_post;
|
||||||
|
}
|
||||||
const auto starsPaid = std::min(
|
const auto starsPaid = std::min(
|
||||||
peer->starsPerMessageChecked(),
|
peer->starsPerMessageChecked(),
|
||||||
action.options.starsApproved);
|
action.options.starsApproved);
|
||||||
|
@ -3906,6 +4085,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
.starsPaid = starsPaid,
|
.starsPaid = starsPaid,
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
.effectId = action.options.effectId,
|
.effectId = action.options.effectId,
|
||||||
|
.suggest = HistoryMessageSuggestInfo(action.options),
|
||||||
}, sending, media);
|
}, sending, media);
|
||||||
const auto done = [=](
|
const auto done = [=](
|
||||||
const MTPUpdates &result,
|
const MTPUpdates &result,
|
||||||
|
@ -3913,6 +4093,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
draftTopicRootId,
|
draftTopicRootId,
|
||||||
|
draftMonoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3929,6 +4110,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
if (clearCloudDraft) {
|
if (clearCloudDraft) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
draftTopicRootId,
|
draftTopicRootId,
|
||||||
|
draftMonoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3955,7 +4137,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
mtpShortcut,
|
mtpShortcut,
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
Api::SuggestToMTP(action.options.suggest)
|
||||||
), done, fail);
|
), done, fail);
|
||||||
} else {
|
} else {
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
|
@ -3974,7 +4157,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
|
||||||
mtpShortcut,
|
mtpShortcut,
|
||||||
MTP_long(action.options.effectId),
|
MTP_long(action.options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
Api::SuggestToMTP(action.options.suggest)
|
||||||
), done, fail);
|
), done, fail);
|
||||||
}
|
}
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
@ -4055,6 +4239,7 @@ void ApiWrap::sendInlineResult(
|
||||||
const auto topicRootId = action.replyTo.messageId
|
const auto topicRootId = action.replyTo.messageId
|
||||||
? action.replyTo.topicRootId
|
? action.replyTo.topicRootId
|
||||||
: 0;
|
: 0;
|
||||||
|
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||||
|
|
||||||
using SendFlag = MTPmessages_SendInlineBotResult::Flag;
|
using SendFlag = MTPmessages_SendInlineBotResult::Flag;
|
||||||
auto flags = NewMessageFlags(peer);
|
auto flags = NewMessageFlags(peer);
|
||||||
|
@ -4107,8 +4292,8 @@ void ApiWrap::sendInlineResult(
|
||||||
.postAuthor = NewMessagePostAuthor(action),
|
.postAuthor = NewMessagePostAuthor(action),
|
||||||
});
|
});
|
||||||
|
|
||||||
history->clearCloudDraft(topicRootId);
|
history->clearCloudDraft(topicRootId, monoforumPeerId);
|
||||||
history->startSavingCloudDraft(topicRootId);
|
history->startSavingCloudDraft(topicRootId, monoforumPeerId);
|
||||||
|
|
||||||
auto &histories = history->owner().histories();
|
auto &histories = history->owner().histories();
|
||||||
histories.sendPreparedMessage(
|
histories.sendPreparedMessage(
|
||||||
|
@ -4129,6 +4314,7 @@ void ApiWrap::sendInlineResult(
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
if (done) {
|
if (done) {
|
||||||
done(true);
|
done(true);
|
||||||
|
@ -4137,6 +4323,7 @@ void ApiWrap::sendInlineResult(
|
||||||
sendMessageFail(error, peer, randomId, newId);
|
sendMessageFail(error, peer, randomId, newId);
|
||||||
history->finishSavingCloudDraft(
|
history->finishSavingCloudDraft(
|
||||||
topicRootId,
|
topicRootId,
|
||||||
|
monoforumPeerId,
|
||||||
UnixtimeFromMsgId(response.outerMsgId));
|
UnixtimeFromMsgId(response.outerMsgId));
|
||||||
if (done) {
|
if (done) {
|
||||||
done(false);
|
done(false);
|
||||||
|
@ -4290,6 +4477,7 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||||
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
||||||
| (options.effectId ? Flag::f_effect : Flag(0))
|
| (options.effectId ? Flag::f_effect : Flag(0))
|
||||||
|
| (options.suggest ? Flag::f_suggested_post : Flag(0))
|
||||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
||||||
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
||||||
|
|
||||||
|
@ -4318,7 +4506,8 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
||||||
MTP_long(options.effectId),
|
MTP_long(options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
Api::SuggestToMTP(options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (done) done(true);
|
if (done) done(true);
|
||||||
if (updateRecentStickers) {
|
if (updateRecentStickers) {
|
||||||
|
@ -4375,6 +4564,7 @@ void ApiWrap::sendMultiPaidMedia(
|
||||||
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
| (options.sendAs ? Flag::f_send_as : Flag(0))
|
||||||
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
|
||||||
| (options.effectId ? Flag::f_effect : Flag(0))
|
| (options.effectId ? Flag::f_effect : Flag(0))
|
||||||
|
| (options.suggest ? Flag::f_suggested_post : Flag(0))
|
||||||
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
| (options.invertCaption ? Flag::f_invert_media : Flag(0))
|
||||||
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
|
||||||
|
|
||||||
|
@ -4402,7 +4592,8 @@ void ApiWrap::sendMultiPaidMedia(
|
||||||
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
|
||||||
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
||||||
MTP_long(options.effectId),
|
MTP_long(options.effectId),
|
||||||
MTP_long(starsPaid)
|
MTP_long(starsPaid),
|
||||||
|
Api::SuggestToMTP(options.suggest)
|
||||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||||
if (const auto album = _sendingAlbums.take(groupId)) {
|
if (const auto album = _sendingAlbums.take(groupId)) {
|
||||||
const auto copy = (*album)->items;
|
const auto copy = (*album)->items;
|
||||||
|
@ -4742,6 +4933,10 @@ Api::Polls &ApiWrap::polls() {
|
||||||
return *_polls;
|
return *_polls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Api::TodoLists &ApiWrap::todoLists() {
|
||||||
|
return *_todoLists;
|
||||||
|
}
|
||||||
|
|
||||||
Api::ChatParticipants &ApiWrap::chatParticipants() {
|
Api::ChatParticipants &ApiWrap::chatParticipants() {
|
||||||
return *_chatParticipants;
|
return *_chatParticipants;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ class ConfirmPhone;
|
||||||
class PeerPhoto;
|
class PeerPhoto;
|
||||||
class PeerColors;
|
class PeerColors;
|
||||||
class Polls;
|
class Polls;
|
||||||
|
class TodoLists;
|
||||||
class ChatParticipants;
|
class ChatParticipants;
|
||||||
class UnreadThings;
|
class UnreadThings;
|
||||||
class Ringtones;
|
class Ringtones;
|
||||||
|
@ -231,6 +232,9 @@ public:
|
||||||
void deleteAllFromParticipant(
|
void deleteAllFromParticipant(
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
not_null<PeerData*> from);
|
not_null<PeerData*> from);
|
||||||
|
void deleteSublistHistory(
|
||||||
|
not_null<ChannelData*> parentChat,
|
||||||
|
not_null<PeerData*> sublistPeer);
|
||||||
|
|
||||||
void requestWebPageDelayed(not_null<WebPageData*> page);
|
void requestWebPageDelayed(not_null<WebPageData*> page);
|
||||||
void clearWebPageRequest(not_null<WebPageData*> page);
|
void clearWebPageRequest(not_null<WebPageData*> page);
|
||||||
|
@ -286,6 +290,7 @@ public:
|
||||||
void requestSharedMedia(
|
void requestSharedMedia(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SliceType slice);
|
SliceType slice);
|
||||||
|
@ -409,6 +414,7 @@ public:
|
||||||
[[nodiscard]] Api::ConfirmPhone &confirmPhone();
|
[[nodiscard]] Api::ConfirmPhone &confirmPhone();
|
||||||
[[nodiscard]] Api::PeerPhoto &peerPhoto();
|
[[nodiscard]] Api::PeerPhoto &peerPhoto();
|
||||||
[[nodiscard]] Api::Polls &polls();
|
[[nodiscard]] Api::Polls &polls();
|
||||||
|
[[nodiscard]] Api::TodoLists &todoLists();
|
||||||
[[nodiscard]] Api::ChatParticipants &chatParticipants();
|
[[nodiscard]] Api::ChatParticipants &chatParticipants();
|
||||||
[[nodiscard]] Api::UnreadThings &unreadThings();
|
[[nodiscard]] Api::UnreadThings &unreadThings();
|
||||||
[[nodiscard]] Api::Ringtones &ringtones();
|
[[nodiscard]] Api::Ringtones &ringtones();
|
||||||
|
@ -502,18 +508,21 @@ private:
|
||||||
void resolveJumpToHistoryDate(
|
void resolveJumpToHistoryDate(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
const QDate &date,
|
const QDate &date,
|
||||||
Fn<void(not_null<PeerData*>, MsgId)> callback);
|
Fn<void(not_null<PeerData*>, MsgId)> callback);
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
void requestMessageAfterDate(
|
void requestMessageAfterDate(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
const QDate &date,
|
const QDate &date,
|
||||||
Callback &&callback);
|
Callback &&callback);
|
||||||
|
|
||||||
void sharedMediaDone(
|
void sharedMediaDone(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
|
PeerId monoforumPeerId,
|
||||||
SharedMediaType type,
|
SharedMediaType type,
|
||||||
Api::SearchResult &&parsed);
|
Api::SearchResult &&parsed);
|
||||||
void globalMediaDone(
|
void globalMediaDone(
|
||||||
|
@ -539,6 +548,9 @@ private:
|
||||||
void deleteAllFromParticipantSend(
|
void deleteAllFromParticipantSend(
|
||||||
not_null<ChannelData*> channel,
|
not_null<ChannelData*> channel,
|
||||||
not_null<PeerData*> from);
|
not_null<PeerData*> from);
|
||||||
|
void deleteSublistHistorySend(
|
||||||
|
not_null<ChannelData*> parentChat,
|
||||||
|
not_null<PeerData*> sublistPeer);
|
||||||
|
|
||||||
void uploadAlbumMedia(
|
void uploadAlbumMedia(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
|
@ -659,6 +671,7 @@ private:
|
||||||
struct SharedMediaRequest {
|
struct SharedMediaRequest {
|
||||||
not_null<PeerData*> peer;
|
not_null<PeerData*> peer;
|
||||||
MsgId topicRootId = 0;
|
MsgId topicRootId = 0;
|
||||||
|
PeerId monoforumPeerId = 0;
|
||||||
SharedMediaType mediaType = {};
|
SharedMediaType mediaType = {};
|
||||||
MsgId aroundId = 0;
|
MsgId aroundId = 0;
|
||||||
SliceType sliceType = {};
|
SliceType sliceType = {};
|
||||||
|
@ -753,6 +766,7 @@ private:
|
||||||
const std::unique_ptr<Api::ConfirmPhone> _confirmPhone;
|
const std::unique_ptr<Api::ConfirmPhone> _confirmPhone;
|
||||||
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
|
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
|
||||||
const std::unique_ptr<Api::Polls> _polls;
|
const std::unique_ptr<Api::Polls> _polls;
|
||||||
|
const std::unique_ptr<Api::TodoLists> _todoLists;
|
||||||
const std::unique_ptr<Api::ChatParticipants> _chatParticipants;
|
const std::unique_ptr<Api::ChatParticipants> _chatParticipants;
|
||||||
const std::unique_ptr<Api::UnreadThings> _unreadThings;
|
const std::unique_ptr<Api::UnreadThings> _unreadThings;
|
||||||
const std::unique_ptr<Api::Ringtones> _ringtones;
|
const std::unique_ptr<Api::Ringtones> _ringtones;
|
||||||
|
|
|
@ -28,7 +28,7 @@ void initLang() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initUiSettings() {
|
void initUiSettings() {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
|
|
||||||
AyuUiSettings::setMonoFont(settings.monoFont);
|
AyuUiSettings::setMonoFont(settings.monoFont);
|
||||||
AyuUiSettings::setWideMultiplier(settings.wideMultiplier);
|
AyuUiSettings::setWideMultiplier(settings.wideMultiplier);
|
||||||
|
|
|
@ -107,7 +107,7 @@ void AyuLanguage::applyLanguageJson(QJsonDocument doc) {
|
||||||
const auto json = doc.object();
|
const auto json = doc.object();
|
||||||
for (const QString &brokenKey : json.keys()) {
|
for (const QString &brokenKey : json.keys()) {
|
||||||
auto key = qsl("ayu_") + brokenKey;
|
auto key = qsl("ayu_") + brokenKey;
|
||||||
const auto val = json.value(brokenKey).toString().replace(qsl("&"), qsl("&")).toUtf8();
|
auto val = json.value(brokenKey).toString().replace(qsl("&"), qsl("&"));
|
||||||
|
|
||||||
if (key.endsWith("_zero") || key.endsWith("_two") || key.endsWith("_few") || key.endsWith("_many")) {
|
if (key.endsWith("_zero") || key.endsWith("_two") || key.endsWith("_few") || key.endsWith("_many")) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -119,8 +119,16 @@ void AyuLanguage::applyLanguageJson(QJsonDocument doc) {
|
||||||
key = key.replace("_other", "#other");
|
key = key.replace("_other", "#other");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (val.contains(qsl("%1$d")) && !val.contains(qsl("%2$d"))) {
|
||||||
|
val = val.replace(qsl("%1$d"), qsl("{count}"));
|
||||||
|
} else if (val.contains(qsl("%1$d")) && val.contains(qsl("%2$d"))) {
|
||||||
|
val = val.replace(qsl("%1$d"), qsl("{count1}")).replace(qsl("%2$d"), qsl("{count2}"));
|
||||||
|
} else if (val.contains(qsl("%1$s"))) {
|
||||||
|
val = val.replace(qsl("%1$s"), qsl("{item}"));
|
||||||
|
}
|
||||||
|
|
||||||
Lang::GetInstance().resetValue(key.toUtf8());
|
Lang::GetInstance().resetValue(key.toUtf8());
|
||||||
Lang::GetInstance().applyValue(key.toUtf8(), val);
|
Lang::GetInstance().applyValue(key.toUtf8(), val.toUtf8());
|
||||||
}
|
}
|
||||||
Lang::GetInstance().updatePluralRules();
|
Lang::GetInstance().updatePluralRules();
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,8 @@ AyuGramSettings::AyuGramSettings() {
|
||||||
|
|
||||||
disableNotificationsDelay = false;
|
disableNotificationsDelay = false;
|
||||||
localPremium = false;
|
localPremium = false;
|
||||||
|
hideChannelReactions = true;
|
||||||
|
hideGroupReactions = true;
|
||||||
|
|
||||||
// ~ Customization
|
// ~ Customization
|
||||||
appIcon =
|
appIcon =
|
||||||
|
@ -417,8 +419,16 @@ void set_localPremium(bool val) {
|
||||||
settings->localPremium = val;
|
settings->localPremium = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_appIcon(QString val) {
|
void set_hideChannelReactions(bool val) {
|
||||||
settings->appIcon = std::move(val);
|
settings->hideChannelReactions = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_hideGroupReactions(bool val) {
|
||||||
|
settings->hideGroupReactions = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_appIcon(const QString &val) {
|
||||||
|
settings->appIcon = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_simpleQuotesAndReplies(bool val) {
|
void set_simpleQuotesAndReplies(bool val) {
|
||||||
|
@ -429,13 +439,13 @@ void set_replaceBottomInfoWithIcons(bool val) {
|
||||||
settings->replaceBottomInfoWithIcons = val;
|
settings->replaceBottomInfoWithIcons = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_deletedMark(QString val) {
|
void set_deletedMark(const QString &val) {
|
||||||
settings->deletedMark = std::move(val);
|
settings->deletedMark = val;
|
||||||
deletedMarkReactive = settings->deletedMark;
|
deletedMarkReactive = settings->deletedMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_editedMark(QString val) {
|
void set_editedMark(const QString &val) {
|
||||||
settings->editedMark = std::move(val);
|
settings->editedMark = val;
|
||||||
editedMarkReactive = settings->editedMark;
|
editedMarkReactive = settings->editedMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,7 +532,7 @@ void set_showStreamerToggleInTray(bool val) {
|
||||||
settings->showStreamerToggleInTray = val;
|
settings->showStreamerToggleInTray = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_monoFont(QString val) {
|
void set_monoFont(const QString &val) {
|
||||||
settings->monoFont = val;
|
settings->monoFont = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,33 @@
|
||||||
|
|
||||||
#include "ayu/libs/json.hpp"
|
#include "ayu/libs/json.hpp"
|
||||||
#include "ayu/libs/json_ext.hpp"
|
#include "ayu/libs/json_ext.hpp"
|
||||||
|
|
||||||
|
// json.hpp in some build environments may not provide helper macros.
|
||||||
|
// To ensure successful compilation, define them here when missing.
|
||||||
|
#ifndef NLOHMANN_JSON_TO
|
||||||
|
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
|
||||||
|
#endif
|
||||||
|
#ifndef NLOHMANN_JSON_FROM
|
||||||
|
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
|
||||||
|
#endif
|
||||||
|
#ifndef NLOHMANN_JSON_FROM_WITH_DEFAULT
|
||||||
|
#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \
|
||||||
|
nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
|
||||||
|
#endif
|
||||||
|
#ifndef NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
|
||||||
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||||
|
inline void to_json(nlohmann::json& nlohmann_json_j, \
|
||||||
|
const Type& nlohmann_json_t) { \
|
||||||
|
NLOHMANN_JSON_EXPAND( \
|
||||||
|
NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \
|
||||||
|
} \
|
||||||
|
inline void from_json(const nlohmann::json& nlohmann_json_j, \
|
||||||
|
Type& nlohmann_json_t) { \
|
||||||
|
const Type nlohmann_json_default_obj{}; \
|
||||||
|
NLOHMANN_JSON_EXPAND( \
|
||||||
|
NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#include "rpl/producer.h"
|
#include "rpl/producer.h"
|
||||||
|
|
||||||
namespace AyuSettings {
|
namespace AyuSettings {
|
||||||
|
@ -49,6 +76,8 @@ public:
|
||||||
|
|
||||||
bool disableNotificationsDelay;
|
bool disableNotificationsDelay;
|
||||||
bool localPremium;
|
bool localPremium;
|
||||||
|
bool hideChannelReactions;
|
||||||
|
bool hideGroupReactions;
|
||||||
|
|
||||||
QString appIcon;
|
QString appIcon;
|
||||||
bool simpleQuotesAndReplies;
|
bool simpleQuotesAndReplies;
|
||||||
|
@ -131,12 +160,14 @@ void set_increaseWebviewWidth(bool val);
|
||||||
|
|
||||||
void set_disableNotificationsDelay(bool val);
|
void set_disableNotificationsDelay(bool val);
|
||||||
void set_localPremium(bool val);
|
void set_localPremium(bool val);
|
||||||
|
void set_hideChannelReactions(bool val);
|
||||||
|
void set_hideGroupReactions(bool val);
|
||||||
|
|
||||||
void set_appIcon(QString val);
|
void set_appIcon(const QString &val);
|
||||||
void set_simpleQuotesAndReplies(bool val);
|
void set_simpleQuotesAndReplies(bool val);
|
||||||
void set_replaceBottomInfoWithIcons(bool val);
|
void set_replaceBottomInfoWithIcons(bool val);
|
||||||
void set_deletedMark(QString val);
|
void set_deletedMark(const QString &val);
|
||||||
void set_editedMark(QString val);
|
void set_editedMark(const QString &val);
|
||||||
void set_recentStickersCount(int val);
|
void set_recentStickersCount(int val);
|
||||||
|
|
||||||
void set_showReactionsPanelInContextMenu(int val);
|
void set_showReactionsPanelInContextMenu(int val);
|
||||||
|
@ -162,7 +193,7 @@ void set_showStreamerToggleInDrawer(bool val);
|
||||||
void set_showGhostToggleInTray(bool val);
|
void set_showGhostToggleInTray(bool val);
|
||||||
void set_showStreamerToggleInTray(bool val);
|
void set_showStreamerToggleInTray(bool val);
|
||||||
|
|
||||||
void set_monoFont(QString val);
|
void set_monoFont(const QString &val);
|
||||||
|
|
||||||
void set_hideNotificationCounters(bool val);
|
void set_hideNotificationCounters(bool val);
|
||||||
void set_hideNotificationBadge(bool val);
|
void set_hideNotificationBadge(bool val);
|
||||||
|
@ -178,68 +209,134 @@ void set_stickerConfirmation(bool val);
|
||||||
void set_gifConfirmation(bool val);
|
void set_gifConfirmation(bool val);
|
||||||
void set_voiceConfirmation(bool val);
|
void set_voiceConfirmation(bool val);
|
||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
|
inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nlohmann_json_t) {
|
||||||
AyuGramSettings,
|
NLOHMANN_JSON_TO(sendReadMessages)
|
||||||
sendReadMessages,
|
NLOHMANN_JSON_TO(sendReadStories)
|
||||||
sendReadStories,
|
NLOHMANN_JSON_TO(sendOnlinePackets)
|
||||||
sendOnlinePackets,
|
NLOHMANN_JSON_TO(sendUploadProgress)
|
||||||
sendUploadProgress,
|
NLOHMANN_JSON_TO(sendOfflinePacketAfterOnline)
|
||||||
sendOfflinePacketAfterOnline,
|
NLOHMANN_JSON_TO(markReadAfterAction)
|
||||||
markReadAfterAction,
|
NLOHMANN_JSON_TO(useScheduledMessages)
|
||||||
useScheduledMessages,
|
NLOHMANN_JSON_TO(sendWithoutSound)
|
||||||
sendWithoutSound,
|
NLOHMANN_JSON_TO(saveDeletedMessages)
|
||||||
saveDeletedMessages,
|
NLOHMANN_JSON_TO(saveMessagesHistory)
|
||||||
saveMessagesHistory,
|
NLOHMANN_JSON_TO(saveForBots)
|
||||||
saveForBots,
|
NLOHMANN_JSON_TO(hideFromBlocked)
|
||||||
hideFromBlocked,
|
NLOHMANN_JSON_TO(disableAds)
|
||||||
disableAds,
|
NLOHMANN_JSON_TO(disableStories)
|
||||||
disableStories,
|
NLOHMANN_JSON_TO(disableCustomBackgrounds)
|
||||||
disableCustomBackgrounds,
|
NLOHMANN_JSON_TO(showOnlyAddedEmojisAndStickers)
|
||||||
showOnlyAddedEmojisAndStickers,
|
NLOHMANN_JSON_TO(collapseSimilarChannels)
|
||||||
collapseSimilarChannels,
|
NLOHMANN_JSON_TO(hideSimilarChannels)
|
||||||
hideSimilarChannels,
|
NLOHMANN_JSON_TO(wideMultiplier)
|
||||||
wideMultiplier,
|
NLOHMANN_JSON_TO(spoofWebviewAsAndroid)
|
||||||
spoofWebviewAsAndroid,
|
NLOHMANN_JSON_TO(increaseWebviewHeight)
|
||||||
increaseWebviewHeight,
|
NLOHMANN_JSON_TO(increaseWebviewWidth)
|
||||||
increaseWebviewWidth,
|
NLOHMANN_JSON_TO(disableNotificationsDelay)
|
||||||
disableNotificationsDelay,
|
NLOHMANN_JSON_TO(localPremium)
|
||||||
localPremium,
|
NLOHMANN_JSON_TO(appIcon)
|
||||||
appIcon,
|
NLOHMANN_JSON_TO(simpleQuotesAndReplies)
|
||||||
simpleQuotesAndReplies,
|
NLOHMANN_JSON_TO(replaceBottomInfoWithIcons)
|
||||||
replaceBottomInfoWithIcons,
|
NLOHMANN_JSON_TO(deletedMark)
|
||||||
deletedMark,
|
NLOHMANN_JSON_TO(editedMark)
|
||||||
editedMark,
|
NLOHMANN_JSON_TO(recentStickersCount)
|
||||||
recentStickersCount,
|
NLOHMANN_JSON_TO(showReactionsPanelInContextMenu)
|
||||||
showReactionsPanelInContextMenu,
|
NLOHMANN_JSON_TO(showViewsPanelInContextMenu)
|
||||||
showViewsPanelInContextMenu,
|
NLOHMANN_JSON_TO(showHideMessageInContextMenu)
|
||||||
showHideMessageInContextMenu,
|
NLOHMANN_JSON_TO(showUserMessagesInContextMenu)
|
||||||
showUserMessagesInContextMenu,
|
NLOHMANN_JSON_TO(showMessageDetailsInContextMenu)
|
||||||
showMessageDetailsInContextMenu,
|
NLOHMANN_JSON_TO(showAttachButtonInMessageField)
|
||||||
showAttachButtonInMessageField,
|
NLOHMANN_JSON_TO(showCommandsButtonInMessageField)
|
||||||
showCommandsButtonInMessageField,
|
NLOHMANN_JSON_TO(showEmojiButtonInMessageField)
|
||||||
showEmojiButtonInMessageField,
|
NLOHMANN_JSON_TO(showMicrophoneButtonInMessageField)
|
||||||
showMicrophoneButtonInMessageField,
|
NLOHMANN_JSON_TO(showAutoDeleteButtonInMessageField)
|
||||||
showAutoDeleteButtonInMessageField,
|
NLOHMANN_JSON_TO(showAttachPopup)
|
||||||
showAttachPopup,
|
NLOHMANN_JSON_TO(showEmojiPopup)
|
||||||
showEmojiPopup,
|
NLOHMANN_JSON_TO(showLReadToggleInDrawer)
|
||||||
showLReadToggleInDrawer,
|
NLOHMANN_JSON_TO(showSReadToggleInDrawer)
|
||||||
showSReadToggleInDrawer,
|
NLOHMANN_JSON_TO(showGhostToggleInDrawer)
|
||||||
showGhostToggleInDrawer,
|
NLOHMANN_JSON_TO(showStreamerToggleInDrawer)
|
||||||
showStreamerToggleInDrawer,
|
NLOHMANN_JSON_TO(showGhostToggleInTray)
|
||||||
showGhostToggleInTray,
|
NLOHMANN_JSON_TO(showStreamerToggleInTray)
|
||||||
showStreamerToggleInTray,
|
NLOHMANN_JSON_TO(monoFont)
|
||||||
monoFont,
|
NLOHMANN_JSON_TO(hideChannelReactions)
|
||||||
hideNotificationCounters,
|
NLOHMANN_JSON_TO(hideGroupReactions)
|
||||||
hideNotificationBadge,
|
NLOHMANN_JSON_TO(hideNotificationCounters)
|
||||||
hideAllChatsFolder,
|
NLOHMANN_JSON_TO(hideNotificationBadge)
|
||||||
channelBottomButton,
|
NLOHMANN_JSON_TO(hideAllChatsFolder)
|
||||||
showPeerId,
|
NLOHMANN_JSON_TO(channelBottomButton)
|
||||||
showMessageSeconds,
|
NLOHMANN_JSON_TO(showPeerId)
|
||||||
showMessageShot,
|
NLOHMANN_JSON_TO(showMessageSeconds)
|
||||||
stickerConfirmation,
|
NLOHMANN_JSON_TO(showMessageShot)
|
||||||
gifConfirmation,
|
NLOHMANN_JSON_TO(stickerConfirmation)
|
||||||
voiceConfirmation
|
NLOHMANN_JSON_TO(gifConfirmation)
|
||||||
);
|
NLOHMANN_JSON_TO(voiceConfirmation)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nlohmann_json_t) {
|
||||||
|
const AyuGramSettings nlohmann_json_default_obj{};
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendReadMessages)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendReadStories)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendOnlinePackets)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendUploadProgress)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendOfflinePacketAfterOnline)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(markReadAfterAction)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(useScheduledMessages)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(sendWithoutSound)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveDeletedMessages)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveMessagesHistory)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveForBots)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideFromBlocked)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableAds)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableStories)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableCustomBackgrounds)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showOnlyAddedEmojisAndStickers)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(collapseSimilarChannels)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideSimilarChannels)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(wideMultiplier)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(spoofWebviewAsAndroid)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewHeight)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewWidth)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableNotificationsDelay)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(localPremium)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(appIcon)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(simpleQuotesAndReplies)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(replaceBottomInfoWithIcons)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(deletedMark)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(editedMark)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(recentStickersCount)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showReactionsPanelInContextMenu)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showViewsPanelInContextMenu)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showHideMessageInContextMenu)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showUserMessagesInContextMenu)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageDetailsInContextMenu)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAttachButtonInMessageField)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showCommandsButtonInMessageField)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showEmojiButtonInMessageField)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMicrophoneButtonInMessageField)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAutoDeleteButtonInMessageField)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAttachPopup)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showEmojiPopup)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showLReadToggleInDrawer)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showSReadToggleInDrawer)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showGhostToggleInDrawer)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showStreamerToggleInDrawer)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showGhostToggleInTray)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showStreamerToggleInTray)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(monoFont)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideChannelReactions)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideGroupReactions)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideNotificationCounters)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideNotificationBadge)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideAllChatsFolder)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(channelBottomButton)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showPeerId)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageSeconds)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageShot)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(stickerConfirmation)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(gifConfirmation)
|
||||||
|
NLOHMANN_JSON_FROM_WITH_DEFAULT(voiceConfirmation)
|
||||||
|
}
|
||||||
|
|
||||||
AyuGramSettings &getInstance();
|
AyuGramSettings &getInstance();
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ void runOnce() {
|
||||||
lateInit();
|
lateInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!settings.sendOfflinePacketAfterOnline) {
|
if (!settings.sendOfflinePacketAfterOnline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
402
Telegram/SourceFiles/ayu/features/forward/ayu_forward.cpp
Normal file
|
@ -0,0 +1,402 @@
|
||||||
|
// This is the source code of AyuGram for Desktop.
|
||||||
|
//
|
||||||
|
// We do not and cannot prevent the use of our code,
|
||||||
|
// but be respectful and credit the original author.
|
||||||
|
//
|
||||||
|
// Copyright @Radolyn, 2025
|
||||||
|
#include "ayu_forward.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "ayu_sync.h"
|
||||||
|
#include "lang_auto.h"
|
||||||
|
#include "ayu/utils/telegram_helpers.h"
|
||||||
|
#include "base/random.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "storage/file_download.h"
|
||||||
|
#include "storage/localimageloader.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
|
||||||
|
namespace AyuForward {
|
||||||
|
|
||||||
|
std::unordered_map<PeerId, std::shared_ptr<ForwardState>> forwardStates;
|
||||||
|
|
||||||
|
bool isForwarding(const PeerId &id) {
|
||||||
|
const auto fwState = forwardStates.find(id);
|
||||||
|
if (id.value && fwState != forwardStates.end()) {
|
||||||
|
const auto state = *fwState->second;
|
||||||
|
|
||||||
|
return state.state != ForwardState::State::Finished
|
||||||
|
&& state.currentChunk < state.totalChunks
|
||||||
|
&& !state.stopRequested
|
||||||
|
&& state.totalChunks
|
||||||
|
&& state.totalMessages;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelForward(const PeerId &id, const Main::Session &session) {
|
||||||
|
const auto fwState = forwardStates.find(id);
|
||||||
|
if (fwState != forwardStates.end()) {
|
||||||
|
fwState->second->stopRequested = true;
|
||||||
|
fwState->second->updateBottomBar(session, &id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<QString, QString> stateName(const PeerId &id) {
|
||||||
|
const auto fwState = forwardStates.find(id);
|
||||||
|
|
||||||
|
|
||||||
|
if (fwState == forwardStates.end()) {
|
||||||
|
return std::make_pair(QString(), QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto state = fwState->second;
|
||||||
|
|
||||||
|
QString messagesString = tr::ayu_AyuForwardStatusSentCount(tr::now,
|
||||||
|
lt_count1,
|
||||||
|
QString::number(state->sentMessages),
|
||||||
|
lt_count2,
|
||||||
|
QString::number(state->totalMessages)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
QString chunkString = tr::ayu_AyuForwardStatusChunkCount(tr::now,
|
||||||
|
lt_count1,
|
||||||
|
QString::number(state->currentChunk + 1),
|
||||||
|
lt_count2,
|
||||||
|
QString::number(state->totalChunks)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto partString = state->totalChunks <= 1 ? messagesString : (messagesString + " • " + chunkString);
|
||||||
|
|
||||||
|
QString status;
|
||||||
|
|
||||||
|
if (state->state == ForwardState::State::Preparing) {
|
||||||
|
status = tr::ayu_AyuForwardStatusPreparing(tr::now);
|
||||||
|
} else if (state->state == ForwardState::State::Downloading) {
|
||||||
|
status = tr::ayu_AyuForwardStatusLoadingMedia(tr::now);
|
||||||
|
} else if (state->state == ForwardState::State::Sending) {
|
||||||
|
status = tr::ayu_AyuForwardStatusForwarding(tr::now);
|
||||||
|
} else {
|
||||||
|
// ForwardState::State::Finished
|
||||||
|
status = tr::ayu_AyuForwardStatusFinished(tr::now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return std::make_pair(status, partString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForwardState::updateBottomBar(const Main::Session &session, const PeerId *peer, const State &st) {
|
||||||
|
state = st;
|
||||||
|
|
||||||
|
session.changes().peerUpdated(session.data().peer(*peer), Data::PeerUpdate::Flag::Rights);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Ui::PreparedList prepareMedia(not_null<Main::Session*> session,
|
||||||
|
const std::vector<not_null<HistoryItem*>> &items,
|
||||||
|
int &i,
|
||||||
|
std::vector<not_null<Data::Media*>> &groupMedia) {
|
||||||
|
const auto prepare = [&](not_null<Data::Media*> media)
|
||||||
|
{
|
||||||
|
groupMedia.emplace_back(media);
|
||||||
|
auto prepared = Ui::PreparedFile(AyuSync::filePath(session, media));
|
||||||
|
Storage::PrepareDetails(prepared, st::sendMediaPreviewSize, PhotoSideLimit());
|
||||||
|
return prepared;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto startItem = items[i];
|
||||||
|
const auto media = startItem->media();
|
||||||
|
const auto groupId = startItem->groupId();
|
||||||
|
|
||||||
|
Ui::PreparedList list;
|
||||||
|
list.files.emplace_back(prepare(media));
|
||||||
|
|
||||||
|
if (!groupId.value) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = i + 1; k < items.size(); ++k) {
|
||||||
|
const auto nextItem = items[k];
|
||||||
|
if (nextItem->groupId() != groupId) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (const auto nextMedia = nextItem->media()) {
|
||||||
|
list.files.emplace_back(prepare(nextMedia));
|
||||||
|
i = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMedia(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const std::shared_ptr<Ui::PreparedBundle> &bundle,
|
||||||
|
not_null<Data::Media*> primaryMedia,
|
||||||
|
Api::MessageToSend &&message,
|
||||||
|
bool sendImagesAsPhotos) {
|
||||||
|
if (const auto document = primaryMedia->document(); document && document->sticker()) {
|
||||||
|
AyuSync::sendStickerSync(session, message, document);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mediaType = [&]
|
||||||
|
{
|
||||||
|
if (const auto document = primaryMedia->document()) {
|
||||||
|
if (document->isVoiceMessage()) {
|
||||||
|
return SendMediaType::Audio;
|
||||||
|
} else if (document->isVideoMessage()) {
|
||||||
|
return SendMediaType::Round;
|
||||||
|
}
|
||||||
|
return SendMediaType::File;
|
||||||
|
}
|
||||||
|
return SendMediaType::Photo;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (mediaType == SendMediaType::Round || mediaType == SendMediaType::Audio) {
|
||||||
|
const auto path = bundle->groups.front().list.files.front().path;
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
auto failed = false;
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
LOG(("failed to open file for forward with reason: %1").arg(file.errorString()));
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
|
if (!failed && data.size()) {
|
||||||
|
file.close();
|
||||||
|
AyuSync::sendVoiceSync(session,
|
||||||
|
data,
|
||||||
|
primaryMedia->document()->duration(),
|
||||||
|
mediaType == SendMediaType::Round,
|
||||||
|
message.action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// at least try to send it as squared-video
|
||||||
|
}
|
||||||
|
|
||||||
|
// workaround for media albums consisting of video and photos
|
||||||
|
if (sendImagesAsPhotos) {
|
||||||
|
mediaType = SendMediaType::Photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &group : bundle->groups) {
|
||||||
|
AyuSync::sendDocumentSync(
|
||||||
|
session,
|
||||||
|
group,
|
||||||
|
mediaType,
|
||||||
|
std::move(message.textWithTags),
|
||||||
|
message.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem*>> &items) {
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (isAyuForwardNeeded(item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(not_null<HistoryItem*> item) {
|
||||||
|
if (item->isDeleted() || item->isAyuNoForwards() || item->ttlDestroyAt()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFullAyuForwardNeeded(not_null<HistoryItem*> item) {
|
||||||
|
return item->from()->isAyuNoForwards() || item->history()->peer->isAyuNoForwards();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ForwardChunk
|
||||||
|
{
|
||||||
|
bool isAyuForwardNeeded;
|
||||||
|
std::vector<not_null<HistoryItem*>> items;
|
||||||
|
};
|
||||||
|
|
||||||
|
void intelligentForward(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
const Data::ResolvedForwardDraft &draft) {
|
||||||
|
const auto history = action.history;
|
||||||
|
history->setForwardDraft(action.replyTo.topicRootId, action.replyTo.monoforumPeerId, {});
|
||||||
|
|
||||||
|
const auto items = draft.items;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
|
||||||
|
auto chunks = std::vector<ForwardChunk>();
|
||||||
|
auto currentArray = std::vector<not_null<HistoryItem*>>();
|
||||||
|
|
||||||
|
auto currentChunk = ForwardChunk({
|
||||||
|
.isAyuForwardNeeded = isAyuForwardNeeded(items[0]),
|
||||||
|
.items = currentArray
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (isAyuForwardNeeded(item) != currentChunk.isAyuForwardNeeded) {
|
||||||
|
currentChunk.items = currentArray;
|
||||||
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
|
currentArray = std::vector<not_null<HistoryItem*>>();
|
||||||
|
|
||||||
|
currentChunk = ForwardChunk({
|
||||||
|
.isAyuForwardNeeded = isAyuForwardNeeded(item),
|
||||||
|
.items = currentArray
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentArray.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentChunk.items = currentArray;
|
||||||
|
chunks.push_back(currentChunk);
|
||||||
|
|
||||||
|
auto state = std::make_shared<ForwardState>(chunks.size());
|
||||||
|
forwardStates[peer->id] = state;
|
||||||
|
|
||||||
|
|
||||||
|
for (const auto &chunk : chunks) {
|
||||||
|
if (chunk.isAyuForwardNeeded) {
|
||||||
|
forwardMessages(session, action, true, Data::ResolvedForwardDraft(chunk.items));
|
||||||
|
} else {
|
||||||
|
state->totalMessages = chunk.items.size();
|
||||||
|
state->sentMessages = 0;
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Sending);
|
||||||
|
|
||||||
|
AyuSync::forwardMessagesSync(session, chunk.items, action, draft.options);
|
||||||
|
|
||||||
|
state->sentMessages = state->totalMessages;
|
||||||
|
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
state->currentChunk++;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMessages(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
bool forwardState,
|
||||||
|
Data::ResolvedForwardDraft draft) {
|
||||||
|
const auto items = draft.items;
|
||||||
|
const auto history = action.history;
|
||||||
|
const auto peer = history->peer;
|
||||||
|
|
||||||
|
history->setForwardDraft(action.replyTo.topicRootId, action.replyTo.monoforumPeerId, {});
|
||||||
|
|
||||||
|
std::shared_ptr<ForwardState> state;
|
||||||
|
|
||||||
|
if (forwardState) {
|
||||||
|
state = std::make_shared<ForwardState>(*forwardStates[peer->id]);
|
||||||
|
} else {
|
||||||
|
state = std::make_shared<ForwardState>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardStates[peer->id] = state;
|
||||||
|
|
||||||
|
std::unordered_map<uint64, uint64> groupIds;
|
||||||
|
|
||||||
|
std::vector<not_null<HistoryItem*>> toBeDownloaded;
|
||||||
|
|
||||||
|
|
||||||
|
for (const auto item : items) {
|
||||||
|
if (mediaDownloadable(item->media())) {
|
||||||
|
toBeDownloaded.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->groupId()) {
|
||||||
|
const auto currentId = groupIds.find(item->groupId().value);
|
||||||
|
|
||||||
|
if (currentId == groupIds.end()) {
|
||||||
|
groupIds[item->groupId().value] = base::RandomValue<uint64>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->totalMessages = items.size();
|
||||||
|
if (!toBeDownloaded.empty()) {
|
||||||
|
state->state = ForwardState::State::Downloading;
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Downloading);
|
||||||
|
AyuSync::loadDocuments(session, toBeDownloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
state->sentMessages = 0;
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Sending);
|
||||||
|
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
const auto item = items[i];
|
||||||
|
|
||||||
|
if (state->stopRequested) {
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto extractedText = extractText(item);
|
||||||
|
if (extractedText.empty() && !mediaDownloadable(item->media())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message = Api::MessageToSend(Api::SendAction(session->data().history(peer->id)));
|
||||||
|
message.action.replyTo = action.replyTo;
|
||||||
|
|
||||||
|
if (draft.options != Data::ForwardOptions::NoNamesAndCaptions) {
|
||||||
|
message.textWithTags = extractedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mediaDownloadable(item->media())) {
|
||||||
|
AyuSync::sendMessageSync(session, message);
|
||||||
|
} else if (const auto media = item->media()) {
|
||||||
|
if (media->poll()) {
|
||||||
|
AyuSync::sendMessageSync(session, message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<not_null<Data::Media*>> groupMedia;
|
||||||
|
auto preparedMedia = prepareMedia(session, items, i, groupMedia);
|
||||||
|
|
||||||
|
Ui::SendFilesWay way;
|
||||||
|
way.setGroupFiles(true);
|
||||||
|
way.setSendImagesAsPhotos(false);
|
||||||
|
for (const auto &media2 : groupMedia) {
|
||||||
|
if (media2->photo()) {
|
||||||
|
way.setSendImagesAsPhotos(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto groups = Ui::DivideByGroups(
|
||||||
|
std::move(preparedMedia),
|
||||||
|
way,
|
||||||
|
peer->slowmodeApplied());
|
||||||
|
|
||||||
|
auto bundle = Ui::PrepareFilesBundle(
|
||||||
|
std::move(groups),
|
||||||
|
way,
|
||||||
|
message.textWithTags,
|
||||||
|
false);
|
||||||
|
sendMedia(session, bundle, media, std::move(message), way.sendImagesAsPhotos());
|
||||||
|
}
|
||||||
|
// if there are grouped messages
|
||||||
|
// "i" is incremented in prepareMedia
|
||||||
|
|
||||||
|
state->sentMessages = i + 1;
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Sending);
|
||||||
|
}
|
||||||
|
state->updateBottomBar(*session, &peer->id, ForwardState::State::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AyuFeatures::AyuForward
|
52
Telegram/SourceFiles/ayu/features/forward/ayu_forward.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// This is the source code of AyuGram for Desktop.
|
||||||
|
//
|
||||||
|
// We do not and cannot prevent the use of our code,
|
||||||
|
// but be respectful and credit the original author.
|
||||||
|
//
|
||||||
|
// Copyright @Radolyn, 2025
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
namespace AyuForward {
|
||||||
|
bool isForwarding(const PeerId &id);
|
||||||
|
void cancelForward(const PeerId &id, const Main::Session &session);
|
||||||
|
std::pair<QString, QString> stateName(const PeerId &id);
|
||||||
|
|
||||||
|
class ForwardState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
Preparing,
|
||||||
|
Downloading,
|
||||||
|
Sending,
|
||||||
|
Finished
|
||||||
|
};
|
||||||
|
void updateBottomBar(const Main::Session &session, const PeerId *peer, const State &st);
|
||||||
|
|
||||||
|
int totalChunks;
|
||||||
|
int currentChunk;
|
||||||
|
int totalMessages;
|
||||||
|
int sentMessages;
|
||||||
|
|
||||||
|
State state = State::Preparing;
|
||||||
|
bool stopRequested = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem*>> &items);
|
||||||
|
bool isAyuForwardNeeded(not_null<HistoryItem*> item);
|
||||||
|
bool isFullAyuForwardNeeded(not_null<HistoryItem*> item);
|
||||||
|
void intelligentForward(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
const Data::ResolvedForwardDraft &draft);
|
||||||
|
void forwardMessages(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const Api::SendAction &action,
|
||||||
|
bool forwardState,
|
||||||
|
Data::ResolvedForwardDraft draft);
|
||||||
|
|
||||||
|
}
|
326
Telegram/SourceFiles/ayu/features/forward/ayu_sync.cpp
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
// This is the source code of AyuGram for Desktop.
|
||||||
|
//
|
||||||
|
// We do not and cannot prevent the use of our code,
|
||||||
|
// but be respectful and credit the original author.
|
||||||
|
//
|
||||||
|
// Copyright @Radolyn, 2025
|
||||||
|
#include "ayu_sync.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "api/api_sending.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/core_settings.h"
|
||||||
|
#include "core/file_utilities.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_photo_media.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "storage/file_download_mtproto.h"
|
||||||
|
#include "storage/localimageloader.h"
|
||||||
|
|
||||||
|
class TimedCountDownLatch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TimedCountDownLatch(int count)
|
||||||
|
: count_(count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void countDown() {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
if (count_ > 0) {
|
||||||
|
count_--;
|
||||||
|
}
|
||||||
|
if (count_ == 0) {
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool await(std::chrono::milliseconds timeout) {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
if (count_ == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return cv_.wait_for(lock, timeout, [this] { return count_ == 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
int count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace AyuSync {
|
||||||
|
|
||||||
|
QString pathForSave(not_null<Main::Session*> session) {
|
||||||
|
const auto path = Core::App().settings().downloadPath();
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return File::DefaultDownloadPath(session);
|
||||||
|
}
|
||||||
|
if (path == FileDialog::Tmp()) {
|
||||||
|
return session->local().tempDirectory();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filePath(not_null<Main::Session*> session, const Data::Media *media) {
|
||||||
|
if (const auto document = media->document()) {
|
||||||
|
if (!document->filename().isEmpty()) {
|
||||||
|
return pathForSave(session) + media->document()->filename();
|
||||||
|
}
|
||||||
|
if (const auto name = document->filepath(true); !name.isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
if (document->isVoiceMessage()) {
|
||||||
|
return pathForSave(session) + "audio_" + QString::number(document->getDC()) + "_" +
|
||||||
|
QString::number(document->id) + ".ogg";
|
||||||
|
}
|
||||||
|
if (document->isVideoMessage()) {
|
||||||
|
return pathForSave(session) + "round_" + QString::number(document->getDC()) + "_" +
|
||||||
|
QString::number(document->id) + ".mp4";
|
||||||
|
}
|
||||||
|
} else if (const auto photo = media->photo()) {
|
||||||
|
return pathForSave(session) + QString::number(photo->getDC()) + "_" + QString::number(photo->id) + ".jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 fileSize(not_null<HistoryItem*> item) {
|
||||||
|
if (const auto path = filePath(&item->history()->session(), item->media()); !path.isEmpty()) {
|
||||||
|
QFile file(path);
|
||||||
|
if (file.exists()) {
|
||||||
|
auto size = file.size();
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadDocuments(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items) {
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (const auto data = item->media()->document()) {
|
||||||
|
const auto size = fileSize(item);
|
||||||
|
|
||||||
|
if (size == data->size) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (size && size < data->size) {
|
||||||
|
// in case there some unfinished file
|
||||||
|
QFile file(filePath(session, item->media()));
|
||||||
|
file.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDocumentSync(session, data, item);
|
||||||
|
} else if (auto photo = item->media()->photo()) {
|
||||||
|
if (fileSize(item) == photo->imageByteSize(Data::PhotoSize::Large)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPhotoSync(session, std::pair(photo, item->fullId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadDocumentSync(not_null<Main::Session*> session, DocumentData *data, not_null<HistoryItem*> item) {
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
data->save(Data::FileOriginMessage(item->fullId()), filePath(session, item->media()));
|
||||||
|
|
||||||
|
rpl::single() | rpl::then(
|
||||||
|
session->downloaderTaskFinished()
|
||||||
|
) | rpl::filter([&]
|
||||||
|
{
|
||||||
|
return data->status == FileDownloadFailed || fileSize(item) == data->size;
|
||||||
|
}) | rpl::start_with_next([&]() mutable
|
||||||
|
{
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMessagesSync(not_null<Main::Session*> session,
|
||||||
|
const std::vector<not_null<HistoryItem*>> &items,
|
||||||
|
const ApiWrap::SendAction &action,
|
||||||
|
Data::ForwardOptions options) {
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
|
||||||
|
crl::on_main([=, &latch]
|
||||||
|
{
|
||||||
|
session->api().forwardMessages(Data::ResolvedForwardDraft(items, options),
|
||||||
|
action,
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
latch->countDown();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadPhotoSync(not_null<Main::Session*> session, const std::pair<not_null<PhotoData*>, FullMsgId> &photo) {
|
||||||
|
const auto folderPath = pathForSave(session);
|
||||||
|
const auto downloadPath = folderPath.isEmpty() ? Core::App().settings().downloadPath() : folderPath;
|
||||||
|
|
||||||
|
const auto path = downloadPath.isEmpty()
|
||||||
|
? File::DefaultDownloadPath(session)
|
||||||
|
: downloadPath == FileDialog::Tmp()
|
||||||
|
? session->local().tempDirectory()
|
||||||
|
: downloadPath;
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!QDir().mkpath(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto view = photo.first->createMediaView();
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view->wanted(Data::PhotoSize::Large, photo.second);
|
||||||
|
|
||||||
|
const auto finalCheck = [=]
|
||||||
|
{
|
||||||
|
return !photo.first->loading();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto saveToFiles = [=]
|
||||||
|
{
|
||||||
|
QDir directory(path);
|
||||||
|
const auto dir = directory.absolutePath();
|
||||||
|
const auto nameBase = dir.endsWith('/') ? dir : dir + '/';
|
||||||
|
const auto fullPath = nameBase + QString::number(photo.first->getDC()) + "_" + QString::number(photo.first->id)
|
||||||
|
+ ".jpg";
|
||||||
|
view->saveToFile(fullPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
if (finalCheck()) {
|
||||||
|
saveToFiles();
|
||||||
|
} else {
|
||||||
|
session->downloaderTaskFinished() | rpl::filter([&]
|
||||||
|
{
|
||||||
|
return finalCheck();
|
||||||
|
}) | rpl::start_with_next([&]() mutable
|
||||||
|
{
|
||||||
|
saveToFiles();
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
},
|
||||||
|
*lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessageSync(not_null<Main::Session*> session, Api::MessageToSend &message) {
|
||||||
|
crl::on_main([=, &message]
|
||||||
|
{
|
||||||
|
// we cannot send events to objects
|
||||||
|
// owned by a different thread
|
||||||
|
// because sendMessage updates UI too
|
||||||
|
|
||||||
|
session->api().sendMessage(std::move(message));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
waitForMsgSync(session, message.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitForMsgSync(not_null<Main::Session*> session, const Api::SendAction &action) {
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
|
||||||
|
session->data().itemIdChanged()
|
||||||
|
| rpl::filter([&](const Data::Session::IdChange &update)
|
||||||
|
{
|
||||||
|
return action.history->peer->id == update.newId.peer;
|
||||||
|
}) | rpl::start_with_next([&]
|
||||||
|
{
|
||||||
|
latch->countDown();
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendDocumentSync(not_null<Main::Session*> session,
|
||||||
|
Ui::PreparedGroup &group,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
const Api::SendAction &action) {
|
||||||
|
const auto size = group.list.files.size();
|
||||||
|
auto latch = std::make_shared<TimedCountDownLatch>(size);
|
||||||
|
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||||
|
|
||||||
|
auto groupId = std::make_shared<SendingAlbum>();
|
||||||
|
groupId->groupId = base::RandomValue<uint64>();
|
||||||
|
|
||||||
|
crl::on_main([=, lst = std::move(group.list), caption = std::move(caption)]() mutable
|
||||||
|
{
|
||||||
|
session->api().sendFiles(std::move(lst), type, std::move(caption), groupId, action);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// probably need to handle
|
||||||
|
// session->uploader().photoFailed()
|
||||||
|
// and
|
||||||
|
// session->uploader().documentFailed()
|
||||||
|
// too
|
||||||
|
|
||||||
|
rpl::merge(
|
||||||
|
session->uploader().documentReady(),
|
||||||
|
session->uploader().photoReady()
|
||||||
|
) | rpl::filter([&](const Storage::UploadedMedia &docOrPhoto)
|
||||||
|
{
|
||||||
|
return docOrPhoto.fullId.peer == action.history->peer->id;
|
||||||
|
}) | rpl::start_with_next([&]
|
||||||
|
{
|
||||||
|
latch->countDown();
|
||||||
|
},
|
||||||
|
*lifetime);
|
||||||
|
|
||||||
|
latch->await(std::chrono::minutes(5 * size));
|
||||||
|
base::take(lifetime)->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendStickerSync(not_null<Main::Session*> session,
|
||||||
|
Api::MessageToSend &message,
|
||||||
|
not_null<DocumentData*> document) {
|
||||||
|
auto &action = message.action;
|
||||||
|
crl::on_main([&]
|
||||||
|
{
|
||||||
|
Api::SendExistingDocument(std::move(message), document, std::nullopt);
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForMsgSync(session, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendVoiceSync(not_null<Main::Session*> session,
|
||||||
|
const QByteArray &data,
|
||||||
|
int64_t duration,
|
||||||
|
bool video,
|
||||||
|
const Api::SendAction &action) {
|
||||||
|
crl::on_main([&]
|
||||||
|
{
|
||||||
|
session->api().sendVoiceMessage(data,
|
||||||
|
QVector<signed char>(),
|
||||||
|
duration,
|
||||||
|
video,
|
||||||
|
action);
|
||||||
|
});
|
||||||
|
waitForMsgSync(session, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AyuSync
|
49
Telegram/SourceFiles/ayu/features/forward/ayu_sync.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// This is the source code of AyuGram for Desktop.
|
||||||
|
//
|
||||||
|
// We do not and cannot prevent the use of our code,
|
||||||
|
// but be respectful and credit the original author.
|
||||||
|
//
|
||||||
|
// Copyright @Radolyn, 2025
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/random.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "storage/file_download.h"
|
||||||
|
#include "storage/file_upload.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
|
|
||||||
|
namespace AyuSync {
|
||||||
|
|
||||||
|
QString pathForSave(not_null<Main::Session*> session);
|
||||||
|
QString filePath(not_null<Main::Session*> session, const Data::Media *media);
|
||||||
|
void loadDocuments(not_null<Main::Session*> session, const std::vector<not_null<HistoryItem*>> &items);
|
||||||
|
bool isMediaDownloadable(Data::Media *media);
|
||||||
|
void sendMessageSync(not_null<Main::Session*> session, Api::MessageToSend &message);
|
||||||
|
|
||||||
|
void sendDocumentSync(not_null<Main::Session*> session,
|
||||||
|
Ui::PreparedGroup &group,
|
||||||
|
SendMediaType type,
|
||||||
|
TextWithTags &&caption,
|
||||||
|
const Api::SendAction &action);
|
||||||
|
|
||||||
|
void sendStickerSync(not_null<Main::Session*> session,
|
||||||
|
Api::MessageToSend &message,
|
||||||
|
not_null<DocumentData*> document);
|
||||||
|
void waitForMsgSync(not_null<Main::Session*> session, const Api::SendAction &action);
|
||||||
|
void loadPhotoSync(not_null<Main::Session*> session, const std::pair<not_null<PhotoData*>, FullMsgId> &photos);
|
||||||
|
void loadDocumentSync(not_null<Main::Session*> session, DocumentData *data, not_null<HistoryItem*> item);
|
||||||
|
void forwardMessagesSync(not_null<Main::Session*> session,
|
||||||
|
const std::vector<not_null<HistoryItem*>> &items,
|
||||||
|
const ApiWrap::SendAction &action,
|
||||||
|
Data::ForwardOptions options);
|
||||||
|
void sendVoiceSync(not_null<Main::Session*> session,
|
||||||
|
const QByteArray &data,
|
||||||
|
int64_t duration,
|
||||||
|
bool video,
|
||||||
|
const Api::SendAction &action);
|
||||||
|
}
|
|
@ -161,7 +161,7 @@ public:
|
||||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||||
HistoryView::Context elementContext() override;
|
HistoryView::Context elementContext() override;
|
||||||
bool elementHideReply(not_null<const HistoryView::Element*> view) override;
|
bool elementHideReply(not_null<const HistoryView::Element*> view) override;
|
||||||
bool elementIsChatWide() override;
|
HistoryView::ElementChatMode elementChatMode() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<QWidget*> _parent;
|
const not_null<QWidget*> _parent;
|
||||||
|
@ -207,8 +207,10 @@ bool MessageShotDelegate::elementHideReply(not_null<const HistoryView::Element*>
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MessageShotDelegate::elementIsChatWide() {
|
HistoryView::ElementChatMode MessageShotDelegate::elementChatMode() {
|
||||||
return true;
|
using Mode = HistoryView::ElementChatMode;
|
||||||
|
// Mode::Wide;
|
||||||
|
return Mode::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage removeEmptySpaceAround(const QImage &original) {
|
QImage removeEmptySpaceAround(const QImage &original) {
|
||||||
|
|
|
@ -133,7 +133,7 @@ extern "C" {
|
||||||
**
|
**
|
||||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||||
** SQLite source code has been stored in the
|
** SQLite source code has been stored in the
|
||||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||||
** a string which identifies a particular check-in of SQLite
|
** a string which identifies a particular check-in of SQLite
|
||||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||||
|
@ -146,9 +146,9 @@ extern "C" {
|
||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.49.1"
|
#define SQLITE_VERSION "3.50.0"
|
||||||
#define SQLITE_VERSION_NUMBER 3049001
|
#define SQLITE_VERSION_NUMBER 3050000
|
||||||
#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70"
|
#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
|
@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
||||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||||
** integer is overwritten with the previous value of M.
|
** integer is overwritten with the previous value of M.
|
||||||
**
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||||
|
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||||
|
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||||
|
** This is used to implement the functionality associated with
|
||||||
|
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||||
|
**
|
||||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||||
|
@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
||||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
#define SQLITE_FCNTL_NULL_IO 43
|
#define SQLITE_FCNTL_NULL_IO 43
|
||||||
|
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
|
@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||||
** the default size of lookaside memory on each [database connection].
|
** the default size of [lookaside memory] on each [database connection].
|
||||||
** The first argument is the
|
** The first argument is the
|
||||||
** size of each lookaside buffer slot and the second is the number of
|
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
** slots allocated to each database connection ("cnt").)^
|
||||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||||
** configuration on individual connections.)^ </dd>
|
** be used to change the lookaside configuration on individual connections.)^
|
||||||
|
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||||
|
** default lookaside configuration at compile-time.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||||
|
@ -2232,31 +2242,50 @@ struct sqlite3_mem_methods {
|
||||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||||
** configuration of the lookaside memory allocator within a database
|
** configuration of the [lookaside memory allocator] within a database
|
||||||
** connection.
|
** connection.
|
||||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||||
** in the [DBCONFIG arguments|usual format].
|
** in the [DBCONFIG arguments|usual format].
|
||||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||||
** should have a total of five parameters.
|
** should have a total of five parameters.
|
||||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
** <ol>
|
||||||
|
** <li><p>The first argument ("buf") is a
|
||||||
** pointer to a memory buffer to use for lookaside memory.
|
** pointer to a memory buffer to use for lookaside memory.
|
||||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
** The first argument may be NULL in which case SQLite will allocate the
|
||||||
** may be NULL in which case SQLite will allocate the
|
** lookaside buffer itself using [sqlite3_malloc()].
|
||||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
** <li><P>The second argument ("sz") is the
|
||||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||||
** slots. The size of the buffer in the first argument must be greater than
|
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||||
** or equal to the product of the second and third arguments. The buffer
|
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
** it does.
|
||||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||||
|
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||||
|
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||||
|
** than 1,000,000.
|
||||||
|
** </ol>
|
||||||
|
** <p>If the "buf" argument is not NULL, then it must
|
||||||
|
** point to a memory buffer with a size that is greater than
|
||||||
|
** or equal to the product of "sz" and "cnt".
|
||||||
|
** The buffer must be aligned to an 8-byte boundary.
|
||||||
|
** The lookaside memory
|
||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].
|
||||||
|
** If the "buf" argument is NULL and an attempt
|
||||||
|
** to allocate memory based on "sz" and "cnt" fails, then
|
||||||
|
** lookaside is silently disabled.
|
||||||
|
** <p>
|
||||||
|
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||||
|
** default lookaside configuration at initialization. The
|
||||||
|
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||||
|
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||||
|
** "sz" and 40 to 100 for "cnt".
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||||
|
@ -2993,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Set the Setlk Timeout
|
||||||
|
** METHOD: sqlite3
|
||||||
|
**
|
||||||
|
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||||
|
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||||
|
** eligible locks taken on wal mode databases by the specified database
|
||||||
|
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||||
|
** not support blocking locks, this function is a no-op.
|
||||||
|
**
|
||||||
|
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||||
|
** -1 to this function requests that the VFS blocks for a long time -
|
||||||
|
** indefinitely if possible. The results of passing any other negative value
|
||||||
|
** are undefined.
|
||||||
|
**
|
||||||
|
** Internally, each SQLite database handle store two timeout values - the
|
||||||
|
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||||
|
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||||
|
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||||
|
** values, this function sets only the setlk-timeout value. Therefore,
|
||||||
|
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||||
|
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||||
|
**
|
||||||
|
** Whenever the number of connections to a wal mode database falls from
|
||||||
|
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||||
|
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||||
|
** new connection that tries to read from the database fails with an
|
||||||
|
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||||
|
** passed to this API, the new connection blocks until the exclusive lock
|
||||||
|
** has been released.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||||
|
*/
|
||||||
|
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Convenience Routines For Running Queries
|
** CAPI3REF: Convenience Routines For Running Queries
|
||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
|
@ -5108,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||||
** sqlite3_step(). Failure to reset the prepared statement using
|
** sqlite3_step(). Failure to reset the prepared statement using
|
||||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||||
** sqlite3_step() began
|
** sqlite3_step() began
|
||||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||||
|
@ -7004,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
||||||
**
|
**
|
||||||
** ^The second argument is a pointer to the function to invoke when a
|
** ^The second argument is a pointer to the function to invoke when a
|
||||||
** row is updated, inserted or deleted in a rowid table.
|
** row is updated, inserted or deleted in a rowid table.
|
||||||
|
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||||
|
** with a NULL pointer as the second parameter.
|
||||||
** ^The first argument to the callback is a copy of the third argument
|
** ^The first argument to the callback is a copy of the third argument
|
||||||
** to sqlite3_update_hook().
|
** to sqlite3_update_hook().
|
||||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||||
|
@ -11486,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
|
||||||
** is inserted while a session object is enabled, then later deleted while
|
** is inserted while a session object is enabled, then later deleted while
|
||||||
** the same session object is disabled, no INSERT record will appear in the
|
** the same session object is disabled, no INSERT record will appear in the
|
||||||
** changeset, even though the delete took place while the session was disabled.
|
** changeset, even though the delete took place while the session was disabled.
|
||||||
** Or, if one field of a row is updated while a session is disabled, and
|
** Or, if one field of a row is updated while a session is enabled, and
|
||||||
** another field of the same row is updated while the session is enabled, the
|
** then another field of the same row is updated while the session is disabled,
|
||||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
** the resulting changeset will contain an UPDATE change that updates both
|
||||||
|
** fields.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3session_changeset(
|
SQLITE_API int sqlite3session_changeset(
|
||||||
sqlite3_session *pSession, /* Session object */
|
sqlite3_session *pSession, /* Session object */
|
||||||
|
@ -11560,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
||||||
** database zFrom the contents of the two compatible tables would be
|
** database zFrom the contents of the two compatible tables would be
|
||||||
** identical.
|
** identical.
|
||||||
**
|
**
|
||||||
** It an error if database zFrom does not exist or does not contain the
|
** Unless the call to this function is a no-op as described above, it is an
|
||||||
** required compatible table.
|
** error if database zFrom does not exist or does not contain the required
|
||||||
|
** compatible table.
|
||||||
**
|
**
|
||||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||||
|
@ -11696,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
||||||
** The following flags may passed via the 4th parameter to
|
** The following flags may passed via the 4th parameter to
|
||||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||||
**
|
**
|
||||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||||
** Invert the changeset while iterating through it. This is equivalent to
|
** Invert the changeset while iterating through it. This is equivalent to
|
||||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||||
** It is an error to specify this flag with a patchset.
|
** It is an error to specify this flag with a patchset.
|
||||||
|
@ -12011,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
|
||||||
void **ppOut /* OUT: Buffer containing output changeset */
|
void **ppOut /* OUT: Buffer containing output changeset */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
|
||||||
*/
|
|
||||||
SQLITE_API int sqlite3changeset_upgrade(
|
|
||||||
sqlite3 *db,
|
|
||||||
const char *zDb,
|
|
||||||
int nIn, const void *pIn, /* Input changeset */
|
|
||||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Changegroup Handle
|
** CAPI3REF: Changegroup Handle
|
||||||
**
|
**
|
||||||
|
|
|
@ -14,7 +14,7 @@ static QImage LAST_LOADED_NO_MARGIN;
|
||||||
namespace AyuAssets {
|
namespace AyuAssets {
|
||||||
|
|
||||||
void loadAppIco() {
|
void loadAppIco() {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
|
|
||||||
QString appDataPath = QDir::fromNativeSeparators(qgetenv("APPDATA"));
|
QString appDataPath = QDir::fromNativeSeparators(qgetenv("APPDATA"));
|
||||||
QString tempIconPath = appDataPath + "/AyuGram.ico";
|
QString tempIconPath = appDataPath + "/AyuGram.ico";
|
||||||
|
@ -30,7 +30,7 @@ void loadAppIco() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadIcons() {
|
void loadIcons() {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (LAST_LOADED_NAME != settings.appIcon) {
|
if (LAST_LOADED_NAME != settings.appIcon) {
|
||||||
LAST_LOADED_NAME = settings.appIcon;
|
LAST_LOADED_NAME = settings.appIcon;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ void loadIcons() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage loadPreview(QString name) {
|
QImage loadPreview(const QString& name) {
|
||||||
return QImage(qsl(":/gui/art/ayu/%1/app_preview.png").arg(name));
|
return QImage(qsl(":/gui/art/ayu/%1/app_preview.png").arg(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ ICON(EXTERA2, "extera2");
|
||||||
|
|
||||||
void loadAppIco();
|
void loadAppIco();
|
||||||
|
|
||||||
QImage loadPreview(QString name);
|
QImage loadPreview(const QString& name);
|
||||||
|
|
||||||
QString currentAppLogoName();
|
QString currentAppLogoName();
|
||||||
QImage currentAppLogo();
|
QImage currentAppLogo();
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
// This is the source code of AyuGram for Desktop.
|
|
||||||
//
|
|
||||||
// We do not and cannot prevent the use of our code,
|
|
||||||
// but be respectful and credit the original author.
|
|
||||||
//
|
|
||||||
// Copyright @Radolyn, 2025
|
|
||||||
#include "edit_edited_mark.h"
|
|
||||||
|
|
||||||
#include "boxes/peer_list_controllers.h"
|
|
||||||
#include "lang/lang_keys.h"
|
|
||||||
#include "styles/style_boxes.h"
|
|
||||||
#include "styles/style_layers.h"
|
|
||||||
#include "styles/style_widgets.h"
|
|
||||||
#include "ui/widgets/popup_menu.h"
|
|
||||||
#include "ui/widgets/fields/input_field.h"
|
|
||||||
#include "ui/widgets/fields/special_fields.h"
|
|
||||||
|
|
||||||
#include "ayu/ayu_settings.h"
|
|
||||||
|
|
||||||
EditEditedMarkBox::EditEditedMarkBox(QWidget *)
|
|
||||||
: _text(
|
|
||||||
this,
|
|
||||||
st::defaultInputField,
|
|
||||||
tr::ayu_EditedMarkText(),
|
|
||||||
AyuSettings::getInstance().editedMark) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEditedMarkBox::prepare() {
|
|
||||||
const auto defaultEditedMark = tr::lng_edited(tr::now);
|
|
||||||
auto newHeight = st::contactPadding.top() + _text->height();
|
|
||||||
|
|
||||||
setTitle(tr::ayu_EditedMarkText());
|
|
||||||
|
|
||||||
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
|
|
||||||
setDimensions(st::boxWidth, newHeight);
|
|
||||||
|
|
||||||
addLeftButton(tr::ayu_BoxActionReset(),
|
|
||||||
[=]
|
|
||||||
{
|
|
||||||
_text->setText(defaultEditedMark);
|
|
||||||
});
|
|
||||||
addButton(tr::lng_settings_save(),
|
|
||||||
[=]
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
});
|
|
||||||
addButton(tr::lng_cancel(),
|
|
||||||
[=]
|
|
||||||
{
|
|
||||||
closeBox();
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto submitted = [=]
|
|
||||||
{
|
|
||||||
submit();
|
|
||||||
};
|
|
||||||
_text->submits(
|
|
||||||
) | rpl::start_with_next(submitted, _text->lifetime());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEditedMarkBox::setInnerFocus() {
|
|
||||||
_text->setFocusFast();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEditedMarkBox::submit() {
|
|
||||||
if (_text->getLastText().trimmed().isEmpty()) {
|
|
||||||
_text->setFocus();
|
|
||||||
_text->showError();
|
|
||||||
} else {
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEditedMarkBox::resizeEvent(QResizeEvent *e) {
|
|
||||||
BoxContent::resizeEvent(e);
|
|
||||||
|
|
||||||
_text->resize(
|
|
||||||
width()
|
|
||||||
- st::boxPadding.left()
|
|
||||||
- st::newGroupInfoPadding.left()
|
|
||||||
- st::boxPadding.right(),
|
|
||||||
_text->height());
|
|
||||||
|
|
||||||
const auto left = st::boxPadding.left() + st::newGroupInfoPadding.left();
|
|
||||||
_text->moveToLeft(left, st::contactPadding.top());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEditedMarkBox::save() {
|
|
||||||
AyuSettings::set_editedMark(_text->getLastText());
|
|
||||||
AyuSettings::save();
|
|
||||||
|
|
||||||
closeBox();
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// This is the source code of AyuGram for Desktop.
|
|
||||||
//
|
|
||||||
// We do not and cannot prevent the use of our code,
|
|
||||||
// but be respectful and credit the original author.
|
|
||||||
//
|
|
||||||
// Copyright @Radolyn, 2025
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "base/timer.h"
|
|
||||||
#include "boxes/abstract_box.h"
|
|
||||||
#include "mtproto/sender.h"
|
|
||||||
|
|
||||||
class EditEditedMarkBox : public Ui::BoxContent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
EditEditedMarkBox(QWidget *);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setInnerFocus() override;
|
|
||||||
void prepare() override;
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void submit();
|
|
||||||
void save();
|
|
||||||
|
|
||||||
object_ptr<Ui::InputField> _text;
|
|
||||||
};
|
|
|
@ -4,7 +4,9 @@
|
||||||
// but be respectful and credit the original author.
|
// but be respectful and credit the original author.
|
||||||
//
|
//
|
||||||
// Copyright @Radolyn, 2025
|
// Copyright @Radolyn, 2025
|
||||||
#include "edit_deleted_mark.h"
|
#include "edit_mark_box.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -17,19 +19,25 @@
|
||||||
|
|
||||||
#include "ayu/ayu_settings.h"
|
#include "ayu/ayu_settings.h"
|
||||||
|
|
||||||
EditDeletedMarkBox::EditDeletedMarkBox(QWidget *)
|
EditMarkBox::EditMarkBox(QWidget *,
|
||||||
: _text(
|
rpl::producer<QString> title,
|
||||||
this,
|
const QString ¤tValue,
|
||||||
st::defaultInputField,
|
QString defaultValue,
|
||||||
tr::ayu_DeletedMarkText(),
|
const Fn<void(const QString &)> &saveCallback)
|
||||||
AyuSettings::getInstance().deletedMark) {
|
: _title(title)
|
||||||
|
, _defaultValue(std::move(defaultValue))
|
||||||
|
, _saveCallback(saveCallback)
|
||||||
|
, _text(
|
||||||
|
this,
|
||||||
|
st::defaultInputField,
|
||||||
|
title,
|
||||||
|
currentValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeletedMarkBox::prepare() {
|
void EditMarkBox::prepare() {
|
||||||
const auto defaultDeletedMark = "🧹";
|
|
||||||
auto newHeight = st::contactPadding.top() + _text->height();
|
auto newHeight = st::contactPadding.top() + _text->height();
|
||||||
|
|
||||||
setTitle(tr::ayu_DeletedMarkText());
|
setTitle(_title);
|
||||||
|
|
||||||
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
|
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
|
||||||
setDimensions(st::boxWidth, newHeight);
|
setDimensions(st::boxWidth, newHeight);
|
||||||
|
@ -37,7 +45,7 @@ void EditDeletedMarkBox::prepare() {
|
||||||
addLeftButton(tr::ayu_BoxActionReset(),
|
addLeftButton(tr::ayu_BoxActionReset(),
|
||||||
[=]
|
[=]
|
||||||
{
|
{
|
||||||
_text->setText(defaultDeletedMark);
|
_text->setText(_defaultValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
addButton(tr::lng_settings_save(),
|
addButton(tr::lng_settings_save(),
|
||||||
|
@ -59,11 +67,11 @@ void EditDeletedMarkBox::prepare() {
|
||||||
) | rpl::start_with_next(submitted, _text->lifetime());
|
) | rpl::start_with_next(submitted, _text->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeletedMarkBox::setInnerFocus() {
|
void EditMarkBox::setInnerFocus() {
|
||||||
_text->setFocusFast();
|
_text->setFocusFast();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeletedMarkBox::submit() {
|
void EditMarkBox::submit() {
|
||||||
if (_text->getLastText().trimmed().isEmpty()) {
|
if (_text->getLastText().trimmed().isEmpty()) {
|
||||||
_text->setFocus();
|
_text->setFocus();
|
||||||
_text->showError();
|
_text->showError();
|
||||||
|
@ -72,7 +80,7 @@ void EditDeletedMarkBox::submit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeletedMarkBox::resizeEvent(QResizeEvent *e) {
|
void EditMarkBox::resizeEvent(QResizeEvent *e) {
|
||||||
BoxContent::resizeEvent(e);
|
BoxContent::resizeEvent(e);
|
||||||
|
|
||||||
_text->resize(
|
_text->resize(
|
||||||
|
@ -86,9 +94,7 @@ void EditDeletedMarkBox::resizeEvent(QResizeEvent *e) {
|
||||||
_text->moveToLeft(left, st::contactPadding.top());
|
_text->moveToLeft(left, st::contactPadding.top());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeletedMarkBox::save() {
|
void EditMarkBox::save() {
|
||||||
AyuSettings::set_deletedMark(_text->getLastText());
|
_saveCallback(_text->getLastText());
|
||||||
AyuSettings::save();
|
|
||||||
|
|
||||||
closeBox();
|
closeBox();
|
||||||
}
|
}
|
|
@ -9,10 +9,14 @@
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
|
|
||||||
class EditDeletedMarkBox : public Ui::BoxContent
|
namespace Ui {
|
||||||
|
class InputField;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditMarkBox : public Ui::BoxContent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EditDeletedMarkBox(QWidget *);
|
EditMarkBox(QWidget *, rpl::producer<QString> title, const QString& currentValue, QString defaultValue, const Fn<void(const QString&)> &saveCallback);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setInnerFocus() override;
|
void setInnerFocus() override;
|
||||||
|
@ -23,5 +27,9 @@ private:
|
||||||
void submit();
|
void submit();
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
|
rpl::producer<QString> _title;
|
||||||
|
QString _defaultValue;
|
||||||
|
Fn<void(const QString&)> _saveCallback;
|
||||||
|
|
||||||
object_ptr<Ui::InputField> _text;
|
object_ptr<Ui::InputField> _text;
|
||||||
};
|
};
|
|
@ -38,7 +38,7 @@ void MessageShotBox::prepare() {
|
||||||
void MessageShotBox::setupContent() {
|
void MessageShotBox::setupContent() {
|
||||||
_selectedPalette = std::make_shared<style::palette>();
|
_selectedPalette = std::make_shared<style::palette>();
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
const auto savedShowColorfulReplies = !settings.simpleQuotesAndReplies;
|
const auto savedShowColorfulReplies = !settings.simpleQuotesAndReplies;
|
||||||
|
|
||||||
using namespace Settings;
|
using namespace Settings;
|
||||||
|
|
|
@ -121,7 +121,7 @@ void IconPicker::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IconPicker::mousePressEvent(QMouseEvent *e) {
|
void IconPicker::mousePressEvent(QMouseEvent *e) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
auto changed = false;
|
auto changed = false;
|
||||||
|
|
||||||
auto x = e->pos().x();
|
auto x = e->pos().x();
|
|
@ -20,25 +20,168 @@
|
||||||
#include "core/mime_type.h"
|
#include "core/mime_type.h"
|
||||||
#include "styles/style_ayu_icons.h"
|
#include "styles/style_ayu_icons.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
|
|
||||||
#include "ayu/ui/message_history/history_section.h"
|
#include "ayu/ui/message_history/history_section.h"
|
||||||
#include "ayu/utils/telegram_helpers.h"
|
#include "ayu/utils/telegram_helpers.h"
|
||||||
|
#include "base/call_delayed.h"
|
||||||
|
#include "base/random.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_search_controller.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
|
||||||
namespace AyuUi {
|
namespace AyuUi {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void DeleteMyMessagesAfterConfirm(not_null<PeerData*> peer) {
|
||||||
|
const auto session = &peer->session();
|
||||||
|
|
||||||
|
auto collected = std::make_shared<std::vector<MsgId>>();
|
||||||
|
|
||||||
|
const auto removeNext = std::make_shared<Fn<void(int)>>();
|
||||||
|
const auto requestNext = std::make_shared<Fn<void(MsgId)>>();
|
||||||
|
|
||||||
|
*removeNext = [=](int index)
|
||||||
|
{
|
||||||
|
if (index >= int(collected->size())) {
|
||||||
|
DEBUG_LOG(("Deleted all %1 my messages in this chat").arg(collected->size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<MTPint> ids;
|
||||||
|
ids.reserve(std::min<int>(100, collected->size() - index));
|
||||||
|
for (auto i = 0; i < 100 && (index + i) < int(collected->size()); ++i) {
|
||||||
|
ids.push_back(MTP_int((*collected)[index + i].bare));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto batch = index / 100 + 1;
|
||||||
|
const auto done = [=](const MTPmessages_AffectedMessages &result)
|
||||||
|
{
|
||||||
|
session->api().applyAffectedMessages(peer, result);
|
||||||
|
if (peer->isChannel()) {
|
||||||
|
session->data().processMessagesDeleted(peer->id, ids);
|
||||||
|
} else {
|
||||||
|
session->data().processNonChannelMessagesDeleted(ids);
|
||||||
|
}
|
||||||
|
const auto deleted = index + ids.size();
|
||||||
|
DEBUG_LOG(("Deleted batch %1, total deleted %2/%3").arg(batch).arg(deleted).arg(collected->size()));
|
||||||
|
const auto delay = crl::time(500 + base::RandomValue<int>() % 500);
|
||||||
|
base::call_delayed(delay, [=] { (*removeNext)(deleted); });
|
||||||
|
};
|
||||||
|
const auto fail = [=](const MTP::Error &error)
|
||||||
|
{
|
||||||
|
DEBUG_LOG(("Delete batch failed: %1").arg(error.type()));
|
||||||
|
const auto delay = crl::time(1000);
|
||||||
|
base::call_delayed(delay, [=] { (*removeNext)(index); });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (const auto channel = peer->asChannel()) {
|
||||||
|
session->api()
|
||||||
|
.request(MTPchannels_DeleteMessages(channel->inputChannel, MTP_vector<MTPint>(ids)))
|
||||||
|
.done(done)
|
||||||
|
.fail(fail)
|
||||||
|
.handleFloodErrors()
|
||||||
|
.send();
|
||||||
|
} else {
|
||||||
|
using Flag = MTPmessages_DeleteMessages::Flag;
|
||||||
|
session->api()
|
||||||
|
.request(MTPmessages_DeleteMessages(MTP_flags(Flag::f_revoke), MTP_vector<MTPint>(ids)))
|
||||||
|
.done(done)
|
||||||
|
.fail(fail)
|
||||||
|
.handleFloodErrors()
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*requestNext = [=](MsgId from)
|
||||||
|
{
|
||||||
|
using Flag = MTPmessages_Search::Flag;
|
||||||
|
auto request = MTPmessages_Search(
|
||||||
|
MTP_flags(Flag::f_from_id),
|
||||||
|
peer->input,
|
||||||
|
MTP_string(),
|
||||||
|
MTP_inputPeerSelf(),
|
||||||
|
MTPInputPeer(),
|
||||||
|
MTPVector<MTPReaction>(),
|
||||||
|
MTP_int(0),
|
||||||
|
// top_msg_id
|
||||||
|
MTP_inputMessagesFilterEmpty(),
|
||||||
|
MTP_int(0),
|
||||||
|
// min_date
|
||||||
|
MTP_int(0),
|
||||||
|
// max_date
|
||||||
|
MTP_int(from.bare),
|
||||||
|
MTP_int(0),
|
||||||
|
// add_offset
|
||||||
|
MTP_int(100),
|
||||||
|
MTP_int(0),
|
||||||
|
// max_id
|
||||||
|
MTP_int(0),
|
||||||
|
// min_id
|
||||||
|
MTP_long(0)); // hash
|
||||||
|
|
||||||
|
session->api()
|
||||||
|
.request(std::move(request))
|
||||||
|
.done([=](const Api::HistoryRequestResult &result)
|
||||||
|
{
|
||||||
|
auto parsed = Api::ParseHistoryResult(peer, from, Data::LoadDirection::Before, result);
|
||||||
|
MsgId minId;
|
||||||
|
int batchCount = 0;
|
||||||
|
for (const auto &id : parsed.messageIds) {
|
||||||
|
if (!minId || id < minId) minId = id;
|
||||||
|
collected->push_back(id);
|
||||||
|
++batchCount;
|
||||||
|
}
|
||||||
|
DEBUG_LOG(("Batch found %1 my messages, total %2").arg(batchCount).arg(collected->size()));
|
||||||
|
if (parsed.messageIds.size() == 100 && minId) {
|
||||||
|
(*requestNext)(minId - MsgId(1));
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG(("Found %1 my messages in this chat (SEARCH)").arg(collected->size()));
|
||||||
|
(*removeNext)(0);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail([=](const MTP::Error &error) { DEBUG_LOG(("History fetch failed: %1").arg(error.type())); })
|
||||||
|
.send();
|
||||||
|
};
|
||||||
|
|
||||||
|
(*requestNext)(MsgId(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Fn<void()> DeleteMyMessagesHandler(not_null<Window::SessionController*> controller, not_null<PeerData*> peer) {
|
||||||
|
return [=]
|
||||||
|
{
|
||||||
|
if (!controller->showFrozenError()) {
|
||||||
|
controller->show(Ui::MakeConfirmBox({
|
||||||
|
.text = tr::ayu_DeleteOwnMessagesConfirmation(tr::now),
|
||||||
|
.confirmed =
|
||||||
|
[=](Fn<void()> &&close)
|
||||||
|
{
|
||||||
|
DeleteMyMessagesAfterConfirm(peer);
|
||||||
|
close();
|
||||||
|
},
|
||||||
|
.confirmText = tr::lng_box_delete(),
|
||||||
|
.cancelText = tr::lng_cancel(),
|
||||||
|
.confirmStyle = &st::attentionBoxButton,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool needToShowItem(int state) {
|
bool needToShowItem(int state) {
|
||||||
return state == 1 || (state == 2 && base::IsExtendedContextMenuModifierPressed());
|
return state == 1 || (state == 2 && base::IsExtendedContextMenuModifierPressed());
|
||||||
}
|
}
|
||||||
|
@ -170,6 +313,34 @@ void AddOpenChannelAction(PeerData *peerData,
|
||||||
&st::menuIconChannel);
|
&st::menuIconChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddDeleteOwnMessagesAction(PeerData *peerData,
|
||||||
|
Data::ForumTopic *topic,
|
||||||
|
not_null<Window::SessionController*> sessionController,
|
||||||
|
const Window::PeerMenuCallback &addCallback) {
|
||||||
|
if (topic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto isGroup = peerData->isChat() || peerData->isMegagroup();
|
||||||
|
if (!isGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (const auto chat = peerData->asChat()) {
|
||||||
|
if (!chat->amIn() || chat->amCreator() || chat->hasAdminRights()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (const auto channel = peerData->asChannel()) {
|
||||||
|
if (!channel->isMegagroup() || !channel->amIn() || channel->amCreator() || channel->hasAdminRights()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addCallback(
|
||||||
|
tr::ayu_DeleteOwnMessages(tr::now),
|
||||||
|
DeleteMyMessagesHandler(sessionController, peerData),
|
||||||
|
&st::menuIconTTL);
|
||||||
|
}
|
||||||
|
|
||||||
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
if (item->hideEditedBadge()) {
|
if (item->hideEditedBadge()) {
|
||||||
return;
|
return;
|
||||||
|
@ -197,7 +368,7 @@ void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!needToShowItem(settings.showHideMessageInContextMenu)) {
|
if (!needToShowItem(settings.showHideMessageInContextMenu)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -207,20 +378,25 @@ void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
|
const auto owner = &history->owner();
|
||||||
menu->addAction(
|
menu->addAction(
|
||||||
tr::ayu_ContextHideMessage(tr::now),
|
tr::ayu_ContextHideMessage(tr::now),
|
||||||
[=]()
|
[=]()
|
||||||
{
|
{
|
||||||
item->destroy();
|
const auto ids = owner->itemOrItsGroup(item);
|
||||||
|
for (const auto &fullId : ids) {
|
||||||
|
if (const auto current = owner->message(fullId)) {
|
||||||
|
current->destroy();
|
||||||
|
AyuState::hide(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
history->requestChatListMessage();
|
history->requestChatListMessage();
|
||||||
|
|
||||||
AyuState::hide(item);
|
|
||||||
},
|
},
|
||||||
&st::menuIconClear);
|
&st::menuIconClear);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!needToShowItem(settings.showUserMessagesInContextMenu)) {
|
if (!needToShowItem(settings.showUserMessagesInContextMenu)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +421,7 @@ void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddMessageDetailsAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
void AddMessageDetailsAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (!needToShowItem(settings.showMessageDetailsInContextMenu)) {
|
if (!needToShowItem(settings.showMessageDetailsInContextMenu)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -464,7 +640,7 @@ void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (settings.sendReadMessages) {
|
if (settings.sendReadMessages) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ void AddJumpToBeginningAction(PeerData *peerData,
|
||||||
void AddOpenChannelAction(PeerData *peerData,
|
void AddOpenChannelAction(PeerData *peerData,
|
||||||
not_null<Window::SessionController*> sessionController,
|
not_null<Window::SessionController*> sessionController,
|
||||||
const Window::PeerMenuCallback &addCallback);
|
const Window::PeerMenuCallback &addCallback);
|
||||||
|
void AddDeleteOwnMessagesAction(PeerData *peerData,
|
||||||
|
Data::ForumTopic* topic,
|
||||||
|
not_null<Window::SessionController*> sessionController,
|
||||||
|
const Window::PeerMenuCallback &addCallback);
|
||||||
|
|
||||||
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||||
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
|
||||||
|
|
|
@ -594,8 +594,9 @@ void InnerWidget::elementSearchInList(
|
||||||
void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) {
|
void InnerWidget::elementHandleViaClick(not_null<UserData*> bot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InnerWidget::elementIsChatWide() {
|
HistoryView::ElementChatMode InnerWidget::elementChatMode() {
|
||||||
return _isChatWide;
|
using Mode = HistoryView::ElementChatMode;
|
||||||
|
return _isChatWide ? Mode::Wide : Mode::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
|
not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
|
||||||
|
|
|
@ -120,7 +120,7 @@ public:
|
||||||
const QString &query,
|
const QString &query,
|
||||||
const FullMsgId &context) override;
|
const FullMsgId &context) override;
|
||||||
void elementHandleViaClick(not_null<UserData*> bot) override;
|
void elementHandleViaClick(not_null<UserData*> bot) override;
|
||||||
bool elementIsChatWide() override;
|
HistoryView::ElementChatMode elementChatMode() override;
|
||||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||||
void elementReplyTo(const FullReplyTo &to) override;
|
void elementReplyTo(const FullReplyTo &to) override;
|
||||||
void elementStartInteraction(
|
void elementStartInteraction(
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
#include "settings_ayu.h"
|
#include "settings_ayu.h"
|
||||||
|
|
||||||
#include "ayu/ayu_settings.h"
|
#include "ayu/ayu_settings.h"
|
||||||
#include "ayu/ui/boxes/edit_deleted_mark.h"
|
#include "ayu/ui/boxes/edit_mark_box.h"
|
||||||
#include "ayu/ui/boxes/edit_edited_mark.h"
|
|
||||||
#include "ayu/ui/boxes/font_selector.h"
|
#include "ayu/ui/boxes/font_selector.h"
|
||||||
|
|
||||||
#include "lang_auto.h"
|
#include "lang_auto.h"
|
||||||
|
@ -27,7 +26,7 @@
|
||||||
#include "styles/style_settings.h"
|
#include "styles/style_settings.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
#include "icon_picker.h"
|
#include "../components/icon_picker.h"
|
||||||
#include "tray.h"
|
#include "tray.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "main/main_domain.h"
|
#include "main/main_domain.h"
|
||||||
|
@ -37,7 +36,6 @@
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/continuous_sliders.h"
|
#include "ui/widgets/continuous_sliders.h"
|
||||||
|
@ -464,41 +462,41 @@ Ayu::Ayu(
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupGhostModeToggle(not_null<Ui::VerticalLayout*> container) {
|
void SetupGhostModeToggle(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::ayu_GhostEssentialsHeader());
|
AddSubsectionTitle(container, tr::ayu_GhostEssentialsHeader());
|
||||||
|
|
||||||
std::vector checkboxes{
|
std::vector checkboxes{
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_DontReadMessages(tr::now), !settings.sendReadMessages, [=](bool enabled)
|
tr::ayu_DontReadMessages(tr::now), !settings->sendReadMessages, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_sendReadMessages(!enabled);
|
AyuSettings::set_sendReadMessages(!enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_DontReadStories(tr::now), !settings.sendReadStories, [=](bool enabled)
|
tr::ayu_DontReadStories(tr::now), !settings->sendReadStories, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_sendReadStories(!enabled);
|
AyuSettings::set_sendReadStories(!enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_DontSendOnlinePackets(tr::now), !settings.sendOnlinePackets, [=](bool enabled)
|
tr::ayu_DontSendOnlinePackets(tr::now), !settings->sendOnlinePackets, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_sendOnlinePackets(!enabled);
|
AyuSettings::set_sendOnlinePackets(!enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_DontSendUploadProgress(tr::now), !settings.sendUploadProgress, [=](bool enabled)
|
tr::ayu_DontSendUploadProgress(tr::now), !settings->sendUploadProgress, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_sendUploadProgress(!enabled);
|
AyuSettings::set_sendUploadProgress(!enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_SendOfflinePacketAfterOnline(tr::now), settings.sendOfflinePacketAfterOnline, [=](bool enabled)
|
tr::ayu_SendOfflinePacketAfterOnline(tr::now), settings->sendOfflinePacketAfterOnline, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_sendOfflinePacketAfterOnline(enabled);
|
AyuSettings::set_sendOfflinePacketAfterOnline(enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
|
@ -510,13 +508,14 @@ void SetupGhostModeToggle(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
SetupGhostModeToggle(container);
|
SetupGhostModeToggle(container);
|
||||||
|
|
||||||
auto markReadAfterActionVal = container->lifetime().make_state<rpl::variable<bool>>(settings.sendOfflinePacketAfterOnline);
|
auto markReadAfterActionVal = container->lifetime().make_state<rpl::variable<bool>>(
|
||||||
|
settings->markReadAfterAction);
|
||||||
auto useScheduledMessagesVal = container->lifetime().make_state<rpl::variable<
|
auto useScheduledMessagesVal = container->lifetime().make_state<rpl::variable<
|
||||||
bool>>(settings.useScheduledMessages);
|
bool>>(settings->useScheduledMessages);
|
||||||
|
|
||||||
AddButtonWithIcon(
|
AddButtonWithIcon(
|
||||||
container,
|
container,
|
||||||
|
@ -528,7 +527,7 @@ void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.sendOfflinePacketAfterOnline);
|
return (enabled != settings->markReadAfterAction);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -555,7 +554,7 @@ void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.useScheduledMessages);
|
return (enabled != settings->useScheduledMessages);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -577,12 +576,12 @@ void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_SendWithoutSoundByDefault(),
|
tr::ayu_SendWithoutSoundByDefault(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.sendWithoutSound)
|
rpl::single(settings->sendWithoutSound)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.sendWithoutSound);
|
return (enabled != settings->sendWithoutSound);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -595,7 +594,7 @@ void SetupGhostEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::ayu_SpyEssentialsHeader());
|
AddSubsectionTitle(container, tr::ayu_SpyEssentialsHeader());
|
||||||
|
|
||||||
|
@ -604,12 +603,12 @@ void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_SaveDeletedMessages(),
|
tr::ayu_SaveDeletedMessages(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.saveDeletedMessages)
|
rpl::single(settings->saveDeletedMessages)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.saveDeletedMessages);
|
return (enabled != settings->saveDeletedMessages);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -623,12 +622,12 @@ void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_SaveMessagesHistory(),
|
tr::ayu_SaveMessagesHistory(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.saveMessagesHistory)
|
rpl::single(settings->saveMessagesHistory)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.saveMessagesHistory);
|
return (enabled != settings->saveMessagesHistory);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -646,12 +645,12 @@ void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_MessageSavingSaveForBots(),
|
tr::ayu_MessageSavingSaveForBots(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.saveForBots)
|
rpl::single(settings->saveForBots)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.saveForBots);
|
return (enabled != settings->saveForBots);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -662,7 +661,7 @@ void SetupSpyEssentials(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMessageFilters(not_null<Ui::VerticalLayout*> container) {
|
void SetupMessageFilters(not_null<Ui::VerticalLayout*> container) {
|
||||||
auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::ayu_RegexFilters());
|
AddSubsectionTitle(container, tr::ayu_RegexFilters());
|
||||||
|
|
||||||
|
@ -671,12 +670,12 @@ void SetupMessageFilters(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_FiltersHideFromBlocked(),
|
tr::ayu_FiltersHideFromBlocked(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.hideFromBlocked)
|
rpl::single(settings->hideFromBlocked)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.hideFromBlocked);
|
return (enabled != settings->hideFromBlocked);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -687,7 +686,7 @@ void SetupMessageFilters(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::ayu_QoLTogglesHeader());
|
AddSubsectionTitle(container, tr::ayu_QoLTogglesHeader());
|
||||||
|
|
||||||
|
@ -696,12 +695,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_DisableAds(),
|
tr::ayu_DisableAds(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.disableAds)
|
rpl::single(settings->disableAds)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.disableAds);
|
return (enabled != settings->disableAds);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -715,12 +714,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_DisableStories(),
|
tr::ayu_DisableStories(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.disableStories)
|
rpl::single(settings->disableStories)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.disableStories);
|
return (enabled != settings->disableStories);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -734,12 +733,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_DisableCustomBackgrounds(),
|
tr::ayu_DisableCustomBackgrounds(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.disableCustomBackgrounds)
|
rpl::single(settings->disableCustomBackgrounds)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.disableCustomBackgrounds);
|
return (enabled != settings->disableCustomBackgrounds);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -753,12 +752,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_SimpleQuotesAndReplies(),
|
tr::ayu_SimpleQuotesAndReplies(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.simpleQuotesAndReplies)
|
rpl::single(settings->simpleQuotesAndReplies)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.simpleQuotesAndReplies);
|
return (enabled != settings->simpleQuotesAndReplies);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -769,14 +768,14 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
|
||||||
std::vector checkboxes = {
|
std::vector checkboxes = {
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_CollapseSimilarChannels(tr::now), settings.collapseSimilarChannels, [=](bool enabled)
|
tr::ayu_CollapseSimilarChannels(tr::now), settings->collapseSimilarChannels, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_collapseSimilarChannels(enabled);
|
AyuSettings::set_collapseSimilarChannels(enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_HideSimilarChannelsTab(tr::now), settings.hideSimilarChannels, [=](bool enabled)
|
tr::ayu_HideSimilarChannelsTab(tr::now), settings->hideSimilarChannels, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_hideSimilarChannels(enabled);
|
AyuSettings::set_hideSimilarChannels(enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
|
@ -795,12 +794,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_DisableNotificationsDelay(),
|
tr::ayu_DisableNotificationsDelay(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.disableNotificationsDelay)
|
rpl::single(settings->disableNotificationsDelay)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.disableNotificationsDelay);
|
return (enabled != settings->disableNotificationsDelay);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -814,12 +813,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_ShowOnlyAddedEmojisAndStickers(),
|
tr::ayu_ShowOnlyAddedEmojisAndStickers(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showOnlyAddedEmojisAndStickers)
|
rpl::single(settings->showOnlyAddedEmojisAndStickers)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showOnlyAddedEmojisAndStickers);
|
return (enabled != settings->showOnlyAddedEmojisAndStickers);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -833,12 +832,12 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_LocalPremium(),
|
tr::ayu_LocalPremium(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.localPremium)
|
rpl::single(settings->localPremium)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.localPremium);
|
return (enabled != settings->localPremium);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -846,6 +845,48 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
},
|
},
|
||||||
container->lifetime());
|
container->lifetime());
|
||||||
|
|
||||||
|
AddSkip(container);
|
||||||
|
AddDivider(container);
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
AddButtonWithIcon(
|
||||||
|
container,
|
||||||
|
tr::ayu_HideChannelReactions(),
|
||||||
|
st::settingsButtonNoIcon
|
||||||
|
)->toggleOn(
|
||||||
|
rpl::single(!settings->hideChannelReactions)
|
||||||
|
)->toggledValue(
|
||||||
|
) | rpl::filter(
|
||||||
|
[=](bool enabled)
|
||||||
|
{
|
||||||
|
return (!enabled != settings->hideChannelReactions);
|
||||||
|
}) | start_with_next(
|
||||||
|
[=](bool enabled)
|
||||||
|
{
|
||||||
|
AyuSettings::set_hideChannelReactions(!enabled);
|
||||||
|
AyuSettings::save();
|
||||||
|
},
|
||||||
|
container->lifetime());
|
||||||
|
|
||||||
|
AddButtonWithIcon(
|
||||||
|
container,
|
||||||
|
tr::ayu_HideGroupReactions(),
|
||||||
|
st::settingsButtonNoIcon
|
||||||
|
)->toggleOn(
|
||||||
|
rpl::single(!settings->hideGroupReactions)
|
||||||
|
)->toggledValue(
|
||||||
|
) | rpl::filter(
|
||||||
|
[=](bool enabled)
|
||||||
|
{
|
||||||
|
return (!enabled != settings->hideGroupReactions);
|
||||||
|
}) | start_with_next(
|
||||||
|
[=](bool enabled)
|
||||||
|
{
|
||||||
|
AyuSettings::set_hideGroupReactions(!enabled);
|
||||||
|
AyuSettings::save();
|
||||||
|
},
|
||||||
|
container->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupAppIcon(not_null<Ui::VerticalLayout*> container) {
|
void SetupAppIcon(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
@ -856,7 +897,7 @@ void SetupAppIcon(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
|
||||||
void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<Window::SessionController*> controller) {
|
not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, tr::ayu_ContextMenuElementsHeader());
|
AddSubsectionTitle(container, tr::ayu_ContextMenuElementsHeader());
|
||||||
|
@ -870,7 +911,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.showReactionsPanelInContextMenu,
|
settings->showReactionsPanelInContextMenu,
|
||||||
options,
|
options,
|
||||||
tr::ayu_SettingsContextMenuReactionsPanel(),
|
tr::ayu_SettingsContextMenuReactionsPanel(),
|
||||||
tr::ayu_SettingsContextMenuTitle(),
|
tr::ayu_SettingsContextMenuTitle(),
|
||||||
|
@ -883,7 +924,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.showViewsPanelInContextMenu,
|
settings->showViewsPanelInContextMenu,
|
||||||
options,
|
options,
|
||||||
tr::ayu_SettingsContextMenuViewsPanel(),
|
tr::ayu_SettingsContextMenuViewsPanel(),
|
||||||
tr::ayu_SettingsContextMenuTitle(),
|
tr::ayu_SettingsContextMenuTitle(),
|
||||||
|
@ -897,7 +938,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.showHideMessageInContextMenu,
|
settings->showHideMessageInContextMenu,
|
||||||
options,
|
options,
|
||||||
tr::ayu_ContextHideMessage(),
|
tr::ayu_ContextHideMessage(),
|
||||||
tr::ayu_SettingsContextMenuTitle(),
|
tr::ayu_SettingsContextMenuTitle(),
|
||||||
|
@ -910,7 +951,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.showUserMessagesInContextMenu,
|
settings->showUserMessagesInContextMenu,
|
||||||
options,
|
options,
|
||||||
tr::ayu_UserMessagesMenuText(),
|
tr::ayu_UserMessagesMenuText(),
|
||||||
tr::ayu_SettingsContextMenuTitle(),
|
tr::ayu_SettingsContextMenuTitle(),
|
||||||
|
@ -923,7 +964,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.showMessageDetailsInContextMenu,
|
settings->showMessageDetailsInContextMenu,
|
||||||
options,
|
options,
|
||||||
tr::ayu_MessageDetailsPC(),
|
tr::ayu_MessageDetailsPC(),
|
||||||
tr::ayu_SettingsContextMenuTitle(),
|
tr::ayu_SettingsContextMenuTitle(),
|
||||||
|
@ -939,7 +980,7 @@ void SetupContextMenuElements(not_null<Ui::VerticalLayout*> container,
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, tr::ayu_MessageFieldElementsHeader());
|
AddSubsectionTitle(container, tr::ayu_MessageFieldElementsHeader());
|
||||||
|
@ -950,12 +991,12 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldAttachIcon}
|
{&st::messageFieldAttachIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showAttachButtonInMessageField)
|
rpl::single(settings->showAttachButtonInMessageField)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showAttachButtonInMessageField);
|
return (enabled != settings->showAttachButtonInMessageField);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -970,12 +1011,12 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldCommandsIcon}
|
{&st::messageFieldCommandsIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showCommandsButtonInMessageField)
|
rpl::single(settings->showCommandsButtonInMessageField)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showCommandsButtonInMessageField);
|
return (enabled != settings->showCommandsButtonInMessageField);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -990,12 +1031,12 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldTTLIcon}
|
{&st::messageFieldTTLIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showAutoDeleteButtonInMessageField)
|
rpl::single(settings->showAutoDeleteButtonInMessageField)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showAutoDeleteButtonInMessageField);
|
return (enabled != settings->showAutoDeleteButtonInMessageField);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1010,12 +1051,12 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldEmojiIcon}
|
{&st::messageFieldEmojiIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showEmojiButtonInMessageField)
|
rpl::single(settings->showEmojiButtonInMessageField)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showEmojiButtonInMessageField);
|
return (enabled != settings->showEmojiButtonInMessageField);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1030,12 +1071,12 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldVoiceIcon}
|
{&st::messageFieldVoiceIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showMicrophoneButtonInMessageField)
|
rpl::single(settings->showMicrophoneButtonInMessageField)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showMicrophoneButtonInMessageField);
|
return (enabled != settings->showMicrophoneButtonInMessageField);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1049,7 +1090,7 @@ void SetupMessageFieldElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMessageFieldPopups(not_null<Ui::VerticalLayout*> container) {
|
void SetupMessageFieldPopups(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, tr::ayu_MessageFieldPopupsHeader());
|
AddSubsectionTitle(container, tr::ayu_MessageFieldPopupsHeader());
|
||||||
|
@ -1060,12 +1101,12 @@ void SetupMessageFieldPopups(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldAttachIcon}
|
{&st::messageFieldAttachIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showAttachPopup)
|
rpl::single(settings->showAttachPopup)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showAttachPopup);
|
return (enabled != settings->showAttachPopup);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1080,12 +1121,12 @@ void SetupMessageFieldPopups(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::messageFieldEmojiIcon}
|
{&st::messageFieldEmojiIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showEmojiPopup)
|
rpl::single(settings->showEmojiPopup)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showEmojiPopup);
|
return (enabled != settings->showEmojiPopup);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1099,7 +1140,7 @@ void SetupMessageFieldPopups(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, tr::ayu_DrawerElementsHeader());
|
AddSubsectionTitle(container, tr::ayu_DrawerElementsHeader());
|
||||||
|
@ -1110,12 +1151,12 @@ void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::ayuLReadMenuIcon}
|
{&st::ayuLReadMenuIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showLReadToggleInDrawer)
|
rpl::single(settings->showLReadToggleInDrawer)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showLReadToggleInDrawer);
|
return (enabled != settings->showLReadToggleInDrawer);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1130,12 +1171,12 @@ void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::ayuSReadMenuIcon}
|
{&st::ayuSReadMenuIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showSReadToggleInDrawer)
|
rpl::single(settings->showSReadToggleInDrawer)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showSReadToggleInDrawer);
|
return (enabled != settings->showSReadToggleInDrawer);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1150,12 +1191,12 @@ void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::ayuGhostIcon}
|
{&st::ayuGhostIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showGhostToggleInDrawer)
|
rpl::single(settings->showGhostToggleInDrawer)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showGhostToggleInDrawer);
|
return (enabled != settings->showGhostToggleInDrawer);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1171,12 +1212,12 @@ void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
st::settingsButton,
|
st::settingsButton,
|
||||||
{&st::ayuStreamerModeMenuIcon}
|
{&st::ayuStreamerModeMenuIcon}
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showStreamerToggleInDrawer)
|
rpl::single(settings->showStreamerToggleInDrawer)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showStreamerToggleInDrawer);
|
return (enabled != settings->showStreamerToggleInDrawer);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1188,7 +1229,7 @@ void SetupDrawerElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupTrayElements(not_null<Ui::VerticalLayout*> container) {
|
void SetupTrayElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
AddSubsectionTitle(container, tr::ayu_TrayElementsHeader());
|
AddSubsectionTitle(container, tr::ayu_TrayElementsHeader());
|
||||||
|
@ -1198,12 +1239,12 @@ void SetupTrayElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_EnableGhostModeTray(),
|
tr::ayu_EnableGhostModeTray(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showGhostToggleInTray)
|
rpl::single(settings->showGhostToggleInTray)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showGhostToggleInTray);
|
return (enabled != settings->showGhostToggleInTray);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1218,12 +1259,12 @@ void SetupTrayElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_EnableStreamerModeTray(),
|
tr::ayu_EnableStreamerModeTray(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showStreamerToggleInTray)
|
rpl::single(settings->showStreamerToggleInTray)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showStreamerToggleInTray);
|
return (enabled != settings->showStreamerToggleInTray);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1236,7 +1277,7 @@ void SetupTrayElements(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
|
||||||
void SetupShowPeerId(not_null<Ui::VerticalLayout*> container,
|
void SetupShowPeerId(not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<Window::SessionController*> controller) {
|
not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
const auto options = std::vector{
|
const auto options = std::vector{
|
||||||
QString(tr::ayu_SettingsShowID_Hide(tr::now)),
|
QString(tr::ayu_SettingsShowID_Hide(tr::now)),
|
||||||
|
@ -1270,7 +1311,7 @@ void SetupShowPeerId(not_null<Ui::VerticalLayout*> container,
|
||||||
{
|
{
|
||||||
.title = tr::ayu_SettingsShowID(),
|
.title = tr::ayu_SettingsShowID(),
|
||||||
.options = options,
|
.options = options,
|
||||||
.initialSelection = settings.showPeerId,
|
.initialSelection = settings->showPeerId,
|
||||||
.callback = save,
|
.callback = save,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -1278,7 +1319,7 @@ void SetupShowPeerId(not_null<Ui::VerticalLayout*> container,
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
container->add(
|
container->add(
|
||||||
object_ptr<Button>(container,
|
object_ptr<Button>(container,
|
||||||
|
@ -1300,7 +1341,7 @@ void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
||||||
{
|
{
|
||||||
label->setText(QString::number(amount));
|
label->setText(QString::number(amount));
|
||||||
};
|
};
|
||||||
updateLabel(settings.recentStickersCount);
|
updateLabel(settings->recentStickersCount);
|
||||||
|
|
||||||
slider->setPseudoDiscrete(
|
slider->setPseudoDiscrete(
|
||||||
200 + 1,
|
200 + 1,
|
||||||
|
@ -1309,7 +1350,7 @@ void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
||||||
{
|
{
|
||||||
return amount;
|
return amount;
|
||||||
},
|
},
|
||||||
settings.recentStickersCount,
|
settings->recentStickersCount,
|
||||||
[=](int amount)
|
[=](int amount)
|
||||||
{
|
{
|
||||||
updateLabel(amount);
|
updateLabel(amount);
|
||||||
|
@ -1325,7 +1366,7 @@ void SetupRecentStickersLimitSlider(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
|
||||||
void SetupWideMultiplierSlider(not_null<Ui::VerticalLayout*> container,
|
void SetupWideMultiplierSlider(not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<Window::SessionController*> controller) {
|
not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
container->add(
|
container->add(
|
||||||
object_ptr<Button>(container,
|
object_ptr<Button>(container,
|
||||||
|
@ -1360,12 +1401,12 @@ void SetupWideMultiplierSlider(not_null<Ui::VerticalLayout*> container,
|
||||||
return kMinSize + index * kStep;
|
return kMinSize + index * kStep;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateLabel(settings.wideMultiplier);
|
updateLabel(settings->wideMultiplier);
|
||||||
|
|
||||||
slider->setPseudoDiscrete(
|
slider->setPseudoDiscrete(
|
||||||
kSizeAmount,
|
kSizeAmount,
|
||||||
[=](int index) { return index; },
|
[=](int index) { return index; },
|
||||||
valueToIndex(settings.wideMultiplier),
|
valueToIndex(settings->wideMultiplier),
|
||||||
[=](int index)
|
[=](int index)
|
||||||
{
|
{
|
||||||
updateLabel(indexToValue(index));
|
updateLabel(indexToValue(index));
|
||||||
|
@ -1394,13 +1435,13 @@ void SetupWideMultiplierSlider(not_null<Ui::VerticalLayout*> container,
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupFonts(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
void SetupFonts(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
const auto monoButton = AddButtonWithLabel(
|
const auto monoButton = AddButtonWithLabel(
|
||||||
container,
|
container,
|
||||||
tr::ayu_MonospaceFont(),
|
tr::ayu_MonospaceFont(),
|
||||||
rpl::single(
|
rpl::single(
|
||||||
settings.monoFont.isEmpty() ? tr::ayu_FontDefault(tr::now) : settings.monoFont
|
settings->monoFont.isEmpty() ? tr::ayu_FontDefault(tr::now) : settings->monoFont
|
||||||
),
|
),
|
||||||
st::settingsButtonNoIcon);
|
st::settingsButtonNoIcon);
|
||||||
const auto monoGuard = Ui::CreateChild<base::binary_guard>(monoButton.get());
|
const auto monoGuard = Ui::CreateChild<base::binary_guard>(monoButton.get());
|
||||||
|
@ -1419,7 +1460,7 @@ void SetupFonts(not_null<Ui::VerticalLayout*> container, not_null<Window::Sessio
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::ayu_ConfirmationsTitle());
|
AddSubsectionTitle(container, tr::ayu_ConfirmationsTitle());
|
||||||
|
|
||||||
|
@ -1428,12 +1469,12 @@ void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_StickerConfirmation(),
|
tr::ayu_StickerConfirmation(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.stickerConfirmation)
|
rpl::single(settings->stickerConfirmation)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.stickerConfirmation);
|
return (enabled != settings->stickerConfirmation);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1447,12 +1488,12 @@ void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_GIFConfirmation(),
|
tr::ayu_GIFConfirmation(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.gifConfirmation)
|
rpl::single(settings->gifConfirmation)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.gifConfirmation);
|
return (enabled != settings->gifConfirmation);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1466,12 +1507,12 @@ void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_VoiceConfirmation(),
|
tr::ayu_VoiceConfirmation(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.voiceConfirmation)
|
rpl::single(settings->voiceConfirmation)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.voiceConfirmation);
|
return (enabled != settings->voiceConfirmation);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1482,19 +1523,19 @@ void SetupSendConfirmations(not_null<Ui::VerticalLayout*> container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMarks(not_null<Ui::VerticalLayout*> container) {
|
void SetupMarks(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddButtonWithIcon(
|
AddButtonWithIcon(
|
||||||
container,
|
container,
|
||||||
tr::ayu_ReplaceMarksWithIcons(),
|
tr::ayu_ReplaceMarksWithIcons(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.replaceBottomInfoWithIcons)
|
rpl::single(settings->replaceBottomInfoWithIcons)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.replaceBottomInfoWithIcons);
|
return (enabled != settings->replaceBottomInfoWithIcons);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1511,7 +1552,16 @@ void SetupMarks(not_null<Ui::VerticalLayout*> container) {
|
||||||
)->addClickHandler(
|
)->addClickHandler(
|
||||||
[=]()
|
[=]()
|
||||||
{
|
{
|
||||||
auto box = Box<EditDeletedMarkBox>();
|
auto box = Box<EditMarkBox>(
|
||||||
|
tr::ayu_DeletedMarkText(),
|
||||||
|
settings->deletedMark,
|
||||||
|
QString("🧹"),
|
||||||
|
[=](const QString &value)
|
||||||
|
{
|
||||||
|
AyuSettings::set_deletedMark(value);
|
||||||
|
AyuSettings::save();
|
||||||
|
}
|
||||||
|
);
|
||||||
Ui::show(std::move(box));
|
Ui::show(std::move(box));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1523,25 +1573,34 @@ void SetupMarks(not_null<Ui::VerticalLayout*> container) {
|
||||||
)->addClickHandler(
|
)->addClickHandler(
|
||||||
[=]()
|
[=]()
|
||||||
{
|
{
|
||||||
auto box = Box<EditEditedMarkBox>();
|
auto box = Box<EditMarkBox>(
|
||||||
|
tr::ayu_EditedMarkText(),
|
||||||
|
settings->editedMark,
|
||||||
|
tr::lng_edited(tr::now),
|
||||||
|
[=](const QString &value)
|
||||||
|
{
|
||||||
|
AyuSettings::set_editedMark(value);
|
||||||
|
AyuSettings::save();
|
||||||
|
}
|
||||||
|
);
|
||||||
Ui::show(std::move(box));
|
Ui::show(std::move(box));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupFolderSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
void SetupFolderSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddButtonWithIcon(
|
AddButtonWithIcon(
|
||||||
container,
|
container,
|
||||||
tr::ayu_HideNotificationCounters(),
|
tr::ayu_HideNotificationCounters(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.hideNotificationCounters)
|
rpl::single(settings->hideNotificationCounters)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.hideNotificationCounters);
|
return (enabled != settings->hideNotificationCounters);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1557,12 +1616,12 @@ void SetupFolderSettings(not_null<Ui::VerticalLayout*> container, not_null<Windo
|
||||||
tr::ayu_HideNotificationBadge(),
|
tr::ayu_HideNotificationBadge(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.hideNotificationBadge)
|
rpl::single(settings->hideNotificationBadge)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.hideNotificationBadge);
|
return (enabled != settings->hideNotificationBadge);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1581,12 +1640,12 @@ void SetupFolderSettings(not_null<Ui::VerticalLayout*> container, not_null<Windo
|
||||||
tr::ayu_HideAllChats(),
|
tr::ayu_HideAllChats(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.hideAllChatsFolder)
|
rpl::single(settings->hideAllChatsFolder)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.hideAllChatsFolder);
|
return (enabled != settings->hideAllChatsFolder);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1597,7 +1656,7 @@ void SetupFolderSettings(not_null<Ui::VerticalLayout*> container, not_null<Windo
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupChannelSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
void SetupChannelSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
const auto options = std::vector{
|
const auto options = std::vector{
|
||||||
tr::ayu_ChannelBottomButtonHide(tr::now),
|
tr::ayu_ChannelBottomButtonHide(tr::now),
|
||||||
|
@ -1608,7 +1667,7 @@ void SetupChannelSettings(not_null<Ui::VerticalLayout*> container, not_null<Wind
|
||||||
AddChooseButtonWithIconAndRightText(
|
AddChooseButtonWithIconAndRightText(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
settings.channelBottomButton,
|
settings->channelBottomButton,
|
||||||
options,
|
options,
|
||||||
tr::ayu_ChannelBottomButton(),
|
tr::ayu_ChannelBottomButton(),
|
||||||
tr::ayu_ChannelBottomButton(),
|
tr::ayu_ChannelBottomButton(),
|
||||||
|
@ -1620,7 +1679,7 @@ void SetupChannelSettings(not_null<Ui::VerticalLayout*> container, not_null<Wind
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupNerdSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
void SetupNerdSettings(not_null<Ui::VerticalLayout*> container, not_null<Window::SessionController*> controller) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
SetupShowPeerId(container, controller);
|
SetupShowPeerId(container, controller);
|
||||||
|
|
||||||
|
@ -1629,12 +1688,12 @@ void SetupNerdSettings(not_null<Ui::VerticalLayout*> container, not_null<Window:
|
||||||
tr::ayu_SettingsShowMessageSeconds(),
|
tr::ayu_SettingsShowMessageSeconds(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showMessageSeconds)
|
rpl::single(settings->showMessageSeconds)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showMessageSeconds);
|
return (enabled != settings->showMessageSeconds);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1648,12 +1707,12 @@ void SetupNerdSettings(not_null<Ui::VerticalLayout*> container, not_null<Window:
|
||||||
tr::ayu_SettingsShowMessageShot(),
|
tr::ayu_SettingsShowMessageShot(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.showMessageShot)
|
rpl::single(settings->showMessageShot)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.showMessageShot);
|
return (enabled != settings->showMessageShot);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1664,7 +1723,7 @@ void SetupNerdSettings(not_null<Ui::VerticalLayout*> container, not_null<Window:
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupWebviewSettings(not_null<Ui::VerticalLayout*> container) {
|
void SetupWebviewSettings(not_null<Ui::VerticalLayout*> container) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
auto *settings = &AyuSettings::getInstance();
|
||||||
|
|
||||||
AddSubsectionTitle(container, rpl::single(QString("Webview")));
|
AddSubsectionTitle(container, rpl::single(QString("Webview")));
|
||||||
|
|
||||||
|
@ -1673,12 +1732,12 @@ void SetupWebviewSettings(not_null<Ui::VerticalLayout*> container) {
|
||||||
tr::ayu_SettingsSpoofWebviewAsAndroid(),
|
tr::ayu_SettingsSpoofWebviewAsAndroid(),
|
||||||
st::settingsButtonNoIcon
|
st::settingsButtonNoIcon
|
||||||
)->toggleOn(
|
)->toggleOn(
|
||||||
rpl::single(settings.spoofWebviewAsAndroid)
|
rpl::single(settings->spoofWebviewAsAndroid)
|
||||||
)->toggledValue(
|
)->toggledValue(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
return (enabled != settings.spoofWebviewAsAndroid);
|
return (enabled != settings->spoofWebviewAsAndroid);
|
||||||
}) | start_with_next(
|
}) | start_with_next(
|
||||||
[=](bool enabled)
|
[=](bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -1689,14 +1748,14 @@ void SetupWebviewSettings(not_null<Ui::VerticalLayout*> container) {
|
||||||
|
|
||||||
std::vector checkboxes = {
|
std::vector checkboxes = {
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_SettingsIncreaseWebviewHeight(tr::now), settings.increaseWebviewHeight, [=](bool enabled)
|
tr::ayu_SettingsIncreaseWebviewHeight(tr::now), settings->increaseWebviewHeight, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_increaseWebviewHeight(enabled);
|
AyuSettings::set_increaseWebviewHeight(enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NestedEntry{
|
NestedEntry{
|
||||||
tr::ayu_SettingsIncreaseWebviewWidth(tr::now), settings.increaseWebviewWidth, [=](bool enabled)
|
tr::ayu_SettingsIncreaseWebviewWidth(tr::now), settings->increaseWebviewWidth, [=](bool enabled)
|
||||||
{
|
{
|
||||||
AyuSettings::set_increaseWebviewWidth(enabled);
|
AyuSettings::set_increaseWebviewWidth(enabled);
|
||||||
AyuSettings::save();
|
AyuSettings::save();
|
||||||
|
|
|
@ -15,7 +15,7 @@ constexpr auto kMaxChannelId = -1000000000000;
|
||||||
QString IDString(not_null<PeerData*> peer) {
|
QString IDString(not_null<PeerData*> peer) {
|
||||||
auto resultId = QString::number(getBareID(peer));
|
auto resultId = QString::number(getBareID(peer));
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (settings.showPeerId == 2) {
|
if (settings.showPeerId == 2) {
|
||||||
if (peer->isChannel()) {
|
if (peer->isChannel()) {
|
||||||
resultId = QString::number(peerToChannel(peer->id).bare - kMaxChannelId).prepend("-");
|
resultId = QString::number(peerToChannel(peer->id).bare - kMaxChannelId).prepend("-");
|
||||||
|
|
|
@ -37,12 +37,13 @@
|
||||||
#include "ayu/ayu_settings.h"
|
#include "ayu/ayu_settings.h"
|
||||||
#include "ayu/ayu_state.h"
|
#include "ayu/ayu_state.h"
|
||||||
#include "ayu/data/messages_storage.h"
|
#include "ayu/data/messages_storage.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto usernameResolverBotId = 189165596L;
|
constexpr auto usernameResolverBotId = 8001593505L;
|
||||||
const auto usernameResolverBotUsername = QString("usinfobot");
|
const auto usernameResolverBotUsername = QString("TgDBSearchBot");
|
||||||
const auto usernameResolverEmpty = QString("¯\\_(ツ)_/¯");
|
const auto usernameResolverEmpty = QString("Error, username or id invalid/not found.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +109,7 @@ bool isMessageHidden(const not_null<HistoryItem*> item) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
if (settings.hideFromBlocked) {
|
if (settings.hideFromBlocked) {
|
||||||
if (item->from()->isUser() &&
|
if (item->from()->isUser() &&
|
||||||
item->from()->asUser()->isBlocked()) {
|
item->from()->asUser()->isBlocked()) {
|
||||||
|
@ -170,13 +171,15 @@ void readReactions(base::weak_ptr<Data::Thread> weakThread) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
|
const auto sublist = thread->asSublist();
|
||||||
const auto peer = thread->peer();
|
const auto peer = thread->peer();
|
||||||
const auto rootId = topic ? topic->rootId() : 0;
|
const auto rootId = topic ? topic->rootId() : 0;
|
||||||
using Flag = MTPmessages_ReadReactions::Flag;
|
using Flag = MTPmessages_ReadReactions::Flag;
|
||||||
peer->session().api().request(MTPmessages_ReadReactions(
|
peer->session().api().request(MTPmessages_ReadReactions(
|
||||||
MTP_flags(rootId ? Flag::f_top_msg_id : Flag(0)),
|
MTP_flags(rootId ? Flag::f_top_msg_id : Flag(0)),
|
||||||
peer->input,
|
peer->input,
|
||||||
MTP_int(rootId)
|
MTP_int(rootId),
|
||||||
|
sublist ? sublist->sublistPeer()->input : MTPInputPeer()
|
||||||
)).done([=](const MTPmessages_AffectedHistory &result)
|
)).done([=](const MTPmessages_AffectedHistory &result)
|
||||||
{
|
{
|
||||||
const auto offset = peer->session().api().applyAffectedHistory(
|
const auto offset = peer->session().api().applyAffectedHistory(
|
||||||
|
@ -185,7 +188,7 @@ void readReactions(base::weak_ptr<Data::Thread> weakThread) {
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
readReactions(weakThread);
|
readReactions(weakThread);
|
||||||
} else {
|
} else {
|
||||||
peer->owner().history(peer)->clearUnreadReactionsFor(rootId);
|
peer->owner().history(peer)->clearUnreadReactionsFor(rootId, sublist);
|
||||||
}
|
}
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
@ -513,7 +516,7 @@ int getScheduleTime(int64 sumSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMessageSavable(const not_null<HistoryItem *> item) {
|
bool isMessageSavable(const not_null<HistoryItem *> item) {
|
||||||
const auto& settings = AyuSettings::getInstance();
|
const auto &settings = AyuSettings::getInstance();
|
||||||
|
|
||||||
if (!settings.saveDeletedMessages) {
|
if (!settings.saveDeletedMessages) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -656,22 +659,21 @@ void searchUser(long long userId, Main::Session *session, bool searchUserFlag, c
|
||||||
return QString();
|
return QString();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (text.isEmpty() || text.startsWith(usernameResolverEmpty)) {
|
if (text.isEmpty() || text.contains(usernameResolverEmpty)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ID id = 0; // 👤
|
ID id = 0; // 🆔
|
||||||
QString title; // 👦🏻
|
QString title; // 🏷
|
||||||
QString username; // 🌐
|
QString username; // 📧
|
||||||
|
|
||||||
for (auto &line : text.split('\n')) {
|
for (auto &line : text.split('\n')) {
|
||||||
line = line.replace("", "");
|
if (line.startsWith("🆔")) {
|
||||||
if (line.startsWith("👤")) {
|
id = line.mid(line.indexOf(": ") + 2).toLongLong();
|
||||||
id = line.mid(line.indexOf(' ') + 1).toLongLong();
|
} else if (line.startsWith("🏷")) {
|
||||||
} else if (line.startsWith("👦🏻")) {
|
title = line.mid(line.indexOf(": ") + 2);
|
||||||
title = line.mid(line.indexOf(' ') + 1);
|
} else if (line.startsWith("📧")) {
|
||||||
} else if (line.startsWith("🌐")) {
|
username = line.mid(line.indexOf(": ") + 2);
|
||||||
username = line.mid(line.indexOf(' ') + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,3 +744,34 @@ ID getUserIdFromPackId(uint64 id) {
|
||||||
|
|
||||||
return ownerId;
|
return ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithTags extractText(not_null<HistoryItem*> item) {
|
||||||
|
TextWithTags result;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto poll = media->poll()) {
|
||||||
|
text.append("\xF0\x9F\x93\x8A ") // 📊
|
||||||
|
.append(poll->question.text).append("\n");
|
||||||
|
for (const auto answer : poll->answers) {
|
||||||
|
text.append("• ").append(answer.text.text).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.tags = TextUtilities::ConvertEntitiesToTextTags(item->originalText().entities);
|
||||||
|
result.text = text.isEmpty() ? item->originalText().text : text;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mediaDownloadable(Data::Media *media) {
|
||||||
|
if (!media
|
||||||
|
|| media->webpage() || media->poll() || media->game()
|
||||||
|
|| media->invoice() || media->location() || media->paper()
|
||||||
|
|| media->giveawayStart() || media->giveawayResults()
|
||||||
|
|| media->sharedContact() || media->call()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "dialogs/dialogs_main_list.h"
|
#include "dialogs/dialogs_main_list.h"
|
||||||
#include "info/profile/info_profile_badge.h"
|
#include "info/profile/info_profile_badge.h"
|
||||||
#include "main/main_domain.h"
|
#include "main/main_domain.h"
|
||||||
|
#include "data/data_poll.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
using UsernameResolverCallback = Fn<void(const QString &, UserData *)>;
|
using UsernameResolverCallback = Fn<void(const QString &, UserData *)>;
|
||||||
|
|
||||||
|
@ -55,3 +57,6 @@ void searchById(ID userId, Main::Session *session, bool retry, const UsernameRes
|
||||||
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
|
void searchById(ID userId, Main::Session *session, const UsernameResolverCallback &callback);
|
||||||
|
|
||||||
ID getUserIdFromPackId(uint64 id);
|
ID getUserIdFromPackId(uint64 id);
|
||||||
|
|
||||||
|
TextWithTags extractText(not_null<HistoryItem*> item);
|
||||||
|
bool mediaDownloadable(Data::Media* media);
|
|
@ -7,29 +7,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/about_box.h"
|
#include "boxes/about_box.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
|
||||||
#include "mainwidget.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "ui/boxes/confirm_box.h"
|
|
||||||
#include "ui/widgets/buttons.h"
|
|
||||||
#include "ui/widgets/labels.h"
|
|
||||||
#include "ui/text/text_utilities.h"
|
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "core/file_utilities.h"
|
|
||||||
#include "core/click_handler_types.h"
|
|
||||||
#include "core/update_checker.h"
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "core/file_utilities.h"
|
||||||
|
#include "core/update_checker.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "window/window_session_controller_link_info.h"
|
#include "window/window_session_controller_link_info.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
rpl::producer<TextWithEntities> Text() {
|
rpl::producer<TextWithEntities> Text() {
|
||||||
|
@ -47,54 +43,52 @@ rpl::producer<TextWithEntities> Text() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AboutBox::AboutBox(QWidget *parent, Window::SessionController* controller)
|
void AboutBox(not_null<Ui::GenericBox*> box, Window::SessionController* controller) {
|
||||||
: _version(this, tr::lng_about_version(tr::now, lt_version, currentVersionText()), st::aboutVersionLink)
|
box->setTitle(rpl::single(u"AyuGram Desktop"_q));
|
||||||
, _text(this, Text(), st::aboutLabel)
|
|
||||||
, _controller(controller) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void AboutBox::prepare() {
|
auto layout = box->verticalLayout();
|
||||||
setTitle(rpl::single(u"AyuGram Desktop"_q));
|
|
||||||
|
|
||||||
addButton(tr::lng_close(), [this] { closeBox(); });
|
const auto version = layout->add(
|
||||||
addLeftButton(
|
object_ptr<Ui::LinkButton>(
|
||||||
|
box,
|
||||||
|
tr::lng_about_version(
|
||||||
|
tr::now,
|
||||||
|
lt_version,
|
||||||
|
currentVersionText()),
|
||||||
|
st::aboutVersionLink),
|
||||||
|
QMargins(
|
||||||
|
st::boxRowPadding.left(),
|
||||||
|
-st::lineWidth * 3,
|
||||||
|
st::boxRowPadding.right(),
|
||||||
|
st::boxRowPadding.bottom()));
|
||||||
|
version->setClickedCallback([=] {
|
||||||
|
File::OpenUrl(Core::App().changelogLink());
|
||||||
|
});
|
||||||
|
|
||||||
|
Ui::AddSkip(layout, st::aboutTopSkip);
|
||||||
|
|
||||||
|
const auto addText = [&](rpl::producer<TextWithEntities> text) {
|
||||||
|
const auto label = layout->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(box, std::move(text), st::aboutLabel),
|
||||||
|
st::boxRowPadding);
|
||||||
|
label->setLinksTrusted();
|
||||||
|
Ui::AddSkip(layout, st::aboutSkip);
|
||||||
|
};
|
||||||
|
|
||||||
|
addText(Text());
|
||||||
|
|
||||||
|
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
|
||||||
|
box->addLeftButton(
|
||||||
rpl::single(QString("@AyuGramReleases")),
|
rpl::single(QString("@AyuGramReleases")),
|
||||||
[this, controller = _controller]
|
[box, controller]
|
||||||
{
|
{
|
||||||
closeBox();
|
box->closeBox();
|
||||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||||
.usernameOrId = QString("ayugramreleases"),
|
.usernameOrId = QString("ayugramreleases"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
_text->setLinksTrusted();
|
box->setWidth(st::aboutWidth);
|
||||||
|
|
||||||
_version->setClickedCallback([this] { showVersionHistory(); });
|
|
||||||
|
|
||||||
setDimensions(st::aboutWidth, st::aboutTextTop + _text->height());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AboutBox::resizeEvent(QResizeEvent *e) {
|
|
||||||
BoxContent::resizeEvent(e);
|
|
||||||
|
|
||||||
const auto available = width()
|
|
||||||
- st::boxPadding.left()
|
|
||||||
- st::boxPadding.right();
|
|
||||||
_version->moveToLeft(st::boxPadding.left(), st::aboutVersionTop);
|
|
||||||
_text->resizeToWidth(available);
|
|
||||||
_text->moveToLeft(st::boxPadding.left(), st::aboutTextTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AboutBox::showVersionHistory() {
|
|
||||||
File::OpenUrl(Core::App().changelogLink());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AboutBox::keyPressEvent(QKeyEvent *e) {
|
|
||||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
|
||||||
closeBox();
|
|
||||||
} else {
|
|
||||||
BoxContent::keyPressEvent(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString currentVersionText() {
|
QString currentVersionText() {
|
||||||
|
@ -109,5 +103,8 @@ QString currentVersionText() {
|
||||||
} else if (Platform::IsWindowsARM64()) {
|
} else if (Platform::IsWindowsARM64()) {
|
||||||
result += " arm64";
|
result += " arm64";
|
||||||
}
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
result += " DEBUG";
|
||||||
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,35 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ui/layers/box_content.h"
|
#include "ui/layers/generic_box.h"
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Ui {
|
void AboutBox(not_null<Ui::GenericBox*> box, Window::SessionController* controller);
|
||||||
class LinkButton;
|
|
||||||
class FlatLabel;
|
|
||||||
} // namespace Ui
|
|
||||||
|
|
||||||
class AboutBox : public Ui::BoxContent {
|
|
||||||
public:
|
|
||||||
AboutBox(QWidget*, Window::SessionController* controller);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void prepare() override;
|
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void showVersionHistory();
|
|
||||||
|
|
||||||
object_ptr<Ui::LinkButton> _version;
|
|
||||||
object_ptr<Ui::FlatLabel> _text;
|
|
||||||
Window::SessionController* _controller;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
QString telegramFaqLink();
|
|
||||||
QString currentVersionText();
|
QString currentVersionText();
|
||||||
|
|
|
@ -559,7 +559,7 @@ void GroupInfoBox::prepare() {
|
||||||
&_navigation->parentController()->window(),
|
&_navigation->parentController()->window(),
|
||||||
Ui::UserpicButton::Role::ChoosePhoto,
|
Ui::UserpicButton::Role::ChoosePhoto,
|
||||||
st::defaultUserpicButton,
|
st::defaultUserpicButton,
|
||||||
(_type == Type::Forum));
|
(_type == Type::Forum) ? Ui::PeerUserpicShape::Forum : Ui::PeerUserpicShape::Auto);
|
||||||
_photo->showCustomOnChosen();
|
_photo->showCustomOnChosen();
|
||||||
_title.create(
|
_title.create(
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -349,7 +349,7 @@ aboutVersionLink: LinkButton(defaultLinkButton) {
|
||||||
color: windowSubTextFg;
|
color: windowSubTextFg;
|
||||||
overColor: windowSubTextFg;
|
overColor: windowSubTextFg;
|
||||||
}
|
}
|
||||||
aboutTextTop: 34px;
|
aboutTopSkip: 19px;
|
||||||
aboutSkip: 14px;
|
aboutSkip: 14px;
|
||||||
aboutLabel: FlatLabel(defaultFlatLabel) {
|
aboutLabel: FlatLabel(defaultFlatLabel) {
|
||||||
minWidth: 300px;
|
minWidth: 300px;
|
||||||
|
|
|
@ -16,9 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
#include "mtproto/facade.h"
|
#include "mtproto/facade.h"
|
||||||
|
#include "settings/settings_common.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "ui/basic_click_handlers.h"
|
#include "ui/basic_click_handlers.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/boxes/peer_qr_box.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/effects/radial_animation.h"
|
#include "ui/effects/radial_animation.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
@ -32,6 +34,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/fields/number_input.h"
|
#include "ui/widgets/fields/number_input.h"
|
||||||
#include "ui/widgets/fields/password_input.h"
|
#include "ui/widgets/fields/password_input.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||||
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
@ -44,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
#include "styles/style_settings.h"
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
@ -54,6 +59,31 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||||
|
|
||||||
using ProxyData = MTP::ProxyData;
|
using ProxyData = MTP::ProxyData;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<QString> ExtractUrlsSimple(const QString &input) {
|
||||||
|
auto urls = std::vector<QString>();
|
||||||
|
static auto urlRegex = QRegularExpression(R"((https?:\/\/[^\s]+))");
|
||||||
|
|
||||||
|
auto it = urlRegex.globalMatch(input);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
urls.push_back(it.next().captured(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString ProxyDataToString(const ProxyData &proxy) {
|
||||||
|
using Type = ProxyData::Type;
|
||||||
|
return u"https://t.me/"_q
|
||||||
|
+ (proxy.type == Type::Socks5 ? "socks" : "proxy")
|
||||||
|
+ "?server=" + proxy.host + "&port=" + QString::number(proxy.port)
|
||||||
|
+ ((proxy.type == Type::Socks5 && !proxy.user.isEmpty())
|
||||||
|
? "&user=" + qthelp::url_encode(proxy.user) : "")
|
||||||
|
+ ((proxy.type == Type::Socks5 && !proxy.password.isEmpty())
|
||||||
|
? "&pass=" + qthelp::url_encode(proxy.password) : "")
|
||||||
|
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
|
||||||
|
? "&secret=" + proxy.password : "");
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] ProxyData ProxyDataFromFields(
|
[[nodiscard]] ProxyData ProxyDataFromFields(
|
||||||
ProxyData::Type type,
|
ProxyData::Type type,
|
||||||
const QMap<QString, QString> &fields) {
|
const QMap<QString, QString> &fields) {
|
||||||
|
@ -70,6 +100,80 @@ using ProxyData = MTP::ProxyData;
|
||||||
return proxy;
|
return proxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void AddProxyFromClipboard(
|
||||||
|
not_null<ProxiesBoxController*> controller,
|
||||||
|
std::shared_ptr<Ui::Show> show) {
|
||||||
|
const auto proxyString = u"proxy"_q;
|
||||||
|
const auto socksString = u"socks"_q;
|
||||||
|
const auto protocol = u"tg://"_q;
|
||||||
|
|
||||||
|
const auto maybeUrls = ExtractUrlsSimple(
|
||||||
|
QGuiApplication::clipboard()->text());
|
||||||
|
const auto isSingle = maybeUrls.size() == 1;
|
||||||
|
|
||||||
|
const auto proceedUrl = [=](const auto &local) {
|
||||||
|
const auto command = base::StringViewMid(
|
||||||
|
local,
|
||||||
|
protocol.size(),
|
||||||
|
8192);
|
||||||
|
|
||||||
|
if (local.startsWith(protocol + proxyString)
|
||||||
|
|| local.startsWith(protocol + socksString)) {
|
||||||
|
|
||||||
|
using namespace qthelp;
|
||||||
|
const auto options = RegExOption::CaseInsensitive;
|
||||||
|
for (const auto &[expression, _] : Core::LocalUrlHandlers()) {
|
||||||
|
const auto midExpression = base::StringViewMid(
|
||||||
|
expression,
|
||||||
|
1);
|
||||||
|
const auto isSocks = midExpression.startsWith(
|
||||||
|
socksString);
|
||||||
|
if (!midExpression.startsWith(proxyString)
|
||||||
|
&& !isSocks) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto match = regex_match(
|
||||||
|
expression,
|
||||||
|
command,
|
||||||
|
options);
|
||||||
|
if (!match) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto type = isSocks
|
||||||
|
? ProxyData::Type::Socks5
|
||||||
|
: ProxyData::Type::Mtproto;
|
||||||
|
const auto fields = url_parse_params(
|
||||||
|
match->captured(1),
|
||||||
|
qthelp::UrlParamNameTransform::ToLower);
|
||||||
|
const auto proxy = ProxyDataFromFields(type, fields);
|
||||||
|
const auto contains = controller->contains(proxy);
|
||||||
|
const auto toast = (contains
|
||||||
|
? tr::lng_proxy_add_from_clipboard_existing_toast
|
||||||
|
: tr::lng_proxy_add_from_clipboard_good_toast)(tr::now);
|
||||||
|
if (isSingle) {
|
||||||
|
show->showToast(toast);
|
||||||
|
}
|
||||||
|
if (!contains) {
|
||||||
|
controller->addNewItem(proxy);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto success = false;
|
||||||
|
for (const auto &maybeUrl : maybeUrls) {
|
||||||
|
success |= proceedUrl(Core::TryConvertUrlToLocal(maybeUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
show->showToast(
|
||||||
|
tr::lng_proxy_add_from_clipboard_failed_toast(tr::now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HostInput : public Ui::MaskedInputField {
|
class HostInput : public Ui::MaskedInputField {
|
||||||
public:
|
public:
|
||||||
HostInput(
|
HostInput(
|
||||||
|
@ -177,6 +281,7 @@ public:
|
||||||
rpl::producer<> restoreClicks() const;
|
rpl::producer<> restoreClicks() const;
|
||||||
rpl::producer<> editClicks() const;
|
rpl::producer<> editClicks() const;
|
||||||
rpl::producer<> shareClicks() const;
|
rpl::producer<> shareClicks() const;
|
||||||
|
rpl::producer<> showQrClicks() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
@ -198,6 +303,7 @@ private:
|
||||||
rpl::event_stream<> _restoreClicks;
|
rpl::event_stream<> _restoreClicks;
|
||||||
rpl::event_stream<> _editClicks;
|
rpl::event_stream<> _editClicks;
|
||||||
rpl::event_stream<> _shareClicks;
|
rpl::event_stream<> _shareClicks;
|
||||||
|
rpl::event_stream<> _showQrClicks;
|
||||||
base::unique_qptr<Ui::DropdownMenu> _menu;
|
base::unique_qptr<Ui::DropdownMenu> _menu;
|
||||||
|
|
||||||
bool _set = false;
|
bool _set = false;
|
||||||
|
@ -222,6 +328,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupContent();
|
void setupContent();
|
||||||
|
@ -319,6 +426,10 @@ rpl::producer<> ProxyRow::shareClicks() const {
|
||||||
return _shareClicks.events();
|
return _shareClicks.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ProxyRow::showQrClicks() const {
|
||||||
|
return _showQrClicks.events();
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyRow::setupControls(View &&view) {
|
void ProxyRow::setupControls(View &&view) {
|
||||||
updateFields(std::move(view));
|
updateFields(std::move(view));
|
||||||
_toggled.stop();
|
_toggled.stop();
|
||||||
|
@ -563,6 +674,9 @@ void ProxyRow::showMenu() {
|
||||||
addAction(tr::lng_proxy_edit_share(tr::now), [=] {
|
addAction(tr::lng_proxy_edit_share(tr::now), [=] {
|
||||||
_shareClicks.fire({});
|
_shareClicks.fire({});
|
||||||
}, &st::menuIconShare);
|
}, &st::menuIconShare);
|
||||||
|
addAction(tr::lng_group_invite_context_qr(tr::now), [=] {
|
||||||
|
_showQrClicks.fire({});
|
||||||
|
}, &st::menuIconQrCode);
|
||||||
}
|
}
|
||||||
if (_view.deleted) {
|
if (_view.deleted) {
|
||||||
addAction(tr::lng_proxy_menu_restore(tr::now), [=] {
|
addAction(tr::lng_proxy_menu_restore(tr::now), [=] {
|
||||||
|
@ -617,6 +731,18 @@ ProxiesBox::ProxiesBox(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxiesBox::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (e->key() == Qt::Key_Copy
|
||||||
|
|| (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier)) {
|
||||||
|
_controller->shareItems();
|
||||||
|
} else if (e->key() == Qt::Key_Paste
|
||||||
|
|| (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier)) {
|
||||||
|
AddProxyFromClipboard(_controller, uiShow());
|
||||||
|
} else {
|
||||||
|
BoxContent::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProxiesBox::prepare() {
|
void ProxiesBox::prepare() {
|
||||||
setTitle(tr::lng_proxy_settings());
|
setTitle(tr::lng_proxy_settings());
|
||||||
|
|
||||||
|
@ -631,67 +757,23 @@ void ProxiesBox::setupTopButton() {
|
||||||
const auto top = addTopButton(st::infoTopBarMenu);
|
const auto top = addTopButton(st::infoTopBarMenu);
|
||||||
const auto menu
|
const auto menu
|
||||||
= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
|
= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
|
||||||
const auto callback = [=] {
|
|
||||||
const auto maybeUrl = QGuiApplication::clipboard()->text();
|
|
||||||
const auto local = Core::TryConvertUrlToLocal(maybeUrl);
|
|
||||||
|
|
||||||
const auto proxyString = u"proxy"_q;
|
|
||||||
const auto socksString = u"socks"_q;
|
|
||||||
const auto protocol = u"tg://"_q;
|
|
||||||
const auto command = base::StringViewMid(
|
|
||||||
local,
|
|
||||||
protocol.size(),
|
|
||||||
8192);
|
|
||||||
|
|
||||||
if (local.startsWith(protocol + proxyString)
|
|
||||||
|| local.startsWith(protocol + socksString)) {
|
|
||||||
|
|
||||||
using namespace qthelp;
|
|
||||||
const auto options = RegExOption::CaseInsensitive;
|
|
||||||
for (const auto &[expression, _] : Core::LocalUrlHandlers()) {
|
|
||||||
const auto midExpression = base::StringViewMid(
|
|
||||||
expression,
|
|
||||||
1);
|
|
||||||
const auto isSocks = midExpression.startsWith(
|
|
||||||
socksString);
|
|
||||||
if (!midExpression.startsWith(proxyString)
|
|
||||||
&& !isSocks) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto match = regex_match(
|
|
||||||
expression,
|
|
||||||
command,
|
|
||||||
options);
|
|
||||||
if (!match) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto type = isSocks
|
|
||||||
? ProxyData::Type::Socks5
|
|
||||||
: ProxyData::Type::Mtproto;
|
|
||||||
const auto fields = url_parse_params(
|
|
||||||
match->captured(1),
|
|
||||||
qthelp::UrlParamNameTransform::ToLower);
|
|
||||||
const auto proxy = ProxyDataFromFields(type, fields);
|
|
||||||
const auto contains = _controller->contains(proxy);
|
|
||||||
const auto toast = (contains
|
|
||||||
? tr::lng_proxy_add_from_clipboard_existing_toast
|
|
||||||
: tr::lng_proxy_add_from_clipboard_good_toast)(tr::now);
|
|
||||||
uiShow()->showToast(toast);
|
|
||||||
if (!contains) {
|
|
||||||
_controller->addNewItem(proxy);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uiShow()->showToast(
|
|
||||||
tr::lng_proxy_add_from_clipboard_failed_toast(tr::now));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
top->setClickedCallback([=] {
|
top->setClickedCallback([=] {
|
||||||
*menu = base::make_unique_q<Ui::PopupMenu>(top, st::defaultPopupMenu);
|
*menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
(*menu)->addAction(
|
top,
|
||||||
tr::lng_proxy_add_from_clipboard(tr::now),
|
st::popupMenuWithIcons);
|
||||||
callback);
|
const auto addAction = Ui::Menu::CreateAddActionCallback(*menu);
|
||||||
|
addAction({
|
||||||
|
.text = tr::lng_proxy_add_from_clipboard(tr::now),
|
||||||
|
.handler = [=] { AddProxyFromClipboard(_controller, uiShow()); },
|
||||||
|
.icon = &st::menuIconImportTheme,
|
||||||
|
});
|
||||||
|
addAction({
|
||||||
|
.text = tr::lng_group_invite_context_delete_all(tr::now),
|
||||||
|
.handler = [=] { _controller->deleteItems(); },
|
||||||
|
.icon = &st::menuIconDeleteAttention,
|
||||||
|
.isAttention = true,
|
||||||
|
});
|
||||||
(*menu)->popup(QCursor::pos());
|
(*menu)->popup(QCursor::pos());
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -791,6 +873,23 @@ void ProxiesBox::setupContent() {
|
||||||
refreshProxyForCalls();
|
refreshProxyForCalls();
|
||||||
_proxyForCalls->finishAnimating();
|
_proxyForCalls->finishAnimating();
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto wrap = inner->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
inner,
|
||||||
|
object_ptr<Ui::VerticalLayout>(inner)));
|
||||||
|
const auto shareList = Settings::AddButtonWithIcon(
|
||||||
|
wrap->entity(),
|
||||||
|
tr::lng_proxy_edit_share_list_button(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ &st::menuIconCopy });
|
||||||
|
shareList->setClickedCallback([=] {
|
||||||
|
_controller->shareItems();
|
||||||
|
});
|
||||||
|
wrap->toggleOn(_controller->listShareableChanges());
|
||||||
|
wrap->finishAnimating();
|
||||||
|
}
|
||||||
|
|
||||||
inner->resizeToWidth(st::boxWideWidth);
|
inner->resizeToWidth(st::boxWideWidth);
|
||||||
|
|
||||||
inner->heightValue(
|
inner->heightValue(
|
||||||
|
@ -898,9 +997,11 @@ void ProxiesBox::setupButtons(int id, not_null<ProxyRow*> button) {
|
||||||
getDelegate()->show(_controller->editItemBox(id));
|
getDelegate()->show(_controller->editItemBox(id));
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
|
|
||||||
button->shareClicks(
|
rpl::merge(
|
||||||
) | rpl::start_with_next([=] {
|
button->shareClicks() | rpl::map_to(false),
|
||||||
_controller->shareItem(id);
|
button->showQrClicks() | rpl::map_to(true)
|
||||||
|
) | rpl::start_with_next([=](bool qr) {
|
||||||
|
_controller->shareItem(id, qr);
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
|
|
||||||
button->clicks(
|
button->clicks(
|
||||||
|
@ -1407,12 +1508,32 @@ void ProxiesBoxController::deleteItem(int id) {
|
||||||
setDeleted(id, true);
|
setDeleted(id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxiesBoxController::deleteItems() {
|
||||||
|
for (const auto &item : _list) {
|
||||||
|
setDeleted(item.id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProxiesBoxController::restoreItem(int id) {
|
void ProxiesBoxController::restoreItem(int id) {
|
||||||
setDeleted(id, false);
|
setDeleted(id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxiesBoxController::shareItem(int id) {
|
void ProxiesBoxController::shareItem(int id, bool qr) {
|
||||||
share(findById(id)->data);
|
share(findById(id)->data, qr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxiesBoxController::shareItems() {
|
||||||
|
auto result = QString();
|
||||||
|
for (const auto &item : _list) {
|
||||||
|
if (!item.deleted) {
|
||||||
|
result += ProxyDataToString(item.data) + '\n' + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QGuiApplication::clipboard()->setText(result);
|
||||||
|
_show->showToast(tr::lng_proxy_edit_share_list_toast(tr::now));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxiesBoxController::applyItem(int id) {
|
void ProxiesBoxController::applyItem(int id) {
|
||||||
|
@ -1621,6 +1742,17 @@ auto ProxiesBoxController::views() const -> rpl::producer<ItemView> {
|
||||||
return _views.events();
|
return _views.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> ProxiesBoxController::listShareableChanges() const {
|
||||||
|
return _views.events_starting_with(ItemView()) | rpl::map([=] {
|
||||||
|
for (const auto &item : _list) {
|
||||||
|
if (!item.deleted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ProxiesBoxController::updateView(const Item &item) {
|
void ProxiesBoxController::updateView(const Item &item) {
|
||||||
const auto selected = (_settings.selected() == item.data);
|
const auto selected = (_settings.selected() == item.data);
|
||||||
const auto deleted = item.deleted;
|
const auto deleted = item.deleted;
|
||||||
|
@ -1653,22 +1785,22 @@ void ProxiesBoxController::updateView(const Item &item) {
|
||||||
deleted,
|
deleted,
|
||||||
!deleted && supportsShare,
|
!deleted && supportsShare,
|
||||||
supportsCalls,
|
supportsCalls,
|
||||||
state });
|
state,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxiesBoxController::share(const ProxyData &proxy) {
|
void ProxiesBoxController::share(const ProxyData &proxy, bool qr) {
|
||||||
if (proxy.type == Type::Http) {
|
if (proxy.type == Type::Http) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto link = u"https://t.me/"_q
|
const auto link = ProxyDataToString(proxy);
|
||||||
+ (proxy.type == Type::Socks5 ? "socks" : "proxy")
|
if (qr) {
|
||||||
+ "?server=" + proxy.host + "&port=" + QString::number(proxy.port)
|
_show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
+ ((proxy.type == Type::Socks5 && !proxy.user.isEmpty())
|
Ui::FillPeerQrBox(box, nullptr, link, rpl::single(QString()));
|
||||||
? "&user=" + qthelp::url_encode(proxy.user) : "")
|
box->setTitle(tr::lng_proxy_edit_share_qr_box_title());
|
||||||
+ ((proxy.type == Type::Socks5 && !proxy.password.isEmpty())
|
}));
|
||||||
? "&pass=" + qthelp::url_encode(proxy.password) : "")
|
return;
|
||||||
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
|
}
|
||||||
? "&secret=" + proxy.password : "");
|
|
||||||
QGuiApplication::clipboard()->setText(link);
|
QGuiApplication::clipboard()->setText(link);
|
||||||
_show->showToast(tr::lng_username_copied(tr::now));
|
_show->showToast(tr::lng_username_copied(tr::now));
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
void deleteItem(int id);
|
void deleteItem(int id);
|
||||||
|
void deleteItems();
|
||||||
void restoreItem(int id);
|
void restoreItem(int id);
|
||||||
void shareItem(int id);
|
void shareItem(int id, bool qr);
|
||||||
|
void shareItems();
|
||||||
void applyItem(int id);
|
void applyItem(int id);
|
||||||
object_ptr<Ui::BoxContent> editItemBox(int id);
|
object_ptr<Ui::BoxContent> editItemBox(int id);
|
||||||
object_ptr<Ui::BoxContent> addNewItemBox();
|
object_ptr<Ui::BoxContent> addNewItemBox();
|
||||||
|
@ -87,6 +89,8 @@ public:
|
||||||
|
|
||||||
rpl::producer<ItemView> views() const;
|
rpl::producer<ItemView> views() const;
|
||||||
|
|
||||||
|
rpl::producer<bool> listShareableChanges() const;
|
||||||
|
|
||||||
~ProxiesBoxController();
|
~ProxiesBoxController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -106,7 +110,7 @@ private:
|
||||||
std::vector<Item>::iterator findByProxy(const ProxyData &proxy);
|
std::vector<Item>::iterator findByProxy(const ProxyData &proxy);
|
||||||
void setDeleted(int id, bool deleted);
|
void setDeleted(int id, bool deleted);
|
||||||
void updateView(const Item &item);
|
void updateView(const Item &item);
|
||||||
void share(const ProxyData &proxy);
|
void share(const ProxyData &proxy, bool qr = false);
|
||||||
void saveDelayed();
|
void saveDelayed();
|
||||||
void refreshChecker(Item &item);
|
void refreshChecker(Item &item);
|
||||||
void setupChecker(int id, const Checker &checker);
|
void setupChecker(int id, const Checker &checker);
|
||||||
|
|