diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 99201a7f7d5865..b85743d5077326 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -86,7 +86,7 @@ jobs:
docker run --rm \
-v $PWD:/usr/src/tdesktop \
- -e DEBUG=1 \
+ -e CONFIG=Debug \
tdesktop:centos_env \
/usr/src/tdesktop/Telegram/build/docker/centos_env/build.sh \
-D CMAKE_C_FLAGS_DEBUG="" \
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index fd09f109e96ae9..677c1da3c11d73 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -41,7 +41,7 @@ jobs:
macos:
name: MacOS
- runs-on: macos-12
+ runs-on: macos-13
strategy:
matrix:
diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml
index 45be743ce2db47..8ed4ffca180217 100644
--- a/.github/workflows/mac_packaged.yml
+++ b/.github/workflows/mac_packaged.yml
@@ -73,6 +73,7 @@ jobs:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version > CACHE_KEY.txt
+ brew list --versions >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
echo "$GITHUB_WORKSPACE" >> CACHE_KEY.txt
if [ "$AUTO_CACHING" = "1" ]; then
diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml
index 4d19e1048d693b..f414a55fd6c584 100644
--- a/.github/workflows/no-response.yml
+++ b/.github/workflows/no-response.yml
@@ -9,11 +9,38 @@ on:
- cron: '0 0 * * *'
jobs:
- noResponse:
+ waiting-for-answer:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
- # Label requiring a response
responseRequiredLabel: waiting for answer
+
+ needs-user-action:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: lee-dohm/no-response@v0.5.0
+ with:
+ token: ${{ github.token }}
+ responseRequiredLabel: needs user action
+
+ cant-reproduce:
+ if: github.event_name != 'issue_comment'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: lee-dohm/no-response@v0.5.0
+ with:
+ token: ${{ github.token }}
+ responseRequiredLabel: cant reproduce
+ closeComment: >
+ This issue has been automatically closed because no developer succeeded to
+ reproduce the issue with the given reproduction steps. With only the
+ information that is currently in the issue, we don't have enough
+ information to take action. Please reach out if you find what's missing to
+ reproduce the issue so that we can investigate further.
+
+
+ Note that GitHub is a developer communication platform. If you're an ordinary
+ user seeking for help, get to support crew via `Settings -> Ask question` in
+ the application.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e0d813e40106d..36f2e594296302 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,9 +60,9 @@ include(cmake/options.cmake)
if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
- set(qt_version 5.15.11)
+ set(qt_version 5.15.12)
elseif (APPLE)
- set(qt_version 6.2.6)
+ set(qt_version 6.2.7)
endif()
endif()
include(cmake/external/qt/package.cmake)
diff --git a/LEGAL b/LEGAL
index e1a7f1a56f55c1..17f0e86298913d 100644
--- a/LEGAL
+++ b/LEGAL
@@ -1,7 +1,7 @@
-This file is part of Telegram Desktop,
+This file is part of rabbitGram Desktop,
the desktop application for the Telegram messaging service.
-Copyright (c) 2023 xmdnusr and RabbitsInIT Team.
+Copyright (c) 2023-2024 xmdnusr.
rabbitGram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 3b2b41e95e1f12..49a4294c8d3a97 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -406,6 +406,8 @@ PRIVATE
chat_helpers/tabbed_section.h
chat_helpers/tabbed_selector.cpp
chat_helpers/tabbed_selector.h
+ chat_helpers/ttl_media_layer_widget.cpp
+ chat_helpers/ttl_media_layer_widget.h
core/application.cpp
core/application.h
core/base_integration.cpp
@@ -513,6 +515,7 @@ PRIVATE
data/data_groups.h
data/data_histories.cpp
data/data_histories.h
+ data/data_lastseen_status.h
data/data_location.cpp
data/data_location.h
data/data_media_rotation.cpp
@@ -609,6 +612,8 @@ PRIVATE
dialogs/dialogs_row.h
dialogs/dialogs_search_from_controllers.cpp
dialogs/dialogs_search_from_controllers.h
+ dialogs/dialogs_search_tags.cpp
+ dialogs/dialogs_search_tags.h
dialogs/dialogs_widget.cpp
dialogs/dialogs_widget.h
dialogs/ui/dialogs_layout.cpp
@@ -671,8 +676,6 @@ PRIVATE
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.h
- history/view/controls/history_view_voice_record_button.cpp
- history/view/controls/history_view_voice_record_button.h
history/view/controls/history_view_webpage_processor.cpp
history/view/controls/history_view_webpage_processor.h
history/view/media/history_view_call.cpp
@@ -748,6 +751,8 @@ PRIVATE
history/view/reactions/history_view_reactions_strip.h
history/view/reactions/history_view_reactions_tabs.cpp
history/view/reactions/history_view_reactions_tabs.h
+ history/view/history_view_about_view.cpp
+ history/view/history_view_about_view.h
history/view/history_view_bottom_info.cpp
history/view/history_view_bottom_info.h
history/view/history_view_contact_status.cpp
@@ -1012,6 +1017,7 @@ PRIVATE
media/audio/media_audio.h
media/audio/media_audio_capture.cpp
media/audio/media_audio_capture.h
+ media/audio/media_audio_capture_common.h
media/audio/media_audio_ffmpeg_loader.cpp
media/audio/media_audio_ffmpeg_loader.h
media/audio/media_audio_loader.cpp
@@ -1227,8 +1233,6 @@ PRIVATE
platform/mac/touchbar/mac_touchbar_manager.mm
platform/mac/touchbar/mac_touchbar_media_view.h
platform/mac/touchbar/mac_touchbar_media_view.mm
- platform/win/audio_win.cpp
- platform/win/audio_win.h
platform/win/file_utilities_win.cpp
platform/win/file_utilities_win.h
platform/win/launcher_win.cpp
@@ -1252,7 +1256,6 @@ PRIVATE
platform/win/windows_autostart_task.h
platform/win/windows_toast_activator.cpp
platform/win/windows_toast_activator.h
- platform/platform_audio.h
platform/platform_file_utilities.h
platform/platform_launcher.h
platform/platform_integration.cpp
@@ -1427,6 +1430,8 @@ PRIVATE
ui/widgets/level_meter.h
ui/countryinput.cpp
ui/countryinput.h
+ ui/dynamic_thumbnails.cpp
+ ui/dynamic_thumbnails.h
ui/filter_icons.cpp
ui/filter_icons.h
ui/filter_icon_panel.cpp
diff --git a/Telegram/Resources/animations/palette.tgs b/Telegram/Resources/animations/palette.tgs
new file mode 100644
index 00000000000000..cc4887f9471607
Binary files /dev/null and b/Telegram/Resources/animations/palette.tgs differ
diff --git a/Telegram/Resources/art/ttl/video_message_icon.svg b/Telegram/Resources/art/ttl/video_message_icon.svg
new file mode 100644
index 00000000000000..aeadc83852231c
--- /dev/null
+++ b/Telegram/Resources/art/ttl/video_message_icon.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/Telegram/Resources/icons/chat/audio_once.png b/Telegram/Resources/icons/chat/audio_once.png
new file mode 100644
index 00000000000000..4d1f93652302cd
Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once.png differ
diff --git a/Telegram/Resources/icons/chat/audio_once@2x.png b/Telegram/Resources/icons/chat/audio_once@2x.png
new file mode 100644
index 00000000000000..f66cfbbcb64460
Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once@2x.png differ
diff --git a/Telegram/Resources/icons/chat/audio_once@3x.png b/Telegram/Resources/icons/chat/audio_once@3x.png
new file mode 100644
index 00000000000000..bce2481c5534a9
Binary files /dev/null and b/Telegram/Resources/icons/chat/audio_once@3x.png differ
diff --git a/Telegram/Resources/icons/chat/large_lockedchat.png b/Telegram/Resources/icons/chat/large_lockedchat.png
new file mode 100644
index 00000000000000..812b705b30dc02
Binary files /dev/null and b/Telegram/Resources/icons/chat/large_lockedchat.png differ
diff --git a/Telegram/Resources/icons/chat/large_lockedchat@2x.png b/Telegram/Resources/icons/chat/large_lockedchat@2x.png
new file mode 100644
index 00000000000000..30bf73e9b7ead2
Binary files /dev/null and b/Telegram/Resources/icons/chat/large_lockedchat@2x.png differ
diff --git a/Telegram/Resources/icons/chat/large_lockedchat@3x.png b/Telegram/Resources/icons/chat/large_lockedchat@3x.png
new file mode 100644
index 00000000000000..a0174e129552ae
Binary files /dev/null and b/Telegram/Resources/icons/chat/large_lockedchat@3x.png differ
diff --git a/Telegram/Resources/icons/chat/mini_media_once.png b/Telegram/Resources/icons/chat/mini_media_once.png
new file mode 100644
index 00000000000000..8984491b7ae14c
Binary files /dev/null and b/Telegram/Resources/icons/chat/mini_media_once.png differ
diff --git a/Telegram/Resources/icons/chat/mini_media_once@2x.png b/Telegram/Resources/icons/chat/mini_media_once@2x.png
new file mode 100644
index 00000000000000..f8514d3af5b634
Binary files /dev/null and b/Telegram/Resources/icons/chat/mini_media_once@2x.png differ
diff --git a/Telegram/Resources/icons/chat/mini_media_once@3x.png b/Telegram/Resources/icons/chat/mini_media_once@3x.png
new file mode 100644
index 00000000000000..3900061be980c1
Binary files /dev/null and b/Telegram/Resources/icons/chat/mini_media_once@3x.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_hidden.png b/Telegram/Resources/icons/dialogs/avatar_hidden.png
new file mode 100644
index 00000000000000..e032148c8ad27d
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_hidden.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_hidden@2x.png b/Telegram/Resources/icons/dialogs/avatar_hidden@2x.png
new file mode 100644
index 00000000000000..ef143054e1659b
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_hidden@2x.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_hidden@3x.png b/Telegram/Resources/icons/dialogs/avatar_hidden@3x.png
new file mode 100644
index 00000000000000..f6ef6b733b92c3
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_hidden@3x.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_notes.png b/Telegram/Resources/icons/dialogs/avatar_notes.png
new file mode 100644
index 00000000000000..740d389103c940
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_notes.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_notes@2x.png b/Telegram/Resources/icons/dialogs/avatar_notes@2x.png
new file mode 100644
index 00000000000000..98dc7c89cfd385
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_notes@2x.png differ
diff --git a/Telegram/Resources/icons/dialogs/avatar_notes@3x.png b/Telegram/Resources/icons/dialogs/avatar_notes@3x.png
new file mode 100644
index 00000000000000..735961bd9030bb
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/avatar_notes@3x.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_arrow.png b/Telegram/Resources/icons/dialogs/mini_arrow.png
new file mode 100644
index 00000000000000..81b5076f827ef7
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_arrow.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_arrow@2x.png b/Telegram/Resources/icons/dialogs/mini_arrow@2x.png
new file mode 100644
index 00000000000000..6e1e87682fb573
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_arrow@2x.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_arrow@3x.png b/Telegram/Resources/icons/dialogs/mini_arrow@3x.png
new file mode 100644
index 00000000000000..36fd0e0c35be42
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_arrow@3x.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_tag_lock.png b/Telegram/Resources/icons/dialogs/mini_tag_lock.png
new file mode 100644
index 00000000000000..cb1e4d76a2e838
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_tag_lock.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_tag_lock@2x.png b/Telegram/Resources/icons/dialogs/mini_tag_lock@2x.png
new file mode 100644
index 00000000000000..cc875b7fa7ff9f
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_tag_lock@2x.png differ
diff --git a/Telegram/Resources/icons/dialogs/mini_tag_lock@3x.png b/Telegram/Resources/icons/dialogs/mini_tag_lock@3x.png
new file mode 100644
index 00000000000000..68254340d43a20
Binary files /dev/null and b/Telegram/Resources/icons/dialogs/mini_tag_lock@3x.png differ
diff --git a/Telegram/Resources/icons/hidden_author_userpic.png b/Telegram/Resources/icons/hidden_author_userpic.png
deleted file mode 100644
index 37acae5ab95aef..00000000000000
Binary files a/Telegram/Resources/icons/hidden_author_userpic.png and /dev/null differ
diff --git a/Telegram/Resources/icons/hidden_author_userpic@2x.png b/Telegram/Resources/icons/hidden_author_userpic@2x.png
deleted file mode 100644
index 31c29045e918bc..00000000000000
Binary files a/Telegram/Resources/icons/hidden_author_userpic@2x.png and /dev/null differ
diff --git a/Telegram/Resources/icons/hidden_author_userpic@3x.png b/Telegram/Resources/icons/hidden_author_userpic@3x.png
deleted file mode 100644
index b60d52b54c1c4f..00000000000000
Binary files a/Telegram/Resources/icons/hidden_author_userpic@3x.png and /dev/null differ
diff --git a/Telegram/Resources/icons/menu/tag_filter.png b/Telegram/Resources/icons/menu/tag_filter.png
new file mode 100644
index 00000000000000..1bef612c5bbd27
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_filter.png differ
diff --git a/Telegram/Resources/icons/menu/tag_filter@2x.png b/Telegram/Resources/icons/menu/tag_filter@2x.png
new file mode 100644
index 00000000000000..056113881671f1
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_filter@2x.png differ
diff --git a/Telegram/Resources/icons/menu/tag_filter@3x.png b/Telegram/Resources/icons/menu/tag_filter@3x.png
new file mode 100644
index 00000000000000..e8661460e7c83e
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_filter@3x.png differ
diff --git a/Telegram/Resources/icons/menu/tag_remove.png b/Telegram/Resources/icons/menu/tag_remove.png
new file mode 100644
index 00000000000000..0a495b4efe5a63
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_remove.png differ
diff --git a/Telegram/Resources/icons/menu/tag_remove@2x.png b/Telegram/Resources/icons/menu/tag_remove@2x.png
new file mode 100644
index 00000000000000..521ab6968f6f0a
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_remove@2x.png differ
diff --git a/Telegram/Resources/icons/menu/tag_remove@3x.png b/Telegram/Resources/icons/menu/tag_remove@3x.png
new file mode 100644
index 00000000000000..5ebfb2b9110f1f
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_remove@3x.png differ
diff --git a/Telegram/Resources/icons/menu/tag_rename.png b/Telegram/Resources/icons/menu/tag_rename.png
new file mode 100644
index 00000000000000..69040f6aaf7d18
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_rename.png differ
diff --git a/Telegram/Resources/icons/menu/tag_rename@2x.png b/Telegram/Resources/icons/menu/tag_rename@2x.png
new file mode 100644
index 00000000000000..2eb0fb82f9d721
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_rename@2x.png differ
diff --git a/Telegram/Resources/icons/menu/tag_rename@3x.png b/Telegram/Resources/icons/menu/tag_rename@3x.png
new file mode 100644
index 00000000000000..ec18a3f00dc7c1
Binary files /dev/null and b/Telegram/Resources/icons/menu/tag_rename@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_color_names.png b/Telegram/Resources/icons/settings/premium/features/feature_color_names.png
new file mode 100644
index 00000000000000..4acd54d82e04e4
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_color_names.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_color_names@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_color_names@2x.png
new file mode 100644
index 00000000000000..f8dd406fe00186
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_color_names@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_color_names@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_color_names@3x.png
new file mode 100644
index 00000000000000..1bd9d32ae02b1f
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_color_names@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_custombg.png b/Telegram/Resources/icons/settings/premium/features/feature_custombg.png
new file mode 100644
index 00000000000000..234f294345dd56
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_custombg.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_custombg@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_custombg@2x.png
new file mode 100644
index 00000000000000..0fb615748e9ee8
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_custombg@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_custombg@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_custombg@3x.png
new file mode 100644
index 00000000000000..571d1379e708b8
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_custombg@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack.png b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack.png
new file mode 100644
index 00000000000000..327d069c7908e7
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@2x.png
new file mode 100644
index 00000000000000..076b0843321828
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@3x.png
new file mode 100644
index 00000000000000..4325db686d856c
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_emoji_pack@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links.png b/Telegram/Resources/icons/settings/premium/features/feature_links.png
new file mode 100644
index 00000000000000..11c44d03f2d3a6
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links2.png b/Telegram/Resources/icons/settings/premium/features/feature_links2.png
new file mode 100644
index 00000000000000..8bfedd0f42cd95
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links2.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links2@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_links2@2x.png
new file mode 100644
index 00000000000000..c89f732258dc24
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links2@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links2@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_links2@3x.png
new file mode 100644
index 00000000000000..66c9d9d3a35770
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links2@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_links@2x.png
new file mode 100644
index 00000000000000..78ebcd3233ae88
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_links@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_links@3x.png
new file mode 100644
index 00000000000000..80b73df03e932f
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_links@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_reactions.png b/Telegram/Resources/icons/settings/premium/features/feature_reactions.png
new file mode 100644
index 00000000000000..efbe55f2a38f02
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_reactions.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_reactions@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_reactions@2x.png
new file mode 100644
index 00000000000000..b7391712616468
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_reactions@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_reactions@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_reactions@3x.png
new file mode 100644
index 00000000000000..c71f495befce24
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_reactions@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_status.png b/Telegram/Resources/icons/settings/premium/features/feature_status.png
new file mode 100644
index 00000000000000..b7969a4600bec5
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_status.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_status@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_status@2x.png
new file mode 100644
index 00000000000000..10f2a645ecb840
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_status@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_status@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_status@3x.png
new file mode 100644
index 00000000000000..5f93a1e33ac734
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_status@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_stories.png b/Telegram/Resources/icons/settings/premium/features/feature_stories.png
new file mode 100644
index 00000000000000..7420822b7533f3
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_stories.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_stories@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_stories@2x.png
new file mode 100644
index 00000000000000..ec1d7e056d7855
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_stories@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_stories@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_stories@3x.png
new file mode 100644
index 00000000000000..86e53f6013b9a6
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_stories@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_voice.png b/Telegram/Resources/icons/settings/premium/features/feature_voice.png
new file mode 100644
index 00000000000000..3f8f0a9c362b76
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_voice.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_voice@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_voice@2x.png
new file mode 100644
index 00000000000000..75ffbbf9d95a90
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_voice@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_voice@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_voice@3x.png
new file mode 100644
index 00000000000000..1761533709ffa2
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_voice@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_wallpaper.png b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper.png
new file mode 100644
index 00000000000000..a88ef05cfdaa4d
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@2x.png b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@2x.png
new file mode 100644
index 00000000000000..ad2bc2ed51ec35
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@3x.png b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@3x.png
new file mode 100644
index 00000000000000..6e0c0765a91e5b
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/features/feature_wallpaper@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_lastseen.png b/Telegram/Resources/icons/settings/premium/large_lastseen.png
new file mode 100644
index 00000000000000..e47bd7935bc38b
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_lastseen.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_lastseen@2x.png b/Telegram/Resources/icons/settings/premium/large_lastseen@2x.png
new file mode 100644
index 00000000000000..305b6a491966b1
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_lastseen@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_lastseen@3x.png b/Telegram/Resources/icons/settings/premium/large_lastseen@3x.png
new file mode 100644
index 00000000000000..0cd1e12ab09dc2
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_lastseen@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_readtime.png b/Telegram/Resources/icons/settings/premium/large_readtime.png
new file mode 100644
index 00000000000000..c06294a82e44e2
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_readtime.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_readtime@2x.png b/Telegram/Resources/icons/settings/premium/large_readtime@2x.png
new file mode 100644
index 00000000000000..f30a355ee8d7aa
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_readtime@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/large_readtime@3x.png b/Telegram/Resources/icons/settings/premium/large_readtime@3x.png
new file mode 100644
index 00000000000000..1e7d022a30e991
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/large_readtime@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/lastseen.png b/Telegram/Resources/icons/settings/premium/lastseen.png
new file mode 100644
index 00000000000000..9299ed21638a2e
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/lastseen.png differ
diff --git a/Telegram/Resources/icons/settings/premium/lastseen@2x.png b/Telegram/Resources/icons/settings/premium/lastseen@2x.png
new file mode 100644
index 00000000000000..994a9071949b00
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/lastseen@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/lastseen@3x.png b/Telegram/Resources/icons/settings/premium/lastseen@3x.png
new file mode 100644
index 00000000000000..42b3f18652aa2e
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/lastseen@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/privacy.png b/Telegram/Resources/icons/settings/premium/privacy.png
new file mode 100644
index 00000000000000..9d2309768f3cf9
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/privacy.png differ
diff --git a/Telegram/Resources/icons/settings/premium/privacy@2x.png b/Telegram/Resources/icons/settings/premium/privacy@2x.png
new file mode 100644
index 00000000000000..f82d78947fa763
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/privacy@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/privacy@3x.png b/Telegram/Resources/icons/settings/premium/privacy@3x.png
new file mode 100644
index 00000000000000..ce3c775438cd9b
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/privacy@3x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/tags.png b/Telegram/Resources/icons/settings/premium/tags.png
new file mode 100644
index 00000000000000..9a761e7b08ea92
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/tags.png differ
diff --git a/Telegram/Resources/icons/settings/premium/tags@2x.png b/Telegram/Resources/icons/settings/premium/tags@2x.png
new file mode 100644
index 00000000000000..194aec6497c97a
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/tags@2x.png differ
diff --git a/Telegram/Resources/icons/settings/premium/tags@3x.png b/Telegram/Resources/icons/settings/premium/tags@3x.png
new file mode 100644
index 00000000000000..f6b8511cfecda1
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/tags@3x.png differ
diff --git a/Telegram/Resources/icons/stories/boosts_mini.png b/Telegram/Resources/icons/stories/boosts_mini.png
new file mode 100644
index 00000000000000..80c667ecc925ae
Binary files /dev/null and b/Telegram/Resources/icons/stories/boosts_mini.png differ
diff --git a/Telegram/Resources/icons/stories/boosts_mini@2x.png b/Telegram/Resources/icons/stories/boosts_mini@2x.png
new file mode 100644
index 00000000000000..95a8063967eb63
Binary files /dev/null and b/Telegram/Resources/icons/stories/boosts_mini@2x.png differ
diff --git a/Telegram/Resources/icons/stories/boosts_mini@3x.png b/Telegram/Resources/icons/stories/boosts_mini@3x.png
new file mode 100644
index 00000000000000..423de820910c74
Binary files /dev/null and b/Telegram/Resources/icons/stories/boosts_mini@3x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_bg.png b/Telegram/Resources/icons/voice_lock/audio_once_bg.png
new file mode 100644
index 00000000000000..62778676ce6400
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_bg.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_bg@2x.png b/Telegram/Resources/icons/voice_lock/audio_once_bg@2x.png
new file mode 100644
index 00000000000000..225d6af3b8da0d
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_bg@2x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_bg@3x.png b/Telegram/Resources/icons/voice_lock/audio_once_bg@3x.png
new file mode 100644
index 00000000000000..1d82b2d6e73cd0
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_bg@3x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_number.png b/Telegram/Resources/icons/voice_lock/audio_once_number.png
new file mode 100644
index 00000000000000..61e22f105da97f
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_number.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_number@2x.png b/Telegram/Resources/icons/voice_lock/audio_once_number@2x.png
new file mode 100644
index 00000000000000..15047cb489de2c
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_number@2x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/audio_once_number@3x.png b/Telegram/Resources/icons/voice_lock/audio_once_number@3x.png
new file mode 100644
index 00000000000000..6e39511e17a9cc
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/audio_once_number@3x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/input_mic_s.png b/Telegram/Resources/icons/voice_lock/input_mic_s.png
new file mode 100644
index 00000000000000..86c1146a5d5dd6
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/input_mic_s.png differ
diff --git a/Telegram/Resources/icons/voice_lock/input_mic_s@2x.png b/Telegram/Resources/icons/voice_lock/input_mic_s@2x.png
new file mode 100644
index 00000000000000..e70fd6a669030d
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/input_mic_s@2x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/input_mic_s@3x.png b/Telegram/Resources/icons/voice_lock/input_mic_s@3x.png
new file mode 100644
index 00000000000000..d7a4ad98140957
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/input_mic_s@3x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/recorded_delete.png b/Telegram/Resources/icons/voice_lock/recorded_delete.png
new file mode 100644
index 00000000000000..f204f8f2206338
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/recorded_delete.png differ
diff --git a/Telegram/Resources/icons/voice_lock/recorded_delete@2x.png b/Telegram/Resources/icons/voice_lock/recorded_delete@2x.png
new file mode 100644
index 00000000000000..8bebe1a61bb735
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/recorded_delete@2x.png differ
diff --git a/Telegram/Resources/icons/voice_lock/recorded_delete@3x.png b/Telegram/Resources/icons/voice_lock/recorded_delete@3x.png
new file mode 100644
index 00000000000000..db0067c534ed82
Binary files /dev/null and b/Telegram/Resources/icons/voice_lock/recorded_delete@3x.png differ
diff --git a/Telegram/Resources/icons/win_quit.png b/Telegram/Resources/icons/win_quit.png
new file mode 100644
index 00000000000000..a823f133e81b47
Binary files /dev/null and b/Telegram/Resources/icons/win_quit.png differ
diff --git a/Telegram/Resources/icons/win_quit@2x.png b/Telegram/Resources/icons/win_quit@2x.png
new file mode 100644
index 00000000000000..569097eda9395b
Binary files /dev/null and b/Telegram/Resources/icons/win_quit@2x.png differ
diff --git a/Telegram/Resources/icons/win_quit@3x.png b/Telegram/Resources/icons/win_quit@3x.png
new file mode 100644
index 00000000000000..c11e92bb2a5a99
Binary files /dev/null and b/Telegram/Resources/icons/win_quit@3x.png differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 5a9db89de21013..7875ea3bfa8c25 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -141,6 +141,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_status_last_week" = "last seen within a week";
"lng_status_last_month" = "last seen within a month";
"lng_status_lastseen_now" = "last seen just now";
+"lng_status_lastseen_when" = "when?";
"lng_status_lastseen_minutes#one" = "last seen {count} minute ago";
"lng_status_lastseen_minutes#other" = "last seen {count} minutes ago";
"lng_status_lastseen_hours#one" = "last seen {count} hour ago";
@@ -168,6 +169,24 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_remember" = "Remember this choice";
+"lng_lastseen_show_title" = "Show Your Last Seen";
+"lng_lastseen_show_about" = "To see **{user}'s** Last Seen time, either start\nshowing your own Last Seen time...";
+"lng_lastseen_show_button" = "Show My Last Seen";
+"lng_lastseen_or" = "or";
+"lng_lastseen_premium_title" = "Upgrade to Premium";
+"lng_lastseen_premium_about" = "Subscription will let you see **{user}'s** Last Seen\nstatus without showing yours.";
+"lng_lastseen_premium_button" = "Subscribe to Telegram Premium";
+"lng_lastseen_shown_toast" = "Your last seen time is now visible.";
+
+"lng_readtime_show_title" = "Show Your Read Date";
+"lng_readtime_show_about" = "To see when **{user}** read the message,\neither start showing your own read time...";
+"lng_readtime_show_button" = "Show My Read Time";
+"lng_readtime_or" = "or";
+"lng_readtime_premium_title" = "Upgrade to Premium";
+"lng_readtime_premium_about" = "Subscription will let you see **{user}'s** read time\nwithout showing yours.";
+"lng_readtime_premium_button" = "Subscribe to Telegram Premium";
+"lng_readtime_shown_toast" = "Your read times are now visible.";
+
"lng_channels_limit_title" = "Too Many Communities";
"lng_channels_limit1#one" = "You are a member of **{count}** groups and channels.";
"lng_channels_limit1#other" = "You are a member of **{count}** groups and channels.";
@@ -414,7 +433,8 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_username_invalid" = "This username is invalid.";
"lng_username_occupied" = "This username is already occupied.";
"lng_username_too_short" = "This username is too short.";
-"lng_username_purchase_available" = "Sorry, this link is occupied by someone. But it's available for purchase through\nofficial {link}.";
+"lng_username_purchase_available" = "**This username is already taken.** However, it is currently available for purchase. {link}";
+"lng_username_purchase_available_link" = "Learn more...";
"lng_username_bad_symbols" = "Only a-z, 0-9, and underscores allowed.";
"lng_username_available" = "This username is available.";
"lng_username_not_found" = "User @{user} not found.";
@@ -611,6 +631,11 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_settings_call_accept_calls" = "Accept calls from this device";
"lng_settings_call_device_default" = "Same as the System";
+"lng_settings_section_devices" = "Speakers and Camera";
+"lng_settings_devices_calls" = "Calls and video chats";
+"lng_settings_devices_calls_same" = "Use the same devices for calls";
+"lng_settings_devices_inactive" = "Unavailable";
+
"lng_settings_language" = "Language";
"lng_settings_default_scale" = "Default interface scale";
"lng_settings_connection_type" = "Connection type";
@@ -624,6 +649,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_settings_phone_number_privacy" = "Phone number";
"lng_settings_forwards_privacy" = "Forwarded messages";
"lng_settings_profile_photo_privacy" = "Profile photo";
+"lng_settings_messages_privacy" = "Messages";
"lng_settings_voices_privacy" = "Voice messages";
"lng_settings_bio_privacy" = "Bio";
"lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages.";
@@ -841,6 +867,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_background_sure_delete" = "Are you sure you want to delete this background?";
"lng_background_other_info" = "{user} will be able to apply this wallpaper";
"lng_background_other_channel" = "All subscribers will see this wallpaper";
+"lng_background_other_group" = "All members will see this wallpaper";
"lng_background_apply1" = "Apply the wallpaper in this chat.";
"lng_background_apply2" = "Enjoy the view.";
"lng_background_apply_button" = "Apply For This Chat";
@@ -850,6 +877,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_background_apply_me" = "Apply for me";
"lng_background_apply_both" = "Apply for me and {user}";
"lng_background_apply_channel" = "Apply For Channel";
+"lng_background_apply_group" = "Apply For Group";
"lng_download_path_ask" = "Ask download path for each file";
"lng_download_path" = "Download path";
@@ -1060,6 +1088,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_edit_privacy_contacts" = "My contacts";
"lng_edit_privacy_close_friends" = "Close friends";
"lng_edit_privacy_nobody" = "Nobody";
+"lng_edit_privacy_premium" = "Premium users";
"lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
@@ -1086,6 +1115,10 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above.";
"lng_edit_privacy_lastseen_always_title" = "Always share with";
"lng_edit_privacy_lastseen_never_title" = "Never share with";
+"lng_edit_lastseen_hide_read_time" = "Hide read time";
+"lng_edit_lastseen_hide_read_time_about" = "Hide the time when you read messages from people who can't see your last seen. If you turn this on, their read time will also be hidden from you. This setting does not affect group chats.";
+"lng_edit_lastseen_subscribe" = "Subscribe to Telegram Premium";
+"lng_edit_lastseen_subscribe_about" = "If you subscribe to Premium, you will see other users' last seen and read time even if you hid yours from them (unless they specifically restricted it).";
"lng_edit_privacy_groups_title" = "Group invite settings";
"lng_edit_privacy_groups_header" = "Who can invite you to groups and channels";
@@ -1157,6 +1190,16 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_edit_privacy_voices_always_title" = "Always allow";
"lng_edit_privacy_voices_never_title" = "Never allow";
+"lng_messages_privacy_title" = "Messages";
+"lng_messages_privacy_subtitle" = "Who can send me messages?";
+"lng_messages_privacy_everyone" = "Everybody";
+"lng_messages_privacy_restricted" = "My Contacts and Premium Users";
+"lng_messages_privacy_about" = "You can restrict incoming messages to only contacts and Premium users.";
+"lng_messages_privacy_premium_button" = "Subscribe to Telegram Premium";
+"lng_messages_privacy_premium_about" = "Subscribe now to change this setting and get access to other exclusive features of Telegram Premium.";
+"lng_messages_privacy_premium" = "Only subscribers of {link} can select this option.";
+"lng_messages_privacy_premium_link" = "Telegram Premium";
+
"lng_self_destruct_title" = "Account self-destruction";
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
"lng_self_destruct_sessions_title" = "Session termination";
@@ -1464,6 +1507,11 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_report_messages_none" = "Select Messages";
"lng_report_messages_count#one" = "Report {count} Message";
"lng_report_messages_count#other" = "Report {count} Messages";
+"lng_report_reaction" = "Report reaction";
+"lng_report_and_ban" = "Ban and report";
+"lng_report_reaction_title" = "Report reaction";
+"lng_report_reaction_about" = "Are you sure you want to report reactions from this user?";
+"lng_report_and_ban_button" = "Ban user";
"lng_report_details_about" = "Please enter any additional details relevant to your report.";
"lng_report_details" = "Additional Details";
"lng_report_reason_spam" = "Spam";
@@ -1690,11 +1738,14 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_action_story_mention_button" = "View Story";
"lng_action_story_mention_me_unavailable" = "The story where you mentioned {user} is no longer available.";
"lng_action_story_mention_unavailable" = "The story where {user} mentioned you is no longer available.";
+"lng_action_giveaway_started_group" = "{from} just started a giveaway of Telegram Premium subscriptions to its members.";
"lng_action_giveaway_started" = "{from} just started a giveaway of Telegram Premium subscriptions to its followers.";
"lng_action_giveaway_results#one" = "{count} winner of the giveaway was randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results#other" = "{count} winners of the giveaway were randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results_some" = "Some winners of the giveaway were randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results_none" = "No winners of the giveaway could be selected.";
+"lng_action_boost_apply#one" = "{from} boosted the group";
+"lng_action_boost_apply#other" = "{from} boosted the group {count} times";
"lng_similar_channels_title" = "Similar channels";
"lng_similar_channels_view_all" = "View all";
@@ -1719,6 +1770,11 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_ttl_voice_expired" = "Voice message expired";
"lng_ttl_round_sent" = "You sent a self-destructing video message.";
"lng_ttl_round_expired" = "Round message expired";
+"lng_ttl_voice_tooltip_in" = "This voice message can only be played once.";
+"lng_ttl_voice_tooltip_out" = "This message will disappear once **{user}** plays it once.";
+"lng_ttl_voice_close_in" = "Delete and close";
+"lng_ttl_round_tooltip_in" = "This video message can only be played once.";
+"lng_ttl_round_tooltip_out" = "This message will disappear once **{user}** plays it once.";
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
"lng_profile_camera_title" = "Capture yourself";
@@ -1932,6 +1988,8 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_group_stickers" = "Group stickers";
"lng_group_stickers_description" = "You can choose a sticker set which will be available for every member while in the group chat.";
"lng_group_stickers_add" = "Choose sticker set";
+"lng_group_emoji" = "Group emoji pack";
+"lng_group_emoji_description" = "Choose an emoji pack that will be available to all members within the group.";
"lng_premium" = "Premium";
"lng_premium_free" = "Free";
@@ -1980,6 +2038,12 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_premium_summary_about_emoji_status" = "Add any of thousands emoji next to your name to display current activity.";
"lng_premium_summary_subtitle_infinite_reactions" = "Infinite Reactions";
"lng_premium_summary_about_infinite_reactions" = "React with thousands of emoji — with multiple reactions per message.";
+"lng_premium_summary_subtitle_tags_for_messages" = "Tags for Messages";
+"lng_premium_summary_about_tags_for_messages" = "Organize your Saved Messages with tags for quicker access.";
+"lng_premium_summary_subtitle_last_seen" = "Last Seen Times";
+"lng_premium_summary_about_last_seen" = "View the last seen and read times of others even if you hide yours.";
+"lng_premium_summary_subtitle_message_privacy" = "Message Privacy";
+"lng_premium_summary_about_message_privacy" = "Restrict people you don't know from sending you messages.";
"lng_premium_summary_subtitle_premium_stickers" = "Premium Stickers";
"lng_premium_summary_about_premium_stickers" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";
"lng_premium_summary_subtitle_animated_emoji" = "Animated Emoji";
@@ -2087,30 +2151,52 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_premium_gifts_about_paid_below#one" = "They now have access to additional features.";
"lng_premium_gifts_about_paid_below#other" = "They now have access to additional features.";
"lng_premium_gifts_summary_subtitle" = "What's Included";
+"lng_premium_gifts_terms" = "By gifting Telegram Premium, you agree to the Telegram {link} and {policy}.";
+"lng_premium_gifts_terms_policy" = "Privacy Policy";
"lng_boost_channel_button" = "Boost Channel";
+"lng_boost_group_button" = "Boost Group";
"lng_boost_again_button" = "Boost Again";
+"lng_boost_group_about" = "Boost your group to unlock additional\nappearance settings.";
"lng_boost_level#one" = "Level {count}";
"lng_boost_level#other" = "Level {count}";
+"lng_boost_level_unlocks#one" = "Level {count} Unlocks:";
+"lng_boost_level_unlocks#other" = "Level {count} Unlocks:";
"lng_boost_channel_title_first" = "Enable stories for channel";
-"lng_boost_channel_needs_first#one" = "{channel} needs **{count}** more boost to enable posting stories. Help make it possible!";
-"lng_boost_channel_needs_first#other" = "{channel} needs **{count}** more boosts to enable posting stories. Help make it possible!";
+"lng_boost_channel_title_first_group" = "Enable stories for group";
+"lng_boost_channel_needs_unlock#one" = "{channel} needs **{count}** more boost to unlock new features.";
+"lng_boost_channel_needs_unlock#other" = "{channel} needs **{count}** more boosts to unlock new features.";
+//"lng_boost_channel_needs_first#one" = "{channel} needs **{count}** more boost to enable posting stories. Help make it possible!";
+//"lng_boost_channel_needs_first#other" = "{channel} needs **{count}** more boosts to enable posting stories. Help make it possible!";
"lng_boost_channel_title_more" = "Help upgrade channel";
-"lng_boost_channel_needs_more#one" = "{channel} needs **{count}** more boost to be able to {post}.";
-"lng_boost_channel_needs_more#other" = "{channel} needs **{count}** more boosts to be able to {post}.";
+"lng_boost_channel_title_more_group" = "Help upgrade group";
+//"lng_boost_channel_needs_more#one" = "{channel} needs **{count}** more boost to be able to {post}.";
+//"lng_boost_channel_needs_more#other" = "{channel} needs **{count}** more boosts to be able to {post}.";
"lng_boost_channel_title_max" = "Maximum level reached";
"lng_boost_channel_you_title" = "You boosted {channel}!";
-"lng_boost_channel_you_first#one" = "This channel needs **{count}** more boost\nto enable stories.";
-"lng_boost_channel_you_first#other" = "This channel needs **{count}** more boosts\nto enable stories.";
-"lng_boost_channel_you_more#one" = "This channel needs **{count}** more boost\nto be able to {post}.";
-"lng_boost_channel_you_more#other" = "This channel needs **{count}** more boosts\nto be able to {post}.";
+//"lng_boost_channel_you_first#one" = "This channel needs **{count}** more boost\nto enable stories.";
+//"lng_boost_channel_you_first#other" = "This channel needs **{count}** more boosts\nto enable stories.";
+//"lng_boost_channel_you_more#one" = "This channel needs **{count}** more boost\nto be able to {post}.";
+//"lng_boost_channel_you_more#other" = "This channel needs **{count}** more boosts\nto be able to {post}.";
"lng_boost_channel_reached_first" = "This channel reached **Level 1** and can now post stories.";
"lng_boost_channel_reached_more#one" = "This channel reached **Level {count}** and can now {post}.";
"lng_boost_channel_reached_more#other" = "This channel reached **Level {count}** and can now {post}.";
+//"lng_boost_channel_you_first_group#one" = "This group needs **{count}** more boost\nto enable stories.";
+//"lng_boost_channel_you_first_group#other" = "This group needs **{count}** more boosts\nto enable stories.";
+//"lng_boost_channel_you_more_group#one" = "This group needs **{count}** more boost\nto be able to {post}.";
+//"lng_boost_channel_you_more_group#other" = "This group needs **{count}** more boosts\nto be able to {post}.";
+"lng_boost_channel_reached_first_group" = "This group reached **Level 1** and can now post stories.";
+"lng_boost_channel_reached_more_group#one" = "This group reached **Level {count}** and can now {post}.";
+"lng_boost_channel_reached_more_group#other" = "This group reached **Level {count}** and can now {post}.";
"lng_boost_channel_post_stories#one" = "post **{count} story** per day";
"lng_boost_channel_post_stories#other" = "post **{count} stories** per day";
+"lng_boost_channel_features" = "Your boosts will help {channel} to unlock new features.";
+"lng_boost_group_lift_restrictions" = "Boost the group to remove messaging restrictions.";
+"lng_boost_group_lift_restrictions_many#one" = "Boost the group **{count} times** to remove messaging restrictions.";
+"lng_boost_group_lift_restrictions_many#other" = "Boost the group **{count} times** to remove messaging restrictions.";
"lng_boost_error_gifted_title" = "Can't boost with gifted Premium!";
"lng_boost_error_gifted_text" = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost channels.";
+"lng_boost_error_gifted_text_group" = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost groups.";
"lng_boost_need_more" = "More boosts needed";
"lng_boost_need_more_text#one" = "To boost {channel}, gift **Telegram Premium** to a friend and get **{count}** boosts.";
"lng_boost_need_more_text#other" = "To boost {channel}, gift **Telegram Premium** to a friend and get **{count}** boosts.";
@@ -2118,10 +2204,13 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_boost_need_more_again#other" = "To boost {channel} again, gift **Telegram Premium** to a friend and get **{count}** additional boosts.";
"lng_boost_error_already_title" = "Already Boosted!";
"lng_boost_error_already_text" = "You are already boosting this channel.";
+"lng_boost_error_already_text_group" = "You are already boosting this group.";
"lng_boost_error_premium_title" = "Premium needed!";
+"lng_boost_error_premium_text_group" = "Only **Telegram Premium** subscribers can boost groups. Do you want to subscribe to **Telegram Premium**?";
"lng_boost_error_premium_text" = "Only **Telegram Premium** subscribers can boost channels. Do you want to subscribe to **Telegram Premium**?";
"lng_boost_error_premium_yes" = "Yes";
"lng_boost_error_flood_title" = "Can't boost too often!";
+"lng_boost_error_flood_text_group" = "You can change the group you boost only once a day. Next time you can boost is in {left}.";
"lng_boost_error_flood_text" = "You can change the channel you boost only once a day. Next time you can boost is in {left}.";
"lng_boost_now_instead" = "You currently boost {channel}. Do you want to boost {other} instead?";
"lng_boost_now_replace" = "Replace";
@@ -2138,6 +2227,10 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_boost_reassign_done#other" = "{count} boosts are reassigned from {channels}.";
"lng_boost_reassign_channels#one" = "{count} channel";
"lng_boost_reassign_channels#other" = "{count} channels";
+"lng_boost_reassign_groups#one" = "{count} group";
+"lng_boost_reassign_groups#other" = "{count} groups";
+"lng_boost_reassign_mixed#one" = "{count} group or channel";
+"lng_boost_reassign_mixed#other" = "{count} groups and channels";
"lng_boost_channel_title_color" = "Enable colors";
"lng_boost_channel_needs_level_color#one" = "Your channel needs to reach **Level {count}** to change channel color.";
@@ -2146,23 +2239,51 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_boost_channel_title_wallpaper" = "Enable wallpapers";
"lng_boost_channel_needs_level_wallpaper#one" = "Your channel needs to reach **Level {count}** to change channel wallpaper.";
"lng_boost_channel_needs_level_wallpaper#other" = "Your channel needs to reach **Level {count}** to change channel wallpaper.";
+"lng_boost_group_needs_level_wallpaper#one" = "Your group needs to reach **Level {count}** to change group wallpaper.";
+"lng_boost_group_needs_level_wallpaper#other" = "Your group needs to reach **Level {count}** to change group wallpaper.";
"lng_boost_channel_title_status" = "Enable emoji status";
"lng_boost_channel_needs_level_status#one" = "Your channel needs to reach **Level {count}** to set emoji status.";
"lng_boost_channel_needs_level_status#other" = "Your channel needs to reach **Level {count}** to set emoji status.";
+"lng_boost_group_needs_level_status#one" = "Your group needs to reach **Level {count}** to set emoji status.";
+"lng_boost_group_needs_level_status#other" = "Your group needs to reach **Level {count}** to set emoji status.";
"lng_boost_channel_title_reactions" = "Custom reactions";
"lng_boost_channel_needs_level_reactions#one" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as a reaction.";
"lng_boost_channel_needs_level_reactions#other" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as reactions.";
+"lng_boost_group_title_emoji" = "Enable emoji pack";
+"lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack.";
+"lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack.";
+
"lng_boost_channel_ask" = "Ask your **Premium** subscribers to boost your channel with this link:";
"lng_boost_channel_ask_button" = "Copy Link";
"lng_boost_channel_or" = "or";
"lng_boost_channel_gifting" = "Boost your channel by gifting your subscribers Telegram Premium. {link}";
"lng_boost_channel_gifting_link" = "Get boosts >";
+"lng_feature_stories#one" = "**{count}** Story Per Day";
+"lng_feature_stories#other" = "**{count}** Stories Per Day";
+"lng_feature_reactions#one" = "**{count}** Custom Reaction";
+"lng_feature_reactions#other" = "**{count}** Custom Reactions";
+"lng_feature_name_color_channel#one" = "**{count}** Channel Name Color";
+"lng_feature_name_color_channel#other" = "**{count}** Channel Name Colors";
+"lng_feature_link_style_channel#one" = "**{count}** Style for Links and Quotes";
+"lng_feature_link_style_channel#other" = "**{count}** Styles for Links and Quotes";
+"lng_feature_link_emoji" = "Custom Logo for Links and Quotes";
+"lng_feature_emoji_status" = "**1000+** Emoji Statuses";
+"lng_feature_backgrounds_channel#one" = "**{count}** Channel Background";
+"lng_feature_backgrounds_channel#other" = "**{count}** Channel Backgrounds";
+"lng_feature_custom_background_channel" = "Custom Channel Background";
+"lng_feature_backgrounds_group#one" = "**{count}** Group Background";
+"lng_feature_backgrounds_group#other" = "**{count}** Group Backgrounds";
+"lng_feature_custom_background_group" = "Custom Group Background";
+"lng_feature_custom_emoji_pack" = "Custom Emoji Pack";
+"lng_feature_transcribe" = "Voice-to-Text Conversion";
+
"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_group" = "Get more boosts for your group by gifting Premium to your subscribers.";
"lng_giveaway_create_option" = "Create Giveaway";
"lng_giveaway_create_subtitle" = "winners are chosen randomly";
"lng_giveaway_award_option" = "Award Specific Users";
@@ -2176,30 +2297,35 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_giveaway_channels_title" = "Channels included in the giveaway";
"lng_giveaway_channels_this#one" = "this channel will receive {count} boost";
"lng_giveaway_channels_this#other" = "this channel will receive {count} boosts";
+"lng_giveaway_channels_this_group#one" = "this group will receive {count} boost";
+"lng_giveaway_channels_this_group#other" = "this group will receive {count} boosts";
"lng_giveaway_channels_add" = "Add Channel";
"lng_giveaway_channels_about" = "Choose the channels the users need to join to take part in the giveaway.";
"lng_giveaway_users_title" = "Users eligible for the giveaway";
"lng_giveaway_users_all" = "All subscribers";
+"lng_giveaway_users_all_group" = "All members";
"lng_giveaway_users_from_all_countries" = "from all countries";
"lng_giveaway_users_from_one_country" = "from {country}";
"lng_giveaway_users_from_countries#one" = "from {count} country";
"lng_giveaway_users_from_countries#other" = "from {count} countries";
"lng_giveaway_users_new" = "Only new subscribers";
+"lng_giveaway_users_new_group" = "Only new members";
"lng_giveaway_users_about" = "Choose if you want to limit the giveaway only to those who joined the channel after the giveaway started or to users from specific countries.";
+"lng_giveaway_users_about_group" = "Choose if you want to limit the giveaway only to those who joined the group after the giveaway started or to members from specific countries.";
"lng_giveaway_start" = "Start Giveaway";
"lng_giveaway_award" = "Gift Premium";
"lng_giveaway_start_sure" = "Are you sure you want to start this prepaid giveaway now? This action cannot be undone.";
"lng_giveaway_date_title" = "Date when giveaway ends";
"lng_giveaway_date" = "Date and Time";
-"lng_giveaway_date_about#one" = "Choose when {count} subscriber of your channel will be randomly selected to receive Telegram Premium.";
+"lng_giveaway_date_about#one" = "Choose when {count} subscriber of your channel will be randomly selected to receive Telegram Premium.";
"lng_giveaway_date_about#other" = "Choose when {count} subscribers of your channel will be randomly selected to receive Telegram Premium.";
+"lng_giveaway_date_about_group#one" = "Choose when {count} members of your group will be randomly selected to receive Telegram Premium.";
+"lng_giveaway_date_about_group#other" = "Choose when {count} members of your group will be randomly selected to receive Telegram Premium.";
"lng_giveaway_duration_title#one" = "Duration of Premium subscription";
"lng_giveaway_duration_title#other" = "Duration of Premium subscriptions";
"lng_giveaway_duration_price" = "{price} x {amount}";
"lng_giveaway_date_select" = "Select Date and Time";
"lng_giveaway_date_confirm" = "Confirm";
-"lng_giveaway_channels_select#one" = "Select up to {count} channel";
-"lng_giveaway_channels_select#other" = "Select up to {count} channels";
"lng_giveaway_recipients_save" = "Save Recipients";
"lng_giveaway_recipients_deselect" = "Deselect All";
"lng_giveaway_maximum_countries_error#one" = "You can select maximum {count} country.";
@@ -2222,8 +2348,10 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_giveaway_created_title" = "Giveaway created";
"lng_giveaway_created_body" = "Check your channels' {link} to see how this giveaway boosted your channel.";
+"lng_giveaway_created_body_group" = "Check your groups' {link} to see how this giveaway boosted your group.";
"lng_giveaway_awarded_title" = "Premium subscriptions gifted";
"lng_giveaway_awarded_body" = "Check your channels' {link} to see how gifts boosted your channel.";
+"lng_giveaway_awarded_body_group" = "Check your groups' {link} to see how gifts boosted your group.";
"lng_giveaway_created_link" = "Statistics";
"lng_prize_title" = "Congratulations!";
@@ -2246,8 +2374,16 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_prizes_participants" = "Participants";
"lng_prizes_participants_all#one" = "All subscribers of the channel:";
"lng_prizes_participants_all#other" = "All subscribers of the channels:";
+"lng_prizes_participants_all_group#one" = "All members of the group:";
+"lng_prizes_participants_all_group#other" = "All members of the groups:";
+"lng_prizes_participants_all_mixed#one" = "All members of the group:";
+"lng_prizes_participants_all_mixed#other" = "All members of the groups and channels:";
"lng_prizes_participants_new#one" = "All users who joined the channel below after this date:";
"lng_prizes_participants_new#other" = "All users who joined the channels below after this date:";
+"lng_prizes_participants_new_group#one" = "All users who joined the group below after this date:";
+"lng_prizes_participants_new_group#other" = "All users who joined the groups below after this date:";
+"lng_prizes_participants_new_mixed#one" = "All users who joined the group below after this date:";
+"lng_prizes_participants_new_mixed#other" = "All users who joined the groups and channels below after this date:";
"lng_prizes_countries" = "from {countries}";
"lng_prizes_countries_and_one" = "{countries}, {country}";
"lng_prizes_countries_and_last" = "{countries} and {country}";
@@ -2258,32 +2394,43 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_prizes_how_text" = "This giveaway is sponsored by {admins}.";
"lng_prizes_end_text" = "This giveaway was sponsored by {admins}.";
"lng_prizes_admins#one" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscription {duration} for its followers";
-"lng_prizes_admins#other" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscriptions {duration} for its followers.";
+"lng_prizes_admins#other" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscriptions {duration} for its followers";
+"lng_prizes_admins_group#one" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscription {duration} for its members";
+"lng_prizes_admins_group#other" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscriptions {duration} for its members";
"lng_prizes_additional_added#one" = "{channel} also included **{count} {prize}** in the prize. Admins of the channel are responsible for delivering this prize.";
"lng_prizes_additional_added#other" = "{channel} also included **{count} {prize}** in the prizes. Admins of the channel are responsible for delivering these prizes.";
+"lng_prizes_additional_added_group#one" = "{channel} also included **{count} {prize}** in the prize. Admins of the group are responsible for delivering this prize.";
+"lng_prizes_additional_added_group#other" = "{channel} also included **{count} {prize}** in the prizes. Admins of the group are responsible for delivering these prizes.";
"lng_prizes_how_when_finish" = "On {date}, Telegram will automatically select {winners}.";
"lng_prizes_end_when_finish" = "On {date}, Telegram automatically selected {winners}.";
"lng_prizes_end_activated#one" = "**{count}** of the winners already used their gift link.";
"lng_prizes_end_activated#other" = "**{count}** of the winners already used their gift links.";
-"lng_prizes_winners_all_of_one#one" = "{count} random subscribers of {channel}";
+"lng_prizes_winners_all_of_one#one" = "{count} random subscriber of {channel}";
"lng_prizes_winners_all_of_one#other" = "{count} random subscribers of {channel}";
-"lng_prizes_winners_all_of_many#one" = "{count} random subscribers of {channel} and other listed channels";
-"lng_prizes_winners_all_of_many#other" = "{count} random subscribers of {channel} and other listed channels";
+"lng_prizes_winners_all_of_one_group#one" = "{count} random member of {channel}";
+"lng_prizes_winners_all_of_one_group#other" = "{count} random members of {channel}";
+"lng_prizes_winners_all_of_many#one" = "{count} random subscriber of {channel} and other listed groups and channels";
+"lng_prizes_winners_all_of_many#other" = "{count} random subscribers of {channel} and other listed groups and channels";
+"lng_prizes_winners_all_of_many_group#one" = "{count} random member of {channel} and other listed groups and channels";
+"lng_prizes_winners_all_of_many_group#other" = "{count} random members of {channel} and other listed groups and channels";
"lng_prizes_winners_new_of_one#one" = "{count} random user that joined {channel} after {start_date}";
"lng_prizes_winners_new_of_one#other" = "{count} random users that joined {channel} after {start_date}";
-"lng_prizes_winners_new_of_many#one" = "{count} random user that joined {channel} and other listed channels after {start_date}";
-"lng_prizes_winners_new_of_many#other" = "{count} random users that joined {channel} and other listed channels after {start_date}";
-"lng_prizes_how_participate_one" = "To take part in this giveaway please join channel {channel} before {date}.";
-"lng_prizes_how_participate_many" = "To take part in this giveaway please join channel {channel} and other listed channels before {date}.";
+"lng_prizes_winners_new_of_many#one" = "{count} random user that joined {channel} and other listed groups and channels after {start_date}";
+"lng_prizes_winners_new_of_many#other" = "{count} random users that joined {channel} and other listed groups and channels after {start_date}";
+"lng_prizes_how_participate_one" = "To take part in this giveaway please join {channel} before {date}.";
+"lng_prizes_how_participate_many" = "To take part in this giveaway please join {channel} and other listed groups and channels before {date}.";
"lng_prizes_how_no_admin" = "You are not eligible to participate in this giveaway, because you are an admin of participating channel ({channel}).";
+"lng_prizes_how_no_admin_group" = "You are not eligible to participate in this giveaway, because you are an admin of participating group ({channel}).";
"lng_prizes_how_no_joined" = "You are not eligible to participate in this giveaway, because you joined this channel on {date}, which is before the contest started.";
+"lng_prizes_how_no_joined_group" = "You are not eligible to participate in this giveaway, because you joined this group on {date}, which is before the contest started.";
"lng_prizes_how_no_country" = "You are not eligible to participate in this giveaway, because your country is not included in the terms of the giveaway.";
-"lng_prizes_how_yes_joined_one" = "You are participating in this giveaway, because you have joined channel {channel}.";
-"lng_prizes_how_yes_joined_many" = "You are participating in this giveaway, because you have joined channel {channel} (and other listed channels).";
+"lng_prizes_how_yes_joined_one" = "You are participating in this giveaway, because you have joined {channel}.";
+"lng_prizes_how_yes_joined_many" = "You are participating in this giveaway, because you have joined {channel} (and other listed groups and channels).";
"lng_prizes_you_won" = "You won a prize in this giveaway {cup}";
"lng_prizes_view_prize" = "View my prize";
"lng_prizes_you_didnt" = "You didn't win a prize in this giveaway.";
"lng_prizes_cancelled" = "The channel cancelled the prizes by reversing the payment for them.";
+"lng_prizes_cancelled_group" = "The channel cancelled the prizes by reversing the payment for them.";
"lng_prizes_badge" = "x{amount}";
"lng_prizes_results_title" = "Winners Selected!";
@@ -2386,6 +2533,10 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_stickers_remove_pack_confirm" = "Remove";
"lng_stickers_archive_pack" = "Archive Stickers";
"lng_stickers_has_been_archived" = "Sticker pack has been archived.";
+"lng_emoji_group_set" = "Group emoji set";
+"lng_emoji_remove_group_set" = "Remove group emoji set?";
+"lng_emoji_group_from_your" = "Choose from your emoji";
+"lng_emoji_group_from_featured" = "Choose from trending emoji";
"lng_masks_archive_pack" = "Archive Masks";
"lng_masks_has_been_archived" = "Mask pack has been archived.";
"lng_masks_installed" = "Mask pack has been installed.";
@@ -2398,6 +2549,8 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_in_dlg_contact" = "Contact";
"lng_in_dlg_audio" = "Voice message";
"lng_in_dlg_video_message" = "Video message";
+"lng_in_dlg_voice_message_ttl" = "One-time Voice Message";
+"lng_in_dlg_video_message_ttl" = "One-time Video Message";
"lng_in_dlg_file" = "File";
"lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
@@ -2463,6 +2616,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_broadcast_silent_ph" = "Silent broadcast...";
"lng_send_anonymous_ph" = "Send anonymously...";
"lng_story_reply_ph" = "Reply privately...";
+"lng_story_comment_ph" = "Comment story...";
"lng_send_text_no" = "Text not allowed.";
"lng_send_text_no_about" = "The admins of this group only allow sending {types}.";
"lng_send_text_type_and_last" = "{types} and {last}";
@@ -2485,6 +2639,8 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_record_listen_cancel_sure" = "Are you sure you want to discard your recorded voice message?";
"lng_record_lock_discard" = "Discard";
"lng_record_hold_tip" = "Please hold the mouse button pressed to record a voice message.";
+"lng_record_once_first_tooltip" = "Click to set this message to **Play Once**.";
+"lng_record_once_active_tooltip" = "The recipients will be able to listen to it only once.";
"lng_will_be_notified" = "Members will be notified when you post";
"lng_wont_be_notified" = "Members will not be notified when you post";
"lng_willbe_history" = "Please select a chat to start messaging";
@@ -2530,6 +2686,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_replies_view_original" = "View in chat";
"lng_replies_messages" = "Replies";
"lng_hidden_author_messages" = "Author Hidden";
+"lng_my_notes" = "My Notes";
"lng_replies_discussion_started" = "Discussion started";
"lng_replies_no_comments" = "No comments here yet...";
@@ -2692,6 +2849,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_context_copy_email" = "Copy Email Address";
"lng_context_copy_hashtag" = "Copy Hashtag";
"lng_context_copy_mention" = "Copy Username";
+"lng_context_copy_filename" = "Copy Filename";
"lng_context_save_image" = "Save Image As...";
"lng_context_copy_image" = "Copy Image";
"lng_context_cancel_download" = "Cancel Download";
@@ -2708,6 +2866,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_context_save_gif" = "Save GIF";
"lng_context_delete_gif" = "Delete GIF";
"lng_context_open_channel" = "Open Channel";
+"lng_context_open_group" = "Open Group";
"lng_context_attached_stickers" = "Attached Stickers";
"lng_context_to_msg" = "Go To Message";
"lng_context_reply_msg" = "Reply";
@@ -2746,17 +2905,34 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_context_seen_reacted#other" = "{count} Reacted";
"lng_context_seen_reacted_none" = "Nobody Reacted";
"lng_context_seen_reacted_all" = "Show All Reactions";
-"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_tag_add_name" = "Add Name";
+"lng_context_tag_edit_name" = "Edit Name";
+"lng_context_remove_tag" = "Remove Tag";
"lng_context_delete_from_disk" = "Delete from disk";
"lng_context_delete_all_files" = "Delete all files";
"lng_context_save_custom_sound" = "Save for notifications";
"lng_context_translate" = "Translate";
"lng_context_translate_selected" = "Translate Selected Text";
+"lng_context_read_hidden" = "read";
+"lng_context_read_show" = "show when";
+
+"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_link" = "Learn More...";
+"lng_edit_tag_about" = "You can label your emoji tag with a text name.";
+"lng_edit_tag_name" = "Name";
+"lng_add_tag_button" = "Add tags";
+"lng_add_tag_phrase" = "to messages {arrow}";
+"lng_add_tag_phrase_long" = "to your Saved Messages {arrow}";
+"lng_unlock_tags" = "Unlock";
"lng_context_animated_emoji" = "This message contains emoji from **{name} pack**.";
"lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**.";
"lng_context_animated_emoji_many#other" = "This message contains emoji from **{count} packs**.";
"lng_context_animated_reaction" = "This reaction is from **{name} pack**.";
+"lng_context_animated_tag" = "This tag is from **{name} pack**.";
"lng_context_animated_reactions" = "Reactions contain emoji from **{name} pack**.";
"lng_context_animated_reactions_many#one" = "Reactions contain emoji from **{count} pack**.";
"lng_context_animated_reactions_many#other" = "Reactions contain emoji from **{count} packs**.";
@@ -2873,8 +3049,12 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_edit_channel_level_min" = "Level 1+";
"lng_edit_channel_wallpaper" = "Channel wallpaper";
"lng_edit_channel_wallpaper_about" = "Set a wallpaper that will be visible for everyone reading your channel.";
+"lng_edit_channel_wallpaper_group" = "Group wallpaper";
+"lng_edit_channel_wallpaper_about_group" = "Set a wallpaper that will be visible for everyone participating in your group.";
"lng_edit_channel_status" = "Channel emoji status";
"lng_edit_channel_status_about" = "Choose a status that will be shown next to the channel's name.";
+"lng_edit_channel_status_group" = "Group emoji status";
+"lng_edit_channel_status_about_group" = "Choose a status that will be shown next to the group's name.";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";
"lng_add_contact" = "Create";
@@ -3394,7 +3574,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_group_call_rtmp_key_copy" = "Copy Stream Key";
"lng_group_call_rtmp_key_copied" = "Stream Key copied to clipboard.";
"lng_group_call_rtmp_key_warning" = "**Never share your Stream Key with anyone or show it on stream!**";
-"lng_group_call_rtmp_info" = "To stream video with another app, enter these Server URL and Stream Key in your streaming app.\n\nOnce you start broadcasting in your streaming app, tap Start Streaming below.";
+"lng_group_call_rtmp_info" = "To stream video with another app, enter these Server URL and Stream Key in your streaming app.\n\nOnce you start broadcasting in your streaming app, click Start Streaming below.";
"lng_group_call_rtmp_start" = "Start Streaming";
"lng_group_call_rtmp_revoke" = "Revoke Stream Key";
"lng_group_call_rtmp_revoke_sure" = "Are you sure you want to revoke your Server URL and Stream Key?";
@@ -3438,6 +3618,9 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_rights_slowmode_interval_seconds#other" = "every {count} seconds";
"lng_rights_slowmode_interval_minutes#one" = "every {count} minute";
"lng_rights_slowmode_interval_minutes#other" = "every {count} minutes";
+"lng_rights_boosts_no_restrict" = "Do not restrict boosters";
+"lng_rights_boosts_about" = "Turn this on to always allow users who boosted your group to send messages and media.";
+"lng_rights_boosts_about_on" = "Choose how many boosts a user must give to the group to bypass restrictions on sending messages.";
"lng_slowmode_enabled"= "Slow mode is enabled. You can send your next message in {left}.";
"lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time.";
@@ -3533,6 +3716,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_restricted_send_gifs" = "The admins of this group restricted you from posting GIFs here.";
"lng_restricted_send_inline" = "The admins of this group restricted you from posting inline content here.";
"lng_restricted_send_polls" = "The admins of this group restricted you from posting polls here.";
+"lng_restricted_boost_group" = "Boost this group to send messages";
"lng_restricted_send_message_until" = "The admins of this group restricted you from writing here until {date}, {time}.";
"lng_restricted_send_photos_until" = "The admins of this group restricted you from posting photos here until {date}, {time}.";
@@ -3562,6 +3746,15 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_restricted_send_voice_messages" = "{user} restricted sending of voice messages to them.";
"lng_restricted_send_video_messages" = "{user} restricted sending of video messages to them.";
+"lng_restricted_send_non_premium" = "Only Premium users can message {user}.";
+"lng_restricted_send_non_premium_more" = "Learn more...";
+
+"lng_send_non_premium_text" = "Subscribe to **Premium**\n to message {user}.";
+"lng_send_non_premium_go" = "Get Premium";
+"lng_send_non_premium_story" = "Replies restricted";
+"lng_send_non_premium_unlock" = "unlock";
+"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_exceptions_list_title" = "Exceptions";
"lng_removed_list_title" = "Removed users";
@@ -3661,6 +3854,9 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_admin_log_changed_stickers_group" = "{from} changed the group's {sticker_set}";
"lng_admin_log_changed_stickers_set" = "sticker set";
"lng_admin_log_removed_stickers_group" = "{from} removed the group's sticker set";
+"lng_admin_log_changed_emoji_group" = "{from} changed the group's {sticker_set}";
+"lng_admin_log_changed_emoji_set" = "emoji set";
+"lng_admin_log_removed_emoji_group" = "{from} removed the group's emoji set";
"lng_admin_log_changed_linked_chat" = "{from} changed the discussion group to «{chat}»";
"lng_admin_log_removed_linked_chat" = "{from} removed the discussion group";
"lng_admin_log_changed_linked_channel" = "{from} changed the linked channel to «{chat}»";
@@ -4229,7 +4425,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
-"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
+"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you clicked on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together.";
"lng_sponsored_info_menu" = "About this ad";
"lng_sponsored_info_submenu" = "Advertiser: {text}";
@@ -4343,6 +4539,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_stories_views#one" = "{count} view";
"lng_stories_views#other" = "{count} views";
"lng_stories_no_views" = "No views";
+"lng_stories_view_reactions" = "View reactions";
"lng_stories_unsupported" = "This story is not supported\nby your version of Telegram.";
"lng_stories_cant_reply" = "You can't reply to this story.";
"lng_stories_about_silent" = "This video has no sound.";
@@ -4397,6 +4594,7 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_stories_channel_archive_done_many#one" = "{count} story is hidden from the channel page.";
"lng_stories_channel_archive_done_many#other" = "{count} stories are hidden from the channel page.";
"lng_stories_save_promo" = "Subscribe to {link} to download other people's unprotected stories to disk.";
+"lng_stories_reaction_as_message" = "Send reaction as a private message";
"lng_stealth_mode_menu_item" = "Stealth Mode";
"lng_stealth_mode_title" = "Stealth Mode";
@@ -4494,10 +4692,12 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_boosts_level" = "Level";
"lng_boosts_existing" = "Existing boosts";
"lng_boosts_premium_audience" = "Premium subscribers";
+"lng_boosts_premium_members" = "Premium members";
"lng_boosts_next_level" = "Boosts to level up";
"lng_boosts_list_title#one" = "{count} Boost";
"lng_boosts_list_title#other" = "{count} Boosts";
"lng_boosts_list_subtext" = "Your channel is currently boosted by these users.";
+"lng_boosts_list_subtext_group" = "Your group is currently boosted by these users.";
"lng_boosts_show_more_boosts#one" = "Show {count} More Boosts";
"lng_boosts_show_more_boosts#other" = "Show {count} More Boosts";
"lng_boosts_show_more_gifts#one" = "Show {count} More Boosts";
@@ -4505,8 +4705,10 @@ https://github.com/rabbitgramdesktop/rabbitgramdesktop/blob/dev/LEGAL
"lng_boosts_list_status" = "boost expires on {date}";
"lng_boosts_link_title" = "Link for boosting";
"lng_boosts_link_subtext" = "Share this link with your subscribers to get more boosts.";
+"lng_boosts_link_subtext_group" = "Share this link with the members of your group to get more boosts.";
"lng_boosts_get_boosts" = "Get Boosts via Gifts";
"lng_boosts_get_boosts_subtext" = "Get more boosts for your channel by gifting Telegram Premium to your subscribers.";
+"lng_boosts_get_boosts_subtext_group" = "Get more boosts for your group by gifting Telegram Premium to the members.";
"lng_boosts_list_unclaimed" = "Unclaimed";
"lng_boosts_list_pending" = "To be distributed";
"lng_boosts_list_pending_about" = "The recipient will be selected when the giveaway ends.";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index 76ea1ef4a3c293..a129237ca0b3d7 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -13,5 +13,6 @@
../../animations/stats.tgs
../../animations/voice_ttl_idle.tgs
../../animations/voice_ttl_start.tgs
+ ../../animations/palette.tgs
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index 11f8e97f834eb3..bcb97cf5041a6c 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -26,6 +26,7 @@
../../art/recording/recording_info_audio.svg
../../art/recording/recording_info_video_landscape.svg
../../art/recording/recording_info_video_portrait.svg
+ ../../art/ttl/video_message_icon.svg
../../icons/settings/dino.svg
../../icons/settings/star.svg
../../icons/settings/starmini.svg
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 785823556e2f9b..de2808649c7cce 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
+ Version="4.15.0.0" />
rabbitGram Desktop
xmdnx
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index cc98f6267c1cc6..d55215d4fe260a 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,14,0,0
- PRODUCTVERSION 4,14,0,0
+ FILEVERSION 4,15,0,0
+ PRODUCTVERSION 4,15,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "xmdnx"
VALUE "FileDescription", "rabbitGram Desktop"
- VALUE "FileVersion", "4.14.0.0"
- VALUE "LegalCopyright", "Copyright (C) 2023"
+ VALUE "FileVersion", "4.15.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2023-2024"
VALUE "ProductName", "rabbitGram Desktop"
- VALUE "ProductVersion", "4.14.0.0"
+ VALUE "ProductVersion", "4.15.0.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 5bf143af0141a9..03862a45adeda1 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,14,0,0
- PRODUCTVERSION 4,14,0,0
+ FILEVERSION 4,15,0,0
+ PRODUCTVERSION 4,15,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "xmdnx"
VALUE "FileDescription", "rabbitGram Desktop Updater"
- VALUE "FileVersion", "4.14.0.0"
- VALUE "LegalCopyright", "Copyright (C) 2023"
+ VALUE "FileVersion", "4.15.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2023-2024"
VALUE "ProductName", "rabbitGram Desktop"
- VALUE "ProductVersion", "4.14.0.0"
+ VALUE "ProductVersion", "4.15.0.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/api/api_common.h b/Telegram/SourceFiles/api/api_common.h
index 763fbdd9881f83..9d949647e367f7 100644
--- a/Telegram/SourceFiles/api/api_common.h
+++ b/Telegram/SourceFiles/api/api_common.h
@@ -25,6 +25,7 @@ struct SendOptions {
bool silent = false;
bool handleSupportSwitch = false;
bool hideViaBot = false;
+ crl::time ttlSeconds = 0;
};
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
diff --git a/Telegram/SourceFiles/api/api_global_privacy.cpp b/Telegram/SourceFiles/api/api_global_privacy.cpp
index 55dbf5af2aa71a..dc1c92ef9e9d03 100644
--- a/Telegram/SourceFiles/api/api_global_privacy.cpp
+++ b/Telegram/SourceFiles/api/api_global_privacy.cpp
@@ -84,18 +84,61 @@ void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
u"AUTOARCHIVE_POPULAR"_q);
}
+void GlobalPrivacy::updateHideReadTime(bool hide) {
+ update(
+ archiveAndMuteCurrent(),
+ unarchiveOnNewMessageCurrent(),
+ hide,
+ newRequirePremiumCurrent());
+}
+
+bool GlobalPrivacy::hideReadTimeCurrent() const {
+ return _hideReadTime.current();
+}
+
+rpl::producer GlobalPrivacy::hideReadTime() const {
+ return _hideReadTime.value();
+}
+
+void GlobalPrivacy::updateNewRequirePremium(bool value) {
+ update(
+ archiveAndMuteCurrent(),
+ unarchiveOnNewMessageCurrent(),
+ hideReadTimeCurrent(),
+ value);
+}
+
+bool GlobalPrivacy::newRequirePremiumCurrent() const {
+ return _newRequirePremium.current();
+}
+
+rpl::producer GlobalPrivacy::newRequirePremium() const {
+ return _newRequirePremium.value();
+}
+
+
void GlobalPrivacy::updateArchiveAndMute(bool value) {
- update(value, unarchiveOnNewMessageCurrent());
+ update(
+ value,
+ unarchiveOnNewMessageCurrent(),
+ hideReadTimeCurrent(),
+ newRequirePremiumCurrent());
}
void GlobalPrivacy::updateUnarchiveOnNewMessage(
UnarchiveOnNewMessage value) {
- update(archiveAndMuteCurrent(), value);
+ update(
+ archiveAndMuteCurrent(),
+ value,
+ hideReadTimeCurrent(),
+ newRequirePremiumCurrent());
}
void GlobalPrivacy::update(
bool archiveAndMute,
- UnarchiveOnNewMessage unarchiveOnNewMessage) {
+ UnarchiveOnNewMessage unarchiveOnNewMessage,
+ bool hideReadTime,
+ bool newRequirePremium) {
using Flag = MTPDglobalPrivacySettings::Flag;
_api.request(_requestId).cancel();
@@ -108,17 +151,26 @@ void GlobalPrivacy::update(
: Flag())
| (unarchiveOnNewMessage != UnarchiveOnNewMessage::AnyUnmuted
? Flag::f_keep_archived_folders
+ : Flag())
+ | (hideReadTime ? Flag::f_hide_read_marks : Flag())
+ | ((newRequirePremium && _session->premium())
+ ? Flag::f_new_noncontact_peers_require_premium
: Flag());
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(
MTP_globalPrivacySettings(MTP_flags(flags))
)).done([=](const MTPGlobalPrivacySettings &result) {
_requestId = 0;
apply(result);
- }).fail([=] {
+ }).fail([=](const MTP::Error &error) {
_requestId = 0;
+ if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
+ update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {});
+ }
}).send();
_archiveAndMute = archiveAndMute;
_unarchiveOnNewMessage = unarchiveOnNewMessage;
+ _hideReadTime = hideReadTime;
+ _newRequirePremium = newRequirePremium;
}
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
@@ -129,6 +181,8 @@ void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
: data.is_keep_archived_folders()
? UnarchiveOnNewMessage::NotInFoldersUnmuted
: UnarchiveOnNewMessage::AnyUnmuted;
+ _hideReadTime = data.is_hide_read_marks();
+ _newRequirePremium = data.is_new_noncontact_peers_require_premium();
});
}
diff --git a/Telegram/SourceFiles/api/api_global_privacy.h b/Telegram/SourceFiles/api/api_global_privacy.h
index e5a072a69a872c..94d9060b158faa 100644
--- a/Telegram/SourceFiles/api/api_global_privacy.h
+++ b/Telegram/SourceFiles/api/api_global_privacy.h
@@ -41,12 +41,22 @@ class GlobalPrivacy final {
[[nodiscard]] rpl::producer<> suggestArchiveAndMute() const;
void dismissArchiveAndMuteSuggestion();
+ void updateHideReadTime(bool hide);
+ [[nodiscard]] bool hideReadTimeCurrent() const;
+ [[nodiscard]] rpl::producer hideReadTime() const;
+
+ void updateNewRequirePremium(bool value);
+ [[nodiscard]] bool newRequirePremiumCurrent() const;
+ [[nodiscard]] rpl::producer newRequirePremium() const;
+
private:
void apply(const MTPGlobalPrivacySettings &data);
void update(
bool archiveAndMute,
- UnarchiveOnNewMessage unarchiveOnNewMessage);
+ UnarchiveOnNewMessage unarchiveOnNewMessage,
+ bool hideReadTime,
+ bool newRequirePremium);
const not_null _session;
MTP::Sender _api;
@@ -55,6 +65,8 @@ class GlobalPrivacy final {
rpl::variable _unarchiveOnNewMessage
= UnarchiveOnNewMessage::None;
rpl::variable _showArchiveAndMute = false;
+ rpl::variable _hideReadTime = false;
+ rpl::variable _newRequirePremium = false;
std::vector> _callbacks;
};
diff --git a/Telegram/SourceFiles/api/api_media.cpp b/Telegram/SourceFiles/api/api_media.cpp
index a1745ca9bcfaab..a39178f58f5b8d 100644
--- a/Telegram/SourceFiles/api/api_media.cpp
+++ b/Telegram/SourceFiles/api/api_media.cpp
@@ -79,16 +79,19 @@ MTPInputMedia PrepareUploadedPhoto(
not_null item,
RemoteFileInfo info) {
using Flag = MTPDinputMediaUploadedPhoto::Flag;
- const auto spoiler = item->media()
- && item->media()->hasSpoiler();
+ const auto spoiler = item->media() && item->media()->hasSpoiler();
+ const auto ttlSeconds = item->media()
+ ? item->media()->ttlSeconds()
+ : 0;
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
- | (info.attachedStickers.empty() ? Flag() : Flag::f_stickers);
+ | (info.attachedStickers.empty() ? Flag() : Flag::f_stickers)
+ | (ttlSeconds ? Flag::f_ttl_seconds : Flag());
return MTP_inputMediaUploadedPhoto(
MTP_flags(flags),
info.file,
MTP_vector(
ranges::to>(info.attachedStickers)),
- MTP_int(0));
+ MTP_int(ttlSeconds));
}
MTPInputMedia PrepareUploadedDocument(
@@ -98,12 +101,15 @@ MTPInputMedia PrepareUploadedDocument(
return MTP_inputMediaEmpty();
}
using Flag = MTPDinputMediaUploadedDocument::Flag;
- const auto spoiler = item->media()
- && item->media()->hasSpoiler();
+ const auto spoiler = item->media() && item->media()->hasSpoiler();
+ const auto ttlSeconds = item->media()
+ ? item->media()->ttlSeconds()
+ : 0;
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
| (info.thumb ? Flag::f_thumb : Flag())
| (item->groupId() ? Flag::f_nosound_video : Flag())
- | (info.attachedStickers.empty() ? Flag::f_stickers : Flag());
+ | (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
+ | (ttlSeconds ? Flag::f_ttl_seconds : Flag());
const auto document = item->media()->document();
return MTP_inputMediaUploadedDocument(
MTP_flags(flags),
@@ -113,7 +119,7 @@ MTPInputMedia PrepareUploadedDocument(
ComposeSendingDocumentAttributes(document),
MTP_vector(
ranges::to>(info.attachedStickers)),
- MTP_int(0));
+ MTP_int(ttlSeconds));
}
bool HasAttachedStickers(MTPInputMedia media) {
diff --git a/Telegram/SourceFiles/api/api_messages_search.cpp b/Telegram/SourceFiles/api/api_messages_search.cpp
index 6acfc17cb1c86e..4f5065c597e2f0 100644
--- a/Telegram/SourceFiles/api/api_messages_search.cpp
+++ b/Telegram/SourceFiles/api/api_messages_search.cpp
@@ -10,6 +10,7 @@ For license and copyright information please follow this link:
#include "apiwrap.h"
#include "data/data_channel.h"
#include "data/data_histories.h"
+#include "data/data_message_reaction_id.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "history/history.h"
@@ -43,6 +44,23 @@ constexpr auto kSearchPerPage = 50;
return result;
}
+[[nodiscard]] QString RequestToToken(
+ const MessagesSearch::Request &request) {
+ auto result = request.query;
+ if (request.from) {
+ result += '\n' + QString::number(request.from->id.value);
+ }
+ for (const auto &tag : request.tags) {
+ result += '\n';
+ if (const auto customId = tag.custom()) {
+ result += u"custom"_q + QString::number(customId);
+ } else {
+ result += u"emoji"_q + tag.emoji();
+ }
+ }
+ return result;
+}
+
} // namespace
MessagesSearch::MessagesSearch(not_null history)
@@ -54,9 +72,8 @@ MessagesSearch::~MessagesSearch() {
base::take(_searchInHistoryRequest));
}
-void MessagesSearch::searchMessages(const QString &query, PeerData *from) {
- _query = query;
- _from = from;
+void MessagesSearch::searchMessages(Request request) {
+ _request = std::move(request);
_offsetId = {};
searchRequest();
}
@@ -69,8 +86,7 @@ void MessagesSearch::searchMore() {
}
void MessagesSearch::searchRequest() {
- const auto nextToken = _query
- + QString::number(_from ? _from->id.value : 0);
+ const auto nextToken = RequestToToken(_request);
if (!_offsetId) {
const auto it = _cacheOfStartByToken.find(nextToken);
if (it != end(_cacheOfStartByToken)) {
@@ -80,17 +96,21 @@ void MessagesSearch::searchRequest() {
}
}
auto callback = [=](Fn finish) {
- const auto flags = _from
- ? MTP_flags(MTPmessages_Search::Flag::f_from_id)
- : MTP_flags(0);
+ using Flag = MTPmessages_Search::Flag;
+ const auto from = _request.from;
+ const auto fromPeer = _history->peer->isUser() ? nullptr : from;
+ const auto savedPeer = _history->peer->isSelf() ? from : nullptr;
_requestId = _history->session().api().request(MTPmessages_Search(
- flags,
+ MTP_flags((fromPeer ? Flag::f_from_id : Flag())
+ | (savedPeer ? Flag::f_saved_peer_id : Flag())
+ | (_request.tags.empty() ? Flag() : Flag::f_saved_reaction)),
_history->peer->input,
- MTP_string(_query),
- (_from
- ? _from->input
- : MTP_inputPeerEmpty()),
- MTPInputPeer(), // saved_peer_id
+ MTP_string(_request.query),
+ (fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
+ (savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
+ MTP_vector_from_range(_request.tags | ranges::views::transform(
+ Data::ReactionToMTP
+ )),
MTPint(), // top_msg_id
MTP_inputMessagesFilterEmpty(),
MTP_int(0), // min_date
diff --git a/Telegram/SourceFiles/api/api_messages_search.h b/Telegram/SourceFiles/api/api_messages_search.h
index 948ca5075e91bf..4566744af05da6 100644
--- a/Telegram/SourceFiles/api/api_messages_search.h
+++ b/Telegram/SourceFiles/api/api_messages_search.h
@@ -7,10 +7,17 @@ For license and copyright information please follow this link:
*/
#pragma once
+#include "base/qt/qt_compare.h"
+#include "data/data_message_reaction_id.h"
+
class HistoryItem;
class History;
class PeerData;
+namespace Data {
+struct ReactionId;
+} // namespace Data
+
namespace Api {
struct FoundMessages {
@@ -21,10 +28,23 @@ struct FoundMessages {
class MessagesSearch final {
public:
+ struct Request {
+ QString query;
+ PeerData *from = nullptr;
+ std::vector tags;
+
+ friend inline bool operator==(
+ const Request &,
+ const Request &) = default;
+ friend inline auto operator<=>(
+ const Request &,
+ const Request &) = default;
+ };
+
explicit MessagesSearch(not_null history);
~MessagesSearch();
- void searchMessages(const QString &query, PeerData *from);
+ void searchMessages(Request request);
void searchMore();
[[nodiscard]] rpl::producer messagesFounds() const;
@@ -41,8 +61,7 @@ class MessagesSearch final {
base::flat_map _cacheOfStartByToken;
- QString _query;
- PeerData *_from = nullptr;
+ Request _request;
MsgId _offsetId;
int _searchInHistoryRequest = 0; // Not real mtpRequestId.
diff --git a/Telegram/SourceFiles/api/api_messages_search_merged.cpp b/Telegram/SourceFiles/api/api_messages_search_merged.cpp
index 85cb23c75b05b1..7b6803a3df7351 100644
--- a/Telegram/SourceFiles/api/api_messages_search_merged.cpp
+++ b/Telegram/SourceFiles/api/api_messages_search_merged.cpp
@@ -11,12 +11,6 @@ For license and copyright information please follow this link:
namespace Api {
-bool MessagesSearchMerged::RequestCompare::operator()(
- const Request &a,
- const Request &b) const {
- return (a.query < b.query) && (a.from < b.from);
-}
-
MessagesSearchMerged::MessagesSearchMerged(not_null history)
: _apiSearch(history) {
if (const auto migrated = history->migrateFrom()) {
@@ -88,9 +82,9 @@ void MessagesSearchMerged::clear() {
void MessagesSearchMerged::search(const Request &search) {
if (_migratedSearch) {
_waitingForTotal = true;
- _migratedSearch->searchMessages(search.query, search.from);
+ _migratedSearch->searchMessages(search);
}
- _apiSearch.searchMessages(search.query, search.from);
+ _apiSearch.searchMessages(search);
}
void MessagesSearchMerged::searchMore() {
diff --git a/Telegram/SourceFiles/api/api_messages_search_merged.h b/Telegram/SourceFiles/api/api_messages_search_merged.h
index eef1273a4944d1..0ffabad6e28dcd 100644
--- a/Telegram/SourceFiles/api/api_messages_search_merged.h
+++ b/Telegram/SourceFiles/api/api_messages_search_merged.h
@@ -12,19 +12,17 @@ For license and copyright information please follow this link:
class History;
class PeerData;
+namespace Data {
+struct ReactionId;
+} // namespace Data
+
namespace Api {
// Search in both of history and migrated history, if it exists.
class MessagesSearchMerged final {
public:
- struct Request {
- QString query;
- PeerData *from = nullptr;
- };
- struct RequestCompare {
- bool operator()(const Request &a, const Request &b) const;
- };
- using CachedRequests = std::set;
+ using Request = MessagesSearch::Request;
+ using CachedRequests = base::flat_set;
MessagesSearchMerged(not_null history);
diff --git a/Telegram/SourceFiles/api/api_peer_colors.cpp b/Telegram/SourceFiles/api/api_peer_colors.cpp
index 50c2382fdcc648..5f3fe12d202bef 100644
--- a/Telegram/SourceFiles/api/api_peer_colors.cpp
+++ b/Telegram/SourceFiles/api/api_peer_colors.cpp
@@ -55,19 +55,42 @@ rpl::producer> PeerColors::suggestedValue() const {
auto PeerColors::indicesValue() const
-> rpl::producer {
- return rpl::single(_colorIndicesCurrent
- ? *_colorIndicesCurrent
- : Ui::ColorIndicesCompressed()
+ return rpl::single(
+ indicesCurrent()
) | rpl::then(_colorIndicesChanged.events() | rpl::map([=] {
- return *_colorIndicesCurrent;
+ return indicesCurrent();
}));
}
-int PeerColors::requiredLevelFor(PeerId channel, uint8 index) const {
+Ui::ColorIndicesCompressed PeerColors::indicesCurrent() const {
+ return _colorIndicesCurrent
+ ? *_colorIndicesCurrent
+ : Ui::ColorIndicesCompressed();
+}
+
+const base::flat_map &PeerColors::requiredLevelsGroup() const {
+ return _requiredLevelsGroup;
+}
+
+const base::flat_map &PeerColors::requiredLevelsChannel() const {
+ return _requiredLevelsChannel;
+}
+
+int PeerColors::requiredGroupLevelFor(PeerId channel, uint8 index) const {
if (Data::DecideColorIndex(channel) == index) {
return 0;
- } else if (const auto i = _requiredLevels.find(index)
- ; i != end(_requiredLevels)) {
+ } else if (const auto i = _requiredLevelsGroup.find(index)
+ ; i != end(_requiredLevelsGroup)) {
+ return i->second;
+ }
+ return 1;
+}
+
+int PeerColors::requiredChannelLevelFor(PeerId channel, uint8 index) const {
+ if (Data::DecideColorIndex(channel) == index) {
+ return 0;
+ } else if (const auto i = _requiredLevelsChannel.find(index)
+ ; i != end(_requiredLevelsChannel)) {
return i->second;
}
return 1;
@@ -100,7 +123,8 @@ void PeerColors::apply(const MTPDhelp_peerColors &data) {
};
const auto &list = data.vcolors().v;
- _requiredLevels.clear();
+ _requiredLevelsGroup.clear();
+ _requiredLevelsChannel.clear();
suggested.reserve(list.size());
for (const auto &color : list) {
const auto &data = color.data();
@@ -110,8 +134,11 @@ void PeerColors::apply(const MTPDhelp_peerColors &data) {
continue;
}
const auto colorIndex = uint8(colorIndexBare);
+ if (const auto min = data.vgroup_min_level()) {
+ _requiredLevelsGroup[colorIndex] = min->v;
+ }
if (const auto min = data.vchannel_min_level()) {
- _requiredLevels[colorIndex] = min->v;
+ _requiredLevelsChannel[colorIndex] = min->v;
}
if (!data.is_hidden()) {
suggested.push_back(colorIndex);
diff --git a/Telegram/SourceFiles/api/api_peer_colors.h b/Telegram/SourceFiles/api/api_peer_colors.h
index 0ad1a63c7ea8e0..f8d379020bfa1d 100644
--- a/Telegram/SourceFiles/api/api_peer_colors.h
+++ b/Telegram/SourceFiles/api/api_peer_colors.h
@@ -25,10 +25,19 @@ class PeerColors final {
[[nodiscard]] std::vector suggested() const;
[[nodiscard]] rpl::producer> suggestedValue() const;
+ [[nodiscard]] Ui::ColorIndicesCompressed indicesCurrent() const;
[[nodiscard]] auto indicesValue() const
-> rpl::producer;
- [[nodiscard]] int requiredLevelFor(
+ [[nodiscard]] auto requiredLevelsGroup() const
+ -> const base::flat_map &;
+ [[nodiscard]] auto requiredLevelsChannel() const
+ -> const base::flat_map &;
+
+ [[nodiscard]] int requiredGroupLevelFor(
+ PeerId channel,
+ uint8 index) const;
+ [[nodiscard]] int requiredChannelLevelFor(
PeerId channel,
uint8 index) const;
@@ -42,7 +51,8 @@ class PeerColors final {
mtpRequestId _requestId = 0;
base::Timer _timer;
rpl::variable> _suggested;
- base::flat_map _requiredLevels;
+ base::flat_map _requiredLevelsGroup;
+ base::flat_map _requiredLevelsChannel;
rpl::event_stream<> _colorIndicesChanged;
std::unique_ptr _colorIndicesCurrent;
diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp
index 55cfe511200bc1..678cf0ea12cc82 100644
--- a/Telegram/SourceFiles/api/api_premium.cpp
+++ b/Telegram/SourceFiles/api/api_premium.cpp
@@ -15,6 +15,10 @@ For license and copyright information please follow this link:
#include "data/data_peer.h"
#include "data/data_peer_values.h"
#include "data/data_session.h"
+#include "data/data_user.h"
+#include "history/view/history_view_element.h"
+#include "history/history.h"
+#include "history/history_item.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
@@ -334,6 +338,72 @@ const Data::SubscriptionOptions &Premium::subscriptionOptions() const {
return _subscriptionOptions;
}
+rpl::producer<> Premium::somePremiumRequiredResolved() const {
+ return _somePremiumRequiredResolved.events();
+}
+
+void Premium::resolvePremiumRequired(not_null user) {
+ _resolvePremiumRequiredUsers.emplace(user);
+ if (!_premiumRequiredRequestScheduled
+ && _resolvePremiumRequestedUsers.empty()) {
+ _premiumRequiredRequestScheduled = true;
+ crl::on_main(_session, [=] {
+ requestPremiumRequiredSlice();
+ });
+ }
+}
+
+void Premium::requestPremiumRequiredSlice() {
+ _premiumRequiredRequestScheduled = false;
+ if (!_resolvePremiumRequestedUsers.empty()
+ || _resolvePremiumRequiredUsers.empty()) {
+ return;
+ }
+ constexpr auto kPerRequest = 100;
+ auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers
+ | ranges::views::transform(&UserData::inputUser));
+ if (users.v.size() > kPerRequest) {
+ auto shortened = users.v;
+ shortened.resize(kPerRequest);
+ users = MTP_vector(std::move(shortened));
+ const auto from = begin(_resolvePremiumRequiredUsers);
+ _resolvePremiumRequestedUsers = { from, from + kPerRequest };
+ _resolvePremiumRequiredUsers.erase(from, from + kPerRequest);
+ } else {
+ _resolvePremiumRequestedUsers
+ = base::take(_resolvePremiumRequiredUsers);
+ }
+ const auto finish = [=](const QVector &list) {
+ constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
+ constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
+ constexpr auto mask = me | known;
+
+ auto index = 0;
+ for (const auto &user : base::take(_resolvePremiumRequestedUsers)) {
+ const auto require = (index < list.size())
+ && mtpIsTrue(list[index++]);
+ user->setFlags((user->flags() & ~mask)
+ | known
+ | (require ? me : UserDataFlag()));
+ }
+ if (!_premiumRequiredRequestScheduled
+ && !_resolvePremiumRequiredUsers.empty()) {
+ _premiumRequiredRequestScheduled = true;
+ crl::on_main(_session, [=] {
+ requestPremiumRequiredSlice();
+ });
+ }
+ _somePremiumRequiredResolved.fire({});
+ };
+ _session->api().request(
+ MTPusers_GetIsPremiumRequiredToContact(std::move(users))
+ ).done([=](const MTPVector &result) {
+ finish(result.v);
+ }).fail([=] {
+ finish({});
+ }).send();
+}
+
PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null peer)
: _peer(peer)
, _api(&peer->session().api().instance()) {
@@ -494,4 +564,49 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
false);
}
+RequirePremiumState ResolveRequiresPremiumToWrite(
+ not_null peer,
+ History *maybeHistory) {
+ const auto user = peer->asUser();
+ if (!user
+ || !user->someRequirePremiumToWrite()
+ || user->session().premium()) {
+ return RequirePremiumState::No;
+ } else if (user->requirePremiumToWriteKnown()) {
+ return user->meRequiresPremiumToWrite()
+ ? RequirePremiumState::Yes
+ : RequirePremiumState::No;
+ } else if (user->flags() & UserDataFlag::MutualContact) {
+ return RequirePremiumState::No;
+ } else if (!maybeHistory) {
+ return RequirePremiumState::Unknown;
+ }
+
+ const auto update = [&](bool require) {
+ using Flag = UserDataFlag;
+ constexpr auto known = Flag::RequirePremiumToWriteKnown;
+ constexpr auto me = Flag::MeRequiresPremiumToWrite;
+ user->setFlags((user->flags() & ~me)
+ | known
+ | (require ? me : Flag()));
+ };
+ // We allow this potentially-heavy loop because in case we've opened
+ // the chat and have a lot of messages `requires_premium` will be known.
+ for (const auto &block : maybeHistory->blocks) {
+ for (const auto &view : block->messages) {
+ const auto item = view->data();
+ if (!item->out() && !item->isService()) {
+ update(false);
+ return RequirePremiumState::No;
+ }
+ }
+ }
+ if (user->isContact() // Here we know, that we're not in his contacts.
+ && maybeHistory->loadedAtTop() // And no incoming messages.
+ && maybeHistory->loadedAtBottom()) {
+ update(true);
+ }
+ return RequirePremiumState::Unknown;
+}
+
} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h
index 462124ef211dce..92122aa7c3eacc 100644
--- a/Telegram/SourceFiles/api/api_premium.h
+++ b/Telegram/SourceFiles/api/api_premium.h
@@ -10,6 +10,7 @@ For license and copyright information please follow this link:
#include "data/data_subscription_option.h"
#include "mtproto/sender.h"
+class History;
class ApiWrap;
namespace Main {
@@ -103,10 +104,14 @@ class Premium final {
[[nodiscard]] auto subscriptionOptions() const
-> const Data::SubscriptionOptions &;
+ [[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const;
+ void resolvePremiumRequired(not_null user);
+
private:
void reloadPromo();
void reloadStickers();
void reloadCloudSet();
+ void requestPremiumRequiredSlice();
const not_null _session;
MTP::Sender _api;
@@ -143,6 +148,11 @@ class Premium final {
Data::SubscriptionOptions _subscriptionOptions;
+ rpl::event_stream<> _somePremiumRequiredResolved;
+ base::flat_set> _resolvePremiumRequiredUsers;
+ base::flat_set> _resolvePremiumRequestedUsers;
+ bool _premiumRequiredRequestScheduled = false;
+
};
class PremiumGiftCodeOptions final {
@@ -196,4 +206,13 @@ class PremiumGiftCodeOptions final {
};
+enum class RequirePremiumState {
+ Unknown,
+ Yes,
+ No,
+};
+[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite(
+ not_null peer,
+ History *maybeHistory);
+
} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_send_progress.cpp b/Telegram/SourceFiles/api/api_send_progress.cpp
index 124f64bf512eb6..46604e5b2425aa 100644
--- a/Telegram/SourceFiles/api/api_send_progress.cpp
+++ b/Telegram/SourceFiles/api/api_send_progress.cpp
@@ -161,14 +161,13 @@ bool SendProgressManager::skipRequest(const Key &key) const {
return true;
}
const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor;
- const auto online = user->onlineTill;
- if (online == -2) { // last seen recently
+ const auto lastseen = user->lastseen();
+ if (lastseen.isRecently()) {
return false;
- } else if (online < 0) {
- return (-online < recently);
- } else {
- return (online < recently);
+ } else if (const auto value = lastseen.onlineTill()) {
+ return (value < recently);
}
+ return true;
}
void SendProgressManager::done(mtpRequestId requestId) {
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 37d60b338347dc..aa2fd58807b09a 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -42,6 +42,9 @@ void InnerFillMessagePostFlags(
not_null peer,
MessageFlags &flags) {
const auto anonymousPost = peer->amAnonymous();
+ if (ShouldSendSilent(peer, options)) {
+ flags |= MessageFlag::Silent;
+ }
if (!anonymousPost || options.sendAs) {
flags |= MessageFlag::HasFromId;
return;
@@ -399,11 +402,7 @@ void SendConfirmedFile(
flags |= MessageFlag::HasReplyInfo;
}
const auto anonymousPost = peer->amAnonymous();
- const auto silentPost = ShouldSendSilent(peer, file->to.options);
FillMessagePostFlags(action, peer, flags);
- if (silentPost) {
- flags |= MessageFlag::Silent;
- }
if (file->to.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
@@ -443,11 +442,30 @@ void SendConfirmedFile(
MTPDocument(), // alt_document
MTPint());
} else if (file->type == SendMediaType::Audio) {
+ const auto ttlSeconds = file->to.options.ttlSeconds;
+ const auto isVoice = [&] {
+ return file->document.match([](const MTPDdocumentEmpty &d) {
+ return false;
+ }, [](const MTPDdocument &d) {
+ return ranges::any_of(d.vattributes().v, [&](
+ const MTPDocumentAttribute &attribute) {
+ using Att = MTPDdocumentAttributeAudio;
+ return attribute.match([](const Att &data) -> bool {
+ return data.vflags().v & Att::Flag::f_voice;
+ }, [](const auto &) {
+ return false;
+ });
+ });
+ });
+ }();
+ using Flag = MTPDmessageMediaDocument::Flag;
return MTP_messageMediaDocument(
- MTP_flags(MTPDmessageMediaDocument::Flag::f_document),
+ MTP_flags(Flag::f_document
+ | (isVoice ? Flag::f_voice : Flag())
+ | (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
file->document,
MTPDocument(), // alt_document
- MTPint());
+ MTP_int(ttlSeconds));
} else {
Unexpected("Type in sendFilesConfirmed.");
}
diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp
index 99f89999675a15..7a2630bdd72de9 100644
--- a/Telegram/SourceFiles/api/api_statistics.cpp
+++ b/Telegram/SourceFiles/api/api_statistics.cpp
@@ -604,7 +604,7 @@ rpl::producer Boosts::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto channel = _peer->asChannel();
- if (!channel || channel->isMegagroup()) {
+ if (!channel) {
return lifetime;
}
@@ -628,6 +628,7 @@ rpl::producer Boosts::request() {
const auto slots = data.vmy_boost_slots();
_boostStatus.overview = Data::BoostsOverview{
+ .group = channel->isMegagroup(),
.mine = slots ? int(slots->v.size()) : 0,
.level = std::max(data.vlevel().v, 0),
.boostCount = std::max(
diff --git a/Telegram/SourceFiles/api/api_transcribes.cpp b/Telegram/SourceFiles/api/api_transcribes.cpp
index 56e35a86f250b0..ad7bb44fdcc352 100644
--- a/Telegram/SourceFiles/api/api_transcribes.cpp
+++ b/Telegram/SourceFiles/api/api_transcribes.cpp
@@ -8,6 +8,7 @@ For license and copyright information please follow this link:
#include "api/api_transcribes.h"
#include "apiwrap.h"
+#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_session.h"
@@ -25,6 +26,14 @@ Transcribes::Transcribes(not_null api)
, _api(&api->instance()) {
}
+bool Transcribes::freeFor(not_null item) const {
+ if (const auto channel = item->history()->peer->asMegagroup()) {
+ const auto owner = &channel->owner();
+ return channel->levelHint() >= owner->groupFreeTranscribeLevel();
+ }
+ return false;
+}
+
bool Transcribes::trialsSupport() {
if (!_trialsSupport) {
const auto count = _session->account().appConfig().get(
diff --git a/Telegram/SourceFiles/api/api_transcribes.h b/Telegram/SourceFiles/api/api_transcribes.h
index 53f8340b902c03..0b2d1f880856af 100644
--- a/Telegram/SourceFiles/api/api_transcribes.h
+++ b/Telegram/SourceFiles/api/api_transcribes.h
@@ -36,6 +36,8 @@ class Transcribes final {
void apply(const MTPDupdateTranscribedAudio &update);
+ [[nodiscard]] bool freeFor(not_null item) const;
+
[[nodiscard]] bool trialsSupport();
[[nodiscard]] TimeId trialsRefreshAt();
[[nodiscard]] int trialsCount();
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 2e705b8f4ebc54..e197330c9c2410 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -944,7 +944,9 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
}
const auto self = session().user();
- self->onlineTill = base::unixtime::now() + (isOnline ? (config.onlineUpdatePeriod / 1000) : -1);
+ const auto onlineFor = (config.onlineUpdatePeriod / 1000);
+ self->updateLastseen(Data::LastseenStatus::OnlineTill(
+ base::unixtime::now() + (isOnline ? onlineFor : -1)));
session().changes().peerUpdated(
self,
Data::PeerUpdate::Flag::OnlineStatus);
@@ -1112,6 +1114,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
(d.is_out()
? peerToMTP(_session->userPeerId())
: MTP_peerUser(d.vuser_id())),
+ MTPint(), // from_boosts_applied
MTP_peerUser(d.vuser_id()),
MTPPeer(), // saved_peer_id
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
@@ -1144,6 +1147,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTP_flags(flags),
d.vid(),
MTP_peerUser(d.vfrom_id()),
+ MTPint(), // from_boosts_applied
MTP_peerChat(d.vchat_id()),
MTPPeer(), // saved_peer_id
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
@@ -1211,8 +1215,8 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
if (user && !requestingDifference()) {
user->madeAction(base::unixtime::now());
}
- ClearMediaAsExpired(item);
}
+ ClearMediaAsExpired(item);
}
} else {
// Perhaps it was an unread mention!
@@ -1850,23 +1854,13 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserStatus: {
auto &d = update.c_updateUserStatus();
- if (auto user = session().data().userLoaded(d.vuser_id())) {
- switch (d.vstatus().type()) {
- case mtpc_userStatusEmpty: user->onlineTill = 0; break;
- case mtpc_userStatusRecently:
- if (user->onlineTill > -10) { // don't modify pseudo-online
- user->onlineTill = -2;
- }
- break;
- case mtpc_userStatusLastWeek: user->onlineTill = -3; break;
- case mtpc_userStatusLastMonth: user->onlineTill = -4; break;
- case mtpc_userStatusOffline: user->onlineTill = d.vstatus().c_userStatusOffline().vwas_online().v; break;
- case mtpc_userStatusOnline: user->onlineTill = d.vstatus().c_userStatusOnline().vexpires().v; break;
+ if (const auto user = session().data().userLoaded(d.vuser_id())) {
+ const auto now = LastseenFromMTP(d.vstatus(), user->lastseen());
+ if (user->updateLastseen(now)) {
+ session().changes().peerUpdated(
+ user,
+ Data::PeerUpdate::Flag::OnlineStatus);
}
- session().changes().peerUpdated(
- user,
- Data::PeerUpdate::Flag::OnlineStatus);
- session().data().maybeStopWatchForOffline(user);
}
if (UserId(d.vuser_id()) == session().userId()) {
if (d.vstatus().type() == mtpc_userStatusOffline
@@ -2508,6 +2502,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
session().data().reactions().refreshRecentDelayed();
} break;
+ case mtpc_updateSavedReactionTags: {
+ session().data().reactions().refreshMyTagsDelayed();
+ } break;
+
////// Cloud saved GIFs
case mtpc_updateSavedGifs: {
session().data().stickers().setLastSavedGifsUpdate(0);
diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp
index de8b859cf17f0e..9e2db51f265393 100644
--- a/Telegram/SourceFiles/api/api_who_reacted.cpp
+++ b/Telegram/SourceFiles/api/api_who_reacted.cpp
@@ -7,6 +7,7 @@ For license and copyright information please follow this link:
*/
#include "api/api_who_reacted.h"
+#include "api/api_global_privacy.h"
#include "history/history_item.h"
#include "history/history.h"
#include "data/stickers/data_custom_emoji.h"
@@ -19,6 +20,7 @@ For license and copyright information please follow this link:
#include "data/data_session.h"
#include "data/data_media_types.h"
#include "data/data_message_reaction_id.h"
+#include "data/data_peer_values.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
@@ -36,10 +38,11 @@ namespace {
constexpr auto kContextReactionsLimit = 50;
using Data::ReactionId;
+using WhoReadState = Ui::WhoReadState;
struct Peers {
std::vector list;
- bool unknown = false;
+ WhoReadState state = WhoReadState::Empty;
friend inline bool operator==(
const Peers &a,
@@ -59,7 +62,7 @@ struct PeersWithReactions {
std::vector list;
std::vector read;
int fullReactionsCount = 0;
- bool unknown = false;
+ WhoReadState state = WhoReadState::Empty;
friend inline bool operator==(
const PeersWithReactions &a,
@@ -68,7 +71,7 @@ struct PeersWithReactions {
struct CachedRead {
CachedRead()
- : data(Peers{ .unknown = true }) {
+ : data(Peers{ .state = WhoReadState::Unknown }) {
}
rpl::variable data;
mtpRequestId requestId = 0;
@@ -76,7 +79,7 @@ struct CachedRead {
struct CachedReacted {
CachedReacted()
- : data(PeersWithReactions{ .unknown = true }) {
+ : data(PeersWithReactions{ .state = WhoReadState::Unknown }) {
}
rpl::variable data;
mtpRequestId requestId = 0;
@@ -186,6 +189,27 @@ struct State {
context->cachedReacted.erase(j);
}
}, context->subscriptions[session]);
+ Data::AmPremiumValue(
+ session
+ ) | rpl::skip(1) | rpl::filter(
+ rpl::mappers::_1
+ ) | rpl::start_with_next([=] {
+ for (auto &[item, cache] : context->cachedRead) {
+ if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
+ cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
+ }
+ }
+ }, context->subscriptions[session]);
+ session->api().globalPrivacy().hideReadTime(
+ ) | rpl::skip(1) | rpl::filter(
+ !rpl::mappers::_1
+ ) | rpl::start_with_next([=] {
+ for (auto &[item, cache] : context->cachedRead) {
+ if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
+ cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
+ }
+ }
+ }, context->subscriptions[session]);
return context;
}
@@ -222,7 +246,38 @@ struct State {
}
const auto context = PreparedContextAt(weak.data(), session);
auto &entry = context->cacheRead(item);
- if (!entry.requestId) {
+ if (entry.requestId) {
+ } else if (const auto user = item->history()->peer->asUser()) {
+ entry.requestId = session->api().request(
+ MTPmessages_GetOutboxReadDate(
+ user->input,
+ MTP_int(item->id)
+ )
+ ).done([=](const MTPOutboxReadDate &result) {
+ const auto &data = result.data();
+ auto &entry = context->cacheRead(item);
+ entry.requestId = 0;
+ auto parsed = Peers();
+ parsed.list.push_back({
+ .peer = user->id,
+ .date = data.vdate().v,
+ });
+ entry.data = std::move(parsed);
+ }).fail([=](const MTP::Error &error) {
+ auto &entry = context->cacheRead(item);
+ entry.requestId = 0;
+ if (entry.data.current().state == WhoReadState::Unknown) {
+ const auto &text = error.type();
+ entry.data = (text == u"YOUR_PRIVACY_RESTRICTED"_q)
+ ? Peers{ .state = WhoReadState::MyHidden }
+ : (text == u"USER_PRIVACY_RESTRICTED"_q)
+ ? Peers{ .state = WhoReadState::HisHidden }
+ : (text == u"MESSAGE_TOO_OLD"_q)
+ ? Peers{ .state = WhoReadState::TooOld }
+ : Peers{ .state = WhoReadState::Empty };
+ }
+ }).send();
+ } else {
entry.requestId = session->api().request(
MTPmessages_GetMessageReadParticipants(
item->history()->peer->input,
@@ -243,8 +298,8 @@ struct State {
}).fail([=] {
auto &entry = context->cacheRead(item);
entry.requestId = 0;
- if (entry.data.current().unknown) {
- entry.data = Peers();
+ if (entry.data.current().state == WhoReadState::Unknown) {
+ entry.data = Peers{ .state = WhoReadState::Empty };
}
}).send();
}
@@ -258,7 +313,7 @@ struct State {
.list = peers.list | ranges::views::transform([](WhoReadPeer peer) {
return PeerWithReaction{ .peerWithDate = peer };
}) | ranges::to_vector,
- .unknown = peers.unknown,
+ .state = peers.state,
};
result.read = std::move(peers.list);
return result;
@@ -319,8 +374,10 @@ struct State {
}).fail([=] {
auto &entry = context->cacheReacted(item, reaction);
entry.requestId = 0;
- if (entry.data.current().unknown) {
- entry.data = PeersWithReactions();
+ if (entry.data.current().state == WhoReadState::Unknown) {
+ entry.data = PeersWithReactions{
+ .state = WhoReadState::Empty,
+ };
}
}).send();
}
@@ -336,8 +393,9 @@ struct State {
WhoReactedIds(item, {}, context),
WhoReadIds(item, context)
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
- if (reacted.unknown || read.unknown) {
- return PeersWithReactions{ .unknown = true };
+ if (reacted.state == WhoReadState::Unknown
+ || read.state == WhoReadState::Unknown) {
+ return PeersWithReactions{ .state = WhoReadState::Unknown};
}
auto &list = reacted.list;
for (const auto &peerWithDate : read.list) {
@@ -531,16 +589,17 @@ rpl::producer WhoReacted(
std::move(
idsWithReactions
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
- if (peers.unknown) {
+ if (peers.state == WhoReadState::Unknown) {
state->userpics.clear();
consumer.put_next(Ui::WhoReadContent{
.type = state->current.type,
.fullReactionsCount = state->current.fullReactionsCount,
.fullReadCount = state->current.fullReadCount,
- .unknown = true,
+ .state = WhoReadState::Unknown,
});
return;
}
+ state->current.state = peers.state;
state->current.fullReadCount = int(peers.read.size());
state->current.fullReactionsCount = peers.fullReactionsCount;
if (whoReadIds) {
@@ -631,6 +690,22 @@ bool WhoReadExists(not_null item) {
}
const auto history = item->history();
const auto peer = history->peer;
+ if (const auto user = peer->asUser()) {
+ if (user->isSelf()
+ || user->isBot()
+ || user->isServiceUser()
+ || user->readDatesPrivate()) {
+ return false;
+ }
+ const auto &appConfig = peer->session().account().appConfig();
+ const auto expirePeriod = appConfig.get(
+ "pm_read_date_expire_period",
+ 7 * 86400);
+ if (item->date() + int64(expirePeriod) <= int64(base::unixtime::now())) {
+ return false;
+ }
+ return true;
+ }
const auto chat = peer->asChat();
const auto megagroup = peer->asMegagroup();
if ((!chat && !megagroup)
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 6dc6f7646b20e5..89b4963dcbce79 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -1922,28 +1922,28 @@ void ApiWrap::saveDraftToCloudDelayed(not_null thread) {
void ApiWrap::updatePrivacyLastSeens() {
const auto now = base::unixtime::now();
- _session->data().enumerateUsers([&](UserData *user) {
- if (user->isSelf() || !user->isLoaded()) {
- return;
- }
- if (user->onlineTill <= 0) {
- return;
- }
+ if (!_session->premium()) {
+ _session->data().enumerateUsers([&](not_null user) {
+ if (user->isSelf()
+ || !user->isLoaded()
+ || user->lastseen().isHidden()) {
+ return;
+ }
- if (user->onlineTill + 3 * 86400 >= now) {
- user->onlineTill = -2; // recently
- } else if (user->onlineTill + 7 * 86400 >= now) {
- user->onlineTill = -3; // last week
- } else if (user->onlineTill + 30 * 86400 >= now) {
- user->onlineTill = -4; // last month
- } else {
- user->onlineTill = 0;
- }
- session().changes().peerUpdated(
- user,
- Data::PeerUpdate::Flag::OnlineStatus);
- session().data().maybeStopWatchForOffline(user);
- });
+ const auto till = user->lastseen().onlineTill();
+ user->updateLastseen((till + 3 * 86400 >= now)
+ ? Data::LastseenStatus::Recently(true)
+ : (till + 7 * 86400 >= now)
+ ? Data::LastseenStatus::WithinWeek(true)
+ : (till + 30 * 86400 >= now)
+ ? Data::LastseenStatus::WithinMonth(true)
+ : Data::LastseenStatus::LongAgo(true));
+ session().changes().peerUpdated(
+ user,
+ Data::PeerUpdate::Flag::OnlineStatus);
+ session().data().maybeStopWatchForOffline(user);
+ });
+ }
if (_contactsStatusesRequestId) {
request(_contactsStatusesRequestId).cancel();
@@ -1951,20 +1951,17 @@ void ApiWrap::updatePrivacyLastSeens() {
_contactsStatusesRequestId = request(MTPcontacts_GetStatuses(
)).done([=](const MTPVector &result) {
_contactsStatusesRequestId = 0;
- for (const auto &item : result.v) {
- Assert(item.type() == mtpc_contactStatus);
- auto &data = item.c_contactStatus();
- if (auto user = _session->data().userLoaded(data.vuser_id())) {
- auto oldOnlineTill = user->onlineTill;
- auto newOnlineTill = OnlineTillFromStatus(
+ for (const auto &status : result.v) {
+ const auto &data = status.data();
+ const auto userId = UserId(data.vuser_id());
+ if (const auto user = _session->data().userLoaded(userId)) {
+ const auto status = LastseenFromMTP(
data.vstatus(),
- oldOnlineTill);
- if (oldOnlineTill != newOnlineTill) {
- user->onlineTill = newOnlineTill;
+ user->lastseen());
+ if (user->updateLastseen(status)) {
session().changes().peerUpdated(
user,
Data::PeerUpdate::Flag::OnlineStatus);
- session().data().maybeStopWatchForOffline(user);
}
}
}
@@ -1973,22 +1970,6 @@ void ApiWrap::updatePrivacyLastSeens() {
}).send();
}
-int ApiWrap::OnlineTillFromStatus(
- const MTPUserStatus &status,
- int currentOnlineTill) {
- switch (status.type()) {
- case mtpc_userStatusEmpty: return 0;
- case mtpc_userStatusRecently:
- // Don't modify pseudo-online.
- return (currentOnlineTill > -10) ? -2 : currentOnlineTill;
- case mtpc_userStatusLastWeek: return -3;
- case mtpc_userStatusLastMonth: return -4;
- case mtpc_userStatusOffline: return status.c_userStatusOffline().vwas_online().v;
- case mtpc_userStatusOnline: return status.c_userStatusOnline().vexpires().v;
- }
- Unexpected("Bad UserStatus type.");
-}
-
void ApiWrap::clearHistory(not_null peer, bool revoke) {
deleteHistory(peer, true, revoke);
}
@@ -2625,6 +2606,22 @@ void ApiWrap::setGroupStickerSet(
_session->data().stickers().notifyUpdated(Data::StickersType::Stickers);
}
+void ApiWrap::setGroupEmojiSet(
+ not_null megagroup,
+ const StickerSetIdentifier &set) {
+ Expects(megagroup->mgInfo != nullptr);
+
+ megagroup->mgInfo->emojiSet = set;
+ request(MTPchannels_SetEmojiStickers(
+ megagroup->inputChannel,
+ Data::InputStickerSet(set)
+ )).send();
+ _session->changes().peerUpdated(
+ megagroup,
+ Data::PeerUpdate::Flag::EmojiSet);
+ _session->data().stickers().notifyUpdated(Data::StickersType::Emoji);
+}
+
std::vector> *ApiWrap::stickersByEmoji(
const QString &key) {
const auto it = _stickersByEmoji.find(key);
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index e75fea53d9a51b..fb8588a7cddf1d 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -246,6 +246,9 @@ class ApiWrap final : public MTP::Sender {
void setGroupStickerSet(
not_null megagroup,
const StickerSetIdentifier &set);
+ void setGroupEmojiSet(
+ not_null megagroup,
+ const StickerSetIdentifier &set);
[[nodiscard]] std::vector> *stickersByEmoji(
const QString &key);
@@ -258,10 +261,6 @@ class ApiWrap final : public MTP::Sender {
void updateNotifySettingsDelayed(Data::DefaultNotify type);
void saveDraftToCloudDelayed(not_null thread);
- static int OnlineTillFromStatus(
- const MTPUserStatus &status,
- int currentOnlineTill);
-
void clearHistory(not_null peer, bool revoke);
void deleteConversation(not_null peer, bool revoke);
diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index 9d0ccdf0c80fc5..9754fc9a879720 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -12,12 +12,10 @@ For license and copyright information please follow this link:
#include "base/random.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/abstract_box.h"
-#include "boxes/peer_list_controllers.h"
#include "boxes/premium_limits_box.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peers/edit_peer_common.h"
#include "boxes/peers/edit_participant_box.h"
-#include "boxes/peers/edit_participants_box.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "chat_helpers/emoji_suggestions_widget.h"
@@ -27,34 +25,26 @@ For license and copyright information please follow this link:
#include "menu/menu_ttl.h"
#include "ui/controls/userpic_button.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/buttons.h"
-#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/fields/special_fields.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text/format_values.h"
-#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
-#include "ui/unread_badge.h"
-#include "ui/ui_utility.h"
#include "ui/painter.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "data/data_changes.h"
-#include "data/data_cloud_file.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
#include "api/api_peer_photo.h"
+#include "api/api_self_destruct.h"
#include "main/main_session.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
-#include "styles/style_boxes.h"
-#include "styles/style_dialogs.h"
-#include "styles/style_widgets.h"
#include
#include
@@ -599,6 +589,8 @@ void GroupInfoBox::prepare() {
addButton(tr::lng_cancel(), [this] { closeBox(); });
if (_type == Type::Group) {
+ _navigation->session().api().selfDestruct().reload();
+
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu =
top->lifetime().make_state>();
@@ -607,21 +599,21 @@ void GroupInfoBox::prepare() {
top,
st::popupMenuWithIcons);
+ const auto ttl = ttlPeriod();
const auto text = tr::lng_manage_messages_ttl_menu(tr::now)
- + (_ttlPeriod
- ? ('\t' + Ui::FormatTTLTiny(_ttlPeriod))
- : QString());
+ + (ttl ? ('\t' + Ui::FormatTTLTiny(ttl)) : QString());
(*menu)->addAction(
text,
[=, show = uiShow()] {
show->showBox(Box(TTLMenu::TTLBox, TTLMenu::Args{
.show = show,
- .startTtl = _ttlPeriod,
+ .startTtl = ttlPeriod(),
.about = nullptr,
.callback = crl::guard(this, [=](
TimeId t,
Fn close) {
_ttlPeriod = t;
+ _ttlPeriodOverridden = true;
close();
}),
}));
@@ -687,6 +679,13 @@ void GroupInfoBox::submitName() {
}
}
+TimeId GroupInfoBox::ttlPeriod() const {
+ return _ttlPeriodOverridden
+ ? _ttlPeriod
+ : _navigation->session().api().selfDestruct()
+ .periodDefaultHistoryTTLCurrent();
+}
+
void GroupInfoBox::createGroup(
QPointer selectUsersBox,
const QString &title,
@@ -705,15 +704,13 @@ void GroupInfoBox::createGroup(
}
}
_creationRequestId = _api.request(MTPmessages_CreateChat(
- MTP_flags(_ttlPeriod
- ? MTPmessages_CreateChat::Flag::f_ttl_period
- : MTPmessages_CreateChat::Flags(0)),
+ MTP_flags(MTPmessages_CreateChat::Flag::f_ttl_period),
MTP_vector(inputs),
MTP_string(title),
- MTP_int(_ttlPeriod)
+ MTP_int(ttlPeriod())
)).done([=](const MTPUpdates &result) {
auto image = _photo->takeResultImage();
- const auto period = _ttlPeriod;
+ const auto period = ttlPeriod();
const auto navigation = _navigation;
const auto done = _done;
@@ -799,16 +796,17 @@ void GroupInfoBox::createChannel(
? Flag::f_megagroup
: Flag::f_broadcast)
| ((_type == Type::Forum) ? Flag::f_forum : Flag())
- | ((_type == Type::Megagroup && _ttlPeriod)
+ | ((_type == Type::Megagroup)
? MTPchannels_CreateChannel::Flag::f_ttl_period
: MTPchannels_CreateChannel::Flags(0));
+ const auto ttl = ttlPeriod();
_creationRequestId = _api.request(MTPchannels_CreateChannel(
MTP_flags(flags),
MTP_string(title),
MTP_string(description),
MTPInputGeoPoint(), // geo_point
MTPstring(), // address
- MTP_int((_type == Type::Megagroup) ? _ttlPeriod : 0)
+ MTP_int((_type == Type::Megagroup) ? ttl : 0)
)).done([=](const MTPUpdates &result) {
_navigation->session().api().applyUpdates(result);
@@ -841,8 +839,8 @@ void GroupInfoBox::createChannel(
channel,
{ std::move(image) });
}
- if (_ttlPeriod && channel->isMegagroup()) {
- channel->setMessagesTTL(_ttlPeriod);
+ if (ttl && channel->isMegagroup()) {
+ channel->setMessagesTTL(ttl);
}
channel->session().api().requestFullPeer(channel);
_createdChannel = channel;
diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h
index 9a7d14ab2c18fb..6fd021df26d0a3 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.h
+++ b/Telegram/SourceFiles/boxes/add_contact_box.h
@@ -132,6 +132,8 @@ class GroupInfoBox : public Ui::BoxContent {
void descriptionResized();
void updateMaxHeight();
+ [[nodiscard]] TimeId ttlPeriod() const;
+
const not_null _navigation;
MTP::Sender _api;
@@ -150,6 +152,7 @@ class GroupInfoBox : public Ui::BoxContent {
bool _creatingInviteLink = false;
ChannelData *_createdChannel = nullptr;
TimeId _ttlPeriod = 0;
+ bool _ttlPeriodOverridden = false;
};
diff --git a/Telegram/SourceFiles/boxes/auto_download_box.cpp b/Telegram/SourceFiles/boxes/auto_download_box.cpp
index 4d3baf558a5322..d092de05fda013 100644
--- a/Telegram/SourceFiles/boxes/auto_download_box.cpp
+++ b/Telegram/SourceFiles/boxes/auto_download_box.cpp
@@ -162,7 +162,7 @@ void AutoDownloadBox::setupContent() {
*downloadValues,
*autoPlayValues);
auto allowMore = values | ranges::views::filter([&](Pair pair) {
- const auto [type, enabled] = pair;
+ const auto &[type, enabled] = pair;
const auto value = enabled ? limitByType(type) : 0;
const auto old = settings->bytesLimit(_source, type);
return (old < value);
@@ -170,7 +170,7 @@ void AutoDownloadBox::setupContent() {
return pair.first;
});
const auto less = ranges::any_of(*autoPlayValues, [&](Pair pair) {
- const auto [type, enabled] = pair;
+ const auto &[type, enabled] = pair;
const auto value = enabled ? limitByType(type) : 0;
return value < settings->bytesLimit(_source, type);
});
@@ -179,7 +179,7 @@ void AutoDownloadBox::setupContent() {
allowMore.end());
const auto changed = ranges::any_of(values, [&](Pair pair) {
- const auto [type, enabled] = pair;
+ const auto &[type, enabled] = pair;
const auto value = enabled ? limitByType(type) : 0;
return value != settings->bytesLimit(_source, type);
});
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index 2e4b90a01a61c9..619f5a9c889585 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -57,7 +57,6 @@ For license and copyright information please follow this link:
namespace {
constexpr auto kMaxWallPaperSlugLength = 255;
-constexpr auto kDefaultDimming = 50;
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
@@ -468,6 +467,10 @@ bool BackgroundPreviewBox::forChannel() const {
return _forPeer && _forPeer->isChannel();
}
+bool BackgroundPreviewBox::forGroup() const {
+ return forChannel() && _forPeer->isMegagroup();
+}
+
void BackgroundPreviewBox::generateBackground() {
if (_paper.backgroundColors().empty()) {
return;
@@ -493,7 +496,9 @@ void BackgroundPreviewBox::resetTitle() {
void BackgroundPreviewBox::rebuildButtons(bool dark) {
clearButtons();
- addButton(forChannel()
+ addButton(forGroup()
+ ? tr::lng_background_apply_group()
+ : forChannel()
? tr::lng_background_apply_channel()
: _forPeer
? tr::lng_background_apply_button()
@@ -709,7 +714,7 @@ void BackgroundPreviewBox::checkLevelForChannel() {
return std::optional();
}
return std::make_optional(Ui::AskBoostReason{
- Ui::AskBoostWallpaper{ required }
+ Ui::AskBoostWallpaper{ required, _forPeer->isMegagroup()}
});
}, [=] { _forPeerLevelCheck = false; });
}
@@ -1084,7 +1089,9 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector &bg) {
_service = GenerateServiceItem(
delegate(),
_serviceHistory,
- (forChannel()
+ (forGroup()
+ ? tr::lng_background_other_group(tr::now)
+ : forChannel()
? tr::lng_background_other_channel(tr::now)
: (_forPeer && !_fromMessageId)
? tr::lng_background_other_info(
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h
index 6d645bc5238155..4d4c11109c78f3 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.h
+++ b/Telegram/SourceFiles/boxes/background_preview_box.h
@@ -95,6 +95,7 @@ class BackgroundPreviewBox
[[nodiscard]] OverridenStyle prepareOverridenStyle(bool dark);
[[nodiscard]] bool forChannel() const;
+ [[nodiscard]] bool forGroup() const;
void checkLevelForChannel();
void recreate(bool dark);
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index 7bca1b8e51a4c2..c804e40a3b3b54 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -791,6 +791,9 @@ slowmodeLabelsMargin: margins(0px, 5px, 0px, 0px);
slowmodeLabel: LabelSimple(defaultLabelSimple) {
textFg: windowSubTextFg;
}
+boostsUnrestrictLabel: FlatLabel(defaultFlatLabel) {
+ textFg: windowSubTextFg;
+}
customBadgeField: InputField(defaultInputField) {
textMargins: margins(2px, 7px, 2px, 0px);
diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp
index 9e9d0a5c6cafe3..5dc21b3ccb5fa8 100644
--- a/Telegram/SourceFiles/boxes/connection_box.cpp
+++ b/Telegram/SourceFiles/boxes/connection_box.cpp
@@ -113,7 +113,8 @@ Base64UrlInput::Base64UrlInput(
rpl::producer placeholder,
const QString &val)
: MaskedInputField(parent, st, std::move(placeholder), val) {
- if (!QRegularExpression("^[a-zA-Z0-9_\\-]+$").match(val).hasMatch()) {
+ static const auto RegExp = QRegularExpression("^[a-zA-Z0-9_\\-]+$");
+ if (!RegExp.match(val).hasMatch()) {
setText(QString());
}
}
@@ -747,7 +748,7 @@ void ProxiesBox::applyView(View &&view) {
const auto wrap = _wrap
? _wrap.data()
: _initialWrap.data();
- const auto [i, ok] = _rows.emplace(id, nullptr);
+ const auto &[i, ok] = _rows.emplace(id, nullptr);
i->second.reset(wrap->insert(
0,
object_ptr(
@@ -831,8 +832,9 @@ void ProxyBox::prepare() {
connect(_host.data(), &HostInput::changed, [=] {
Ui::PostponeCall(_host, [=] {
const auto host = _host->getLastText().trimmed();
- static const auto mask = u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q;
- const auto match = QRegularExpression(mask).match(host);
+ static const auto mask = QRegularExpression(
+ u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q);
+ const auto match = mask.match(host);
if (_host->cursorPosition() == host.size()
&& match.hasMatch()) {
const auto port = match.captured(1);
@@ -1107,6 +1109,10 @@ void ProxiesBoxController::ShowApplyConfirmation(
proxy.password = fields.value(u"secret"_q);
}
if (proxy) {
+ static const auto UrlStartRegExp = QRegularExpression(
+ "^https://",
+ QRegularExpression::CaseInsensitiveOption);
+ static const auto UrlEndRegExp = QRegularExpression("/$");
const auto displayed = "https://" + server + "/";
const auto parsed = QUrl::fromUserInput(displayed);
const auto displayUrl = !UrlClickHandler::IsSuspicious(displayed)
@@ -1117,11 +1123,9 @@ void ProxiesBoxController::ShowApplyConfirmation(
const auto displayServer = QString(
displayUrl
).replace(
- QRegularExpression(
- "^https://",
- QRegularExpression::CaseInsensitiveOption),
+ UrlStartRegExp,
QString()
- ).replace(QRegularExpression("/$"), QString());
+ ).replace(UrlEndRegExp, QString());
const auto text = tr::lng_sure_enable_socks(
tr::now,
lt_server,
diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp
index a22714584dbf18..dbecd528b70dc5 100644
--- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp
+++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp
@@ -221,7 +221,7 @@ void DeleteMessagesBox::prepare() {
? QString()
: QString(" (%1)").arg(total));
});
- search->searchMessages(QString(), _moderateFrom);
+ search->searchMessages({ .from = _moderateFrom });
}
} else {
details.text = (_ids.size() == 1)
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 271bb2c422e4c7..4e2f226a93f17c 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -470,8 +470,8 @@ void EditCaptionBox::rebuildPreview() {
void EditCaptionBox::setupField() {
const auto peer = _historyItem->history()->peer;
- const auto allow = [=](const auto&) {
- return Data::AllowEmojiWithoutPremium(peer);
+ const auto allow = [=](not_null emoji) {
+ return Data::AllowEmojiWithoutPremium(peer, emoji);
};
InitMessageFieldHandlers(
_controller,
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
index bccf7bec34641b..d38cf5a664fdf0 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
@@ -7,15 +7,22 @@ For license and copyright information please follow this link:
*/
#include "boxes/edit_privacy_box.h"
+#include "api/api_global_privacy.h"
+#include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
+#include "ui/widgets/shadow.h"
#include "ui/text/text_utilities.h"
+#include "ui/toast/toast.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
+#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "history/history.h"
#include "boxes/peer_list_controllers.h"
+#include "settings/settings_common.h"
+#include "settings/settings_premium.h"
#include "settings/settings_privacy_security.h"
#include "calls/calls_instance.h"
#include "base/binary_guard.h"
@@ -28,8 +35,39 @@ For license and copyright information please follow this link:
#include "window/window_session_controller.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
+#include "styles/style_menu_icons.h"
namespace {
+namespace {
+
+void CreateRadiobuttonLock(
+ not_null widget,
+ const style::Checkbox &st) {
+ const auto lock = Ui::CreateChild(widget.get());
+ lock->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+ lock->resize(st::defaultRadio.diameter, st::defaultRadio.diameter);
+
+ widget->sizeValue(
+ ) | rpl::start_with_next([=, &st](QSize size) {
+ lock->move(st.checkPosition);
+ }, lock->lifetime());
+
+ lock->paintRequest() | rpl::start_with_next([=] {
+ auto p = QPainter(lock);
+ auto hq = PainterHighQualityEnabler(p);
+ const auto &icon = st::messagePrivacyLock;
+ const auto size = st::defaultRadio.diameter;
+ const auto image = icon.instance(st::checkboxFg->c);
+ p.drawImage(QRectF(
+ (size - icon.width()) / 2.,
+ (size - icon.height()) / 2.,
+ icon.width(),
+ icon.height()), image);
+ }, lock->lifetime());
+}
+
+} // namespace
class PrivacyExceptionsBoxController : public ChatsListBoxController {
public:
@@ -340,7 +378,7 @@ void EditPrivacyBox::setupContent() {
auto middle = _controller->setupMiddleWidget(
_window,
content,
- std::move(optionValue));
+ rpl::duplicate(optionValue));
if (middle) {
content->add(std::move(middle));
}
@@ -357,7 +395,11 @@ void EditPrivacyBox::setupContent() {
_controller->exceptionsDescription() | Ui::Text::ToWithEntities(),
st::defaultVerticalListSkip);
- if (auto below = _controller->setupBelowWidget(_window, content)) {
+ auto below = _controller->setupBelowWidget(
+ _window,
+ content,
+ rpl::duplicate(optionValue));
+ if (below) {
content->add(std::move(below));
}
@@ -394,3 +436,119 @@ void EditPrivacyBox::setupContent() {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
}
+
+void EditMessagesPrivacyBox(
+ not_null box,
+ not_null controller) {
+ box->setTitle(tr::lng_messages_privacy_title());
+ box->setWidth(st::boxWideWidth);
+
+ constexpr auto kOptionAll = 0;
+ constexpr auto kOptionPremium = 1;
+
+ const auto premium = controller->session().premium();
+ const auto privacy = &controller->session().api().globalPrivacy();
+ const auto inner = box->verticalLayout();
+ inner->add(object_ptr(box));
+
+ Ui::AddSkip(inner, st::messagePrivacyTopSkip);
+ Ui::AddSubsectionTitle(inner, tr::lng_messages_privacy_subtitle());
+ const auto group = std::make_shared(
+ privacy->newRequirePremiumCurrent() ? kOptionPremium : kOptionAll);
+ inner->add(
+ object_ptr(
+ inner,
+ group,
+ kOptionAll,
+ tr::lng_messages_privacy_everyone(tr::now),
+ st::messagePrivacyCheck),
+ st::settingsSendTypePadding);
+ const auto restricted = inner->add(
+ object_ptr(
+ inner,
+ group,
+ kOptionPremium,
+ tr::lng_messages_privacy_restricted(tr::now),
+ st::messagePrivacyCheck),
+ st::settingsSendTypePadding + style::margins(
+ 0,
+ st::messagePrivacyRadioSkip,
+ 0,
+ st::messagePrivacyBottomSkip));
+
+ using WeakToast = base::weak_ptr;
+ const auto toast = std::make_shared();
+ const auto showToast = [=] {
+ auto link = Ui::Text::Link(
+ Ui::Text::Semibold(
+ tr::lng_messages_privacy_premium_link(tr::now)));
+ (*toast) = controller->showToast({
+ .text = tr::lng_messages_privacy_premium(
+ tr::now,
+ lt_link,
+ link,
+ Ui::Text::WithEntities),
+ .st = &st::defaultMultilineToast,
+ .duration = Ui::Toast::kDefaultDuration * 2,
+ .multiline = true,
+ .filter = crl::guard(&controller->session(), [=](
+ const ClickHandlerPtr &,
+ Qt::MouseButton button) {
+ if (button == Qt::LeftButton) {
+ if (const auto strong = toast->get()) {
+ strong->hideAnimated();
+ (*toast) = nullptr;
+ Settings::ShowPremium(
+ controller,
+ u"noncontact_peers_require_premium"_q);
+ return true;
+ }
+ }
+ return false;
+ }),
+ });
+ };
+ if (!premium) {
+ CreateRadiobuttonLock(restricted, st::messagePrivacyCheck);
+
+ group->setChangedCallback([=](int value) {
+ if (value == kOptionPremium) {
+ group->setValue(kOptionAll);
+ showToast();
+ }
+ });
+ }
+
+ Ui::AddDividerText(inner, tr::lng_messages_privacy_about());
+ if (!premium) {
+ Ui::AddSkip(inner);
+ Settings::AddButtonWithIcon(
+ inner,
+ tr::lng_messages_privacy_premium_button(),
+ st::messagePrivacySubscribe,
+ { .icon = &st::menuBlueIconPremium }
+ )->setClickedCallback([=] {
+ Settings::ShowPremium(
+ controller,
+ u"noncontact_peers_require_premium"_q);
+ });
+ Ui::AddSkip(inner);
+ Ui::AddDividerText(inner, tr::lng_messages_privacy_premium_about());
+ box->addButton(tr::lng_about_done(), [=] {
+ box->closeBox();
+ });
+ } else {
+ box->addButton(tr::lng_settings_save(), [=] {
+ if (controller->session().premium()) {
+ privacy->updateNewRequirePremium(
+ group->value() == kOptionPremium);
+ box->closeBox();
+ } else {
+ showToast();
+ }
+ });
+ box->addButton(tr::lng_cancel(), [=] {
+ box->closeBox();
+ });
+ }
+}
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h
index 791c854483da64..27984e9d5525ed 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.h
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h
@@ -12,6 +12,7 @@ For license and copyright information please follow this link:
#include "api/api_user_privacy.h"
namespace Ui {
+class GenericBox;
class VerticalLayout;
class FlatLabel;
class LinkButton;
@@ -74,7 +75,8 @@ class EditPrivacyController {
}
[[nodiscard]] virtual object_ptr setupBelowWidget(
not_null controller,
- not_null parent) {
+ not_null parent,
+ rpl::producer