diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 6ac646b3c..fe6d3752c 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -85,7 +85,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 2a570aef7..8e99d7162 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -40,7 +40,7 @@ jobs:
macos:
name: MacOS
- runs-on: macos-12
+ runs-on: macos-13
strategy:
matrix:
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 46ff421e6..7b41428e1 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -744,8 +744,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
@@ -1487,6 +1485,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 000000000..cc4887f94
Binary files /dev/null and b/Telegram/Resources/animations/palette.tgs 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 000000000..4acd54d82
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 000000000..f8dd406fe
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 000000000..1bd9d32ae
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 000000000..234f29434
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 000000000..0fb615748
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 000000000..571d1379e
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 000000000..327d069c7
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 000000000..076b08433
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 000000000..4325db686
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 000000000..11c44d03f
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 000000000..8bfedd0f4
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 000000000..c89f73225
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 000000000..66c9d9d3a
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 000000000..78ebcd323
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 000000000..80b73df03
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 000000000..efbe55f2a
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 000000000..b73917126
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 000000000..c71f495be
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 000000000..b7969a460
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 000000000..10f2a645e
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 000000000..5f93a1e33
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 000000000..7420822b7
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 000000000..ec1d7e056
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 000000000..86e53f601
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 000000000..3f8f0a9c3
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 000000000..75ffbbf9d
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 000000000..176153370
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 000000000..a88ef05cf
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 000000000..ad2bc2ed5
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 000000000..6e0c0765a
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/lastseen.png b/Telegram/Resources/icons/settings/premium/lastseen.png
new file mode 100644
index 000000000..9299ed216
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 000000000..994a90719
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 000000000..42b3f1865
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 000000000..9d2309768
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 000000000..f82d78947
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 000000000..ce3c77543
Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/privacy@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 000000000..80c667ecc
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 000000000..95a806396
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 000000000..423de8209
Binary files /dev/null and b/Telegram/Resources/icons/stories/boosts_mini@3x.png differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 51851db0e..7605dd583 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -867,6 +867,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -876,6 +877,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -1736,11 +1738,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -1983,6 +1988,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -2033,6 +2040,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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";
@@ -2144,28 +2155,48 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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.";
@@ -2173,10 +2204,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -2193,6 +2227,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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.";
@@ -2201,23 +2239,51 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -2231,30 +2297,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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.";
@@ -2277,8 +2348,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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!";
@@ -2301,8 +2374,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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}";
@@ -2313,32 +2394,43 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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!";
@@ -2441,6 +2533,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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.";
@@ -2520,6 +2616,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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}";
@@ -2752,6 +2849,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -2768,6 +2866,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -2950,8 +3049,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -3515,6 +3618,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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.";
@@ -3610,6 +3716,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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}.";
@@ -3747,6 +3854,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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}»";
@@ -4582,10 +4692,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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";
@@ -4593,8 +4705,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/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 76ea1ef4a..a129237ca 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/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index c3069c310..54c27edef 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
+ Version="4.15.0.0" />
Telegram Desktop
Telegram Messenger LLP
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index eaac34070..5b93e930d 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,13,0
- PRODUCTVERSION 4,14,13,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", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
- VALUE "FileVersion", "4.14.13.0"
+ VALUE "FileVersion", "4.15.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.14.13.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 8b304b6be..af1902b73 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,13,0
- PRODUCTVERSION 4,14,13,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", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
- VALUE "FileVersion", "4.14.13.0"
+ VALUE "FileVersion", "4.15.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.14.13.0"
+ VALUE "ProductVersion", "4.15.0.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/api/api_peer_colors.cpp b/Telegram/SourceFiles/api/api_peer_colors.cpp
index 50c2382fd..5f3fe12d2 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 0ad1a63c7..f8d379020 100644
--- a/Telegram/SourceFiles/api/api_peer_colors.h
+++ b/Telegram/SourceFiles/api/api_peer_colors.h
@@ -25,10 +25,19 @@ public:
[[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 @@ private:
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_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp
index f24e156d0..789211f33 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 ac792883b..d7064abcd 100644
--- a/Telegram/SourceFiles/api/api_transcribes.cpp
+++ b/Telegram/SourceFiles/api/api_transcribes.cpp
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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 a5c5923ec..b074e42f1 100644
--- a/Telegram/SourceFiles/api/api_transcribes.h
+++ b/Telegram/SourceFiles/api/api_transcribes.h
@@ -36,6 +36,8 @@ public:
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 2548c5cbb..d10af3e42 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -1122,6 +1122,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(),
@@ -1154,6 +1155,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(),
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 89f2cc169..a722b99dd 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -2638,6 +2638,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 192e14f28..615960126 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -246,6 +246,9 @@ public:
void setGroupStickerSet(
not_null megagroup,
const StickerSetIdentifier &set);
+ void setGroupEmojiSet(
+ not_null megagroup,
+ const StickerSetIdentifier &set);
[[nodiscard]] std::vector> *stickersByEmoji(
const QString &key);
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index db72d5690..6dadab562 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -467,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;
@@ -492,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()
@@ -708,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; });
}
@@ -1083,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 3eb06d1a5..96150c6ba 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.h
+++ b/Telegram/SourceFiles/boxes/background_preview_box.h
@@ -95,6 +95,7 @@ private:
[[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 dbba29270..8bde31e8d 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/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index defc4bd64..8e94beeb9 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/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index f6e7feb40..7e0e6b5dc 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -1363,20 +1363,26 @@ void GiveawayInfoBox(
? start->quantity
: (results->winnersCount + results->unclaimedCount);
const auto months = start ? start->months : results->months;
+ const auto group = results
+ ? results->channel->isMegagroup()
+ : (!start->channels.empty()
+ && start->channels.front()->isMegagroup());
text.append((finished
? tr::lng_prizes_end_text
: tr::lng_prizes_how_text)(
tr::now,
lt_admins,
- tr::lng_prizes_admins(
- tr::now,
- lt_count,
- quantity,
- lt_channel,
- Ui::Text::Bold(first),
- lt_duration,
- TextWithEntities{ GiftDuration(months) },
- Ui::Text::RichLangValue),
+ (group
+ ? tr::lng_prizes_admins_group
+ : tr::lng_prizes_admins)(
+ tr::now,
+ lt_count,
+ quantity,
+ lt_channel,
+ Ui::Text::Bold(first),
+ lt_duration,
+ TextWithEntities{ GiftDuration(months) },
+ Ui::Text::RichLangValue),
Ui::Text::RichLangValue));
const auto many = start
? (start->channels.size() > 1)
@@ -1387,8 +1393,12 @@ void GiveawayInfoBox(
const auto all = start ? start->all : results->all;
auto winners = all
? (many
- ? tr::lng_prizes_winners_all_of_many
- : tr::lng_prizes_winners_all_of_one)(
+ ? (group
+ ? tr::lng_prizes_winners_all_of_many_group
+ : tr::lng_prizes_winners_all_of_many)
+ : (group
+ ? tr::lng_prizes_winners_all_of_one_group
+ : tr::lng_prizes_winners_all_of_one))(
tr::now,
lt_count,
count,
@@ -1411,15 +1421,17 @@ void GiveawayInfoBox(
? results->additionalPrize
: start->additionalPrize;
if (!additionalPrize.isEmpty()) {
- text.append("\n\n").append(tr::lng_prizes_additional_added(
- tr::now,
- lt_count,
- count,
- lt_channel,
- Ui::Text::Bold(first),
- lt_prize,
- TextWithEntities{ additionalPrize },
- Ui::Text::RichLangValue));
+ text.append("\n\n").append((group
+ ? tr::lng_prizes_additional_added_group
+ : tr::lng_prizes_additional_added)(
+ tr::now,
+ lt_count,
+ count,
+ lt_channel,
+ Ui::Text::Bold(first),
+ lt_prize,
+ TextWithEntities{ additionalPrize },
+ Ui::Text::RichLangValue));
}
const auto untilDate = start
? start->untilDate
@@ -1448,18 +1460,25 @@ void GiveawayInfoBox(
if (info.adminChannelId) {
const auto channel = controller->session().data().channel(
info.adminChannelId);
- text.append("\n\n").append(tr::lng_prizes_how_no_admin(
- tr::now,
- lt_channel,
- Ui::Text::Bold(channel->name()),
- Ui::Text::RichLangValue));
+ text.append("\n\n").append((channel->isMegagroup()
+ ? tr::lng_prizes_how_no_admin_group
+ : tr::lng_prizes_how_no_admin)(
+ tr::now,
+ lt_channel,
+ Ui::Text::Bold(channel->name()),
+ Ui::Text::RichLangValue));
} else if (info.tooEarlyDate) {
- text.append("\n\n").append(tr::lng_prizes_how_no_joined(
- tr::now,
- lt_date,
- Ui::Text::Bold(
- langDateTime(base::unixtime::parse(info.tooEarlyDate))),
- Ui::Text::RichLangValue));
+ const auto channel = controller->session().data().channel(
+ info.adminChannelId);
+ text.append("\n\n").append((channel->isMegagroup()
+ ? tr::lng_prizes_how_no_joined_group
+ : tr::lng_prizes_how_no_joined)(
+ tr::now,
+ lt_date,
+ Ui::Text::Bold(
+ langDateTime(
+ base::unixtime::parse(info.tooEarlyDate))),
+ Ui::Text::RichLangValue));
} else if (!info.disallowedCountry.isEmpty()) {
text.append("\n\n").append(tr::lng_prizes_how_no_country(
tr::now,
@@ -1499,7 +1518,9 @@ void GiveawayInfoBox(
box.get(),
object_ptr(
box.get(),
- tr::lng_prizes_cancelled(),
+ (group
+ ? tr::lng_prizes_cancelled_group()
+ : tr::lng_prizes_cancelled()),
st::giveawayRefundedLabel),
st::giveawayRefundedPadding),
{ padding.left(), 0, padding.right(), padding.bottom() });
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
index 38f064cb2..2627d2342 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
@@ -13,11 +13,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/peers/replace_boost_box.h"
#include "boxes/background_box.h"
+#include "boxes/stickers_box.h"
#include "chat_helpers/compose/compose_show.h"
+#include "data/stickers/data_custom_emoji.h"
+#include "data/stickers/data_stickers.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
-#include "data/stickers/data_custom_emoji.h"
+#include "data/data_document_media.h"
#include "data/data_emoji_statuses.h"
+#include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_web_page.h"
@@ -28,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_emoji_status_panel.h"
#include "info/info_memento.h"
#include "lang/lang_keys.h"
+#include "lottie/lottie_icon.h"
+#include "lottie/lottie_single_player.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
@@ -478,6 +484,7 @@ void Set(
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
MTP_int(values.colorIndex),
MTP_long(values.backgroundEmojiId)));
+ } else if (peer->isMegagroup()) {
} else if (const auto channel = peer->asChannel()) {
using Flag = MTPchannels_UpdateColor::Flag;
send(MTPchannels_UpdateColor(
@@ -527,9 +534,13 @@ void Apply(
} else {
CheckBoostLevel(show, peer, [=](int level) {
const auto peerColors = &peer->session().api().peerColors();
- const auto colorRequired = peerColors->requiredLevelFor(
- peer->id,
- values.colorIndex);
+ const auto colorRequired = peer->isMegagroup()
+ ? peerColors->requiredGroupLevelFor(
+ peer->id,
+ values.colorIndex)
+ : peerColors->requiredChannelLevelFor(
+ peer->id,
+ values.colorIndex);
const auto iconRequired = values.backgroundEmojiId
? session->account().appConfig().get(
"channel_bg_icon_level_min",
@@ -553,7 +564,10 @@ void Apply(
}
const auto reason = [&]() -> Ui::AskBoostReason {
if (level < statusRequired) {
- return { Ui::AskBoostEmojiStatus{ statusRequired } };
+ return { Ui::AskBoostEmojiStatus{
+ statusRequired,
+ peer->isMegagroup()
+ } };
} else if (level < iconRequired) {
return { Ui::AskBoostChannelColor{ iconRequired } };
}
@@ -670,6 +684,44 @@ int ColorSelector::resizeGetHeight(int newWidth) {
return (top - skip) + ((count % columns) ? (isize + skip) : 0);
}
+[[nodiscard]] auto ButtonStyleWithAddedPadding(
+ not_null parent,
+ const style::SettingsButton &basicSt,
+ QMargins added) {
+ const auto st = parent->lifetime().make_state(
+ basicSt);
+ st->padding += added;
+ return st;
+}
+
+struct ButtonWithEmoji {
+ not_null st;
+ int emojiWidth = 0;
+ int noneWidth = 0;
+ int added = 0;
+};
+
+[[nodiscard]] ButtonWithEmoji ButtonStyleWithRightEmoji(
+ not_null parent) {
+ const auto ratio = style::DevicePixelRatio();
+ const auto emojiWidth = Data::FrameSizeFromTag({}) / ratio;
+
+ const auto noneWidth = st::normalFont->width(
+ tr::lng_settings_color_emoji_off(tr::now));
+
+ const auto added = st::normalFont->spacew;
+ const auto rightAdded = std::max(noneWidth, emojiWidth);
+ return {
+ .st = ButtonStyleWithAddedPadding(
+ parent,
+ st::peerAppearanceButton,
+ QMargins(0, 0, added + rightAdded, 0)),
+ .emojiWidth = emojiWidth,
+ .noneWidth = noneWidth,
+ .added = added,
+ };
+}
+
[[nodiscard]] object_ptr CreateEmojiIconButton(
not_null parent,
std::shared_ptr show,
@@ -677,22 +729,12 @@ int ColorSelector::resizeGetHeight(int newWidth) {
rpl::producer colorIndexValue,
rpl::producer emojiIdValue,
Fn emojiIdChosen) {
- const auto &basicSt = st::settingsButtonNoIcon;
- const auto ratio = style::DevicePixelRatio();
- const auto added = st::normalFont->spacew;
- const auto emojiSize = Data::FrameSizeFromTag({}) / ratio;
- const auto noneWidth = added
- + st::normalFont->width(tr::lng_settings_color_emoji_off(tr::now));
- const auto emojiWidth = added + emojiSize;
- const auto rightPadding = std::max(noneWidth, emojiWidth)
- + basicSt.padding.right();
- const auto st = parent->lifetime().make_state(
- basicSt);
- st->padding.setRight(rightPadding);
- auto result = object_ptr(
+ const auto button = ButtonStyleWithRightEmoji(parent);
+ auto result = Settings::CreateButtonWithIcon(
parent,
tr::lng_settings_color_emoji(),
- *st);
+ *button.st,
+ { &st::menuBlueIconColorNames });
const auto raw = result.data();
const auto right = Ui::CreateChild(raw);
@@ -719,6 +761,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
}, right->lifetime());
const auto session = &show->session();
+ const auto added = st::normalFont->spacew;
std::move(emojiIdValue) | rpl::start_with_next([=](DocumentId emojiId) {
state->emojiId = emojiId;
state->emoji = emojiId
@@ -727,7 +770,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
[=] { right->update(); })
: nullptr;
right->resize(
- (emojiId ? emojiWidth : noneWidth) + added,
+ (emojiId ? button.emojiWidth : button.noneWidth) + button.added,
right->height());
right->update();
}, right->lifetime());
@@ -738,7 +781,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
) | rpl::start_with_next([=](QSize outer, int width) {
right->resize(width, outer.height());
const auto skip = st::settingsButton.padding.right();
- right->moveToRight(skip - added, 0, outer.width());
+ right->moveToRight(skip - button.added, 0, outer.width());
}, right->lifetime());
right->paintRequest(
@@ -752,7 +795,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
const auto colors = style->coloredValues(false, state->index);
state->emoji->paint(p, {
.textColor = colors.name,
- .position = QPoint(added, (height - emojiSize) / 2),
+ .position = QPoint(added, (height - button.emojiWidth) / 2),
.internal = {
.forceFirstFrame = true,
},
@@ -791,23 +834,16 @@ int ColorSelector::resizeGetHeight(int newWidth) {
not_null parent,
std::shared_ptr show,
rpl::producer statusIdValue,
- Fn statusIdChosen) {
- const auto &basicSt = st::settingsButtonNoIcon;
- const auto ratio = style::DevicePixelRatio();
- const auto added = st::normalFont->spacew;
- const auto emojiSize = Data::FrameSizeFromTag({}) / ratio;
- const auto noneWidth = added
- + st::normalFont->width(tr::lng_settings_color_emoji_off(tr::now));
- const auto emojiWidth = added + emojiSize;
- const auto rightPadding = std::max(noneWidth, emojiWidth)
- + basicSt.padding.right();
- const auto st = parent->lifetime().make_state(
- basicSt);
- st->padding.setRight(rightPadding);
- auto result = object_ptr(
+ Fn statusIdChosen,
+ bool group) {
+ const auto button = ButtonStyleWithRightEmoji(parent);
+ auto result = Settings::CreateButtonWithIcon(
parent,
- tr::lng_edit_channel_status(),
- *st);
+ (group
+ ? tr::lng_edit_channel_status_group()
+ : tr::lng_edit_channel_status()),
+ *button.st,
+ { &st::menuBlueIconEmojiStatus });
const auto raw = result.data();
const auto right = Ui::CreateChild(raw);
@@ -834,7 +870,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
[=] { right->update(); })
: nullptr;
right->resize(
- (id ? emojiWidth : noneWidth) + added,
+ (id ? button.emojiWidth : button.noneWidth) + button.added,
right->height());
right->update();
}, right->lifetime());
@@ -845,7 +881,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
) | rpl::start_with_next([=](QSize outer, int width) {
right->resize(width, outer.height());
const auto skip = st::settingsButton.padding.right();
- right->moveToRight(skip - added, 0, outer.width());
+ right->moveToRight(skip - button.added, 0, outer.width());
}, right->lifetime());
right->paintRequest(
@@ -861,14 +897,18 @@ int ColorSelector::resizeGetHeight(int newWidth) {
st::stickerPanPremium1,
st::stickerPanPremium2,
0.5),
- .position = QPoint(added, (height - emojiSize) / 2),
+ .position = QPoint(
+ button.added,
+ (height - button.emojiWidth) / 2),
});
} else {
const auto &font = st::normalFont;
p.setFont(font);
p.setPen(st::windowActiveTextFg);
p.drawText(
- QPoint(added, (height - font->height) / 2 + font->ascent),
+ QPoint(
+ button.added,
+ (height - font->height) / 2 + font->ascent),
tr::lng_settings_color_emoji_off(tr::now));
}
}, right->lifetime());
@@ -889,6 +929,116 @@ int ColorSelector::resizeGetHeight(int newWidth) {
return result;
}
+[[nodiscard]] object_ptr CreateEmojiPackButton(
+ not_null parent,
+ std::shared_ptr show,
+ not_null channel) {
+ Expects(channel->mgInfo != nullptr);
+
+ const auto button = ButtonStyleWithRightEmoji(parent);
+ auto result = Settings::CreateButtonWithIcon(
+ parent,
+ tr::lng_group_emoji(),
+ *button.st,
+ { &st::menuBlueIconEmojiPack });
+ const auto raw = result.data();
+
+ struct State {
+ DocumentData *icon = nullptr;
+ std::unique_ptr custom;
+ QImage cache;
+ };
+ const auto state = parent->lifetime().make_state();
+
+ const auto right = Ui::CreateChild(raw);
+ right->show();
+ right->resize(
+ button.emojiWidth + button.added,
+ right->height());
+
+ rpl::combine(
+ raw->sizeValue(),
+ right->widthValue()
+ ) | rpl::start_with_next([=](QSize outer, int width) {
+ right->resize(width, outer.height());
+ const auto skip = st::settingsButton.padding.right();
+ right->moveToRight(skip - button.added, 0, outer.width());
+ }, right->lifetime());
+
+ right->paintRequest(
+ ) | rpl::filter([=] {
+ return state->icon != nullptr;
+ }) | rpl::start_with_next([=] {
+ auto p = QPainter(right);
+ const auto x = button.added;
+ const auto y = (right->height() - button.emojiWidth) / 2;
+ const auto active = right->window()->isActiveWindow();
+ if (const auto emoji = state->icon) {
+ if (!state->custom
+ && emoji->sticker()
+ && emoji->sticker()->setType == Data::StickersType::Emoji) {
+ auto &manager = emoji->owner().customEmojiManager();
+ state->custom = manager.create(
+ emoji->id,
+ [=] { right->update(); },
+ {});
+ }
+ if (state->custom) {
+ state->custom->paint(p, Ui::Text::CustomEmoji::Context{
+ .textColor = st::windowFg->c,
+ .now = crl::now(),
+ .position = { x, y },
+ .paused = !active,
+ });
+ }
+ }
+ }, right->lifetime());
+
+ raw->setClickedCallback([=] {
+ const auto isEmoji = true;
+ show->showBox(Box(show, channel, isEmoji));
+ });
+
+ channel->session().changes().peerFlagsValue(
+ channel,
+ Data::PeerUpdate::Flag::EmojiSet
+ ) | rpl::map([=]() -> rpl::producer {
+ const auto id = channel->mgInfo->emojiSet.id;
+ if (!id) {
+ return rpl::single(nullptr);
+ }
+ const auto sets = &channel->owner().stickers().sets();
+ auto wrapLoaded = [=](Data::StickersSets::const_iterator it) {
+ return it->second->lookupThumbnailDocument();
+ };
+ const auto it = sets->find(id);
+ if (it != sets->cend()
+ && !(it->second->flags & Data::StickersSetFlag::NotLoaded)) {
+ return rpl::single(wrapLoaded(it));
+ }
+ return rpl::single(
+ nullptr
+ ) | rpl::then(channel->owner().stickers().updated(
+ Data::StickersType::Emoji
+ ) | rpl::filter([=] {
+ const auto it = sets->find(id);
+ return (it != sets->cend())
+ && !(it->second->flags & Data::StickersSetFlag::NotLoaded);
+ }) | rpl::map([=] {
+ return wrapLoaded(sets->find(id));
+ }));
+ }) | rpl::flatten_latest(
+ ) | rpl::start_with_next([=](DocumentData *icon) {
+ if (state->icon != icon) {
+ state->icon = icon;
+ state->custom = nullptr;
+ right->update();
+ }
+ }, right->lifetime());
+
+ return result;
+}
+
} // namespace
void EditPeerColorBox(
@@ -897,7 +1047,12 @@ void EditPeerColorBox(
not_null peer,
std::shared_ptr style,
std::shared_ptr theme) {
- box->setTitle(tr::lng_settings_color_title());
+ const auto group = peer->isMegagroup();
+ const auto container = box->verticalLayout();
+
+ box->setTitle(peer->isSelf()
+ ? tr::lng_settings_color_title()
+ : tr::lng_edit_channel_color());
box->setWidth(st::boxWideWidth);
struct State {
@@ -914,53 +1069,94 @@ void EditPeerColorBox(
state->emojiId = peer->backgroundEmojiId();
state->statusId = peer->emojiStatusId();
- box->addRow(object_ptr(
- box,
- style,
- theme,
- peer,
- state->index.value(),
- state->emojiId.value()
- ), {});
+ if (group) {
+ const auto divider = Ui::CreateChild(
+ box.get());
+ const auto verticalLayout = box->verticalLayout()->add(
+ object_ptr(box.get()));
- auto indices = peer->session().api().peerColors().suggestedValue();
- const auto margin = st::settingsColorRadioMargin;
- const auto skip = st::settingsColorRadioSkip;
- box->addRow(
- object_ptr(
+ auto icon = CreateLottieIcon(
+ verticalLayout,
+ {
+ .name = u"palette"_q,
+ .sizeOverride = {
+ st::settingsCloudPasswordIconSize,
+ st::settingsCloudPasswordIconSize,
+ },
+ },
+ st::peerAppearanceIconPadding);
+ box->setShowFinishedCallback([animate = std::move(icon.animate)] {
+ animate(anim::repeat::once);
+ });
+ verticalLayout->add(std::move(icon.widget));
+ verticalLayout->add(
+ object_ptr(
+ verticalLayout,
+ tr::lng_boost_group_about(),
+ st::peerAppearanceCoverLabel),
+ st::peerAppearanceCoverLabelMargin);
+
+ verticalLayout->geometryValue(
+ ) | rpl::start_with_next([=](const QRect &r) {
+ divider->setGeometry(r);
+ }, divider->lifetime());
+ } else {
+ box->addRow(object_ptr(
box,
style,
- std::move(indices),
- state->index.current(),
- [=](uint8 index) { state->index = index; }),
- { margin, skip, margin, skip });
+ theme,
+ peer,
+ state->index.value(),
+ state->emojiId.value()
+ ), {});
- const auto container = box->verticalLayout();
- Ui::AddDividerText(container, peer->isSelf()
- ? tr::lng_settings_color_about()
- : tr::lng_settings_color_about_channel());
+ auto indices = peer->session().api().peerColors().suggestedValue();
+ const auto margin = st::settingsColorRadioMargin;
+ const auto skip = st::settingsColorRadioSkip;
+ box->addRow(
+ object_ptr(
+ box,
+ style,
+ std::move(indices),
+ state->index.current(),
+ [=](uint8 index) { state->index = index; }),
+ { margin, skip, margin, skip });
- Ui::AddSkip(container, st::settingsColorSampleSkip);
+ Ui::AddDividerText(
+ container,
+ (peer->isSelf()
+ ? tr::lng_settings_color_about()
+ : tr::lng_settings_color_about_channel()),
+ st::peerAppearanceDividerTextMargin);
- container->add(CreateEmojiIconButton(
- container,
- show,
- style,
- state->index.value(),
- state->emojiId.value(),
- [=](DocumentId id) { state->emojiId = id; }));
+ Ui::AddSkip(container, st::settingsColorSampleSkip);
- Ui::AddSkip(container, st::settingsColorSampleSkip);
- Ui::AddDividerText(container, peer->isSelf()
- ? tr::lng_settings_color_emoji_about()
- : tr::lng_settings_color_emoji_about_channel());
+ container->add(CreateEmojiIconButton(
+ container,
+ show,
+ style,
+ state->index.value(),
+ state->emojiId.value(),
+ [=](DocumentId id) { state->emojiId = id; }));
+
+ Ui::AddSkip(container, st::settingsColorSampleSkip);
+ Ui::AddDividerText(
+ container,
+ (peer->isSelf()
+ ? tr::lng_settings_color_emoji_about()
+ : tr::lng_settings_color_emoji_about_channel()),
+ st::peerAppearanceDividerTextMargin);
+ }
if (const auto channel = peer->asChannel()) {
Ui::AddSkip(container, st::settingsColorSampleSkip);
- container->add(object_ptr(
+ Settings::AddButtonWithIcon(
container,
- tr::lng_edit_channel_wallpaper(),
- st::settingsButtonNoIcon)
+ (group
+ ? tr::lng_edit_channel_wallpaper_group()
+ : tr::lng_edit_channel_wallpaper()),
+ st::peerAppearanceButton,
+ { &st::menuBlueIconWallpaper }
)->setClickedCallback([=] {
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (const auto strong = show->resolveWindow(usage)) {
@@ -971,7 +1167,25 @@ void EditPeerColorBox(
Ui::AddSkip(container, st::settingsColorSampleSkip);
Ui::AddDividerText(
container,
- tr::lng_edit_channel_wallpaper_about());
+ (group
+ ? tr::lng_edit_channel_wallpaper_about_group()
+ : tr::lng_edit_channel_wallpaper_about()),
+ st::peerAppearanceDividerTextMargin);
+
+ if (group) {
+ Ui::AddSkip(container, st::settingsColorSampleSkip);
+
+ container->add(CreateEmojiPackButton(
+ container,
+ show,
+ channel));
+
+ Ui::AddSkip(container, st::settingsColorSampleSkip);
+ Ui::AddDividerText(
+ container,
+ tr::lng_group_emoji_description(),
+ st::peerAppearanceDividerTextMargin);
+ }
// Preload exceptions list.
const auto peerPhoto = &channel->session().api().peerPhoto();
@@ -992,10 +1206,16 @@ void EditPeerColorBox(
state->statusId = id;
state->statusUntil = until;
state->statusChanged = true;
- }));
+ },
+ group));
Ui::AddSkip(container, st::settingsColorSampleSkip);
- Ui::AddDividerText(container, tr::lng_edit_channel_status_about());
+ Ui::AddDividerText(
+ container,
+ (group
+ ? tr::lng_edit_channel_status_about_group()
+ : tr::lng_edit_channel_status_about()),
+ st::peerAppearanceDividerTextMargin);
}
box->addButton(tr::lng_settings_apply(), [=] {
@@ -1020,19 +1240,11 @@ void EditPeerColorBox(
});
}
-void AddPeerColorButton(
- not_null container,
- std::shared_ptr show,
- not_null peer) {
- auto label = peer->isSelf()
- ? tr::lng_settings_theme_name_color()
- : tr::lng_edit_channel_color();
- const auto button = AddButtonWithIcon(
- container,
- rpl::duplicate(label),
- st::settingsColorButton,
- { &st::menuIconChangeColors });
-
+void SetupPeerColorSample(
+ not_null